diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..dfc3865 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "plugin_test/fuel-qa"] + path = plugin_test/fuel-qa + url = https://github.com/openstack/fuel-qa + branch = stable/mitaka diff --git a/plugin_test/__init__.py b/plugin_test/__init__.py new file mode 100644 index 0000000..f50a10a --- /dev/null +++ b/plugin_test/__init__.py @@ -0,0 +1,14 @@ +"""Copyright 2016 Mirantis, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); you may +not use this file except in compliance with the License. You may obtain +copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations +under the License. +""" diff --git a/plugin_test/fuel-qa b/plugin_test/fuel-qa new file mode 160000 index 0000000..1568197 --- /dev/null +++ b/plugin_test/fuel-qa @@ -0,0 +1 @@ +Subproject commit 15681971a6ff4adc9a8fd0b567f7443a3db6ffab diff --git a/plugin_test/helpers/__init__.py b/plugin_test/helpers/__init__.py new file mode 100644 index 0000000..f50a10a --- /dev/null +++ b/plugin_test/helpers/__init__.py @@ -0,0 +1,14 @@ +"""Copyright 2016 Mirantis, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); you may +not use this file except in compliance with the License. You may obtain +copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations +under the License. +""" diff --git a/plugin_test/helpers/settings.py b/plugin_test/helpers/settings.py new file mode 100644 index 0000000..a59ec0e --- /dev/null +++ b/plugin_test/helpers/settings.py @@ -0,0 +1,81 @@ +"""Copyright 2016 Mirantis, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); you may +not use this file except in compliance with the License. You may obtain +copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations +under the License. +""" + +import os + +from fuelweb_test.settings import get_var_as_bool +from fuelweb_test.settings import iface_alias +from fuelweb_test.settings import NEUTRON_SEGMENT_TYPE + + +HALF_MIN_WAIT = 30 # 30 seconds +WAIT_FOR_COMMAND = 60 * 3 # 3 minutes +WAIT_FOR_LONG_DEPLOY = 60 * 180 # 180 minutes + +EXT_IP = '8.8.8.8' # Google DNS ^_^ +PRIVATE_NET = os.environ.get('PRIVATE_NET', 'admin_internal_net') +ADMIN_NET = os.environ.get('ADMIN_NET', 'admin_floating_net') +DEFAULT_ROUTER_NAME = os.environ.get('DEFAULT_ROUTER_NAME', 'router04') +METADATA_IP = os.environ.get('METADATA_IP', '169.254.169.254') +VM_USER = 'cirros' +VM_PASS = 'cubswin:)' +AZ_VCENTER1 = 'vcenter' +AZ_VCENTER2 = 'vcenter2' +FLAVOR_NAME = 'm1.micro128' + +PLUGIN_NAME = os.environ.get('PLUGIN_NAME', 'nsx-t') +NSXT_PLUGIN_PATH = os.environ.get('NSXT_PLUGIN_PATH') +NSXT_PLUGIN_VERSION = os.environ.get('NSXT_PLUGIN_VERSION', '1.0.0') +NSXT_MANAGERS_IP = os.environ.get('NSXT_MANAGERS_IP') +NSXT_USER = os.environ.get('NSXT_USER') + + +assigned_networks = { + iface_alias('eth0'): ['fuelweb_admin', 'private'], + iface_alias('eth1'): ['public'], + iface_alias('eth2'): ['storage'], + iface_alias('eth4'): ['management'] +} + +cluster_settings = { + 'net_provider': 'neutron', + 'assign_to_all_nodes': True, + 'net_segment_type': NEUTRON_SEGMENT_TYPE +} + +plugin_configuration = { + 'insecure/value': get_var_as_bool(os.environ.get('NSXT_INSECURE'), True), + 'nsx_api_managers/value': NSXT_MANAGERS_IP, + 'nsx_api_user/value': NSXT_USER, + 'nsx_api_password/value': os.environ.get('NSXT_PASSWORD'), + 'default_overlay_tz_uuid/value': os.environ.get('NSXT_OVERLAY_TZ_UUID'), + 'default_vlan_tz_uuid/value': os.environ.get('NSXT_VLAN_TZ_UUID'), + 'default_tier0_router_uuid/value': os.environ.get( + 'NSXT_TIER0_ROUTER_UUID'), + 'default_edge_cluster_uuid/value': os.environ.get( + 'NSXT_EDGE_CLUSTER_UUID'), + 'uplink_profile_uuid/value': os.environ.get('NSXT_UPLINK_PROFILE_UUID'), + 'controller_ip_pool_uuid/value': os.environ.get( + 'NSXT_CONTROLLER_IP_POOL_UUID'), + 'controller_pnics_pairs/value': os.environ.get( + 'NSXT_CONTROLLER_PNICS_PAIRS'), + 'compute_ip_pool_uuid/value': os.environ.get('NSXT_COMPUTE_IP_POOL_UUID'), + 'compute_pnics_pairs/value': os.environ.get('NSXT_COMPUTE_PNICS_PAIRS'), + 'floating_ip_range/value': os.environ.get('NSXT_FLOATING_IP_RANGE'), + 'floating_net_cidr/value': os.environ.get('NSXT_FLOATING_NET_CIDR'), + 'internal_net_cidr/value': os.environ.get('NSXT_INTERNAL_NET_CIDR'), + 'floating_net_gw/value': os.environ.get('NSXT_FLOATING_NET_GW'), + 'internal_net_dns/value': os.environ.get('NSXT_INTERNAL_NET_DNS') +} diff --git a/plugin_test/helpers/tools.py b/plugin_test/helpers/tools.py new file mode 100644 index 0000000..25968a6 --- /dev/null +++ b/plugin_test/helpers/tools.py @@ -0,0 +1,54 @@ +"""Copyright 2016 Mirantis, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); you may +not use this file except in compliance with the License. You may obtain +copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations +under the License. +""" + +from functools import wraps + +from fuelweb_test import logger + + +def find_first(seq, predicate): + """Find the first item of sequence for which predicate is performed.""" + return next((x for x in seq if predicate(x)), None) + + +class ShowPos(object): + """Print func name and its parameters for each call.""" + + @staticmethod + def deco(f): + """Logger decorator.""" + def wrapper(*args, **kwargs): + logger.debug("Call {0}({1}, {2})".format(f.__name__, args, kwargs)) + return f(*args, **kwargs) + return wrapper + + def __getattribute__(self, name): + """Log by attributes.""" + attr = object.__getattribute__(self, name) + if callable(attr): + return ShowPos.deco(attr) + else: + return attr + + +def show_pos(f): + """Wrapper shows current POSition in debug output.""" + @wraps(f) + def wrapper(*args, **kwargs): + logger.debug('Call {func}({args}, {kwargs})'.format(func=f.__name__, + args=args, + kwargs=kwargs)) + return f(*args, **kwargs) + return wrapper diff --git a/plugin_test/run_tests.py b/plugin_test/run_tests.py new file mode 100644 index 0000000..94384ae --- /dev/null +++ b/plugin_test/run_tests.py @@ -0,0 +1,65 @@ +"""Copyright 2016 Mirantis, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); you may +not use this file except in compliance with the License. You may obtain +copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations +under the License. +""" +import os +import re +import sys + +from nose.plugins import Plugin +from paramiko.transport import _join_lingering_threads + + +class CloseSSHConnectionsPlugin(Plugin): + """Closes all paramiko's ssh connections after each test case. + + Plugin fixes proboscis disability to run cleanup of any kind. + 'afterTest' calls _join_lingering_threads function from paramiko, + which stops all threads (set the state to inactive and join for 10s) + """ + + name = 'closesshconnections' + + def options(self, parser, env=os.environ): + super(CloseSSHConnectionsPlugin, self).options(parser, env=env) + + def configure(self, options, conf): + super(CloseSSHConnectionsPlugin, self).configure(options, conf) + self.enabled = True + + def afterTest(self, *args, **kwargs): + _join_lingering_threads() + + +def import_tests(): + from tests import test_plugin_nsxt # noqa + + +def run_tests(): + from proboscis import TestProgram # noqa + import_tests() + + # Run Proboscis and exit. + TestProgram(addplugins=[CloseSSHConnectionsPlugin()]).run_and_exit() + + +if __name__ == '__main__': + sys.path.append(sys.path[0] + "/fuel-qa") + import_tests() + from fuelweb_test.helpers.patching import map_test + if any(re.search(r'--group=patching_master_tests', arg) + for arg in sys.argv): + map_test('master') + elif any(re.search(r'--group=patching.*', arg) for arg in sys.argv): + map_test('environment') + run_tests() diff --git a/plugin_test/test_templates/default.yaml b/plugin_test/test_templates/default.yaml new file mode 100644 index 0000000..97b9704 --- /dev/null +++ b/plugin_test/test_templates/default.yaml @@ -0,0 +1,209 @@ +--- +template: + devops_settings: + aliases: + + dynamic_address_pool: + - &pool_default !os_env POOL_DEFAULT, 10.109.0.0/16:24 + + default_interface_model: + - &interface_model !os_env INTERFACE_MODEL, e1000 + + rack-01-slave-interfaces: &rack-01-slave-interfaces + - label: eth0 + l2_network_device: admin # Libvirt bridge name. It is *NOT* Nailgun networks + interface_model: *interface_model + - label: eth1 + l2_network_device: public + interface_model: *interface_model + - label: eth2 + l2_network_device: management + interface_model: *interface_model + - label: eth3 + l2_network_device: private + interface_model: *interface_model + - label: eth4 + l2_network_device: storage + interface_model: *interface_model + + rack-01-slave-network_config: &rack-01-slave-network_config + eth0: + networks: + - fuelweb_admin + eth1: + networks: + - public + eth2: + networks: + - management + eth3: + networks: + - private + eth4: + networks: + - storage + + rack-01-slave-node-params: &rack-01-slave-node-params + vcpu: !os_env SLAVE_NODE_CPU, 4 + memory: !os_env SLAVE_NODE_MEMORY, 8192 + boot: + - network + - hd + volumes: + - name: system + capacity: !os_env NODE_VOLUME_SIZE, 55 + format: qcow2 + - name: cinder + capacity: !os_env NODE_VOLUME_SIZE, 55 + format: qcow2 + - name: swift + capacity: !os_env NODE_VOLUME_SIZE, 55 + format: qcow2 + interfaces: *rack-01-slave-interfaces + network_config: *rack-01-slave-network_config + + rack-02-slave-node-params: &rack-02-slave-node-params + vcpu: !os_env SLAVE_NODE_CPU, 2 + memory: !os_env SLAVE_NODE_MEMORY, 3072 + boot: + - network + - hd + volumes: + - name: system + capacity: !os_env NODE_VOLUME_SIZE, 55 + format: qcow2 + - name: cinder + capacity: !os_env NODE_VOLUME_SIZE, 55 + format: qcow2 + - name: swift + capacity: !os_env NODE_VOLUME_SIZE, 55 + format: qcow2 + interfaces: *rack-01-slave-interfaces + network_config: *rack-01-slave-network_config + + + env_name: !os_env ENV_NAME + + address_pools: + # Network pools used by the environment + fuelweb_admin-pool01: + net: *pool_default + params: + tag: 0 + public-pool01: + net: *pool_default + params: + tag: 0 + storage-pool01: + net: *pool_default + params: + tag: 101 + management-pool01: + net: *pool_default + params: + tag: 102 + private-pool01: + net: *pool_default + params: + tag: 103 + + groups: + - name: cat + driver: + name: devops.driver.libvirt.libvirt_driver + params: + connection_string: !os_env CONNECTION_STRING, qemu:///system + storage_pool_name: !os_env STORAGE_POOL_NAME, default + stp: True + hpet: False + use_host_cpu: !os_env DRIVER_USE_HOST_CPU, true + + network_pools: # Address pools for OpenStack networks. + # Actual names should be used for keys + # (the same as in Nailgun, for example) + + fuelweb_admin: fuelweb_admin-pool01 + public: public-pool01 + storage: storage-pool01 + management: management-pool01 + private: private-pool01 + + l2_network_devices: # Libvirt bridges. It is *NOT* Nailgun networks + admin: + address_pool: fuelweb_admin-pool01 + dhcp: false + forward: + mode: nat + + public: + address_pool: public-pool01 + dhcp: false + forward: + mode: nat + + storage: + address_pool: storage-pool01 + dhcp: false + + management: + address_pool: management-pool01 + dhcp: false + + private: + address_pool: private-pool01 + dhcp: false + + nodes: + - name: admin # Custom name of VM for Fuel admin node + role: fuel_master # Fixed role for Fuel master node properties + params: + vcpu: !os_env ADMIN_NODE_CPU, 2 + memory: !os_env ADMIN_NODE_MEMORY, 8192 + boot: + - hd + - cdrom # for boot from usb - without 'cdrom' + volumes: + - name: system + capacity: !os_env ADMIN_NODE_VOLUME_SIZE, 80 + format: qcow2 + - name: iso + source_image: !os_env ISO_PATH # if 'source_image' set, then volume capacity is calculated from it's size + format: raw + device: cdrom # for boot from usb - 'disk' + bus: ide # for boot from usb - 'usb' + interfaces: + - label: eth0 + l2_network_device: admin # Libvirt bridge name. It is *NOT* a Nailgun network + interface_model: *interface_model + network_config: + eth0: + networks: + - fuelweb_admin + + - name: slave-01 + role: fuel_slave + params: *rack-01-slave-node-params + - name: slave-02 + role: fuel_slave + params: *rack-01-slave-node-params + - name: slave-03 + role: fuel_slave + params: *rack-01-slave-node-params + - name: slave-04 + role: fuel_slave + params: *rack-02-slave-node-params + - name: slave-05 + role: fuel_slave + params: *rack-02-slave-node-params + - name: slave-06 + role: fuel_slave + params: *rack-02-slave-node-params + - name: slave-07 + role: fuel_slave + params: *rack-02-slave-node-params + - name: slave-08 + role: fuel_slave + params: *rack-02-slave-node-params + - name: slave-09 + role: fuel_slave + params: *rack-02-slave-node-params diff --git a/plugin_test/tests/__init__.py b/plugin_test/tests/__init__.py new file mode 100644 index 0000000..f50a10a --- /dev/null +++ b/plugin_test/tests/__init__.py @@ -0,0 +1,14 @@ +"""Copyright 2016 Mirantis, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); you may +not use this file except in compliance with the License. You may obtain +copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations +under the License. +""" diff --git a/plugin_test/tests/base_plugin_test.py b/plugin_test/tests/base_plugin_test.py new file mode 100644 index 0000000..28aae5b --- /dev/null +++ b/plugin_test/tests/base_plugin_test.py @@ -0,0 +1,98 @@ +"""Copyright 2016 Mirantis, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); you may +not use this file except in compliance with the License. You may obtain +copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations +under the License. +""" + +import os + +from proboscis.asserts import assert_true + +from fuelweb_test import logger +from fuelweb_test.helpers import utils +from fuelweb_test.helpers.utils import pretty_log +from fuelweb_test.tests.base_test_case import TestBasic +from helpers import settings + + +class TestNSXtBase(TestBasic): + """Base class for NSX-T plugin tests""" + + def __init__(self): + super(TestNSXtBase, self).__init__() + self.default = settings + + def install_nsxt_plugin(self): + """Download and install NSX-T plugin on master node. + + :return: None + """ + master_ip = self.ssh_manager.admin_ip + utils.upload_tarball(ip=master_ip, + tar_path=self.default.NSXT_PLUGIN_PATH, + tar_target='/var') + + utils.install_plugin_check_code( + ip=master_ip, + plugin=os.path.basename(self.default.NSXT_PLUGIN_PATH)) + + def enable_plugin(self, cluster_id, settings=None): + """Enable NSX-T plugin on cluster. + + :param cluster_id: cluster id + :param settings: settings in dict format + :return: None + """ + msg = "Plugin couldn't be enabled. Check plugin version. Test aborted" + settings = settings if settings else {} + checker = self.fuel_web.check_plugin_exists(cluster_id, + self.default.PLUGIN_NAME) + assert_true(checker, msg) + logger.info('Configure cluster with ' + 'following parameters: \n{}'.format(pretty_log(settings))) + self.fuel_web.update_plugin_settings( + cluster_id, + self.default.PLUGIN_NAME, + self.default.NSXT_PLUGIN_VERSION, + dict(self.default.plugin_configuration, **settings)) + + def reconfigure_cluster_interfaces(self, cluster_id): + # clear network mapping enp0s6 for all deployed nodes + nodes = self.fuel_web.client.list_cluster_nodes(cluster_id) + for node in nodes: + self.fuel_web.update_node_networks(node['id'], + settings.assigned_networks) + + def delete_nsxt_plugin(self, failover=False): + """Delete NSX-T plugin + + :param failover: True if we expect that plugin won't be deleted + :return: + """ + plugin_name = self.default.PLUGIN_NAME + plugin_vers = self.default.NSXT_PLUGIN_VERSION + tmp = "Plugin '{0}' {1} removed" + msg = tmp.format(plugin_name, 'was' if failover else "wasn't") + cmd = 'fuel plugins --remove {0}=={1}'.format(plugin_name, plugin_vers) + + self.ssh_manager.check_call( + ip=self.ssh_manager.admin_ip, + command=cmd, + expected=[1 if failover else 0], + raise_on_err=not failover + ) + + output = self.ssh_manager.check_call( + ip=self.ssh_manager.admin_ip, + command='fuel2 plugins list -f value -c name' + ).stdout[-1].split(' ') + assert_true(plugin_name in output != failover, msg) diff --git a/plugin_test/tests/test_plugin_nsxt.py b/plugin_test/tests/test_plugin_nsxt.py new file mode 100644 index 0000000..dc0b625 --- /dev/null +++ b/plugin_test/tests/test_plugin_nsxt.py @@ -0,0 +1,204 @@ +"""Copyright 2016 Mirantis, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); you may +not use this file except in compliance with the License. You may obtain +copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations +under the License. +""" + +from proboscis import test +from proboscis.asserts import assert_true + +from fuelweb_test.helpers.decorators import log_snapshot_after_test +from fuelweb_test.settings import DEPLOYMENT_MODE +from fuelweb_test.tests.base_test_case import SetupEnvironment +from tests.base_plugin_test import TestNSXtBase + + +@test(groups=["plugins", "nsxt_plugin", 'nsxt_smoke_scenarios']) +class TestNSXtSmoke(TestNSXtBase): + """Tests from test plan that have been marked as 'Automated'.""" + + @test(depends_on=[SetupEnvironment.prepare_slaves_1], + groups=["nsxt_install", 'nsxt_smoke']) + @log_snapshot_after_test + def nsxt_install(self): + """Check that plugin can be installed. + + Scenario: + 1. Connect to the Fuel master node via ssh. + 2. Upload NSX-T plugin. + 3. Install NSX-T plugin. + 4. Run command 'fuel plugins'. + 5. Check name, version and package version of plugin. + + Duration 30 min + + """ + self.env.revert_snapshot('ready_with_1_slaves') + + self.show_step(1) + self.show_step(2) + self.show_step(3) + self.install_nsxt_plugin() + + self.show_step(4) + output = self.ssh_manager.execute_on_remote( + ip=self.ssh_manager.admin_ip, cmd='fuel plugins list' + )['stdout'].pop().split(' ') + + self.show_step(5) + msg = "Plugin '{0}' is not installed.".format(self.default.PLUGIN_NAME) + # check name + assert_true(self.default.PLUGIN_NAME in output, msg) + # check version + assert_true(self.default.NSXT_PLUGIN_VERSION in output, msg) + + self.env.make_snapshot("nsxt_install", is_make=True) + + @test(depends_on=[nsxt_install], + groups=["nsxt_uninstall", 'nsxt_smoke']) + @log_snapshot_after_test + def nsxt_uninstall(self): + """Check that NSX-T plugin can be removed. + + Scenario: + 1. Revert to snapshot nsxt_install + 2. Remove NSX-T plugin. + 3. Run command 'fuel plugins' to ensure the NSX-T plugin has + been removed. + + Duration: 5 min + """ + self.show_step(1) + self.env.revert_snapshot("nsxt_install") + + self.show_step(2) + cmd = 'fuel plugins --remove {0}=={1}'.format( + self.default.PLUGIN_NAME, self.default.NSXT_PLUGIN_VERSION) + + self.ssh_manager.execute_on_remote( + ip=self.ssh_manager.admin_ip, + cmd=cmd, + err_msg='Can not remove plugin.') + + self.show_step(3) + self.delete_nsxt_plugin() + + @test(depends_on=[nsxt_install], + groups=['nsxt_kvm_smoke', 'nsxt_smoke']) + @log_snapshot_after_test + def nsxt_kvm_smoke(self): + """Deploy a cluster with NSXt Plugin. + + Scenario: + 1. Upload the plugin to master node. + 2. Create cluster. + 3. Provision one controller node. + 4. Configure NSXt for that cluster. + 5. Deploy cluster with plugin. + 6. Run 'smoke' OSTF. + + Duration 90 min + + """ + self.show_step(1) + self.env.revert_snapshot('nsxt_install') + + self.show_step(2) + cluster_id = self.fuel_web.create_cluster( + name=self.__class__.__name__, + mode=DEPLOYMENT_MODE, + settings=self.default.cluster_settings, + configure_ssl=False) + + self.show_step(3) + self.fuel_web.update_nodes(cluster_id, {'slave-01': ['controller']}) + + self.reconfigure_cluster_interfaces(cluster_id) + + self.show_step(4) + self.enable_plugin(cluster_id) + + self.show_step(5) + self.fuel_web.deploy_cluster_wait(cluster_id) + + self.show_step(6) + self.fuel_web.run_ostf(cluster_id=cluster_id, test_sets=['smoke']) + + +@test(groups=["plugins", "nsxt_plugin", 'nsxt_bvt_scenarios']) +class TestNSXtBVT(TestNSXtBase): + """NSX-t BVT scenarios""" + + @test(depends_on=[SetupEnvironment.prepare_slaves_9], + groups=["nsxt_bvt"]) + @log_snapshot_after_test + def nsxt_bvt(self): + """Deploy cluster with plugin and vmware datastore backend. + + Scenario: + 1. Upload plugins to the master node. + 2. Create cluster with vcenter. + 3. Add 3 node with controller role, 3 ceph, + compute-vmware + cinder-vmware, compute. + 4. Configure vcenter. + 5. Configure NSXt for that cluster. + 6. Deploy cluster. + 7. Run OSTF. + + Duration 3 hours + + """ + self.env.revert_snapshot("ready_with_9_slaves") + + self.show_step(1) + self.install_nsxt_plugin() + + self.show_step(2) + settings = self.default.cluster_settings + settings["images_ceph"] = True + + cluster_id = self.fuel_web.create_cluster( + name=self.__class__.__name__, + mode=DEPLOYMENT_MODE, + settings=settings, + configure_ssl=False) + + self.show_step(3) + self.fuel_web.update_nodes( + cluster_id, + {'slave-01': ['controller'], + 'slave-02': ['controller'], + 'slave-03': ['controller'], + 'slave-04': ['ceph-osd'], + 'slave-05': ['ceph-osd'], + 'slave-06': ['ceph-osd'], + 'slave-07': ['compute-vmware', 'cinder-vmware'], + 'slave-08': ['compute']} + ) + + self.reconfigure_cluster_interfaces(cluster_id) + + self.show_step(4) + target_node_2 = \ + self.fuel_web.get_nailgun_node_by_name('slave-07')['hostname'] + self.fuel_web.vcenter_configure(cluster_id, + multiclusters=True, + target_node_2=target_node_2) + self.show_step(5) + self.enable_plugin(cluster_id) + + self.show_step(6) + self.fuel_web.deploy_cluster_wait(cluster_id) + + self.show_step(7) + self.fuel_web.run_ostf( + cluster_id=cluster_id, test_sets=['smoke', 'sanity', 'ha']) diff --git a/plugin_test/utils/jenkins/system_tests.sh b/plugin_test/utils/jenkins/system_tests.sh new file mode 100755 index 0000000..9074b60 --- /dev/null +++ b/plugin_test/utils/jenkins/system_tests.sh @@ -0,0 +1,660 @@ +#!/bin/sh +PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + +INVALIDOPTS_ERR=100 +NOJOBNAME_ERR=101 +NOISOPATH_ERR=102 +NOTASKNAME_ERR=103 +NOWORKSPACE_ERR=104 +NOISOFOUND_ERR=107 +CDWORKSPACE_ERR=110 +ISODOWNLOAD_ERR=111 +INVALIDTASK_ERR=112 + +# Defaults + +export REBOOT_TIMEOUT=${REBOOT_TIMEOUT:-5000} +export ALWAYS_CREATE_DIAGNOSTIC_SNAPSHOT=${ALWAYS_CREATE_DIAGNOSTIC_SNAPSHOT:-true} + +ShowHelp() { +cat << EOF +System Tests Script + +It can perform several actions depending on Jenkins JOB_NAME it's ran from +or it can take names from exported environment variables or command line options +if you do need to override them. + +-w (dir) - Path to workspace where fuelweb git repository was checked out. + Uses Jenkins' WORKSPACE if not set +-e (name) - Directly specify environment name used in tests + Uses ENV_NAME variable is set. +-j (name) - Name of this job. Determines ISO name, Task name and used by tests. + Uses Jenkins' JOB_NAME if not set +-v - Do not use virtual environment +-V (dir) - Path to python virtual environment +-i (file) - Full path to ISO file to build or use for tests. + Made from iso dir and name if not set. +-t (name) - Name of task this script should perform. Should be one of defined ones. + Taken from Jenkins' job's suffix if not set. +-o (str) - Allows you any extra command line option to run test job if you + want to use some parameters. +-a (str) - Allows you to path NOSE_ATTR to the test job if you want + to use some parameters. +-A (str) - Allows you to path NOSE_EVAL_ATTR if you want to enter attributes + as python expressions. +-m (name) - Use this mirror to build ISO from. + Uses 'srt' if not set. +-U - ISO URL for tests. + Null by default. +-r (yes/no) - Should built ISO file be placed with build number tag and + symlinked to the last build or just copied over the last file. +-b (num) - Allows you to override Jenkins' build number if you need to. +-l (dir) - Path to logs directory. Can be set by LOGS_DIR evironment variable. + Uses WORKSPACE/logs if not set. +-d - Dry run mode. Only show what would be done and do nothing. + Useful for debugging. +-k - Keep previously created test environment before tests run +-K - Keep test environment after tests are finished +-h - Show this help page + +Most variables uses guesses from Jenkins' job name but can be overriden +by exported variable before script is run or by one of command line options. + +You can override following variables using export VARNAME="value" before running this script +WORKSPACE - path to directory where Fuelweb repository was checked out by Jenkins or manually +JOB_NAME - name of Jenkins job that determines which task should be done and ISO file name. + +If task name is "iso" it will make iso file +Other defined names will run Nose tests using previously built ISO file. + +ISO file name is taken from job name prefix +Task name is taken from job name suffix +Separator is one dot '.' + +For example if JOB_NAME is: +mytest.somestring.iso +ISO name: mytest.iso +Task name: iso +If ran with such JOB_NAME iso file with name mytest.iso will be created + +If JOB_NAME is: +mytest.somestring.node +ISO name: mytest.iso +Task name: node +If script was run with this JOB_NAME node tests will be using ISO file mytest.iso. + +First you should run mytest.somestring.iso job to create mytest.iso. +Then you can ran mytest.somestring.node job to start tests using mytest.iso and other tests too. +EOF +} + +GlobalVariables() { + # where built iso's should be placed + # use hardcoded default if not set before by export + ISO_DIR="${ISO_DIR:=/var/www/fuelweb-iso}" + + # name of iso file + # taken from jenkins job prefix + # if not set before by variable export + if [ -z "${ISO_NAME}" ]; then + ISO_NAME="${JOB_NAME%.*}.iso" + fi + + # full path where iso file should be placed + # make from iso name and path to iso shared directory + # if was not overriden by options or export + if [ -z "${ISO_PATH}" ]; then + ISO_PATH="${ISO_DIR}/${ISO_NAME}" + fi + + # what task should be ran + # it's taken from jenkins job name suffix if not set by options + if [ -z "${TASK_NAME}" ]; then + TASK_NAME="${JOB_NAME##*.}" + fi + + # do we want to keep iso's for each build or just copy over single file + ROTATE_ISO="${ROTATE_ISO:=yes}" + + # choose mirror to build iso from. Default is 'srt' for Saratov's mirror + # you can change mirror by exporting USE_MIRROR variable before running this script + USE_MIRROR="${USE_MIRROR:=srt}" + + # only show what commands would be executed but do nothing + # this feature is useful if you want to debug this script's behaviour + DRY_RUN="${DRY_RUN:=no}" + + VENV="${VENV:=yes}" +} + +GetoptsVariables() { + while getopts ":w:j:i:t:o:a:A:m:U:r:b:V:l:dkKe:v:h" opt; do + case $opt in + w) + WORKSPACE="${OPTARG}" + ;; + j) + JOB_NAME="${OPTARG}" + ;; + i) + ISO_PATH="${OPTARG}" + ;; + t) + TASK_NAME="${OPTARG}" + ;; + o) + TEST_OPTIONS="${TEST_OPTIONS} ${OPTARG}" + ;; + a) + NOSE_ATTR="${OPTARG}" + ;; + A) + NOSE_EVAL_ATTR="${OPTARG}" + ;; + m) + USE_MIRROR="${OPTARG}" + ;; + U) + ISO_URL="${OPTARG}" + ;; + r) + ROTATE_ISO="${OPTARG}" + ;; + V) + VENV_PATH="${OPTARG}" + ;; + l) + LOGS_DIR="${OPTARG}" + ;; + k) + KEEP_BEFORE="yes" + ;; + K) + KEEP_AFTER="yes" + ;; + e) + ENV_NAME="${OPTARG}" + ;; + d) + DRY_RUN="yes" + ;; + v) + VENV="no" + ;; + h) + ShowHelp + exit 0 + ;; + \?) + echo "Invalid option: -$OPTARG" + ShowHelp + exit $INVALIDOPTS_ERR + ;; + :) + echo "Option -$OPTARG requires an argument." + ShowHelp + exit $INVALIDOPTS_ERR + ;; + esac + done +} + +CheckVariables() { + + if [ -z "${JOB_NAME}" ]; then + echo "Error! JOB_NAME is not set!" + exit $NOJOBNAME_ERR + fi + if [ -z "${ISO_PATH}" ]; then + echo "Error! ISO_PATH is not set!" + exit $NOISOPATH_ERR + fi + if [ -z "${TASK_NAME}" ]; then + echo "Error! TASK_NAME is not set!" + exit $NOTASKNAME_ERR + fi + if [ -z "${WORKSPACE}" ]; then + echo "Error! WORKSPACE is not set!" + exit $NOWORKSPACE_ERR + fi + + if [ -z "${POOL_PUBLIC}" ]; then + export POOL_PUBLIC='172.16.0.0/24:24' + fi + if [ -z "${POOL_MANAGEMENT}" ]; then + export POOL_MANAGEMENT='172.16.1.0/24:24' + fi + if [ -z "${POOL_PRIVATE}" ]; then + export POOL_PRIVATE='192.168.0.0/24:24' + fi + + # vCenter variables + if [ -z "${DISABLE_SSL}" ]; then + export DISABLE_SSL="true" + fi + if [ -z "${VCENTER_USE}" ]; then + export VCENTER_USE="true" + fi + if [ -z "${VCENTER_IP}" ]; then + export VCENTER_IP="172.16.0.254" + fi + if [ -z "${VCENTER_USERNAME}" ]; then + export VCENTER_USERNAME="administrator@vsphere.local" + fi + if [ -z "${VCENTER_PASSWORD}" ]; then + echo "Error! VCENTER_PASSWORD is not set!" + exit 1 + fi + if [ -z "${VC_DATACENTER}" ]; then + export VC_DATACENTER="Datacenter" + fi + if [ -z "${VC_DATASTORE}" ]; then + export VC_DATASTORE="nfs" + fi + if [ -z "${VCENTER_IMAGE_DIR}" ]; then + export VCENTER_IMAGE_DIR="/openstack_glance" + fi + if [ -z "${WORKSTATION_NODES}" ]; then + export WORKSTATION_NODES="esxi1 esxi2 esxi3 vcenter trusty nsx-edge" + fi + if [ -z "${WORKSTATION_IFS}" ]; then + export WORKSTATION_IFS="vmnet1 vmnet2 vmnet5" + fi + if [ -z "${VCENTER_CLUSTERS}" ]; then + export VCENTER_CLUSTERS="Cluster1,Cluster2" + fi + if [ -z "${WORKSTATION_SNAPSHOT}" ]; then + echo "Error! WORKSTATION_SNAPSHOT is not set!" + exit 1 + fi + if [ -z "${WORKSTATION_USERNAME}" ]; then + echo "Error! WORKSTATION_USERNAME is not set!" + exit 1 + fi + if [ -z "${WORKSTATION_PASSWORD}" ]; then + echo "Error! WORKSTATION_PASSWORD is not set!" + exit 1 + fi + + # NSXt variables + if [ -z "${NSXT_PLUGIN_PATH}" ]; then + echo "Error! NSXT_PLUGIN_PATH is not set!" + exit 1 + fi + if [ -z "${NEUTRON_SEGMENT_TYPE}" ]; then + export NEUTRON_SEGMENT_TYPE="tun" + fi + if [ -z "${NSXT_INSECURE}" ]; then + export NSXT_INSECURE='true' + fi + if [ -z "${NSXT_MANAGERS_IP}" ]; then + export NSXT_MANAGERS_IP="172.16.0.249" + fi + if [ -z "${NSXT_USER}" ]; then + export NSXT_USER='admin' + fi + if [ -z "${NSXT_PASSWORD}" ]; then + echo "Error! NSXT_PASSWORD is not set!" + exit 1 + fi + if [ -z "${NSXT_OVERLAY_TZ_UUID}" ]; then + export NSXT_OVERLAY_TZ_UUID='0eeb1b85-c826-403d-8762-6a9c23a4f132' + fi + if [ -z "${NSXT_VLAN_TZ_UUID}" ]; then + export NSXT_VLAN_TZ_UUID='8efe20d2-e71a-4d6e-acdd-f78a2ec2e90c' + fi + if [ -z "${NSXT_TIER0_ROUTER_UUID}" ]; then + export NSXT_TIER0_ROUTER_UUID='606acd01-c5f8-40ea-ae20-9a91eb7ebcb4' + fi + if [ -z "${NSXT_EDGE_CLUSTER_UUID}" ]; then + export NSXT_EDGE_CLUSTER_UUID='c53d602a-4010-47cc-a8b1-4ef11d0a3edd' + fi + if [ -z "${NSXT_UPLINK_PROFILE_UUID}" ]; then + export NSXT_UPLINK_PROFILE_UUID='99864272-b34f-46a5-89c8-5657fa7042ea' + fi + if [ -z "${NSXT_CONTROLLER_IP_POOL_UUID}" ]; then + export NSXT_CONTROLLER_IP_POOL_UUID='2e06fcb2-7c5b-4515-a7a9-98809c7b863a' + fi + if [ -z "${NSXT_CONTROLLER_PNICS_PAIRS}" ]; then + export NSXT_CONTROLLER_PNICS_PAIRS='enp0s6:uplink' + fi + if [ -z "${NSXT_COMPUTE_IP_POOL_UUID}" ]; then + export NSXT_COMPUTE_IP_POOL_UUID='2e06fcb2-7c5b-4515-a7a9-98809c7b863a' + fi + if [ -z "${NSXT_COMPUTE_PNICS_PAIRS}" ]; then + export NSXT_COMPUTE_PNICS_PAIRS='enp0s6:uplink' + fi + + if [ -z "${NSXT_FLOATING_IP_RANGE}" ]; then + export NSXT_FLOATING_IP_RANGE='172.16.212.2-172.16.212.40' + fi + if [ -z "${NSXT_FLOATING_NET_CIDR}" ]; then + export NSXT_FLOATING_NET_CIDR='172.16.212.0/24' + fi + if [ -z "${NSXT_ROUTING_NET_CIDR}" ]; then + export NSXT_ROUTING_NET_CIDR='172.16.214.0/30' + fi + if [ -z "${NSXT_FLOATING_NET_GW}" ]; then + export NSXT_FLOATING_NET_GW='172.16.212.1' + fi + if [ -z "${NSXT_INTERNAL_NET_CIDR}" ]; then + export NSXT_INTERNAL_NET_CIDR='192.168.251.0/24' + fi + if [ -z "${NSXT_INTERNAL_NET_DNS}" ]; then + export NSXT_INTERNAL_NET_DNS='8.8.8.8' + fi + + if [ ! -f "${DEVOPS_SETTINGS_TEMPLATE}" ]; then + if [ -z "${NODE_VOLUME_SIZE}" ]; then + export NODE_VOLUME_SIZE=350 + fi + if [ -z "${ADMIN_NODE_MEMORY}" ]; then + export ADMIN_NODE_MEMORY=4096 + fi + if [ -z "${ADMIN_NODE_CPU}" ]; then + export ADMIN_NODE_CPU=4 + fi + if [ -z "${SLAVE_NODE_MEMORY}" ]; then + export SLAVE_NODE_MEMORY=4096 + fi + if [ -z "${SLAVE_NODE_CPU}" ]; then + export SLAVE_NODE_CPU=4 + fi + fi +} + +CdWorkSpace() { + # chdir into workspace or fail if could not + if [ "${DRY_RUN}" != "yes" ]; then + cd "${WORKSPACE}" + ec=$? + + if [ "${ec}" -gt "0" ]; then + echo "Error! Cannot cd to WORKSPACE!" + exit $CDWORKSPACE_ERR + fi + else + echo cd "${WORKSPACE}" + fi +} + +RunTest() { + # Run test selected by task name + + # check if iso file exists + if [ ! -f "${ISO_PATH}" ]; then + if [ -z "${ISO_URL}" -a "${DRY_RUN}" != "yes" ]; then + echo "Error! File ${ISO_PATH} not found and no ISO_URL (-U key) for downloading!" + exit $NOISOFOUND_ERR + else + if [ "${DRY_RUN}" = "yes" ]; then + echo wget -c ${ISO_URL} -O ${ISO_PATH} + else + echo "No ${ISO_PATH} found. Trying to download file." + wget -c ${ISO_URL} -O ${ISO_PATH} + rc=$? + if [ $rc -ne 0 ]; then + echo "Failed to fetch ISO from ${ISO_URL}" + exit $ISODOWNLOAD_ERR + fi + fi + fi + fi + + if [ -z "${VENV_PATH}" ]; then + VENV_PATH="/home/jenkins/venv-nailgun-tests" + fi + + # run python virtualenv + if [ "${VENV}" = "yes" ]; then + if [ "${DRY_RUN}" = "yes" ]; then + echo . $VENV_PATH/bin/activate + else + . $VENV_PATH/bin/activate + fi + fi + + if [ "${ENV_NAME}" = "" ]; then + ENV_NAME="${JOB_NAME}_system_test" + fi + + if [ "${LOGS_DIR}" = "" ]; then + LOGS_DIR="${WORKSPACE}/logs" + fi + + if [ ! -f "$LOGS_DIR" ]; then + mkdir -p $LOGS_DIR + fi + + export ENV_NAME + export LOGS_DIR + export ISO_PATH + + if [ "${KEEP_BEFORE}" != "yes" ]; then + # remove previous environment + if [ "${DRY_RUN}" = "yes" ]; then + echo dos.py erase "${ENV_NAME}" + else + if dos.py list | grep -q "^${ENV_NAME}\$" ; then + dos.py erase "${ENV_NAME}" + fi + fi + fi + + # gather additional option for this nose test run + OPTS="" + if [ -n "${NOSE_ATTR}" ]; then + OPTS="${OPTS} -a ${NOSE_ATTR}" + fi + if [ -n "${NOSE_EVAL_ATTR}" ]; then + OPTS="${OPTS} -A ${NOSE_EVAL_ATTR}" + fi + if [ -n "${TEST_OPTIONS}" ]; then + OPTS="${OPTS} ${TEST_OPTIONS}" + fi + + clean_old_bridges + + # run python test set to create environments, deploy and test product + if [ "${DRY_RUN}" = "yes" ]; then + echo export PYTHONPATH="${PYTHONPATH:+${PYTHONPATH}:}${WORKSPACE}" + echo python plugin_test/run_tests.py -q --nologcapture --with-xunit ${OPTS} + else + export PYTHONPATH="${PYTHONPATH:+${PYTHONPATH}:}${WORKSPACE}" + echo ${PYTHONPATH} + python plugin_test/run_tests.py -q --nologcapture --with-xunit ${OPTS} & + + fi + + SYSTEST_PID=$! + + if ! ps -p $SYSTEST_PID > /dev/null + then + echo System tests exited prematurely, aborting + exit 1 + fi + + while [ "$(virsh net-list | grep -c $ENV_NAME)" -ne 5 ];do sleep 10 + if ! ps -p $SYSTEST_PID > /dev/null + then + echo System tests exited prematurely, aborting + exit 1 + fi + done + sleep 10 + + + # Configre vcenter nodes and interfaces + setup_net $ENV_NAME + clean_iptables + setup_stt $ENV_NAME + setup_external_net + + revert_ws "$WORKSTATION_NODES" || { echo "killing $SYSTEST_PID and its childs" && pkill --parent $SYSTEST_PID && kill $SYSTEST_PID && exit 1; } + + echo waiting for system tests to finish + wait $SYSTEST_PID + + export RES=$? + echo ENVIRONMENT NAME is $ENV_NAME + virsh net-dumpxml ${ENV_NAME}_admin | grep -P "(\d+\.){3}" -o | awk '{print "Fuel master node IP: "$0"2"}' + + if [ "${KEEP_AFTER}" != "yes" ]; then + # remove environment after tests + if [ "${DRY_RUN}" = "yes" ]; then + echo dos.py destroy "${ENV_NAME}" + else + dos.py destroy "${ENV_NAME}" + fi + fi + + exit "${RES}" +} + +RouteTasks() { + # this selector defines task names that are recognised by this script + # and runs corresponding jobs for them + # running any jobs should exit this script + + case "${TASK_NAME}" in + test) + RunTest + ;; + *) + echo "Unknown task: ${TASK_NAME}!" + exit $INVALIDTASK_ERR + ;; + esac + exit 0 +} + +add_interface_to_bridge() { + env=$1 + net_name=$2 + nic=$3 + ip=$4 + + for net in $(virsh net-list |grep ${env}_${net_name} |awk '{print $1}');do + bridge=$(virsh net-info $net |grep -i bridge |awk '{print $2}') + setup_bridge $bridge $nic $ip && echo $net_name bridge $bridge ready + done +} + +setup_bridge() { + bridge=$1 + nic=$2 + ip=$3 + + sudo /sbin/brctl stp $bridge off + sudo /sbin/brctl addif $bridge $nic + # set if with existing ip down + for itf in $(sudo ip -o addr show to $ip | cut -d' ' -f2); do + echo deleting $ip from $itf + sudo ip addr del dev $itf $ip + done + echo adding $ip to $bridge + sudo /sbin/ip addr add $ip dev $bridge + echo $nic added to $bridge + sudo /sbin/ip link set dev $bridge up + if sudo /sbin/iptables-save |grep $bridge | grep -i reject| grep -q FORWARD; then + sudo /sbin/iptables -D FORWARD -o $bridge -j REJECT --reject-with icmp-port-unreachable + sudo /sbin/iptables -D FORWARD -i $bridge -j REJECT --reject-with icmp-port-unreachable + fi +} + +clean_old_bridges() { + for intf in $WORKSTATION_IFS; do + for br in $(/sbin/brctl show | grep -v "bridge name" | cut -f1 -d' '); do + /sbin/brctl show $br| grep -q $intf && sudo /sbin/brctl delif $br $intf \ + && sudo /sbin/ip link set dev $br down && echo $intf deleted from $br + done + done +} + +clean_iptables() { + sudo /sbin/iptables -F + sudo /sbin/iptables -t nat -F + sudo /sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE +} + +revert_ws() { + for i in $1 + do + vmrun -T ws-shared -h https://localhost:443/sdk -u $WORKSTATION_USERNAME -p $WORKSTATION_PASSWORD listRegisteredVM | grep -q $i || { echo "VM $i does not exist"; continue; } + echo vmrun: reverting $i to $WORKSTATION_SNAPSHOT + vmrun -T ws-shared -h https://localhost:443/sdk -u $WORKSTATION_USERNAME -p $WORKSTATION_PASSWORD revertToSnapshot "[standard] $i/$i.vmx" $WORKSTATION_SNAPSHOT || { echo "Error: revert of $i failed"; return 1; } + done + + for i in $1 + do + echo vmrun: starting $i + vmrun -T ws-shared -h https://localhost:443/sdk -u $WORKSTATION_USERNAME -p $WORKSTATION_PASSWORD start "[standard] $i/$i.vmx" || { echo "Error: $i failed to start"; return 1; } + done +} + +setup_net() { + env=$1 + add_interface_to_bridge $env public vmnet1 172.16.0.1/24 +} + +setup_stt() { + set -e + env=$1 + net_name='private' + nic='vmnet2' + + for net in $(virsh net-list |grep ${env}_${net_name} | awk '{print $1}');do + bridge=$(virsh net-info $net | grep -i bridge | awk '{print $2}') + done + sudo /sbin/brctl stp $bridge off + sudo /sbin/brctl addif $bridge $nic + echo $nic added to $bridge + sudo /sbin/ip link set dev $bridge up + if sudo /sbin/iptables-save | grep $bridge | grep -i reject| grep -q FORWARD; then + sudo /sbin/iptables -D FORWARD -o $bridge -j REJECT --reject-with icmp-port-unreachable + sudo /sbin/iptables -D FORWARD -i $bridge -j REJECT --reject-with icmp-port-unreachable + fi + + echo "Stt added to $net_name bridge $bridge" +} + +setup_external_net() { + nic='vmnet5' + + ip=${NSXT_ROUTING_NET_CIDR%\.*}.1 + gw_ip=${NSXT_ROUTING_NET_CIDR%\.*}.2 + mask=${NSXT_ROUTING_NET_CIDR##*\/} + + #set if with existing ip down + for itf in $(sudo ip -o addr show to $ip | cut -d' ' -f2); do + echo deleting $ip from $itf + sudo ip addr del $ip/$mask dev $itf + done + + for itf in $(sudo ip -o ro show to ${NSXT_FLOATING_NET_CIDR} | cut -d' ' -f5); do + echo deleting route to ${NSXT_FLOATING_NET_CIDR} dev $itf + sudo ip ro del ${NSXT_FLOATING_NET_CIDR} dev $itf + done + + set -e + sudo /sbin/ip addr add ${ip}/${mask} dev $nic + sudo /sbin/ip ro add ${NSXT_FLOATING_NET_CIDR} via ${gw_ip} + echo "Routing net added to $nic" +} + +# MAIN + +# first we want to get variable from command line options +GetoptsVariables "${@}" + +# then we define global variables and there defaults when needed +GlobalVariables + +# check do we have all critical variables set +CheckVariables + +# first we chdir into our working directory unless we dry run +CdWorkSpace + +# finally we can choose what to do according to TASK_NAME +RouteTasks diff --git a/spec/conf.py b/spec/conf.py index 125700d..dec989b 100644 --- a/spec/conf.py +++ b/spec/conf.py @@ -1,29 +1,27 @@ -# -*- coding: utf-8 -*- -# -# Fuel NSXv plugin documentation build configuration file, created by -# sphinx-quickstart on Fri Aug 14 12:14:29 2015. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. +"""Copyright 2016 Mirantis, Inc. -import sys -import os +Licensed under the Apache License, Version 2.0 (the "License"); you may +not use this file except in compliance with the License. You may obtain +copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations +under the License. +""" # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) +# sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom @@ -37,7 +35,7 @@ templates_path = ['_templates'] source_suffix = '.rst' # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' @@ -57,13 +55,13 @@ release = '1.0.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -71,27 +69,27 @@ exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # -- Options for HTML output ---------------------------------------------- @@ -103,26 +101,26 @@ html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -132,48 +130,48 @@ html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -#html_extra_path = [] +# html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'FuelNSXTplugindoc' @@ -183,13 +181,13 @@ htmlhelp_basename = 'FuelNSXTplugindoc' latex_elements = { # The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', +# 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', +# 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. -#'preamble': '', +# 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples @@ -202,23 +200,23 @@ latex_documents = [ # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output --------------------------------------- @@ -231,7 +229,7 @@ man_pages = [ ] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -246,13 +244,13 @@ texinfo_documents = [ ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False