summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFeng Pan <fpan@redhat.com>2017-09-13 18:25:34 +0000
committerGerrit Code Review <gerrit@opnfv.org>2017-09-13 18:25:34 +0000
commit7b3a8455baa35bdde4d7e078d88f9b17a6df9195 (patch)
tree0d0617382f42d30d4bb0a9a99248cb100006554c
parent071de3a4a11326d5f0c371a2ebeed703f1a40980 (diff)
parentcb606f45e3852432787ed895dc55665caa950161 (diff)
Merge "Migrates clean to python"
-rw-r--r--apex/clean.py83
-rw-r--r--apex/common/exceptions.py8
-rw-r--r--apex/common/parsers.py25
-rw-r--r--apex/network/jumphost.py236
-rw-r--r--apex/tests/config/bad_ifcfg-br-external8
-rw-r--r--apex/tests/config/bad_nova_output.json23
-rw-r--r--apex/tests/config/ifcfg-br-dummy9
-rw-r--r--apex/tests/config/ifcfg-br-external10
-rw-r--r--apex/tests/config/ifcfg-dummy7
-rw-r--r--apex/tests/test_apex_clean.py69
-rw-r--r--apex/tests/test_apex_common_parsers.py21
-rw-r--r--apex/tests/test_apex_network_jumphost.py276
-rw-r--r--build/rpm_specs/opnfv-apex-common.spec3
-rwxr-xr-xci/clean.sh215
-rw-r--r--setup.cfg1
15 files changed, 716 insertions, 278 deletions
diff --git a/apex/clean.py b/apex/clean.py
index af9e8ce0..81ae1770 100644
--- a/apex/clean.py
+++ b/apex/clean.py
@@ -7,16 +7,21 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-# Clean will eventually be migrated to this file
-
import argparse
+import fileinput
+import libvirt
import logging
import os
import pyipmi
import pyipmi.interfaces
import sys
-from .common import utils
+from apex.common import (
+ constants,
+ utils)
+from apex.network import jumphost
+from apex.common.exceptions import ApexCleanException
+from virtualbmc import manager as vbmc_lib
def clean_nodes(inventory):
@@ -41,11 +46,59 @@ def clean_nodes(inventory):
sys.exit(1)
+def clean_vbmcs():
+ vbmc_manager = vbmc_lib.VirtualBMCManager()
+ vbmcs = vbmc_manager.list()
+ for vbmc in vbmcs:
+ logging.info("Deleting vbmc: {}".format(vbmc['domain_name']))
+ vbmc_manager.delete(vbmc['domain_name'])
+
+
+def clean_vms():
+ logging.info('Destroying all Apex VMs')
+ conn = libvirt.open('qemu:///system')
+ if not conn:
+ raise ApexCleanException('Unable to open libvirt connection')
+ pool = conn.storagePoolLookupByName('default')
+ domains = conn.listAllDomains()
+
+ for domain in domains:
+ vm = domain.name()
+ if vm != 'undercloud' and not vm.startswith('baremetal'):
+ continue
+ logging.info("Cleaning domain: {}".format(vm))
+ if domain.isActive():
+ logging.debug('Destroying domain')
+ domain.destroy()
+ domain.undefine()
+ # delete storage volume
+ try:
+ stgvol = pool.storageVolLookupByName("{}.qcow2".format(vm))
+ except libvirt.libvirtError:
+ logging.warning("Skipping volume cleanup as volume not found for "
+ "vm: {}".format(vm))
+ stgvol = None
+ if stgvol:
+ logging.info('Deleting storage volume')
+ stgvol.wipe(0)
+ stgvol.delete(0)
+ pool.refresh()
+
+
+def clean_ssh_keys(key_file='/root/.ssh/authorized_keys'):
+ logging.info('Removing any stack pub keys from root authorized keys')
+ for line in fileinput.input(key_file, inplace=True):
+ line = line.strip('\n')
+ if 'stack@undercloud' not in line:
+ print(line)
+
+
def main():
clean_parser = argparse.ArgumentParser()
clean_parser.add_argument('-f',
dest='inv_file',
- required=True,
+ required=False,
+ default=None,
help='File which contains inventory')
args = clean_parser.parse_args(sys.argv[1:])
os.makedirs(os.path.dirname('./apex_clean.log'), exist_ok=True)
@@ -58,8 +111,28 @@ def main():
console.setLevel(logging.DEBUG)
console.setFormatter(logging.Formatter(formatter))
logging.getLogger('').addHandler(console)
- clean_nodes(args.inv_file)
+ if args.inv_file:
+ if not os.path.isfile(args.inv_file):
+ logging.error("Inventory file not found: {}".format(args.inv_file))
+ raise FileNotFoundError("Inventory file does not exist")
+ else:
+ logging.info("Shutting down baremetal nodes")
+ clean_nodes(args.inv_file)
+ # Delete all VMs
+ clean_vms()
+ # Delete vbmc
+ clean_vbmcs()
+ # Clean network config
+ for network in constants.ADMIN_NETWORK, constants.EXTERNAL_NETWORK:
+ logging.info("Cleaning Jump Host Network config for network "
+ "{}".format(network))
+ jumphost.detach_interface_from_ovs(network)
+ jumphost.remove_ovs_bridge(network)
+
+ # clean pub keys from root's auth keys
+ clean_ssh_keys()
+ logging.info('Apex clean complete!')
if __name__ == '__main__':
main()
diff --git a/apex/common/exceptions.py b/apex/common/exceptions.py
index c660213f..54d99834 100644
--- a/apex/common/exceptions.py
+++ b/apex/common/exceptions.py
@@ -10,3 +10,11 @@
class ApexDeployException(Exception):
pass
+
+
+class JumpHostNetworkException(Exception):
+ pass
+
+
+class ApexCleanException(Exception):
+ pass
diff --git a/apex/common/parsers.py b/apex/common/parsers.py
index 8744c862..91b8905b 100644
--- a/apex/common/parsers.py
+++ b/apex/common/parsers.py
@@ -71,3 +71,28 @@ def parse_overcloudrc(in_file):
logging.debug("os cred not found in: {}".format(line))
return creds
+
+
+def parse_ifcfg_file(in_file):
+ """
+ Parses ifcfg file information
+ :param in_file:
+ :return: dictionary of ifcfg key value pairs
+ """
+ ifcfg_params = {
+ 'IPADDR': '',
+ 'NETMASK': '',
+ 'GATEWAY': '',
+ 'METRIC': '',
+ 'DNS1': '',
+ 'DNS2': '',
+ 'PREFIX': ''
+ }
+ with open(in_file, 'r') as fh:
+ for line in fh:
+ for param in ifcfg_params.keys():
+ match = re.search("^\s*{}=(.*)$".format(param), line)
+ if match:
+ ifcfg_params[param] = match.group(1)
+ break
+ return ifcfg_params
diff --git a/apex/network/jumphost.py b/apex/network/jumphost.py
index f3f06ad6..2ecb7f4e 100644
--- a/apex/network/jumphost.py
+++ b/apex/network/jumphost.py
@@ -9,11 +9,11 @@
import logging
import os
-import re
import shutil
import subprocess
-from apex.common.exceptions import ApexDeployException
+from apex.common.exceptions import JumpHostNetworkException
+from apex.common import parsers
from apex.network import ip_utils
NET_MAP = {
@@ -24,6 +24,8 @@ NET_MAP = {
'api': 'br-api'
}
+NET_CFG_PATH = '/etc/sysconfig/network-scripts'
+
def configure_bridges(ns):
"""
@@ -68,81 +70,98 @@ def configure_bridges(ns):
except subprocess.CalledProcessError:
logging.error("Unable to configure IP address on "
"bridge {}".format(NET_MAP[network]))
+ raise
-def attach_interface_to_ovs(bridge, interface, network):
+def generate_ifcfg_params(if_file, network):
"""
- Attaches jumphost interface to OVS for baremetal deployments
- :param bridge: bridge to attach to
- :param interface: interface to attach to bridge
- :param network: Apex network type for these interfaces
- :return: None
+ Generates and validates ifcfg parameters required for a network
+ :param if_file: ifcfg file to parse
+ :param network: Apex network
+ :return: dictionary of generated/validated ifcfg params
"""
+ ifcfg_params = parsers.parse_ifcfg_file(if_file)
+ if not ifcfg_params['IPADDR']:
+ logging.error("IPADDR missing in {}".format(if_file))
+ raise JumpHostNetworkException("IPADDR missing in {}".format(if_file))
+ if not (ifcfg_params['NETMASK'] or ifcfg_params['PREFIX']):
+ logging.error("NETMASK/PREFIX missing in {}".format(if_file))
+ raise JumpHostNetworkException("NETMASK/PREFIX missing in {}".format(
+ if_file))
+ if network == 'external' and not ifcfg_params['GATEWAY']:
+ logging.error("GATEWAY is required to be in {} for external "
+ "network".format(if_file))
+ raise JumpHostNetworkException("GATEWAY is required to be in {} for "
+ "external network".format(if_file))
- net_cfg_path = '/etc/sysconfig/network-scripts'
- if_file = os.path.join(net_cfg_path, "ifcfg-{}".format(interface))
- ovs_file = os.path.join(net_cfg_path, "ifcfg-{}".format(bridge))
+ if ifcfg_params['DNS1'] or ifcfg_params['DNS2']:
+ ifcfg_params['PEERDNS'] = 'yes'
+ else:
+ ifcfg_params['PEERDNS'] = 'no'
+ return ifcfg_params
- logging.info("Attaching interface: {} to bridge: {} on network {}".format(
- bridge, interface, network
- ))
+def is_ovs_bridge(bridge):
+ """
+ Finds an OVS bridge
+ :param bridge: OVS bridge to find
+ :return: boolean if OVS bridge exists
+ """
try:
output = subprocess.check_output(['ovs-vsctl', 'show'],
stderr=subprocess.STDOUT)
if bridge not in output.decode('utf-8'):
- logging.debug("Bridge {} not found. Creating...".format(bridge))
- subprocess.check_call(['ovs-vsctl', 'add-br', bridge])
+ logging.debug("Bridge {} not found".format(bridge))
+ return False
else:
logging.debug("Bridge {} found".format(bridge))
+ return True
except subprocess.CalledProcessError:
- logging.error("Unable to validate/create OVS bridge {}".format(bridge))
+ logging.error("Unable to validate OVS bridge {}".format(bridge))
raise
+
+
+def dump_ovs_ports(bridge):
+ """
+ Returns
+ :param bridge: OVS bridge to list ports
+ :return: list of ports
+ """
try:
output = subprocess.check_output(['ovs-vsctl', 'list-ports', bridge],
stderr=subprocess.STDOUT)
- if interface in output.decode('utf-8'):
- logging.debug("Interface already attached to bridge")
- return
- except subprocess.CalledProcessError as e:
- logging.error("Unable to dump ports for bridge: {}".format(bridge))
- logging.error("Error output: {}".format(e.output))
+ except subprocess.CalledProcessError:
+ logging.error("Unable to show ports for {}".format(bridge))
raise
+ return output.decode('utf-8').strip().split('\n')
- if not os.path.isfile(if_file):
- logging.error("Interface ifcfg not found: {}".format(if_file))
- raise FileNotFoundError("Interface file missing: {}".format(if_file))
- ifcfg_params = {
- 'IPADDR': '',
- 'NETMASK': '',
- 'GATEWAY': '',
- 'METRIC': '',
- 'DNS1': '',
- 'DNS2': '',
- 'PREFIX': ''
- }
- with open(if_file, 'r') as fh:
- interface_output = fh.read()
-
- for param in ifcfg_params.keys():
- match = re.search("{}=(.*)\n".format(param), interface_output)
- if match:
- ifcfg_params[param] = match.group(1)
+def attach_interface_to_ovs(bridge, interface, network):
+ """
+ Attaches jumphost interface to OVS for baremetal deployments
+ :param bridge: bridge to attach to
+ :param interface: interface to attach to bridge
+ :param network: Apex network type for these interfaces
+ :return: None
+ """
- if not ifcfg_params['IPADDR']:
- logging.error("IPADDR missing in {}".format(if_file))
- raise ApexDeployException("IPADDR missing in {}".format(if_file))
- if not (ifcfg_params['NETMASK'] or ifcfg_params['PREFIX']):
- logging.error("NETMASK/PREFIX missing in {}".format(if_file))
- raise ApexDeployException("NETMASK/PREFIX missing in {}".format(
- if_file))
- if network == 'external' and not ifcfg_params['GATEWAY']:
- logging.error("GATEWAY is required to be in {} for external "
- "network".format(if_file))
- raise ApexDeployException("GATEWAY is required to be in {} for "
- "external network".format(if_file))
+ if_file = os.path.join(NET_CFG_PATH, "ifcfg-{}".format(interface))
+ ovs_file = os.path.join(NET_CFG_PATH, "ifcfg-{}".format(bridge))
+
+ logging.info("Attaching interface: {} to bridge: {} on network {}".format(
+ bridge, interface, network
+ ))
+ if not is_ovs_bridge(bridge):
+ subprocess.check_call(['ovs-vsctl', 'add-br', bridge])
+ elif interface in dump_ovs_ports(bridge):
+ logging.debug("Interface already attached to bridge")
+ return
+
+ if not os.path.isfile(if_file):
+ logging.error("Interface ifcfg not found: {}".format(if_file))
+ raise FileNotFoundError("Interface file missing: {}".format(if_file))
+ ifcfg_params = generate_ifcfg_params(if_file, network)
shutil.move(if_file, "{}.orig".format(if_file))
if_content = """DEVICE={}
DEVICETYPE=ovs
@@ -160,13 +179,9 @@ BOOTPROTO=static
ONBOOT=yes
TYPE=OVSBridge
PROMISC=yes""".format(bridge)
- peer_dns = 'no'
for param, value in ifcfg_params.items():
if value:
bridge_content += "\n{}={}".format(param, value)
- if param == 'DNS1' or param == 'DNS2':
- peer_dns = 'yes'
- bridge_content += "\n{}={}".format('PEERDNS', peer_dns)
logging.debug("New interface file content:\n{}".format(if_content))
logging.debug("New bridge file content:\n{}".format(bridge_content))
@@ -181,3 +196,108 @@ PROMISC=yes""".format(bridge)
except subprocess.CalledProcessError:
logging.error("Failed to restart Linux networking")
raise
+
+
+def detach_interface_from_ovs(network):
+ """
+ Detach interface from OVS for baremetal deployments
+ :param network: Apex network to detach single interface from
+ :return: None
+ """
+
+ bridge = NET_MAP[network]
+ logging.debug("Detaching interfaces from bridge on network: {}".format(
+ network))
+ # ensure bridge exists
+ if not is_ovs_bridge(bridge):
+ return
+
+ # check if real port is on bridge
+ for interface in dump_ovs_ports(bridge):
+ if interface and not interface.startswith('vnet'):
+ logging.debug("Interface found: {}".format(interface))
+ real_interface = interface
+ break
+ else:
+ logging.info("No jumphost interface exists on bridge {}".format(
+ bridge))
+ return
+
+ # check if original backup ifcfg file exists or create
+ orig_ifcfg_file = os.path.join(NET_CFG_PATH,
+ "ifcfg-{}.orig".format(real_interface))
+ ifcfg_file = orig_ifcfg_file[:-len('.orig')]
+ if os.path.isfile(orig_ifcfg_file):
+ logging.debug("Original interface file found: "
+ "{}".format(orig_ifcfg_file))
+ shutil.move(orig_ifcfg_file, ifcfg_file)
+ else:
+ logging.info("No original ifcfg file found...will attempt to use "
+ "bridge icfg file and re-create")
+ bridge_ifcfg_file = os.path.join(NET_CFG_PATH,
+ "ifcfg-{}".format(bridge))
+ if os.path.isfile(bridge_ifcfg_file):
+ ifcfg_params = generate_ifcfg_params(bridge_ifcfg_file, network)
+ if_content = """DEVICE={}
+BOOTPROTO=static
+ONBOOT=yes
+TYPE=Ethernet
+NM_CONTROLLED=no""".format(real_interface)
+ for param, value in ifcfg_params.items():
+ if value:
+ if_content += "\n{}={}".format(param, value)
+ logging.debug("Interface file content:\n{}".format(if_content))
+ # write original backup
+ with open(orig_ifcfg_file, 'w') as fh:
+ fh.write(if_content)
+ logging.debug("Original interface file created: "
+ "{}".format(orig_ifcfg_file))
+ else:
+ logging.error("Unable to find original interface config file: {} "
+ "or bridge config file:{}".format(orig_ifcfg_file,
+ bridge_ifcfg_file))
+ raise FileNotFoundError("Unable to locate bridge or original "
+ "interface ifcfg file")
+
+ # move original file back and rewrite bridge ifcfg
+ shutil.move(orig_ifcfg_file, ifcfg_file)
+ bridge_content = """DEVICE={}
+DEVICETYPE=ovs
+BOOTPROTO=static
+ONBOOT=yes
+TYPE=OVSBridge
+PROMISC=yes""".format(bridge)
+ with open(bridge_ifcfg_file, 'w') as fh:
+ fh.write(bridge_content)
+ # restart linux networking
+ logging.info("Restarting Linux networking")
+ try:
+ subprocess.check_call(['systemctl', 'restart', 'network'])
+ except subprocess.CalledProcessError:
+ logging.error("Failed to restart Linux networking")
+ raise
+
+
+def remove_ovs_bridge(network):
+ """
+ Unconfigure and remove an OVS bridge
+ :param network: Apex network to remove OVS bridge for
+ :return:
+ """
+ bridge = NET_MAP[network]
+ if is_ovs_bridge(bridge):
+ logging.info("Removing bridge: {}".format(bridge))
+ try:
+ subprocess.check_call(['ovs-vsctl', 'del-br', bridge])
+ except subprocess.CalledProcessError:
+ logging.error('Unable to destroy OVS bridge')
+ raise
+
+ logging.debug('Bridge destroyed')
+ bridge_ifcfg_file = os.path.join(NET_CFG_PATH,
+ "ifcfg-{}".format(bridge))
+ if os.path.isfile(bridge_ifcfg_file):
+ os.remove(bridge_ifcfg_file)
+ logging.debug("Bridge ifcfg file removed: {}".format)
+ else:
+ logging.debug('Bridge ifcfg file not found')
diff --git a/apex/tests/config/bad_ifcfg-br-external b/apex/tests/config/bad_ifcfg-br-external
new file mode 100644
index 00000000..85b81959
--- /dev/null
+++ b/apex/tests/config/bad_ifcfg-br-external
@@ -0,0 +1,8 @@
+DEVICE=br-external
+DEVICETYPE=ovs
+BOOTPROTO=static
+ONBOOT=yes
+TYPE=OVSBridge
+PROMISC=yes
+IPADDR=172.30.9.66
+NETMASK=255.255.255.0
diff --git a/apex/tests/config/bad_nova_output.json b/apex/tests/config/bad_nova_output.json
new file mode 100644
index 00000000..137750e5
--- /dev/null
+++ b/apex/tests/config/bad_nova_output.json
@@ -0,0 +1,23 @@
+[
+ {
+ "Status": "ACTIVE",
+ "Networks": "",
+ "ID": "a5ff8aeb-5fd0-467f-9d89-791dfbc6267b",
+ "Image Name": "overcloud-full",
+ "Name": "test3"
+ },
+ {
+ "Status": "ACTIVE",
+ "Networks": "",
+ "ID": "c8be26ae-6bef-4841-bb03-c7f336cfd785",
+ "Image Name": "overcloud-full",
+ "Name": "test2"
+ },
+ {
+ "Status": "ACTIVE",
+ "Networks": "",
+ "ID": "105d1c61-78d3-498f-9191-6b21823b8544",
+ "Image Name": "overcloud-full",
+ "Name": "test1"
+ }
+]
diff --git a/apex/tests/config/ifcfg-br-dummy b/apex/tests/config/ifcfg-br-dummy
new file mode 100644
index 00000000..117ca726
--- /dev/null
+++ b/apex/tests/config/ifcfg-br-dummy
@@ -0,0 +1,9 @@
+DEVICE=br-dummy
+DEVICETYPE=ovs
+BOOTPROTO=static
+ONBOOT=yes
+TYPE=OVSBridge
+PROMISC=yes
+IPADDR=152.30.9.11
+NETMASK=255.255.255.0
+PEERDNS=no \ No newline at end of file
diff --git a/apex/tests/config/ifcfg-br-external b/apex/tests/config/ifcfg-br-external
new file mode 100644
index 00000000..9717d6e3
--- /dev/null
+++ b/apex/tests/config/ifcfg-br-external
@@ -0,0 +1,10 @@
+DEVICE=br-external
+DEVICETYPE=ovs
+BOOTPROTO=static
+ONBOOT=yes
+TYPE=OVSBridge
+PROMISC=yes
+IPADDR=172.30.9.66
+NETMASK=255.255.255.0
+GATEWAY=172.30.9.1
+#DNS1=1.1.1.1
diff --git a/apex/tests/config/ifcfg-dummy b/apex/tests/config/ifcfg-dummy
new file mode 100644
index 00000000..f9ca21d4
--- /dev/null
+++ b/apex/tests/config/ifcfg-dummy
@@ -0,0 +1,7 @@
+DEVICE=enpfakes0
+TYPE=Ethernet
+ONBOOT=yes
+BOOTPROTO=static
+NM_CONTROLLED=no
+IPADDR=152.30.9.11
+NETMASK=255.255.255.0
diff --git a/apex/tests/test_apex_clean.py b/apex/tests/test_apex_clean.py
index 7b7df512..b6b9d428 100644
--- a/apex/tests/test_apex_clean.py
+++ b/apex/tests/test_apex_clean.py
@@ -8,12 +8,48 @@
##############################################################################
import mock
+import os
import pyipmi
import pyipmi.chassis
from mock import patch
-from nose import tools
+from nose.tools import (
+ assert_raises,
+ assert_equal
+)
from apex import clean_nodes
+from apex import clean
+from apex.tests import constants as con
+
+
+class dummy_domain:
+
+ def isActive(self):
+ return True
+
+ def destroy(self):
+ pass
+
+ def undefine(self):
+ pass
+
+
+class dummy_vol:
+
+ def wipe(self, *args):
+ pass
+
+ def delete(self, *args):
+ pass
+
+
+class dummy_pool:
+
+ def storageVolLookupByName(self, *args, **kwargs):
+ return dummy_vol()
+
+ def refresh(self):
+ pass
class TestClean:
@@ -31,11 +67,36 @@ class TestClean:
def teardown(self):
"""This method is run once after _each_ test method is executed"""
- def test_clean(self):
+ def test_clean_nodes(self):
with mock.patch.object(pyipmi.Session, 'establish') as mock_method:
with patch.object(pyipmi.chassis.Chassis,
'chassis_control_power_down') as mock_method2:
clean_nodes('apex/tests/config/inventory.yaml')
- tools.assert_equal(mock_method.call_count, 5)
- tools.assert_equal(mock_method2.call_count, 5)
+ assert_equal(mock_method.call_count, 5)
+ assert_equal(mock_method2.call_count, 5)
+
+ @patch('virtualbmc.manager.VirtualBMCManager.list',
+ return_value=[{'domain_name': 'dummy1'}, {'domain_name': 'dummy2'}])
+ @patch('virtualbmc.manager.VirtualBMCManager.delete')
+ def test_vmbc_clean(self, vbmc_del_func, vbmc_list_func):
+ assert clean.clean_vbmcs() is None
+
+ def test_clean_ssh_keys(self):
+ ssh_file = os.path.join(con.TEST_DUMMY_CONFIG, 'authorized_dummy')
+ with open(ssh_file, 'w') as fh:
+ fh.write('ssh-rsa 2LwlofGD8rNUFAlafY2/oUsKOf1mQ1 stack@undercloud')
+ assert clean.clean_ssh_keys(ssh_file) is None
+ with open(ssh_file, 'r') as fh:
+ output = fh.read()
+ assert 'stack@undercloud' not in output
+ if os.path.isfile(ssh_file):
+ os.remove(ssh_file)
+
+ @patch('libvirt.open')
+ def test_clean_vms(self, mock_libvirt):
+ ml = mock_libvirt.return_value
+ ml.storagePoolLookupByName.return_value = dummy_pool()
+ ml.listDefinedDomains.return_value = ['undercloud']
+ ml.lookupByName.return_value = dummy_domain()
+ assert clean.clean_vms() is None
diff --git a/apex/tests/test_apex_common_parsers.py b/apex/tests/test_apex_common_parsers.py
index bed2a8c5..d272a749 100644
--- a/apex/tests/test_apex_common_parsers.py
+++ b/apex/tests/test_apex_common_parsers.py
@@ -11,9 +11,11 @@ import os
from apex.tests import constants as con
from apex.common import parsers as apex_parsers
+from apex.common.exceptions import ApexDeployException
from nose.tools import (
assert_is_instance,
- assert_dict_equal
+ assert_dict_equal,
+ assert_raises
)
@@ -41,9 +43,13 @@ class TestCommonParsers:
'overcloud-novacompute-0': '192.30.9.10',
'overcloud-novacompute-1': '192.30.9.9'
}
- print(output)
assert_dict_equal(output, nodes)
+ def test_negative_parse_nova_output(self):
+ assert_raises(ApexDeployException, apex_parsers.parse_nova_output,
+ os.path.join(con.TEST_DUMMY_CONFIG,
+ 'bad_nova_output.json'))
+
def test_parse_overcloudrc(self):
output = apex_parsers.parse_overcloudrc(
os.path.join(con.TEST_DUMMY_CONFIG, 'test_overcloudrc'))
@@ -52,3 +58,14 @@ class TestCommonParsers:
assert output['OS_AUTH_TYPE'] == 'password'
assert 'OS_PASSWORD' in output.keys()
assert output['OS_PASSWORD'] == 'Wd8ruyf6qG8cmcms6dq2HM93f'
+
+ def test_parse_ifcfg(self):
+ output = apex_parsers.parse_ifcfg_file(
+ os.path.join(con.TEST_DUMMY_CONFIG, 'ifcfg-br-external'))
+ assert_is_instance(output, dict)
+ assert 'IPADDR' in output.keys()
+ assert output['IPADDR'] == '172.30.9.66'
+ assert 'NETMASK' in output.keys()
+ assert output['NETMASK'] == '255.255.255.0'
+ assert 'DNS1' in output.keys()
+ assert not output['DNS1']
diff --git a/apex/tests/test_apex_network_jumphost.py b/apex/tests/test_apex_network_jumphost.py
new file mode 100644
index 00000000..a23f1c56
--- /dev/null
+++ b/apex/tests/test_apex_network_jumphost.py
@@ -0,0 +1,276 @@
+##############################################################################
+# Copyright (c) 2016 Dan Radez (Red Hat)
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import os
+import shutil
+import subprocess
+
+from apex import NetworkSettings
+from apex.tests import constants as con
+from apex.common import constants as apex_constants
+from apex.network import jumphost
+from apex.common.exceptions import JumpHostNetworkException
+from ipaddress import IPv4Interface
+from mock import patch
+from nose.tools import (
+ assert_is_instance,
+ assert_dict_equal,
+ assert_raises,
+ assert_true,
+ assert_false
+)
+
+
+def bridge_show_output(*args, **kwargs):
+ return b"""
+ b6f1b54a-b8ba-4e86-9c5b-733ab71b5712
+ Bridge br-admin
+ Port br-admin
+ Interface br-admin
+ type: internal
+ ovs_version: "2.5.0"
+"""
+
+
+def bridge_port_list(*args, **kwargs):
+ return b"""
+enp6s0
+vnet1
+"""
+
+
+def subprocess_exception(*args, **kwargs):
+ raise subprocess.CalledProcessError(returncode=2, cmd='dummy')
+
+
+class TestNetworkJumpHost:
+ @classmethod
+ def setup_class(cls):
+ """This method is run once for each class before any tests are run"""
+
+ @classmethod
+ def teardown_class(cls):
+ """This method is run once for each class _after_ all tests are run"""
+
+ def setup(self):
+ """This method is run once before _each_ test method is executed"""
+
+ def teardown(self):
+ """This method is run once after _each_ test method is executed"""
+
+ @patch('subprocess.check_output', side_effect=bridge_show_output)
+ def test_is_ovs_bridge(self, bridge_output_function):
+ assert_true(jumphost.is_ovs_bridge('br-admin'))
+ assert_false(jumphost.is_ovs_bridge('br-blah'))
+
+ @patch('subprocess.check_output', side_effect=bridge_port_list)
+ def test_dump_ovs_ports(self, bridge_function):
+ output = jumphost.dump_ovs_ports('br-admin')
+ assert_is_instance(output, list)
+ assert 'enp6s0' in output
+
+ def test_generate_ifcfg_params(self):
+ output = jumphost.generate_ifcfg_params(
+ os.path.join(con.TEST_DUMMY_CONFIG, 'ifcfg-br-external'),
+ apex_constants.EXTERNAL_NETWORK)
+ assert_is_instance(output, dict)
+ assert output['IPADDR'] == '172.30.9.66'
+ assert output['PEERDNS'] == 'no'
+
+ def test_negative_generate_ifcfg_params(self):
+ assert_raises(JumpHostNetworkException, jumphost.generate_ifcfg_params,
+ os.path.join(con.TEST_DUMMY_CONFIG,
+ 'bad_ifcfg-br-external'),
+ apex_constants.EXTERNAL_NETWORK)
+
+ @patch('subprocess.check_call')
+ @patch('apex.network.ip_utils.get_interface', return_value=IPv4Interface(
+ '10.10.10.2'))
+ def test_configure_bridges_ip_exists(self, interface_function,
+ subprocess_func):
+ ns = NetworkSettings(os.path.join(con.TEST_CONFIG_DIR,
+ 'network', 'network_settings.yaml'))
+ assert jumphost.configure_bridges(ns) is None
+
+ @patch('subprocess.check_call')
+ @patch('apex.network.ip_utils.get_interface', return_value=None)
+ def test_configure_bridges_no_ip(self, interface_function,
+ subprocess_func):
+ ns = NetworkSettings(os.path.join(con.TEST_CONFIG_DIR,
+ 'network', 'network_settings.yaml'))
+ assert jumphost.configure_bridges(ns) is None
+
+ @patch('subprocess.check_call', side_effect=subprocess_exception)
+ @patch('apex.network.ip_utils.get_interface', return_value=None)
+ def test_negative_configure_bridges(self, interface_function,
+ subprocess_func):
+ ns = NetworkSettings(os.path.join(con.TEST_CONFIG_DIR,
+ 'network', 'network_settings.yaml'))
+ assert_raises(subprocess.CalledProcessError,
+ jumphost.configure_bridges, ns)
+
+ @patch('subprocess.check_call')
+ @patch('apex.network.jumphost.is_ovs_bridge', return_value=True)
+ @patch('apex.network.jumphost.dump_ovs_ports', return_value=[])
+ def test_attach_interface(self, dump_ports_func, is_bridge_func,
+ subprocess_func):
+ ifcfg_dir = con.TEST_DUMMY_CONFIG
+ shutil.copyfile(os.path.join(ifcfg_dir, 'ifcfg-dummy'),
+ os.path.join(ifcfg_dir, 'ifcfg-enpfakes0'))
+ shutil.copyfile(os.path.join(ifcfg_dir, 'ifcfg-br-dummy'),
+ os.path.join(ifcfg_dir, 'ifcfg-br-admin'))
+ jumphost.NET_CFG_PATH = ifcfg_dir
+ output = jumphost.attach_interface_to_ovs('br-admin', 'enpfakes0',
+ 'admin')
+ assert output is None
+ assert os.path.isfile(os.path.join(ifcfg_dir, 'ifcfg-enpfakes0'))
+ assert os.path.isfile(os.path.join(ifcfg_dir, 'ifcfg-br-admin'))
+ assert os.path.isfile(os.path.join(ifcfg_dir, 'ifcfg-enpfakes0.orig'))
+
+ for ifcfg in ('ifcfg-enpfakes0', 'ifcfg-enpfakes0.orig',
+ 'ifcfg-br-admin'):
+ ifcfg_path = os.path.join(ifcfg_dir, ifcfg)
+ if os.path.isfile(ifcfg_path):
+ os.remove(ifcfg_path)
+
+ @patch('subprocess.check_call')
+ @patch('apex.network.jumphost.is_ovs_bridge', return_value=True)
+ @patch('apex.network.jumphost.dump_ovs_ports', return_value=['dummy_int'])
+ def test_already_attached_interface(self, dump_ports_func, is_bridge_func,
+ subprocess_func):
+ output = jumphost.attach_interface_to_ovs('br-dummy', 'dummy_int',
+ 'admin')
+ assert output is None
+
+ @patch('subprocess.check_call')
+ @patch('apex.network.jumphost.is_ovs_bridge', return_value=True)
+ @patch('apex.network.jumphost.dump_ovs_ports', return_value=[])
+ def test_negative_attach_interface(self, dump_ports_func, is_bridge_func,
+ subprocess_func):
+ ifcfg_dir = con.TEST_DUMMY_CONFIG
+ jumphost.NET_CFG_PATH = ifcfg_dir
+ assert_raises(FileNotFoundError, jumphost.attach_interface_to_ovs,
+ 'br-dummy', 'dummy_int', 'admin')
+
+ @patch('subprocess.check_call', side_effect=subprocess_exception)
+ @patch('apex.network.jumphost.is_ovs_bridge', return_value=True)
+ @patch('apex.network.jumphost.dump_ovs_ports', return_value=[])
+ def test_negative_attach_interface_process_error(
+ self, dump_ports_func, is_bridge_func, subprocess_func):
+ ifcfg_dir = con.TEST_DUMMY_CONFIG
+ shutil.copyfile(os.path.join(ifcfg_dir, 'ifcfg-dummy'),
+ os.path.join(ifcfg_dir, 'ifcfg-enpfakes0'))
+ shutil.copyfile(os.path.join(ifcfg_dir, 'ifcfg-br-dummy'),
+ os.path.join(ifcfg_dir, 'ifcfg-br-admin'))
+ jumphost.NET_CFG_PATH = ifcfg_dir
+ assert_raises(subprocess.CalledProcessError,
+ jumphost.attach_interface_to_ovs,
+ 'br-admin', 'enpfakes0', 'admin')
+ assert os.path.isfile(os.path.join(ifcfg_dir, 'ifcfg-enpfakes0'))
+ assert os.path.isfile(os.path.join(ifcfg_dir, 'ifcfg-br-admin'))
+ assert os.path.isfile(os.path.join(ifcfg_dir, 'ifcfg-enpfakes0.orig'))
+
+ for ifcfg in ('ifcfg-enpfakes0', 'ifcfg-enpfakes0.orig',
+ 'ifcfg-br-admin'):
+ ifcfg_path = os.path.join(ifcfg_dir, ifcfg)
+ if os.path.isfile(ifcfg_path):
+ os.remove(ifcfg_path)
+
+ @patch('subprocess.check_call')
+ @patch('apex.network.jumphost.is_ovs_bridge', return_value=True)
+ @patch('apex.network.jumphost.dump_ovs_ports', return_value=['enpfakes0'])
+ def test_detach_interface(self, dump_ports_func, is_bridge_func,
+ subprocess_func):
+ ifcfg_dir = con.TEST_DUMMY_CONFIG
+ shutil.copyfile(os.path.join(ifcfg_dir, 'ifcfg-br-dummy'),
+ os.path.join(ifcfg_dir, 'ifcfg-br-admin'))
+ jumphost.NET_CFG_PATH = ifcfg_dir
+ output = jumphost.detach_interface_from_ovs('admin')
+ assert output is None
+ assert os.path.isfile(os.path.join(ifcfg_dir, 'ifcfg-enpfakes0'))
+ assert os.path.isfile(os.path.join(ifcfg_dir, 'ifcfg-br-admin'))
+
+ for ifcfg in ('ifcfg-enpfakes0', 'ifcfg-enpfakes0.orig',
+ 'ifcfg-br-admin'):
+ ifcfg_path = os.path.join(ifcfg_dir, ifcfg)
+ if os.path.isfile(ifcfg_path):
+ os.remove(ifcfg_path)
+
+ @patch('subprocess.check_call')
+ @patch('apex.network.jumphost.is_ovs_bridge', return_value=False)
+ @patch('apex.network.jumphost.dump_ovs_ports', return_value=[])
+ def test_detach_interface_no_bridge(self, dump_ports_func,
+ is_bridge_func, subprocess_func):
+ ifcfg_dir = con.TEST_DUMMY_CONFIG
+ jumphost.NET_CFG_PATH = ifcfg_dir
+ output = jumphost.detach_interface_from_ovs('admin')
+ assert output is None
+
+ @patch('subprocess.check_call')
+ @patch('apex.network.jumphost.is_ovs_bridge', return_value=True)
+ @patch('apex.network.jumphost.dump_ovs_ports', return_value=[])
+ def test_detach_interface_no_int_to_remove(self, dump_ports_func,
+ is_bridge_func,
+ subprocess_func):
+ ifcfg_dir = con.TEST_DUMMY_CONFIG
+ jumphost.NET_CFG_PATH = ifcfg_dir
+ output = jumphost.detach_interface_from_ovs('admin')
+ assert output is None
+
+ @patch('subprocess.check_call')
+ @patch('apex.network.jumphost.is_ovs_bridge', return_value=True)
+ @patch('apex.network.jumphost.dump_ovs_ports', return_value=['enpfakes0'])
+ def test_negative_detach_interface(self, dump_ports_func, is_bridge_func,
+ subprocess_func):
+ ifcfg_dir = con.TEST_DUMMY_CONFIG
+ jumphost.NET_CFG_PATH = ifcfg_dir
+ assert_raises(FileNotFoundError, jumphost.detach_interface_from_ovs,
+ 'admin')
+
+ @patch('subprocess.check_call', side_effect=subprocess_exception)
+ @patch('apex.network.jumphost.is_ovs_bridge', return_value=True)
+ @patch('apex.network.jumphost.dump_ovs_ports', return_value=['enpfakes0'])
+ def test_negative_detach_interface_process_error(
+ self, dump_ports_func, is_bridge_func, subprocess_func):
+ ifcfg_dir = con.TEST_DUMMY_CONFIG
+ shutil.copyfile(os.path.join(ifcfg_dir, 'ifcfg-br-dummy'),
+ os.path.join(ifcfg_dir, 'ifcfg-br-admin'))
+ jumphost.NET_CFG_PATH = ifcfg_dir
+ assert_raises(subprocess.CalledProcessError,
+ jumphost.detach_interface_from_ovs, 'admin')
+ assert os.path.isfile(os.path.join(ifcfg_dir, 'ifcfg-enpfakes0'))
+ assert os.path.isfile(os.path.join(ifcfg_dir, 'ifcfg-br-admin'))
+
+ for ifcfg in ('ifcfg-enpfakes0', 'ifcfg-enpfakes0.orig',
+ 'ifcfg-br-admin'):
+ ifcfg_path = os.path.join(ifcfg_dir, ifcfg)
+ if os.path.isfile(ifcfg_path):
+ os.remove(ifcfg_path)
+
+ @patch('subprocess.check_call')
+ @patch('apex.network.jumphost.is_ovs_bridge', return_value=True)
+ def test_remove_ovs_bridge(self, is_bridge_func, subprocess_func):
+ ifcfg_dir = con.TEST_DUMMY_CONFIG
+ jumphost.NET_CFG_PATH = ifcfg_dir
+ shutil.copyfile(os.path.join(ifcfg_dir, 'ifcfg-br-dummy'),
+ os.path.join(ifcfg_dir, 'ifcfg-br-admin'))
+ assert jumphost.remove_ovs_bridge(apex_constants.ADMIN_NETWORK) is None
+ assert not os.path.isfile(os.path.join(ifcfg_dir, 'ifcfg-br-admin'))
+
+ # test without file
+ assert jumphost.remove_ovs_bridge(apex_constants.ADMIN_NETWORK) is None
+
+ @patch('subprocess.check_call', side_effect=subprocess_exception)
+ @patch('apex.network.jumphost.is_ovs_bridge', return_value=True)
+ def test_negative_remove_ovs_bridge(self, is_bridge_func, subprocess_func):
+ ifcfg_dir = con.TEST_DUMMY_CONFIG
+ jumphost.NET_CFG_PATH = ifcfg_dir
+ assert_raises(subprocess.CalledProcessError,
+ jumphost.remove_ovs_bridge,
+ apex_constants.ADMIN_NETWORK)
diff --git a/build/rpm_specs/opnfv-apex-common.spec b/build/rpm_specs/opnfv-apex-common.spec
index 37e32145..c2e2f14e 100644
--- a/build/rpm_specs/opnfv-apex-common.spec
+++ b/build/rpm_specs/opnfv-apex-common.spec
@@ -35,7 +35,6 @@ rst2html docs/release/release-notes/release-notes.rst docs/release/release-notes
%install
mkdir -p %{buildroot}%{_bindir}/
%py3_install
-install ci/clean.sh %{buildroot}%{_bindir}/opnfv-clean
install ci/util.sh %{buildroot}%{_bindir}/opnfv-util
mkdir -p %{buildroot}%{_sysconfdir}/bash_completion.d/
@@ -113,6 +112,8 @@ install config/inventory/pod_example_settings.yaml %{buildroot}%{_docdir}/opnfv/
%doc %{_docdir}/opnfv/inventory.yaml.example
%changelog
+* Fri Sep 08 2017 Tim Rozet <trozet@redhat.com> - 5.0-6
+- Updates clean to use python
* Wed Aug 23 2017 Tim Rozet <trozet@redhat.com> - 5.0-5
- Updated requirements
* Mon Aug 14 2017 Tim Rozet <trozet@redhat.com> - 5.0-4
diff --git a/ci/clean.sh b/ci/clean.sh
index ef810416..c2b987c0 100755
--- a/ci/clean.sh
+++ b/ci/clean.sh
@@ -11,211 +11,10 @@
#Clean script to uninstall provisioning server for Apex
#author: Dan Radez (dradez@redhat.com)
#author: Tim Rozet (trozet@redhat.com)
-
-reset=$(tput sgr0 || echo "")
-blue=$(tput setaf 4 || echo "")
-red=$(tput setaf 1 || echo "")
-green=$(tput setaf 2 || echo "")
-
-vm_index=4
-ovs_bridges="br-admin br-tenant br-external br-storage"
-ovs_bridges+=" br-private br-public" # Legacy names, remove in E river
-
-#OPNFV_NETWORK_TYPES=$(python3 -c 'from apex.common.constants import OPNFV_NETWORK_TYPES; print(" ".join(OPNFV_NETWORK_TYPES))')
-OPNFV_NETWORK_TYPES+=" admin tenant external storage api"
-OPNFV_NETWORK_TYPES+=" admin_network private_network public_network storage_network api_network" # Legecy names, remove in E river
-
-##detach interface from OVS and set the network config correctly
-##params: bridge to detach from
-##assumes only 1 real interface attached to OVS
-function detach_interface_from_ovs {
- local bridge
- local port_output ports_no_orig
- local net_path
- local if_ip if_mask if_gw if_prefix
- local if_metric if_dns1 if_dns2
-
- net_path=/etc/sysconfig/network-scripts/
- if [[ -z "$1" ]]; then
- return 1
- else
- bridge=$1
- fi
-
- # if no interfaces attached then return
- if ! ovs-vsctl list-ports ${bridge} | grep -Ev "vnet[0-9]*"; then
- return 0
- fi
-
- # look for .orig ifcfg files to use
- port_output=$(ovs-vsctl list-ports ${bridge} | grep -Ev "vnet[0-9]*")
- while read -r line; do
- if [ -z "$line" ]; then
- continue
- elif [ -e ${net_path}/ifcfg-${line}.orig ]; then
- mv -f ${net_path}/ifcfg-${line}.orig ${net_path}/ifcfg-${line}
- elif [ -e ${net_path}/ifcfg-${bridge} ]; then
- if_ip=$(sed -n 's/^IPADDR=\(.*\)$/\1/p' ${net_path}/ifcfg-${bridge})
- if_mask=$(sed -n 's/^NETMASK=\(.*\)$/\1/p' ${net_path}/ifcfg-${bridge})
- if_gw=$(sed -n 's/^GATEWAY=\(.*\)$/\1/p' ${net_path}/ifcfg-${bridge})
- if_metric=$(sed -n 's/^METRIC=\(.*\)$/\1/p' ${net_path}/ifcfg-${bridge})
- if_dns1=$(sed -n 's/^DNS1=\(.*\)$/\1/p' ${net_path}/ifcfg-${bridge})
- if_dns2=$(sed -n 's/^DNS2=\(.*\)$/\1/p' ${net_path}/ifcfg-${bridge})
-
- if [ -z "$if_mask" ]; then
- if_prefix=$(sed -n 's/^PREFIX=[^0-9]*\([0-9][0-9]*\)[^0-9]*$/\1/p' ${net_path}/ifcfg-${bridge})
- if_mask=$(prefix2mask ${if_prefix})
- fi
-
- if [[ -z "$if_ip" || -z "$if_mask" ]]; then
- echo "ERROR: IPADDR or PREFIX/NETMASK missing for ${bridge} and no .orig file for interface ${line}"
- return 1
- fi
-
- # create if cfg
- echo "DEVICE=${line}
-IPADDR=${if_ip}
-NETMASK=${if_mask}
-BOOTPROTO=static
-ONBOOT=yes
-TYPE=Ethernet
-NM_CONTROLLED=no
-PEERDNS=no" > ${net_path}/ifcfg-${line}
-
- if [ -n "$if_gw" ]; then
- echo "GATEWAY=${if_gw}" >> ${net_path}/ifcfg-${line}
- fi
-
- if [ -n "$if_metric" ]; then
- echo "METRIC=${if_metric}" >> ${net_path}/ifcfg-${line}
- fi
-
- if [[ -n "$if_dns1" || -n "$if_dns2" ]]; then
- sed -i '/PEERDNS/c\PEERDNS=yes' ${net_path}/ifcfg-${line}
-
- if [ -n "$if_dns1" ]; then
- echo "DNS1=${if_dns1}" >> ${net_path}/ifcfg-${line}
- fi
-
- if [ -n "$if_dns2" ]; then
- echo "DNS2=${if_dns2}" >> ${net_path}/ifcfg-${line}
- fi
- fi
- break
- else
- echo "ERROR: Real interface ${line} attached to bridge, but no interface or ${bridge} ifcfg file exists"
- return 1
- fi
-
- done <<< "$port_output"
-
- # modify the bridge ifcfg file
- # to remove IP params
- sudo sed -i 's/IPADDR=.*//' ${net_path}/ifcfg-${bridge}
- sudo sed -i 's/NETMASK=.*//' ${net_path}/ifcfg-${bridge}
- sudo sed -i 's/GATEWAY=.*//' ${net_path}/ifcfg-${bridge}
- sudo sed -i 's/DNS1=.*//' ${net_path}/ifcfg-${bridge}
- sudo sed -i 's/DNS2=.*//' ${net_path}/ifcfg-${bridge}
- sudo sed -i 's/METRIC=.*//' ${net_path}/ifcfg-${bridge}
- sudo sed -i 's/PEERDNS=.*//' ${net_path}/ifcfg-${bridge}
-
- sudo systemctl restart network
-}
-
-display_usage() {
- echo -e "Usage:\n$0 [arguments] \n"
- echo -e " -i|--inventory : Full path to inventory yaml file. Required only for baremetal node clean"
-}
-
-##translates the command line parameters into variables
-##params: $@ the entire command line is passed
-##usage: parse_cmd_line() "$@"
-parse_cmdline() {
- echo -e "\n\n${blue}This script is used to clean an Apex environment${reset}\n\n"
- echo "Use -h to display help"
- sleep 2
-
- while [ "${1:0:1}" = "-" ]
- do
- case "$1" in
- -h|--help)
- display_usage
- exit 0
- ;;
- -i|--inventory)
- INVENTORY_FILE=$2
- shift 2
- ;;
- *)
- display_usage
- exit 1
- ;;
- esac
- done
-
- if [[ ! -z "$INVENTORY_FILE" && ! -f "$INVENTORY_FILE" ]]; then
- echo -e "{$red}ERROR: Inventory File: ${INVENTORY_FILE} does not exist! Exiting...${reset}"
- exit 1
- fi
-}
-
-parse_cmdline "$@"
-
-if [ -n "$INVENTORY_FILE" ]; then
- echo -e "${blue}INFO: Parsing inventory file...${reset}"
- # hack for now (until we switch fully over to clean.py) to tell if
- # we should install apex from python or if rpm is being used
- if ! rpm -q python34-opnfv-apex > /dev/null; then
- pushd ../ && python3 setup.py install > /dev/null
- popd
- fi
- if ! python3 -m apex.clean -f ${INVENTORY_FILE}; then
- echo -e "${red}WARN: Unable to shutdown all nodes! Please check /var/log/apex.log${reset}"
- else
- echo -e "${blue}INFO: Node shutdown complete...${reset}"
- fi
-fi
-
-# Clean off instack/undercloud VM
-for vm in instack undercloud; do
- virsh destroy $vm 2> /dev/null | xargs echo -n
- virsh undefine --nvram $vm 2> /dev/null | xargs echo -n
- /usr/bin/touch /var/lib/libvirt/images/${vm}.qcow2
- virsh vol-delete ${vm}.qcow2 --pool default 2> /dev/null | xargs echo -n
- rm -f /var/lib/libvirt/images/${vm}.qcow2 2> /dev/null
-done
-
-# Clean off baremetal VMs in case they exist
-for i in $(seq 0 $vm_index); do
- virsh destroy baremetal$i 2> /dev/null | xargs echo -n
- virsh undefine baremetal$i 2> /dev/null | xargs echo -n
- /usr/bin/touch /var/lib/libvirt/images/baremetal${i}.qcow2
- virsh vol-delete baremetal${i}.qcow2 --pool default 2> /dev/null | xargs echo -n
- rm -f /var/lib/libvirt/images/baremetal${i}.qcow2 2> /dev/null
- if [ -e /root/.vbmc/baremetal$i ]; then vbmc delete baremetal$i; fi
-done
-
-for network in ${OPNFV_NETWORK_TYPES}; do
- virsh net-destroy ${network} 2> /dev/null
- virsh net-undefine ${network} 2> /dev/null
-done
-
-# Clean off created bridges
-for bridge in ${ovs_bridges}; do
- if detach_interface_from_ovs ${bridge} 2> /dev/null; then
- ovs-vsctl del-br ${bridge} 2> /dev/null
- rm -f /etc/sysconfig/network-scripts/ifcfg-${bridge}
- fi
-done
-
-# clean pub keys from root's auth keys
-sed -i '/stack@undercloud.localdomain/d' /root/.ssh/authorized_keys
-
-
-# force storage cleanup
-virsh pool-refresh default
-
-# remove temporary files
-rm -f /tmp/network-environment.yaml
-
-echo "Cleanup Completed"
+set -e
+yum -y install python34 python34-devel libvirt-devel python34-pip python-tox ansible
+mkdir -p ~/tmp
+mv -f .build ~/tmp/
+sudo pip3 install --upgrade --force-reinstall .
+mv -f ~/tmp/.build .
+opnfv-clean $@
diff --git a/setup.cfg b/setup.cfg
index ee3105af..9c181f5e 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -23,6 +23,7 @@ setup-hooks =
[entry_points]
console_scripts =
opnfv-deploy = apex.deploy:main
+ opnfv-clean = apex.clean:main
[files]
packages =