diff options
Diffstat (limited to 'apex/tests')
22 files changed, 2398 insertions, 133 deletions
diff --git a/apex/tests/config/98faaca.diff b/apex/tests/config/98faaca.diff new file mode 100644 index 00000000..96462d5f --- /dev/null +++ b/apex/tests/config/98faaca.diff @@ -0,0 +1,331 @@ +From 98faacad44e39a456d9fe1a1d21f5a65e8de4fc1 Mon Sep 17 00:00:00 2001 +From: Janki Chhatbar <jchhatba@redhat.com> +Date: Tue, 23 Jan 2018 22:43:49 +0530 +Subject: [PATCH] Minor update steps for ODL + +Updating OpenStack (within release) means updating ODL from v1 to v1.1. +This is done by "openstack overcloud update" which collects +update_tasks. ODL needs 2 different steps to achieve this +minor update. These are called Level1 and Level2. L1 is +simple - stop ODL, update, start. This is taken care by paunch +and no separate implementation is needed. L2 has extra steps +which are implemented in update_tasks and post_update_tasks. + +Updating ODL within the same major release (1->1.1) consists of either +L1 or L2 steps. These steps are decided from ODLUpdateLevel parameter +specified in environments/services-docker/update-odl.yaml. + +Upgrading ODL to the next major release (1.1->2) requires +only the L2 steps. These are implemented as upgrade_tasks and +post_upgrade_tasks in https://review.opendev.org/489201. + +Steps involved in level 2 update are + 1. Block OVS instances to connect to ODL + 2. Set ODL upgrade flag to True + 3. Start ODL + 4. Start Neutron re-sync and wait for it to finish + 5. Delete OVS groups and ports + 6. Stop OVS + 7. Unblock OVS ports + 8. Start OVS + 9. Unset ODL upgrade flag + +These steps are exactly same as upgrade_tasks. +The logic implemented is: +follow upgrade_tasks; when update_level == 2 + +Change-Id: Ie532800663dd24313a7350b5583a5080ddb796e7 +--- + +diff --git a/common/deploy-steps.j2 b/common/deploy-steps.j2 +index 595e16c..c4fb05f 100644 +--- a/common/deploy-steps.j2 ++++ b/common/deploy-steps.j2 +@@ -23,6 +23,7 @@ + {% set post_upgrade_steps_max = 4 -%} + {% set fast_forward_upgrade_steps_max = 9 -%} + {% set fast_forward_upgrade_prep_steps_max = 3 -%} ++{% set post_update_steps_max = 4 -%} + + heat_template_version: queens + +@@ -590,3 +591,15 @@ + - include_tasks: {{role.name}}/fast_forward_upgrade_tasks.yaml + when: role_name == '{{role.name}}' and ansible_hostname == {{role.name}}[0] + {%- endfor %} ++ post_update_steps_tasks: | ++{%- for role in roles %} ++ - include: {{role.name}}/post_update_tasks.yaml ++ when: role_name == '{{role.name}}' ++{%- endfor %} ++ post_update_steps_playbook: | ++ - hosts: overcloud ++ tasks: ++ - include: post_update_steps_tasks.yaml ++ with_sequence: start=0 end={{post_update_steps_max-1}} ++ loop_control: ++ loop_var: step +diff --git a/common/services.yaml b/common/services.yaml +index 2a62c1b..c197b05 100644 +--- a/common/services.yaml ++++ b/common/services.yaml +@@ -283,6 +283,16 @@ + expression: coalesce($.data, []).where($ != null).select($.get('update_tasks')).where($ != null).flatten().distinct() + data: {get_attr: [ServiceChain, role_data]} + ++ PostUpdateTasks: ++ type: OS::Heat::Value ++ properties: ++ type: comma_delimited_list ++ value: ++ yaql: ++ # Note we use distinct() here to filter any identical tasks, e.g yum update for all services ++ expression: coalesce($.data, []).where($ != null).select($.get('post_update_tasks')).where($ != null).flatten().distinct() ++ data: {get_attr: [ServiceChain, role_data]} ++ + UpgradeBatchTasks: + type: OS::Heat::Value + properties: +@@ -349,6 +359,7 @@ + upgrade_tasks: {get_attr: [UpgradeTasks, value]} + post_upgrade_tasks: {get_attr: [PostUpgradeTasks, value]} + update_tasks: {get_attr: [UpdateTasks, value]} ++ post_update_tasks: {get_attr: [PostUpdateTasks, value]} + upgrade_batch_tasks: {get_attr: [UpgradeBatchTasks, value]} + service_metadata_settings: {get_attr: [ServiceServerMetadataHook, metadata]} + +diff --git a/docker/services/opendaylight-api.yaml b/docker/services/opendaylight-api.yaml +index 6175db9..3cafe53 100644 +--- a/docker/services/opendaylight-api.yaml ++++ b/docker/services/opendaylight-api.yaml +@@ -44,6 +44,14 @@ + type: string + description: Specifies the default CA cert to use if TLS is used for + services in the internal network. ++ ODLUpdateLevel: ++ default: 1 ++ description: Specify the level of update ++ type: number ++ constraints: ++ - allowed_values: ++ - 1 ++ - 2 + + conditions: + +@@ -167,23 +175,25 @@ + - opendaylight_enabled.rc == 0 + service: name=opendaylight state=stopped enabled=no + # Containarised deployment upgrade steps +- - name: remove journal and snapshots +- when: step|int == 0 +- file: +- path: /var/lib/opendaylight/{{item}} +- state: absent +- with_items: +- - snapshots +- - journal +- - name: Set ODL upgrade flag to True +- copy: +- dest: /var/lib/opendaylight/etc/opendaylight/datastore/initial/config/genius-mdsalutil-config.xml +- content: | +- <config xmlns="urn:opendaylight:params:xml:ns:yang:mdsalutil"> +- <upgradeInProgress>true</upgradeInProgress> +- </config> +- when: step|int == 1 +- post_upgrade_tasks: ++ - name: ODL container L2 update and upgrade tasks ++ block: &odl_container_upgrade_tasks ++ - name: remove journal and snapshots ++ when: step|int == 0 ++ file: ++ path: /var/lib/opendaylight/{{item}} ++ state: absent ++ with_items: ++ - snapshots ++ - journal ++ - name: Set ODL upgrade flag to True ++ copy: ++ dest: /var/lib/opendaylight/etc/opendaylight/datastore/initial/config/genius-mdsalutil-config.xml ++ content: | ++ <config xmlns="urn:opendaylight:params:xml:ns:yang:mdsalutil"> ++ <upgradeInProgress>true</upgradeInProgress> ++ </config> ++ when: step|int == 1 ++ post_upgrade_tasks: &odl_container_post_upgrade_tasks + - name: Unset upgrade flag in ODL + shell: + str_replace: +@@ -192,7 +202,20 @@ + -H "Content-Type: application/json" \ + $ODL_URI/restconf/config/genius-mdsalutil:config' + params: +- $ODL_USERNAME: {get_param: [OpenDaylightBase, OpenDaylightUsername]} +- $ODL_PASSWORD: {get_param: [OpenDaylightBase, OpenDaylightPassword]} ++ $ODL_USERNAME: {get_attr: [OpenDaylightBase, role_data, config_settings, 'opendaylight::username']} ++ $ODL_PASSWORD: {get_attr: [OpenDaylightBase, role_data, config_settings, 'opendaylight::password']} + $ODL_URI: {get_param: [EndpointMap, OpenDaylightInternal, uri]} + when: step|int == 0 ++ update_tasks: ++ - name: Get ODL update level ++ block: &get_odl_update_level ++ - name: store update level to update_level variable ++ set_fact: ++ odl_update_level: {get_param: ODLUpdateLevel} ++ - name: Run L2 update tasks that are similar to upgrade_tasks when update level is 2 ++ block: *odl_container_upgrade_tasks ++ when: odl_update_level == 2 ++ post_update_tasks: ++ - block: *get_odl_update_level ++ - block: *odl_container_post_upgrade_tasks ++ when: odl_update_level == 2 +\ No newline at end of file +diff --git a/environments/services-docker/update-odl.yaml b/environments/services-docker/update-odl.yaml +new file mode 100644 +index 0000000..87d74ef +--- /dev/null ++++ b/environments/services-docker/update-odl.yaml +@@ -0,0 +1,11 @@ ++# This file describes parameters needed for ODL update. ++# This file is to be used along with other env files during ++# level 2 minor update. ++# Level 2 update involves yang changes in ODL within same ODL release and ++# hence needs DB wipe and resync. ++# Level 1 is simple update - stop ODL, pull new image, start ODL ++# This file is not be used during level1 update or major upgrade. ++# In case doubt, please reach out to ODL developers on #tripleo IRC channel ++ ++parameter_defaults: ++ ODLUpdateLevel: 2 +\ No newline at end of file +diff --git a/puppet/services/opendaylight-ovs.yaml b/puppet/services/opendaylight-ovs.yaml +index 3390645..958e1bb 100644 +--- a/puppet/services/opendaylight-ovs.yaml ++++ b/puppet/services/opendaylight-ovs.yaml +@@ -104,6 +104,14 @@ + type: string + description: Specifies the default CA cert to use if TLS is used for + services in the internal network. ++ ODLUpdateLevel: ++ default: 1 ++ description: Specify the level of update ++ type: number ++ constraints: ++ - allowed_values: ++ - 1 ++ - 2 + + parameter_groups: + - label: deprecated +@@ -230,14 +238,16 @@ + - openvswitch_enabled.rc == 0 + service: name=openvswitch state=stopped + # Container upgrade steps. +- - name: Block connections to ODL. #This rule will be inserted at the top. +- iptables: chain=OUTPUT action=insert protocol=tcp destination_port={{ item }} jump=DROP +- when: step|int == 0 +- with_items: +- - 6640 +- - 6653 +- - 6633 +- post_upgrade_tasks: ++ - name: ODL container L2 update and upgrade tasks ++ block: &odl_container_upgrade_tasks ++ - name: Block connections to ODL. #This rule will be inserted at the top. ++ iptables: chain=OUTPUT action=insert protocol=tcp destination_port={{ item }} jump=DROP ++ when: step|int == 0 ++ with_items: ++ - 6640 ++ - 6653 ++ - 6633 ++ post_upgrade_tasks: &odl_container_post_upgrade_tasks + - name: Check service openvswitch is running + command: systemctl is-active --quiet openvswitch + tags: common +@@ -260,6 +270,20 @@ + - name: start openvswitch service + when: step|int == 3 + service : name=openvswitch state=started ++ update_tasks: ++ - name: Get ODL update level ++ block: &get_odl_update_level ++ - name: store update level to update_level variable ++ set_fact: ++ odl_update_level: {get_param: ODLUpdateLevel} ++ - name: Run L2 update tasks that are similar to upgrade_tasks when update level is 2 ++ block: *odl_container_upgrade_tasks ++ when: odl_update_level == 2 ++ post_update_tasks: ++ - block: *get_odl_update_level ++ - block: *odl_container_post_upgrade_tasks ++ when: odl_update_level == 2 ++ + metadata_settings: + if: + - internal_tls_enabled +@@ -267,4 +291,4 @@ + - service: ovs + network: {get_param: [ServiceNetMap, OpendaylightApiNetwork]} + type: node +- - null ++ - null +\ No newline at end of file +diff --git a/releasenotes/notes/odl_upgrade-f5540d242b9a6b52.yaml b/releasenotes/notes/odl_upgrade-f5540d242b9a6b52.yaml +index 45703d0..e2943de 100644 +--- a/releasenotes/notes/odl_upgrade-f5540d242b9a6b52.yaml ++++ b/releasenotes/notes/odl_upgrade-f5540d242b9a6b52.yaml +@@ -1,6 +1,6 @@ + --- + +-features: ++upgrade: + - Add ODL upgradability + Steps of upgrade are as follows + 1. Block OVS instances to connect to ODL done in upgrade_tasks +diff --git a/releasenotes/notes/update_odl-cb997ce5c136ebb7.yaml b/releasenotes/notes/update_odl-cb997ce5c136ebb7.yaml +new file mode 100644 +index 0000000..1bcf8ed +--- /dev/null ++++ b/releasenotes/notes/update_odl-cb997ce5c136ebb7.yaml +@@ -0,0 +1,19 @@ ++--- ++features: ++ - Minor update ODL steps are added. ODL minor update (within same ODL ++ release) can have 2 different workflow. These are called level 1 and ++ level2. Level 1 is simple - stop, update and start ODL. Level 2 is ++ complex and involved yang model changes. This requires wiping of ++ DB and resync to repopulate the data. ++ Steps involved in level 2 update are ++ 1. Block OVS instances to connect to ODL ++ 2. Set ODL upgrade flag to True ++ 3. Start ODL ++ 4. Start Neutron re-sync and wait for it to finish ++ 5. Delete OVS groups and ports ++ 6. Stop OVS ++ 7. Unblock OVS ports ++ 8. Start OVS ++ 9. Unset ODL upgrade flag ++ To achieve L2 update, use "-e environments/services-docker/ ++ update-odl.yaml" along with other env files to the update command. +\ No newline at end of file +diff --git a/tools/yaml-validate.py b/tools/yaml-validate.py +index 59473f5..9ab6a87 100755 +--- a/tools/yaml-validate.py ++++ b/tools/yaml-validate.py +@@ -46,11 +46,11 @@ + OPTIONAL_DOCKER_SECTIONS = ['docker_puppet_tasks', 'upgrade_tasks', + 'fast_forward_upgrade_tasks', + 'post_upgrade_tasks', 'update_tasks', +- 'service_config_settings', 'host_prep_tasks', +- 'metadata_settings', 'kolla_config', +- 'global_config_settings', 'logging_source', +- 'logging_groups', 'external_deploy_tasks', +- 'external_post_deploy_tasks', ++ 'post_update_tasks', 'service_config_settings', ++ 'host_prep_tasks', 'metadata_settings', ++ 'kolla_config', 'global_config_settings', ++ 'logging_source', 'logging_groups', ++ 'external_deploy_tasks', 'external_post_deploy_tasks', + 'docker_config_scripts', 'step_config'] + REQUIRED_DOCKER_PUPPET_CONFIG_SECTIONS = ['config_volume', 'step_config', + 'config_image'] diff --git a/apex/tests/config/admin.xml b/apex/tests/config/admin.xml new file mode 100644 index 00000000..69b15b1f --- /dev/null +++ b/apex/tests/config/admin.xml @@ -0,0 +1,7 @@ +<network connections='1' ipv6='yes'> + <name>admin</name> + <uuid>761c34f8-2a72-4205-8e69-5ed6626c6efa</uuid> + <forward mode='bridge'/> + <bridge name='br-admin'/> + <virtualport type='openvswitch'/> +</network> diff --git a/apex/tests/config/baremetal0.xml b/apex/tests/config/baremetal0.xml new file mode 100644 index 00000000..4ff8f65a --- /dev/null +++ b/apex/tests/config/baremetal0.xml @@ -0,0 +1,73 @@ +<domain type='kvm'> + <name>baremetal0</name> + <uuid>25bf15b6-130c-4bca-87af-e5cbc14bb454</uuid> + <memory unit='KiB'>12582912</memory> + <currentMemory unit='KiB'>12582912</currentMemory> + <vcpu placement='static'>4</vcpu> + <resource> + <partition>/machine</partition> + </resource> + <os> + <type arch='x86_64' machine='pc-i440fx-rhel7.0.0'>hvm</type> + <boot dev='hd'/> + <bootmenu enable='no'/> + </os> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <cpu mode='host-passthrough'/> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <devices> + <emulator>/usr/libexec/qemu-kvm</emulator> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2' cache='unsafe'/> + <source file='/home/images/baremetal0.qcow2'/> + <target dev='sda' bus='sata'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='scsi' index='0' model='virtio-scsi'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </controller> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <controller type='sata' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> + </controller> + <interface type='bridge'> + <mac address='00:5b:06:25:0c:dc'/> + <source bridge='br-admin'/> + <virtualport type='openvswitch'> + <parameters interfaceid='04b63cb9-21a9-4385-bbd6-df677a5eeecf'/> + </virtualport> + <model type='virtio'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </interface> + <serial type='pty'> + <target port='0'/> + </serial> + <console type='pty'> + <target type='serial' port='0'/> + </console> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1'> + <listen type='address' address='127.0.0.1'/> + </graphics> + <video> + <model type='cirrus' vram='16384' heads='1' primary='yes'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> + </video> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/> + </memballoon> + </devices> + <seclabel type='dynamic' model='selinux' relabel='yes'/> + <seclabel type='dynamic' model='dac' relabel='yes'/> +</domain> diff --git a/apex/tests/config/common-patches.yaml b/apex/tests/config/common-patches.yaml new file mode 100644 index 00000000..fef8fcd0 --- /dev/null +++ b/apex/tests/config/common-patches.yaml @@ -0,0 +1,6 @@ +--- +patches: + queens: + undercloud: + - change-id: I2e0a40d7902f592e4b7bd727f57048111e0bea36 + project: openstack/tripleo-common diff --git a/apex/tests/config/dummy-deploy-settings.yaml b/apex/tests/config/dummy-deploy-settings.yaml new file mode 100644 index 00000000..54890f38 --- /dev/null +++ b/apex/tests/config/dummy-deploy-settings.yaml @@ -0,0 +1,19 @@ +--- +global_params: + ha_enabled: false + patches: + undercloud: + - change-id: I2e0a40d7902f592e4b7bd727f57048111e0bea36 + project: openstack/tripleo-common + overcloud: + - change-id: Ie988ba6a2d444a614e97c0edf5fce24b23970310 + project: openstack/puppet-tripleo +deploy_options: + containers: true + os_version: queens + sdn_controller: opendaylight + odl_version: oxygen + tacker: false + congress: false + sfc: false + vpn: false diff --git a/apex/tests/config/inventory-virt-1-compute-node.yaml b/apex/tests/config/inventory-virt-1-compute-node.yaml new file mode 100644 index 00000000..4c2dc5d4 --- /dev/null +++ b/apex/tests/config/inventory-virt-1-compute-node.yaml @@ -0,0 +1,14 @@ +--- +nodes: + node0: + arch: x86_64 + capabilities: profile:compute + cpu: 4 + disk: 41 + ipmi_ip: 192.168.122.1 + ipmi_pass: password + ipmi_user: admin + mac_address: 00:a8:58:29:f9:99 + memory: 10240 + pm_port: 6230 + pm_type: pxe_ipmitool diff --git a/apex/tests/config/node.yaml b/apex/tests/config/node.yaml new file mode 100644 index 00000000..e05644c9 --- /dev/null +++ b/apex/tests/config/node.yaml @@ -0,0 +1,12 @@ +--- +servers: + overcloud-controller-0.opnfvlf.org: + address: 192.0.2.28 + orig-ctl-mac: 00:5b:06:25:0c:dc + ovs-controller: tcp:192.0.2.28:6653 + ovs-managers: + - ptcp:6639:127.0.0.1 + - tcp:192.0.2.28:6640 + type: controller + user: heat-admin + vNode-name: baremetal0 diff --git a/apex/tests/config/snapshot.properties b/apex/tests/config/snapshot.properties new file mode 100644 index 00000000..64c149e2 --- /dev/null +++ b/apex/tests/config/snapshot.properties @@ -0,0 +1,2 @@ +OPNFV_SNAP_URL=artifacts.opnfv.org/apex/master/noha/apex-csit-snap-2018-08-05.tar.gz +OPNFV_SNAP_SHA512SUM=bb0c6fa0e675dcb39cfad11d81bb99f309d5cfc236e36a74d05ee813584f3e5bb92aa23dec775846317b75d574f8c86186c666f78a299c24fb68849897bdd4bc diff --git a/apex/tests/test_apex_build_utils.py b/apex/tests/test_apex_build_utils.py index d9d542d6..36caaf1f 100644 --- a/apex/tests/test_apex_build_utils.py +++ b/apex/tests/test_apex_build_utils.py @@ -9,17 +9,20 @@ import argparse import git +import os +import unittest from mock import patch from apex import build_utils +from apex.tests import constants as con from nose.tools import ( assert_is_instance, assert_raises) -class TestBuildUtils(object): +class TestBuildUtils(unittest.TestCase): @classmethod def setup_class(cls): """This method is run once for each class before any tests are run""" @@ -165,3 +168,25 @@ class TestBuildUtils(object): def test_main_debug(self, mock_get_parser): with patch.object(build_utils.sys, 'argv', self.sys_argv_debug): build_utils.main() + + def test_strip_patch_sections(self): + with open(os.path.join(con.TEST_DUMMY_CONFIG, '98faaca.diff')) as fh: + dummy_patch = fh.read() + tmp_patch = build_utils.strip_patch_sections(dummy_patch) + self.assertNotRegex(tmp_patch, 'releasenotes') + self.assertNotRegex(tmp_patch, 'Minor update ODL steps') + self.assertNotRegex(tmp_patch, 'Steps of upgrade are as follows') + self.assertNotRegex(tmp_patch, 'Steps invlolved in level 2 update') + + def test_is_path_in_patch(self): + with open(os.path.join(con.TEST_DUMMY_CONFIG, '98faaca.diff')) as fh: + dummy_patch = fh.read() + self.assertTrue(build_utils.is_path_in_patch(dummy_patch, + 'releasenotes/')) + + def test_strip_no_patch_sections(self): + with open(os.path.join(con.TEST_DUMMY_CONFIG, '98faaca.diff')) as fh: + dummy_patch = fh.read() + tmp_patch = build_utils.strip_patch_sections(dummy_patch, + sections=[]) + self.assertEqual(dummy_patch, tmp_patch) diff --git a/apex/tests/test_apex_common_builder.py b/apex/tests/test_apex_common_builder.py index c32f72c9..3ff95bb5 100644 --- a/apex/tests/test_apex_common_builder.py +++ b/apex/tests/test_apex_common_builder.py @@ -10,11 +10,22 @@ import unittest from apex.builders import common_builder as c_builder +from apex.builders import exceptions from apex.common import constants as con from mock import patch from mock import mock_open from mock import MagicMock +DOCKER_YAML = { + 'resource_registry': { + 'OS::TripleO::Services::NovaApi': '../docker/services/nova-api.yaml', + 'OS::TripleO::Services::NovaConductor': + '../docker/services/nova-conductor.yaml' + } +} + +a_mock_open = mock_open(read_data=None) + class TestCommonBuilder(unittest.TestCase): @classmethod @@ -39,13 +50,55 @@ class TestCommonBuilder(unittest.TestCase): path = '/etc/puppet/modules/tripleo' self.assertEquals(c_builder.project_to_path(project), path) project = 'openstack/nova' - path = '/usr/lib/python2.7/site-packages/nova' + path = '/usr/lib/python2.7/site-packages/' self.assertEquals(c_builder.project_to_path(project), path) + def test_is_patch_promoted(self): + dummy_change = {'submitted': '2017-06-05 20:23:09.000000000', + 'status': 'MERGED'} + self.assertTrue(c_builder.is_patch_promoted(dummy_change, + 'master', + con.DOCKERHUB_OOO)) + + def test_is_patch_promoted_docker(self): + dummy_change = {'submitted': '2017-06-05 20:23:09.000000000', + 'status': 'MERGED'} + dummy_image = 'centos-binary-opendaylight' + self.assertTrue(c_builder.is_patch_promoted(dummy_change, + 'master', + con.DOCKERHUB_OOO, + docker_image=dummy_image)) + + def test_patch_not_promoted(self): + dummy_change = {'submitted': '2900-06-05 20:23:09.000000000', + 'status': 'MERGED'} + self.assertFalse(c_builder.is_patch_promoted(dummy_change, + 'master', + con.DOCKERHUB_OOO)) + + def test_patch_not_promoted_docker(self): + dummy_change = {'submitted': '2900-06-05 20:23:09.000000000', + 'status': 'MERGED'} + dummy_image = 'centos-binary-opendaylight' + self.assertFalse(c_builder.is_patch_promoted(dummy_change, + 'master', + con.DOCKERHUB_OOO, + docker_image=dummy_image)) + + def test_patch_not_promoted_and_not_merged(self): + dummy_change = {'submitted': '2900-06-05 20:23:09.000000000', + 'status': 'BLAH'} + self.assertFalse(c_builder.is_patch_promoted(dummy_change, + 'master', + con.DOCKERHUB_OOO)) + @patch('builtins.open', mock_open()) + @patch('apex.builders.common_builder.is_patch_promoted') + @patch('apex.build_utils.get_change') @patch('apex.build_utils.get_patch') @patch('apex.virtual.utils.virt_customize') - def test_add_upstream_patches(self, mock_customize, mock_get_patch): + def test_add_upstream_patches(self, mock_customize, mock_get_patch, + mock_get_change, mock_is_patch_promoted): mock_get_patch.return_value = None change_id = 'I301370fbf47a71291614dd60e4c64adc7b5ebb42' patches = [{ @@ -64,10 +117,116 @@ class TestCommonBuilder(unittest.TestCase): {con.VIRT_RUN_CMD: "cd {} && patch -p1 < {}".format( project_path, patch_file)}] mock_get_patch.return_value = 'some random diff' + mock_is_patch_promoted.return_value = False c_builder.add_upstream_patches(patches, 'dummy.qcow2', '/dummytmp/') mock_customize.assert_called_once_with(test_virt_ops, 'dummy.qcow2') @patch('builtins.open', mock_open()) + @patch('apex.builders.common_builder.is_patch_promoted') + @patch('apex.build_utils.get_change') + @patch('apex.build_utils.get_patch') + @patch('apex.virtual.utils.virt_customize') + def test_add_upstream_patches_docker_puppet( + self, mock_customize, mock_get_patch, mock_get_change, + mock_is_patch_promoted): + change_id = 'I301370fbf47a71291614dd60e4c64adc7b5ebb42' + patches = [{ + 'change-id': change_id, + 'project': 'openstack/puppet-tripleo' + }] + project_path = '/etc/puppet/modules/tripleo' + patch_file = "{}.patch".format(change_id) + patch_file_path = "/dummytmp/{}".format(patch_file) + test_virt_ops = [ + {con.VIRT_INSTALL: 'patch'}, + {con.VIRT_UPLOAD: "{}:{}".format(patch_file_path, + project_path)}, + {con.VIRT_RUN_CMD: "cd {} && patch -p1 < {}".format( + project_path, patch_file)}] + mock_get_patch.return_value = 'some random diff' + mock_is_patch_promoted.return_value = False + c_builder.add_upstream_patches(patches, 'dummy.qcow2', '/dummytmp/', + uc_ip='192.0.2.1', + docker_tag='latest') + mock_customize.assert_called_once_with(test_virt_ops, 'dummy.qcow2') + + @patch('builtins.open', mock_open()) + @patch('apex.builders.common_builder.is_patch_promoted') + @patch('apex.build_utils.get_change') + @patch('apex.builders.common_builder.project_to_docker_image') + @patch('apex.builders.overcloud_builder.build_dockerfile') + @patch('apex.build_utils.get_patch') + @patch('apex.virtual.utils.virt_customize') + def test_add_upstream_patches_docker_python( + self, mock_customize, mock_get_patch, mock_build_docker_file, + mock_project2docker, ock_get_change, mock_is_patch_promoted): + mock_project2docker.return_value = ['NovaApi'] + change_id = 'I301370fbf47a71291614dd60e4c64adc7b5ebb42' + patches = [{ + 'change-id': change_id, + 'project': 'openstack/nova' + }] + mock_get_patch.return_value = 'some random diff' + mock_is_patch_promoted.return_value = False + services = c_builder.add_upstream_patches(patches, 'dummy.qcow2', + '/dummytmp/', + uc_ip='192.0.2.1', + docker_tag='latest') + assert mock_customize.not_called + assert mock_build_docker_file.called + self.assertSetEqual(services, {'NovaApi'}) + + @patch('builtins.open', mock_open()) + @patch('apex.builders.common_builder.is_patch_promoted') + @patch('apex.build_utils.get_change') + @patch('apex.builders.common_builder.project_to_docker_image') + @patch('apex.builders.overcloud_builder.build_dockerfile') + @patch('apex.build_utils.get_patch') + @patch('apex.virtual.utils.virt_customize') + def test_not_add_upstream_patches_docker_python( + self, mock_customize, mock_get_patch, mock_build_docker_file, + mock_project2docker, ock_get_change, mock_is_patch_promoted): + # Test that the calls are not made when the patch is already merged and + # promoted + mock_project2docker.return_value = ['NovaApi'] + change_id = 'I301370fbf47a71291614dd60e4c64adc7b5ebb42' + patches = [{ + 'change-id': change_id, + 'project': 'openstack/nova' + }] + mock_get_patch.return_value = 'some random diff' + mock_is_patch_promoted.return_value = True + services = c_builder.add_upstream_patches(patches, 'dummy.qcow2', + '/dummytmp/', + uc_ip='192.0.2.1', + docker_tag='latest') + assert mock_customize.not_called + assert mock_build_docker_file.not_called + assert len(services) == 0 + + @patch('builtins.open', mock_open()) + @patch('apex.builders.common_builder.is_patch_promoted') + @patch('apex.build_utils.get_change') + @patch('apex.build_utils.get_patch') + @patch('apex.virtual.utils.virt_customize') + def test_not_upstream_patches_docker_puppet( + self, mock_customize, mock_get_patch, mock_get_change, + mock_is_patch_promoted): + # Test that the calls are not made when the patch is already merged and + # promoted + change_id = 'I301370fbf47a71291614dd60e4c64adc7b5ebb42' + patches = [{ + 'change-id': change_id, + 'project': 'openstack/puppet-tripleo' + }] + mock_get_patch.return_value = 'some random diff' + mock_is_patch_promoted.return_value = True + c_builder.add_upstream_patches(patches, 'dummy.qcow2', '/dummytmp/', + uc_ip='192.0.2.1', + docker_tag='latest') + assert mock_customize.not_called + + @patch('builtins.open', mock_open()) @patch('apex.virtual.utils.virt_customize') def test_add_repo(self, mock_customize): c_builder.add_repo('fake/url', 'dummyrepo', 'dummy.qcow2', @@ -85,3 +244,67 @@ class TestCommonBuilder(unittest.TestCase): self.assertEqual(c_builder.create_git_archive('fake/url', 'dummyrepo', '/dummytmp/'), '/dummytmp/dummyrepo.tar') + + def test_project_to_docker_image(self): + found_services = c_builder.project_to_docker_image('nova', + con.DOCKERHUB_OOO) + assert 'nova-api' in found_services + + @patch('apex.common.utils.open_webpage') + def test_project_to_docker_image_bad_web_content( + self, mock_open_web): + mock_open_web.return_value = b'{"blah": "blah"}' + self.assertRaises(exceptions.ApexCommonBuilderException, + c_builder.project_to_docker_image, + 'nova', + con.DOCKERHUB_OOO) + + def test_get_neutron_driver(self): + ds_opts = {'dataplane': 'fdio', + 'sdn_controller': 'opendaylight', + 'odl_version': 'master', + 'vpn': False, + 'sriov': False} + self.assertEquals(c_builder.get_neutron_driver(ds_opts), + 'odl') + ds_opts['sdn_controller'] = None + ds_opts['vpp'] = True + self.assertEquals(c_builder.get_neutron_driver(ds_opts), + 'vpp') + ds_opts['sdn_controller'] = 'ovn' + self.assertEquals(c_builder.get_neutron_driver(ds_opts), + 'ovn') + + @patch('apex.builders.common_builder.yaml') + @patch('apex.overcloud.deploy.os.path.isfile') + @patch('builtins.open', a_mock_open, create=True) + def test_prepare_container_images(self, mock_is_file, mock_yaml): + mock_yaml.safe_load.return_value = { + 'parameter_defaults': { + 'ContainerImagePrepare': [ + {'set': + {'namespace': 'blah', + 'neutron_driver': 'null', + } + } + ] + } + } + expected_output = { + 'parameter_defaults': { + 'ContainerImagePrepare': [ + {'set': + {'namespace': 'docker.io/tripleoqueens', + 'neutron_driver': 'odl', + } + } + ] + } + } + + c_builder.prepare_container_images('dummy.yaml', 'queens', + 'odl') + mock_yaml.safe_dump.assert_called_with( + expected_output, + a_mock_open.return_value, + default_flow_style=False) diff --git a/apex/tests/test_apex_common_utils.py b/apex/tests/test_apex_common_utils.py index 6f2a9476..1ecb7df6 100644 --- a/apex/tests/test_apex_common_utils.py +++ b/apex/tests/test_apex_common_utils.py @@ -12,12 +12,14 @@ import os import shutil import urllib.error +from apex.common import exceptions from apex.common import utils from apex.settings.network_settings import NetworkSettings from apex.tests.constants import ( TEST_CONFIG_DIR, TEST_PLAYBOOK_DIR) +from mock import patch, mock_open from nose.tools import ( assert_equal, assert_is_instance, @@ -25,6 +27,7 @@ from nose.tools import ( assert_raises) NET_SETS = os.path.join(TEST_CONFIG_DIR, 'network', 'network_settings.yaml') +a_mock_open = mock_open(read_data=None) class TestCommonUtils: @@ -61,8 +64,11 @@ class TestCommonUtils: def test_run_ansible(self): playbook = 'apex/tests/playbooks/test_playbook.yaml' + extra_vars = [{'testvar1': 'value1', 'testvar2': 'value2'}] assert_equal(utils.run_ansible(None, os.path.join(playbook), dry_run=True), None) + assert_equal(utils.run_ansible(extra_vars, os.path.join(playbook), + dry_run=True, host='1.1.1.1'), None) def test_failed_run_ansible(self): playbook = 'apex/tests/playbooks/test_failed_playbook.yaml' @@ -78,7 +84,7 @@ class TestCommonUtils: def test_fetch_upstream_previous_file(self): test_file = 'overcloud-full.tar.md5' - url = 'https://images.rdoproject.org/master/delorean/' \ + url = 'https://images.rdoproject.org/master/rdo_trunk/' \ 'current-tripleo/stable/' os.makedirs('/tmp/fetch_test', exist_ok=True) open("/tmp/fetch_test/{}".format(test_file), 'w').close() @@ -100,3 +106,57 @@ class TestCommonUtils: url, ['dummy_test.tar']) assert os.path.isfile('/tmp/fetch_test/test.txt') shutil.rmtree('/tmp/fetch_test') + + def test_nofetch_upstream_and_unpack(self): + test_file = 'overcloud-full.tar.md5' + url = 'https://images.rdoproject.org/master/delorean/' \ + 'current-tripleo/stable/' + os.makedirs('/tmp/fetch_test', exist_ok=True) + target = "/tmp/fetch_test/{}".format(test_file) + open(target, 'w').close() + target_mtime = os.path.getmtime(target) + utils.fetch_upstream_and_unpack('/tmp/fetch_test', + url, [test_file], fetch=False) + post_target_mtime = os.path.getmtime(target) + shutil.rmtree('/tmp/fetch_test') + assert_equal(target_mtime, post_target_mtime) + + def test_nofetch_upstream_and_unpack_no_target(self): + test_file = 'overcloud-full.tar.md5' + url = 'https://images.rdoproject.org/master/delorean/' \ + 'current-tripleo/stable/' + utils.fetch_upstream_and_unpack('/tmp/fetch_test', + url, [test_file]) + assert os.path.isfile("/tmp/fetch_test/{}".format(test_file)) + shutil.rmtree('/tmp/fetch_test') + + def test_open_webpage(self): + output = utils.open_webpage('http://opnfv.org') + assert output is not None + + def test_open_invalid_webpage(self): + assert_raises(exceptions.FetchException, utils.open_webpage, + 'http://inv4lIdweb-page.com') + + @patch('builtins.open', a_mock_open) + @patch('yaml.safe_dump') + @patch('yaml.safe_load') + def test_edit_tht_env(self, mock_yaml_load, mock_yaml_dump): + settings = {'SomeParameter': 'some_value'} + mock_yaml_load.return_value = { + 'parameter_defaults': {'SomeParameter': 'dummy'} + } + utils.edit_tht_env('/dummy-environment.yaml', 'parameter_defaults', + settings) + new_data = {'parameter_defaults': settings} + mock_yaml_dump.assert_called_once_with(new_data, a_mock_open(), + default_flow_style=False) + + def test_unique(self): + dummy_list = [1, 2, 1, 3, 4, 5, 5] + assert_equal(utils.unique(dummy_list), [1, 2, 3, 4, 5]) + + def test_find_container_client(self): + for version in 'rocky', 'queens': + assert_equal(utils.find_container_client(version), 'docker') + assert_equal(utils.find_container_client('master'), 'podman') diff --git a/apex/tests/test_apex_deploy.py b/apex/tests/test_apex_deploy.py index 403b7099..004c21c1 100644 --- a/apex/tests/test_apex_deploy.py +++ b/apex/tests/test_apex_deploy.py @@ -8,6 +8,7 @@ ############################################################################## import argparse +import os import unittest from mock import patch @@ -17,12 +18,12 @@ from mock import mock_open from apex.common.exceptions import ApexDeployException from apex.common.constants import DEFAULT_OS_VERSION -from apex.deploy import deploy_quickstart from apex.deploy import validate_cross_settings from apex.deploy import build_vms from apex.deploy import create_deploy_parser from apex.deploy import validate_deploy_args from apex.deploy import main +from apex.tests.constants import TEST_DUMMY_CONFIG from nose.tools import ( assert_is_instance, @@ -48,9 +49,6 @@ class TestDeploy(unittest.TestCase): def teardown(self): """This method is run once after _each_ test method is executed""" - def test_deloy_quickstart(self): - deploy_quickstart(None, None, None) - def test_validate_cross_settings(self): deploy_settings = {'deploy_options': {'dataplane': 'ovs'}} net_settings = Mock() @@ -85,12 +83,23 @@ class TestDeploy(unittest.TestCase): args = Mock() args.inventory_file = None args.virtual = True + args.snapshot = False + validate_deploy_args(args) + + def test_validate_snapshot_deploy_args(self): + args = Mock() + args.deploy_settings_file = os.path.join(TEST_DUMMY_CONFIG, + 'dummy-deploy-settings.yaml') + args.inventory_file = None + args.virtual = True + args.snapshot = True validate_deploy_args(args) def test_validate_deploy_args_no_virt_no_inv(self): args = Mock() args.inventory_file = 'file_name' args.virtual = False + args.snapshot = False assert_raises(ApexDeployException, validate_deploy_args, args) @patch('apex.deploy.os.path') @@ -99,14 +108,19 @@ class TestDeploy(unittest.TestCase): args = Mock() args.inventory_file = None args.virtual = True + args.snapshot = False assert_raises(ApexDeployException, validate_deploy_args, args) def test_validate_deploy_args_virt_and_inv_file(self): args = Mock() args.inventory_file = 'file_name' args.virtual = True + args.snapshot = False assert_raises(ApexDeployException, validate_deploy_args, args) + @patch('apex.deploy.c_builder') + @patch('apex.deploy.ApexDeployment') + @patch('apex.deploy.uc_builder') @patch('apex.deploy.network_data.create_network_data') @patch('apex.deploy.shutil') @patch('apex.deploy.oc_deploy') @@ -132,7 +146,8 @@ class TestDeploy(unittest.TestCase): mock_deploy_sets, mock_net_sets, mock_net_env, mock_utils, mock_parsers, mock_oc_cfg, mock_virt_utils, mock_inv, mock_build_vms, mock_uc_lib, - mock_oc_deploy, mock_shutil, mock_network_data): + mock_oc_deploy, mock_shutil, mock_network_data, + mock_uc_builder, mock_deployment, mock_c_builder): net_sets_dict = {'networks': MagicMock(), 'dns_servers': 'test'} ds_opts_dict = {'global_params': MagicMock(), @@ -142,13 +157,16 @@ class TestDeploy(unittest.TestCase): 'dataplane': 'ovs', 'sfc': False, 'vpn': False, + 'vim': 'openstack', 'yardstick': 'test', - 'os_version': DEFAULT_OS_VERSION}} + 'os_version': DEFAULT_OS_VERSION, + 'containers': False}} args = mock_parser.return_value.parse_args.return_value args.virtual = False args.quickstart = False args.debug = False - args.upstream = False + args.snapshot = False + args.upstream = True net_sets = mock_net_sets.return_value net_sets.enabled_network_list = ['external'] net_sets.__getitem__.side_effect = net_sets_dict.__getitem__ @@ -159,6 +177,7 @@ class TestDeploy(unittest.TestCase): mock_parsers.parse_nova_output.return_value = {'testnode1': 'test'} main() + @patch('apex.deploy.SnapshotDeployment') @patch('apex.deploy.validate_cross_settings') @patch('apex.deploy.virt_utils') @patch('apex.deploy.utils') @@ -169,15 +188,19 @@ class TestDeploy(unittest.TestCase): @patch('apex.deploy.os') @patch('apex.deploy.create_deploy_parser') @patch('builtins.open', a_mock_open, create=True) - def test_main_qs(self, mock_parser, mock_os, mock_deploy, - mock_net_sets, mock_net_env, mock_inv, mock_utils, - mock_virt_utils, mock_cross): + def test_main_snapshot(self, mock_parser, mock_os, mock_deploy, + mock_net_sets, mock_net_env, mock_inv, mock_utils, + mock_virt_utils, mock_cross, mock_snap_deployment): args = mock_parser.return_value.parse_args.return_value args.virtual = False - args.quickstart = True + args.snapshot = True args.debug = True main() + mock_snap_deployment.assert_called() + @patch('apex.deploy.c_builder') + @patch('apex.deploy.ApexDeployment') + @patch('apex.deploy.uc_builder') @patch('apex.deploy.network_data.create_network_data') @patch('apex.deploy.shutil') @patch('apex.deploy.oc_deploy') @@ -203,7 +226,8 @@ class TestDeploy(unittest.TestCase): mock_deploy_sets, mock_net_sets, mock_net_env, mock_utils, mock_parsers, mock_oc_cfg, mock_virt_utils, mock_inv, mock_build_vms, mock_uc_lib, - mock_oc_deploy, mock_shutil, mock_network_data): + mock_oc_deploy, mock_shutil, mock_network_data, + mock_uc_builder, mock_deployment, mock_c_builder): # didn't work yet line 412 # net_sets_dict = {'networks': {'admin': {'cidr': MagicMock()}}, # 'dns_servers': 'test'} @@ -215,8 +239,10 @@ class TestDeploy(unittest.TestCase): 'dataplane': 'ovs', 'sfc': False, 'vpn': False, + 'vim': 'openstack', 'yardstick': 'test', - 'os_version': DEFAULT_OS_VERSION}} + 'os_version': DEFAULT_OS_VERSION, + 'containers': False}} args = mock_parser.return_value.parse_args.return_value args.virtual = True args.quickstart = False @@ -226,7 +252,72 @@ class TestDeploy(unittest.TestCase): args.virt_compute_nodes = 1 args.virt_compute_ram = None args.virt_default_ram = 12 - args.upstream = False + args.upstream = True + args.snapshot = False + net_sets = mock_net_sets.return_value + net_sets.enabled_network_list = ['admin'] + deploy_sets = mock_deploy_sets.return_value + deploy_sets.__getitem__.side_effect = ds_opts_dict.__getitem__ + deploy_sets.__contains__.side_effect = ds_opts_dict.__contains__ + main() + args.virt_compute_ram = 16 + args.virt_default_ram = 10 + main() + + @patch('apex.deploy.ApexDeployment') + @patch('apex.deploy.c_builder') + @patch('apex.deploy.uc_builder') + @patch('apex.deploy.oc_builder') + @patch('apex.deploy.network_data.create_network_data') + @patch('apex.deploy.shutil') + @patch('apex.deploy.oc_deploy') + @patch('apex.deploy.uc_lib') + @patch('apex.deploy.build_vms') + @patch('apex.deploy.Inventory') + @patch('apex.deploy.virt_utils') + @patch('apex.deploy.oc_cfg') + @patch('apex.deploy.parsers') + @patch('apex.deploy.utils') + @patch('apex.deploy.NetworkEnvironment') + @patch('apex.deploy.NetworkSettings') + @patch('apex.deploy.DeploySettings') + @patch('apex.deploy.os') + @patch('apex.deploy.json') + @patch('apex.deploy.jumphost') + @patch('apex.deploy.validate_cross_settings') + @patch('apex.deploy.validate_deploy_args') + @patch('apex.deploy.create_deploy_parser') + @patch('builtins.open', a_mock_open, create=True) + def test_main_virt_containers_upstream( + self, mock_parser, mock_val_args, mock_cross_sets, mock_jumphost, + mock_json, mock_os, mock_deploy_sets, mock_net_sets, mock_net_env, + mock_utils, mock_parsers, mock_oc_cfg, mock_virt_utils, + mock_inv, mock_build_vms, mock_uc_lib, mock_oc_deploy, + mock_shutil, mock_network_data, mock_oc_builder, + mock_uc_builder, mock_c_builder, mock_deployment): + + ds_opts_dict = {'global_params': MagicMock(), + 'deploy_options': {'gluon': False, + 'congress': False, + 'sdn_controller': 'opendaylight', + 'dataplane': 'ovs', + 'sfc': False, + 'vpn': False, + 'vim': 'openstack', + 'yardstick': 'test', + 'os_version': DEFAULT_OS_VERSION, + 'containers': True}} + args = mock_parser.return_value.parse_args.return_value + args.virtual = True + args.quickstart = False + args.debug = True + args.virt_default_ram = 10 + args.ha_enabled = True + args.virt_compute_nodes = 1 + args.virt_compute_ram = None + args.virt_default_ram = 12 + args.upstream = True + args.snapshot = False net_sets = mock_net_sets.return_value net_sets.enabled_network_list = ['admin'] deploy_sets = mock_deploy_sets.return_value @@ -236,3 +327,67 @@ class TestDeploy(unittest.TestCase): args.virt_compute_ram = 16 args.virt_default_ram = 10 main() + mock_oc_deploy.prep_image.assert_called() + # TODO(trozet) add assertions here with arguments for functions in + # deploy main + + @patch('apex.deploy.c_builder') + @patch('apex.deploy.ApexDeployment') + @patch('apex.deploy.uc_builder') + @patch('apex.deploy.network_data.create_network_data') + @patch('apex.deploy.shutil') + @patch('apex.deploy.git') + @patch('apex.deploy.oc_deploy') + @patch('apex.deploy.uc_lib') + @patch('apex.deploy.build_vms') + @patch('apex.deploy.Inventory') + @patch('apex.deploy.virt_utils') + @patch('apex.deploy.oc_cfg') + @patch('apex.deploy.parsers') + @patch('apex.deploy.utils') + @patch('apex.deploy.NetworkEnvironment') + @patch('apex.deploy.NetworkSettings') + @patch('apex.deploy.DeploySettings') + @patch('apex.deploy.os') + @patch('apex.deploy.json') + @patch('apex.deploy.jumphost') + @patch('apex.deploy.validate_cross_settings') + @patch('apex.deploy.validate_deploy_args') + @patch('apex.deploy.create_deploy_parser') + @patch('builtins.open', a_mock_open, create=True) + def test_main_k8s(self, mock_parser, mock_val_args, mock_cross_sets, + mock_jumphost, mock_json, mock_os, + mock_deploy_sets, mock_net_sets, mock_net_env, + mock_utils, mock_parsers, mock_oc_cfg, + mock_virt_utils, mock_inv, mock_build_vms, mock_uc_lib, + mock_oc_deploy, mock_git, mock_shutil, + mock_network_data, mock_uc_builder, mock_deployment, + mock_c_builder): + net_sets_dict = {'networks': MagicMock(), + 'dns_servers': 'test'} + ds_opts_dict = {'global_params': MagicMock(), + 'deploy_options': {'gluon': False, + 'congress': True, + 'sdn_controller': False, + 'dataplane': 'ovs', + 'sfc': False, + 'vpn': False, + 'vim': 'k8s', + 'yardstick': 'test', + 'os_version': DEFAULT_OS_VERSION, + 'containers': False}} + args = mock_parser.return_value.parse_args.return_value + args.virtual = False + args.quickstart = False + args.debug = False + args.upstream = False + args.snapshot = False + net_sets = mock_net_sets.return_value + net_sets.enabled_network_list = ['external'] + net_sets.__getitem__.side_effect = net_sets_dict.__getitem__ + net_sets.__contains__.side_effect = net_sets_dict.__contains__ + deploy_sets = mock_deploy_sets.return_value + deploy_sets.__getitem__.side_effect = ds_opts_dict.__getitem__ + deploy_sets.__contains__.side_effect = ds_opts_dict.__contains__ + mock_parsers.parse_nova_output.return_value = {'testnode1': 'test'} + main() diff --git a/apex/tests/test_apex_deployment_snapshot.py b/apex/tests/test_apex_deployment_snapshot.py new file mode 100644 index 00000000..d7542585 --- /dev/null +++ b/apex/tests/test_apex_deployment_snapshot.py @@ -0,0 +1,374 @@ +############################################################################## +# Copyright (c) 2018 Tim Rozet (trozet@redhat.com) (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 +############################################################################## + +from mock import patch +import os +import unittest +import urllib.request + +from apex.common import exceptions as exc +from apex.deployment.snapshot import SnapshotDeployment +from apex.settings.deploy_settings import DeploySettings +from apex.tests.constants import TEST_DUMMY_CONFIG + +DUMMY_SNAP_DIR = '/tmp/dummy_cache' + + +class TestSnapshotDeployment(unittest.TestCase): + @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('apex.deployment.snapshot.SnapshotDeployment.pull_snapshot') + @patch('apex.deployment.snapshot.libvirt.open') + @patch('apex.deployment.snapshot.SnapshotDeployment.deploy_snapshot') + def test_init(self, mock_deploy_snap, mock_libvirt_open, mock_pull_snap): + + ds_file = os.path.join(TEST_DUMMY_CONFIG, 'dummy-deploy-settings.yaml') + ds = DeploySettings(ds_file) + d = SnapshotDeployment(deploy_settings=ds, + snap_cache_dir=DUMMY_SNAP_DIR, + fetch=True, all_in_one=False) + snap_dir = os.path.join(DUMMY_SNAP_DIR, 'queens', 'noha') + self.assertEqual(d.snap_cache_dir, snap_dir) + mock_pull_snap.assert_called() + mock_deploy_snap.assert_called() + self.assertEqual(d.ha_ext, 'noha') + + @patch('apex.deployment.snapshot.SnapshotDeployment.pull_snapshot') + @patch('apex.deployment.snapshot.libvirt.open') + @patch('apex.deployment.snapshot.SnapshotDeployment.deploy_snapshot') + def test_init_allinone_no_fetch(self, mock_deploy_snap, mock_libvirt_open, + mock_pull_snap): + + ds_file = os.path.join(TEST_DUMMY_CONFIG, 'dummy-deploy-settings.yaml') + ds = DeploySettings(ds_file) + d = SnapshotDeployment(deploy_settings=ds, + snap_cache_dir=DUMMY_SNAP_DIR, + fetch=False, all_in_one=True) + snap_dir = os.path.join(DUMMY_SNAP_DIR, 'queens', 'noha-allinone') + self.assertEqual(d.snap_cache_dir, snap_dir) + mock_pull_snap.assert_not_called() + mock_deploy_snap.assert_called() + self.assertEqual(d.ha_ext, 'noha-allinone') + + @patch('apex.deployment.snapshot.utils.fetch_upstream_and_unpack') + @patch('apex.deployment.snapshot.utils.fetch_properties') + def test_pull_snapshot_is_latest(self, mock_fetch_props, + mock_fetch_artifact): + mock_fetch_props.return_value = { + 'OPNFV_SNAP_URL': 'artifacts.opnfv.org/apex/master/noha/' + 'apex-csit-snap-2018-08-05.tar.gz', + 'OPNFV_SNAP_SHA512SUM': 'bb0c6fa0e675dcb39cfad11d81bb99f309d5cfc23' + '6e36a74d05ee813584f3e5bb92aa23dec77584631' + '7b75d574f8c86186c666f78a299c24fb68849897b' + 'dd4bc' + } + SnapshotDeployment.pull_snapshot('http://dummy_url', + TEST_DUMMY_CONFIG) + mock_fetch_artifact.assert_not_called() + + @patch('apex.deployment.snapshot.utils.fetch_upstream_and_unpack') + @patch('apex.deployment.snapshot.utils.fetch_properties') + def test_pull_snapshot_fetch_props_failure(self, mock_fetch_props, + mock_fetch_artifact): + mock_fetch_props.side_effect = exc.FetchException + self.assertRaises(exc.FetchException, + SnapshotDeployment.pull_snapshot, + 'http://dummy_url', TEST_DUMMY_CONFIG) + + @patch('apex.deployment.snapshot.utils.fetch_upstream_and_unpack') + @patch('apex.deployment.snapshot.utils.fetch_properties') + def test_pull_snapshot_is_not_latest(self, mock_fetch_props, + mock_fetch_artifact): + mock_fetch_props.side_effect = [{ + 'OPNFV_SNAP_URL': 'artifacts.opnfv.org/apex/master/noha/' + 'apex-csit-snap-2018-08-05.tar.gz', + 'OPNFV_SNAP_SHA512SUM': '123c6fa0e675dcb39cfad11d81bb99f309d5cfc23' + '6e36a74d05ee813584f3e5bb92aa23dec77584631' + '7b75d574f8c86186c666f78a299c24fb68849897b' + 'dd4bc'}, + { + 'OPNFV_SNAP_URL': 'artifacts.opnfv.org/apex/master/noha/' + 'apex-csit-snap-2018-08-05.tar.gz', + 'OPNFV_SNAP_SHA512SUM': 'bb0c6fa0e675dcb39cfad11d81bb99f309d5cfc23' + '6e36a74d05ee813584f3e5bb92aa23dec77584631' + '7b75d574f8c86186c666f78a299c24fb68849897b' + 'dd4bc'}] + SnapshotDeployment.pull_snapshot('http://dummy_url', + TEST_DUMMY_CONFIG) + mock_fetch_artifact.assert_called() + + @patch('apex.deployment.snapshot.OvercloudNode') + @patch('apex.deployment.snapshot.SnapshotDeployment.pull_snapshot') + @patch('apex.deployment.snapshot.libvirt.open') + @patch('apex.deployment.snapshot.SnapshotDeployment.deploy_snapshot') + def test_create_networks(self, mock_deploy_snap, mock_libvirt_open, + mock_pull_snap, mock_oc_node): + ds_file = os.path.join(TEST_DUMMY_CONFIG, 'dummy-deploy-settings.yaml') + ds = DeploySettings(ds_file) + d = SnapshotDeployment(deploy_settings=ds, + snap_cache_dir=DUMMY_SNAP_DIR, + fetch=False, all_in_one=False) + d.snap_cache_dir = TEST_DUMMY_CONFIG + conn = mock_libvirt_open('qemu:///system') + d.create_networks() + conn.networkCreateXML.assert_called() + + @patch('apex.deployment.snapshot.OvercloudNode') + @patch('apex.deployment.snapshot.SnapshotDeployment.pull_snapshot') + @patch('apex.deployment.snapshot.libvirt.open') + @patch('apex.deployment.snapshot.SnapshotDeployment.deploy_snapshot') + def test_create_networks_invalid_cache(self, mock_deploy_snap, + mock_libvirt_open, mock_pull_snap, + mock_oc_node): + ds_file = os.path.join(TEST_DUMMY_CONFIG, 'dummy-deploy-settings.yaml') + ds = DeploySettings(ds_file) + d = SnapshotDeployment(deploy_settings=ds, + snap_cache_dir=DUMMY_SNAP_DIR, + fetch=False, all_in_one=False) + d.snap_cache_dir = '/doesnotexist/' + self.assertRaises(exc.SnapshotDeployException, d.create_networks) + + @patch('apex.deployment.snapshot.fnmatch') + @patch('apex.deployment.snapshot.OvercloudNode') + @patch('apex.deployment.snapshot.SnapshotDeployment.pull_snapshot') + @patch('apex.deployment.snapshot.libvirt.open') + @patch('apex.deployment.snapshot.SnapshotDeployment.deploy_snapshot') + def test_create_networks_no_net_xmls(self, mock_deploy_snap, + mock_libvirt_open, mock_pull_snap, + mock_oc_node, mock_fnmatch): + ds_file = os.path.join(TEST_DUMMY_CONFIG, 'dummy-deploy-settings.yaml') + ds = DeploySettings(ds_file) + d = SnapshotDeployment(deploy_settings=ds, + snap_cache_dir=DUMMY_SNAP_DIR, + fetch=False, all_in_one=False) + d.snap_cache_dir = '/doesnotexist/' + mock_fnmatch.filter.return_value = [] + self.assertRaises(exc.SnapshotDeployException, d.create_networks) + + @patch('apex.deployment.snapshot.OvercloudNode') + @patch('apex.deployment.snapshot.SnapshotDeployment.pull_snapshot') + @patch('apex.deployment.snapshot.libvirt.open') + @patch('apex.deployment.snapshot.SnapshotDeployment.deploy_snapshot') + def test_parse_and_create_nodes(self, mock_deploy_snap, mock_libvirt_open, + mock_pull_snap, mock_oc_node): + ds_file = os.path.join(TEST_DUMMY_CONFIG, 'dummy-deploy-settings.yaml') + ds = DeploySettings(ds_file) + d = SnapshotDeployment(deploy_settings=ds, + snap_cache_dir=DUMMY_SNAP_DIR, + fetch=False, all_in_one=False) + d.snap_cache_dir = TEST_DUMMY_CONFIG + node = mock_oc_node() + d.parse_and_create_nodes() + node.start.assert_called() + self.assertListEqual([node], d.oc_nodes) + + @patch('apex.deployment.snapshot.utils.parse_yaml') + @patch('apex.deployment.snapshot.OvercloudNode') + @patch('apex.deployment.snapshot.SnapshotDeployment.pull_snapshot') + @patch('apex.deployment.snapshot.libvirt.open') + @patch('apex.deployment.snapshot.SnapshotDeployment.deploy_snapshot') + def test_parse_and_create_nodes_invalid_node_yaml( + self, mock_deploy_snap, mock_libvirt_open, mock_pull_snap, + mock_oc_node, mock_parse_yaml): + ds_file = os.path.join(TEST_DUMMY_CONFIG, 'dummy-deploy-settings.yaml') + ds = DeploySettings(ds_file) + d = SnapshotDeployment(deploy_settings=ds, + snap_cache_dir=DUMMY_SNAP_DIR, + fetch=False, all_in_one=False) + d.snap_cache_dir = TEST_DUMMY_CONFIG + node = mock_oc_node() + mock_parse_yaml.return_value = {'blah': 'dummy'} + self.assertRaises(exc.SnapshotDeployException, + d.parse_and_create_nodes) + node.start.assert_not_called() + + @patch('apex.deployment.snapshot.OvercloudNode') + @patch('apex.deployment.snapshot.SnapshotDeployment.pull_snapshot') + @patch('apex.deployment.snapshot.libvirt.open') + @patch('apex.deployment.snapshot.SnapshotDeployment.deploy_snapshot') + def test_get_controllers(self, mock_deploy_snap, mock_libvirt_open, + mock_pull_snap, mock_oc_node): + ds_file = os.path.join(TEST_DUMMY_CONFIG, 'dummy-deploy-settings.yaml') + ds = DeploySettings(ds_file) + d = SnapshotDeployment(deploy_settings=ds, + snap_cache_dir=DUMMY_SNAP_DIR, + fetch=False, all_in_one=False) + d.snap_cache_dir = TEST_DUMMY_CONFIG + node = mock_oc_node() + node.role = 'controller' + d.oc_nodes = [node] + self.assertListEqual(d.get_controllers(), [node]) + + @patch('apex.deployment.snapshot.OvercloudNode') + @patch('apex.deployment.snapshot.SnapshotDeployment.pull_snapshot') + @patch('apex.deployment.snapshot.libvirt.open') + @patch('apex.deployment.snapshot.SnapshotDeployment.deploy_snapshot') + def test_get_controllers_none(self, mock_deploy_snap, mock_libvirt_open, + mock_pull_snap, mock_oc_node): + ds_file = os.path.join(TEST_DUMMY_CONFIG, 'dummy-deploy-settings.yaml') + ds = DeploySettings(ds_file) + d = SnapshotDeployment(deploy_settings=ds, + snap_cache_dir=DUMMY_SNAP_DIR, + fetch=False, all_in_one=False) + d.snap_cache_dir = TEST_DUMMY_CONFIG + node = mock_oc_node() + node.role = 'compute' + d.oc_nodes = [node] + self.assertListEqual(d.get_controllers(), []) + + @patch('apex.deployment.snapshot.SnapshotDeployment.get_controllers') + @patch('apex.deployment.snapshot.time') + @patch('apex.deployment.snapshot.socket') + @patch('apex.deployment.snapshot.OvercloudNode') + @patch('apex.deployment.snapshot.SnapshotDeployment.pull_snapshot') + @patch('apex.deployment.snapshot.libvirt.open') + @patch('apex.deployment.snapshot.SnapshotDeployment.deploy_snapshot') + def test_is_openstack_up(self, mock_deploy_snap, mock_libvirt_open, + mock_pull_snap, mock_oc_node, mock_socket, + mock_time, mock_get_ctrls): + ds_file = os.path.join(TEST_DUMMY_CONFIG, 'dummy-deploy-settings.yaml') + ds = DeploySettings(ds_file) + d = SnapshotDeployment(deploy_settings=ds, + snap_cache_dir=DUMMY_SNAP_DIR, + fetch=False, all_in_one=False) + d.snap_cache_dir = TEST_DUMMY_CONFIG + node = mock_oc_node() + node.ip = '123.123.123.123' + node.name = 'dummy-controller-0' + mock_get_ctrls.return_value = [node] + sock = mock_socket.socket(mock_socket.AF_INET, mock_socket.SOCK_STREAM) + sock.connect_ex.return_value = 0 + self.assertTrue(d.is_service_up('openstack')) + + @patch('apex.deployment.snapshot.SnapshotDeployment.get_controllers') + @patch('apex.deployment.snapshot.time') + @patch('apex.deployment.snapshot.socket') + @patch('apex.deployment.snapshot.OvercloudNode') + @patch('apex.deployment.snapshot.SnapshotDeployment.pull_snapshot') + @patch('apex.deployment.snapshot.libvirt.open') + @patch('apex.deployment.snapshot.SnapshotDeployment.deploy_snapshot') + def test_is_openstack_up_false(self, mock_deploy_snap, mock_libvirt_open, + mock_pull_snap, mock_oc_node, mock_socket, + mock_time, mock_get_ctrls): + ds_file = os.path.join(TEST_DUMMY_CONFIG, 'dummy-deploy-settings.yaml') + ds = DeploySettings(ds_file) + d = SnapshotDeployment(deploy_settings=ds, + snap_cache_dir=DUMMY_SNAP_DIR, + fetch=False, all_in_one=False) + d.snap_cache_dir = TEST_DUMMY_CONFIG + node = mock_oc_node() + node.ip = '123.123.123.123' + node.name = 'dummy-controller-0' + mock_get_ctrls.return_value = [node] + sock = mock_socket.socket(mock_socket.AF_INET, mock_socket.SOCK_STREAM) + sock.connect_ex.return_value = 1 + self.assertFalse(d.is_service_up('openstack')) + + @patch('apex.deployment.snapshot.SnapshotDeployment.get_controllers') + @patch('apex.deployment.snapshot.time') + @patch('apex.deployment.snapshot.utils') + @patch('apex.deployment.snapshot.OvercloudNode') + @patch('apex.deployment.snapshot.SnapshotDeployment.pull_snapshot') + @patch('apex.deployment.snapshot.libvirt.open') + @patch('apex.deployment.snapshot.SnapshotDeployment.deploy_snapshot') + def test_is_opendaylight_up(self, mock_deploy_snap, mock_libvirt_open, + mock_pull_snap, mock_oc_node, mock_utils, + mock_time, mock_get_ctrls): + ds_file = os.path.join(TEST_DUMMY_CONFIG, 'dummy-deploy-settings.yaml') + ds = DeploySettings(ds_file) + d = SnapshotDeployment(deploy_settings=ds, + snap_cache_dir=DUMMY_SNAP_DIR, + fetch=False, all_in_one=False) + d.snap_cache_dir = TEST_DUMMY_CONFIG + node = mock_oc_node() + node.ip = '123.123.123.123' + node.name = 'dummy-controller-0' + mock_get_ctrls.return_value = [node] + mock_utils.open_webpage.return_value = 0 + self.assertTrue(d.is_service_up('opendaylight')) + + @patch('apex.deployment.snapshot.SnapshotDeployment.get_controllers') + @patch('apex.deployment.snapshot.time') + @patch('apex.deployment.snapshot.utils') + @patch('apex.deployment.snapshot.OvercloudNode') + @patch('apex.deployment.snapshot.SnapshotDeployment.pull_snapshot') + @patch('apex.deployment.snapshot.libvirt.open') + @patch('apex.deployment.snapshot.SnapshotDeployment.deploy_snapshot') + def test_is_opendaylight_up_false(self, mock_deploy_snap, + mock_libvirt_open, mock_pull_snap, + mock_oc_node, mock_utils, + mock_time, mock_get_ctrls): + ds_file = os.path.join(TEST_DUMMY_CONFIG, 'dummy-deploy-settings.yaml') + ds = DeploySettings(ds_file) + d = SnapshotDeployment(deploy_settings=ds, + snap_cache_dir=DUMMY_SNAP_DIR, + fetch=False, all_in_one=False) + d.snap_cache_dir = TEST_DUMMY_CONFIG + node = mock_oc_node() + node.ip = '123.123.123.123' + node.name = 'dummy-controller-0' + mock_get_ctrls.return_value = [node] + mock_utils.open_webpage.side_effect = urllib.request.URLError( + reason='blah') + self.assertFalse(d.is_service_up('opendaylight')) + + @patch('apex.deployment.snapshot.os.path.isfile') + @patch('apex.deployment.snapshot.SnapshotDeployment.is_service_up') + @patch('apex.deployment.snapshot.SnapshotDeployment' + '.parse_and_create_nodes') + @patch('apex.deployment.snapshot.SnapshotDeployment.pull_snapshot') + @patch('apex.deployment.snapshot.libvirt.open') + @patch('apex.deployment.snapshot.SnapshotDeployment.create_networks') + def test_deploy_snapshot(self, mock_create_networks, mock_libvirt_open, + mock_pull_snap, mock_parse_create, + mock_service_up, mock_is_file): + mock_is_file.return_value = True + ds_file = os.path.join(TEST_DUMMY_CONFIG, 'dummy-deploy-settings.yaml') + ds = DeploySettings(ds_file) + SnapshotDeployment(deploy_settings=ds, snap_cache_dir=DUMMY_SNAP_DIR, + fetch=False, all_in_one=False) + mock_parse_create.assert_called() + mock_create_networks.assert_called() + mock_service_up.assert_called() + + @patch('apex.deployment.snapshot.os.path.isfile') + @patch('apex.deployment.snapshot.SnapshotDeployment.is_service_up') + @patch('apex.deployment.snapshot.SnapshotDeployment' + '.parse_and_create_nodes') + @patch('apex.deployment.snapshot.SnapshotDeployment.pull_snapshot') + @patch('apex.deployment.snapshot.libvirt.open') + @patch('apex.deployment.snapshot.SnapshotDeployment.create_networks') + def test_deploy_snapshot_services_down(self, mock_create_networks, + mock_libvirt_open, + mock_pull_snap, mock_parse_create, + mock_service_up, mock_is_file): + mock_is_file.return_value = True + ds_file = os.path.join(TEST_DUMMY_CONFIG, 'dummy-deploy-settings.yaml') + ds = DeploySettings(ds_file) + mock_service_up.return_value = False + self.assertRaises(exc.SnapshotDeployException, + SnapshotDeployment, + ds, DUMMY_SNAP_DIR, False, False) + + mock_service_up.side_effect = [True, False] + self.assertRaises(exc.SnapshotDeployException, + SnapshotDeployment, + ds, DUMMY_SNAP_DIR, False, False) diff --git a/apex/tests/test_apex_deployment_tripleo.py b/apex/tests/test_apex_deployment_tripleo.py new file mode 100644 index 00000000..912fe104 --- /dev/null +++ b/apex/tests/test_apex_deployment_tripleo.py @@ -0,0 +1,49 @@ +############################################################################## +# Copyright (c) 2018 Tim Rozet (trozet@redhat.com) (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 unittest + +from apex.deployment.tripleo import ApexDeployment +from apex.settings.deploy_settings import DeploySettings +from apex.tests.constants import TEST_DUMMY_CONFIG + + +class TestApexDeployment(unittest.TestCase): + @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""" + + def test_determine_patches(self): + self.maxDiff = None + ds_file = os.path.join(TEST_DUMMY_CONFIG, 'dummy-deploy-settings.yaml') + ds = DeploySettings(ds_file) + patches_file = os.path.join(TEST_DUMMY_CONFIG, 'common-patches.yaml') + d = ApexDeployment(deploy_settings=ds, patch_file=patches_file, + ds_file=ds_file) + patches = d.determine_patches() + test_patches = { + 'undercloud': + [{'change-id': 'I2e0a40d7902f592e4b7bd727f57048111e0bea36', + 'project': 'openstack/tripleo-common'}], + 'overcloud': + [{'change-id': 'Ie988ba6a2d444a614e97c0edf5fce24b23970310', + 'project': 'openstack/puppet-tripleo'}] + } + self.assertDictEqual(patches, test_patches) diff --git a/apex/tests/test_apex_inventory.py b/apex/tests/test_apex_inventory.py index 71979465..38a4271a 100644 --- a/apex/tests/test_apex_inventory.py +++ b/apex/tests/test_apex_inventory.py @@ -56,10 +56,15 @@ class TestInventory: os.path.join(TEST_DUMMY_CONFIG, 'inventory-virt.yaml'), virtual=True, ha=True) + def test_inventory_valid_allinone_count(self): + i = Inventory(os.path.join(TEST_DUMMY_CONFIG, + 'inventory-virt-1-node.yaml'), ha=False) + assert_equal(list(i.get_node_counts()), [1, 0]) + def test_inventory_invalid_noha_count(self): assert_raises(ApexInventoryException, Inventory, os.path.join(TEST_DUMMY_CONFIG, - 'inventory-virt-1-node.yaml'), + 'inventory-virt-1-compute-node.yaml'), virtual=True, ha=False) def test_inventory_virtual(self): diff --git a/apex/tests/test_apex_network_environment.py b/apex/tests/test_apex_network_environment.py index 79a72a55..7aa6ef15 100644 --- a/apex/tests/test_apex_network_environment.py +++ b/apex/tests/test_apex_network_environment.py @@ -165,3 +165,10 @@ class TestNetworkEnvironment: e = NetworkEnvException("test") print(e) assert_is_instance(e, NetworkEnvException) + + def test_service_netmap(self): + ns = copy(self.ns) + ns.enabled_network_list = ['admin'] + ne = NetworkEnvironment(ns, os.path.join(TEST_BUILD_DIR, NET_ENV_FILE)) + for network in ne['parameter_defaults']['ServiceNetMap'].values(): + assert_equal(network, 'ctlplane') diff --git a/apex/tests/test_apex_network_settings.py b/apex/tests/test_apex_network_settings.py index 5e2fa072..764c9ef4 100644 --- a/apex/tests/test_apex_network_settings.py +++ b/apex/tests/test_apex_network_settings.py @@ -112,6 +112,9 @@ class TestNetworkSettings: # remove vlan from storage net storage_net_nicmap['compute'].pop('vlan', None) assert_is_instance(NetworkSettings(ns), NetworkSettings) + for role in ('compute', 'controller'): + assert_equal(ns['networks'][ADMIN_NETWORK]['nic_mapping'][ + role]['vlan'], 'native') # TODO # need to manipulate interfaces some how diff --git a/apex/tests/test_apex_overcloud_builder.py b/apex/tests/test_apex_overcloud_builder.py index e9a6e6cf..8bed3d70 100644 --- a/apex/tests/test_apex_overcloud_builder.py +++ b/apex/tests/test_apex_overcloud_builder.py @@ -11,7 +11,9 @@ import unittest from apex.builders import overcloud_builder as oc_builder from apex.common import constants as con -from mock import patch +from mock import patch, mock_open + +a_mock_open = mock_open(read_data=None) class TestOvercloudBuilder(unittest.TestCase): @@ -37,14 +39,71 @@ class TestOvercloudBuilder(unittest.TestCase): mock_git_archive.return_value = '/dummytmp/puppet-opendaylight.tar' archive = '/dummytmp/puppet-opendaylight.tar' test_virt_ops = [ - {con.VIRT_INSTALL: 'opendaylight'}, {con.VIRT_UPLOAD: "{}:/etc/puppet/modules/".format(archive)}, {con.VIRT_RUN_CMD: 'rm -rf /etc/puppet/modules/opendaylight'}, {con.VIRT_RUN_CMD: "cd /etc/puppet/modules/ && tar xvf " - "puppet-opendaylight.tar"} + "puppet-opendaylight.tar"}, + {con.VIRT_INSTALL: "java-1.8.0-openjdk"}, + {con.VIRT_INSTALL: 'opendaylight'} ] oc_builder.inject_opendaylight(con.DEFAULT_ODL_VERSION, 'dummy.qcow2', - '/dummytmp/') + '/dummytmp/', uc_ip='192.0.2.2', + os_version=con.DEFAULT_OS_VERSION) + assert mock_git_archive.called + assert mock_add_repo.called + mock_customize.assert_called_once_with(test_virt_ops, 'dummy.qcow2') + + @patch('apex.builders.overcloud_builder.build_dockerfile') + @patch('apex.builders.common_builder.create_git_archive') + @patch('apex.builders.common_builder.add_repo') + @patch('apex.virtual.utils.virt_customize') + def test_inject_opendaylight_docker(self, mock_customize, mock_add_repo, + mock_git_archive, mock_build_docker): + mock_git_archive.return_value = '/dummytmp/puppet-opendaylight.tar' + archive = '/dummytmp/puppet-opendaylight.tar' + test_virt_ops = [ + {con.VIRT_UPLOAD: "{}:/etc/puppet/modules/".format(archive)}, + {con.VIRT_RUN_CMD: 'rm -rf /etc/puppet/modules/opendaylight'}, + {con.VIRT_RUN_CMD: "cd /etc/puppet/modules/ && tar xvf " + "puppet-opendaylight.tar"}, + {con.VIRT_INSTALL: "java-1.8.0-openjdk"}, + ] + oc_builder.inject_opendaylight('oxygen', 'dummy.qcow2', + '/dummytmp/', uc_ip='192.0.2.2', + os_version=con.DEFAULT_OS_VERSION, + docker_tag='latest') + odl_url = "https://nexus.opendaylight.org/content/repositories" \ + "/opendaylight-oxygen-epel-7-x86_64-devel/" + docker_cmds = [ + "RUN yum remove opendaylight -y", + "RUN echo $'[opendaylight]\\n\\", + "baseurl={}\\n\\".format(odl_url), + "gpgcheck=0\\n\\", + "enabled=1' > /etc/yum.repos.d/opendaylight.repo", + "RUN yum -y install opendaylight" + ] + src_img_uri = "192.0.2.1:8787/nova-api/centos-binary-master:latest" assert mock_git_archive.called assert mock_add_repo.called + assert mock_build_docker.called_once_with( + 'opendaylight', '/dummytmp', docker_cmds, src_img_uri + ) mock_customize.assert_called_once_with(test_virt_ops, 'dummy.qcow2') + + @patch('builtins.open', a_mock_open) + @patch('os.makedirs') + @patch('os.path.isfile') + @patch('os.path.isdir') + def test_build_dockerfile(self, mock_isdir, mock_isfile, mock_makedirs): + src_img_uri = "192.0.2.1:8787/nova-api/centos-binary-master:latest" + oc_builder.build_dockerfile('nova-api', '/tmpdummy/', ['RUN dummy'], + src_img_uri) + a_mock_open.assert_called_with( + '/tmpdummy/containers/nova-api/Dockerfile', 'a+') + a_mock_open().write.assert_called_once_with('RUN dummy') + + @patch('tarfile.open') + @patch('os.path.isdir') + def test_archive_docker_patches(self, mock_isdir, mock_tarfile): + oc_builder.archive_docker_patches('/tmpdummy/') + assert mock_tarfile.assert_called diff --git a/apex/tests/test_apex_overcloud_deploy.py b/apex/tests/test_apex_overcloud_deploy.py index 59e9048f..79dbf54b 100644 --- a/apex/tests/test_apex_overcloud_deploy.py +++ b/apex/tests/test_apex_overcloud_deploy.py @@ -7,6 +7,8 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## +import mock +import os import sys import unittest @@ -24,8 +26,11 @@ from apex.overcloud.deploy import make_ssh_key from apex.overcloud.deploy import prep_env from apex.overcloud.deploy import generate_ceph_key from apex.overcloud.deploy import prep_storage_env +from apex.overcloud.deploy import prep_sriov_env from apex.overcloud.deploy import external_network_cmds from apex.overcloud.deploy import create_congress_cmds +from apex.overcloud.deploy import SDN_FILE_MAP +from apex.overcloud.deploy import get_docker_sdn_files from nose.tools import ( assert_regexp_matches, @@ -70,19 +75,41 @@ class TestOvercloudDeploy(unittest.TestCase): res = '/usr/share/openstack-tripleo-heat-templates/environments/test' assert_equal(build_sdn_env_list(ds, sdn_map), [res]) + def test_build_sdn_env_list_with_string(self): + ds = {'sdn_controller': 'opendaylight', + 'sriov': 'xxx'} + prefix = '/usr/share/openstack-tripleo-heat-templates/environments' + res = [os.path.join(prefix, 'neutron-opendaylight.yaml'), + os.path.join(prefix, 'neutron-opendaylight-sriov.yaml')] + assert_equal(build_sdn_env_list(ds, SDN_FILE_MAP), res) + + def test_build_sdn_env_list_with_default(self): + ds = {'sdn_controller': 'opendaylight', + 'vpn': True} + prefix = '/usr/share/openstack-tripleo-heat-templates/environments' + res = [os.path.join(prefix, 'neutron-opendaylight.yaml'), + os.path.join(prefix, 'neutron-bgpvpn-opendaylight.yaml')] + assert_equal(build_sdn_env_list(ds, SDN_FILE_MAP), res) + + @patch('apex.overcloud.deploy.prep_sriov_env') @patch('apex.overcloud.deploy.prep_storage_env') @patch('apex.overcloud.deploy.build_sdn_env_list') @patch('builtins.open', mock_open()) - def test_create_deploy_cmd(self, mock_sdn_list, mock_prep_storage): + def test_create_deploy_cmd(self, mock_sdn_list, mock_prep_storage, + mock_prep_sriov): mock_sdn_list.return_value = [] - ds = {'deploy_options': MagicMock(), + ds = {'deploy_options': + {'ha_enabled': True, + 'congress': True, + 'tacker': True, + 'containers': False, + 'barometer': True, + 'ceph': False, + 'sriov': False, + 'vim': 'openstack' + }, 'global_params': MagicMock()} - ds['global_params'].__getitem__.side_effect = \ - lambda i: True if i == 'ha_enabled' else MagicMock() - ds['deploy_options'].__getitem__.side_effect = \ - lambda i: True if i == 'congress' else MagicMock() - ds['deploy_options'].__contains__.side_effect = \ - lambda i: True if i == 'congress' else MagicMock() + ns = {'ntp': ['ntp']} inv = MagicMock() inv.get_node_counts.return_value = (3, 2) @@ -96,16 +123,63 @@ class TestOvercloudDeploy(unittest.TestCase): assert_in('--control-scale 3', result_cmd) assert_in('--compute-scale 2', result_cmd) + @patch('apex.overcloud.deploy.prep_sriov_env') + @patch('apex.overcloud.deploy.prep_storage_env') + @patch('builtins.open', mock_open()) + def test_create_deploy_cmd_containers_sdn(self, mock_prep_storage, + mock_prep_sriov): + ds = {'deploy_options': + {'ha_enabled': True, + 'congress': False, + 'tacker': False, + 'containers': True, + 'barometer': False, + 'vpn': False, + 'ceph': True, + 'sdn_controller': 'opendaylight', + 'sriov': False, + 'os_version': 'queens', + 'vim': 'openstack' + }, + 'global_params': MagicMock()} + + ns = {'ntp': ['ntp']} + inv = MagicMock() + inv.get_node_counts.return_value = (3, 2) + virt = True + result_cmd = create_deploy_cmd(ds, ns, inv, '/tmp', virt) + assert_in('--ntp-server ntp', result_cmd) + assert_not_in('enable_tacker.yaml', result_cmd) + assert_not_in('enable_congress.yaml', result_cmd) + assert_not_in('enable_barometer.yaml', result_cmd) + assert_in('virtual-environment.yaml', result_cmd) + assert_in('--control-scale 3', result_cmd) + assert_in('--compute-scale 2', result_cmd) + assert_in('docker-images.yaml', result_cmd) + assert_in('/usr/share/openstack-tripleo-heat-templates/environments' + '/docker.yaml', result_cmd) + assert_in('/usr/share/openstack-tripleo-heat-templates/environments/' + 'storage-environment.yaml', result_cmd) + assert_in('/usr/share/openstack-tripleo-heat-templates/environments' + '/services/neutron-opendaylight.yaml', result_cmd) + ds['deploy_options']['os_version'] = 'master' + result_cmd = create_deploy_cmd(ds, ns, inv, '/tmp', virt) + assert_in('/usr/share/openstack-tripleo-heat-templates/environments' + '/services/neutron-opendaylight.yaml', result_cmd) + + @patch('apex.overcloud.deploy.prep_sriov_env') @patch('apex.overcloud.deploy.prep_storage_env') @patch('apex.overcloud.deploy.build_sdn_env_list') @patch('builtins.open', mock_open()) def test_create_deploy_cmd_no_ha_bm(self, mock_sdn_list, - mock_prep_storage): + mock_prep_storage, mock_prep_sriov): mock_sdn_list.return_value = [] ds = {'deploy_options': MagicMock(), 'global_params': MagicMock()} ds['global_params'].__getitem__.side_effect = \ lambda i: False if i == 'ha_enabled' else MagicMock() + ds['deploy_options'].__getitem__.side_effect = \ + lambda i: 'master' if i == 'os_version' else MagicMock() ns = {'ntp': ['ntp']} inv = MagicMock() inv.get_node_counts.return_value = (3, 2) @@ -119,57 +193,61 @@ class TestOvercloudDeploy(unittest.TestCase): assert_not_in('enable_congress.yaml', result_cmd) assert_not_in('enable_barometer.yaml', result_cmd) - @patch('apex.overcloud.deploy.prep_storage_env') - @patch('apex.overcloud.deploy.build_sdn_env_list') - def test_create_deploy_cmd_raises(self, mock_sdn_list, mock_prep_storage): - mock_sdn_list.return_value = [] - ds = {'deploy_options': MagicMock(), - 'global_params': MagicMock()} - ns = {} - inv = MagicMock() - inv.get_node_counts.return_value = (0, 0) - virt = False - assert_raises(ApexDeployException, create_deploy_cmd, - ds, ns, inv, '/tmp', virt) - + @patch('apex.builders.overcloud_builder.inject_opendaylight') @patch('apex.overcloud.deploy.virt_utils') @patch('apex.overcloud.deploy.shutil') - @patch('apex.overcloud.deploy.os.path') + @patch('apex.overcloud.deploy.os.path.isfile') @patch('builtins.open', mock_open()) - def test_prep_image(self, mock_os_path, mock_shutil, mock_virt_utils): + def test_prep_image(self, mock_is_file, mock_shutil, mock_virt_utils, + mock_inject_odl): + mock_is_file.return_value = True ds_opts = {'dataplane': 'fdio', 'sdn_controller': 'opendaylight', - 'odl_version': 'master'} + 'odl_version': 'master', + 'vpn': False, + 'sriov': False} ds = {'deploy_options': MagicMock(), 'global_params': MagicMock()} ds['deploy_options'].__getitem__.side_effect = \ lambda i: ds_opts.get(i, MagicMock()) - prep_image(ds, 'undercloud.qcow2', '/tmp', root_pw='test') + ns = MagicMock() + prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test') mock_virt_utils.virt_customize.assert_called() + mock_inject_odl.assert_called() @patch('apex.overcloud.deploy.virt_utils') @patch('apex.overcloud.deploy.shutil') - @patch('apex.overcloud.deploy.os.path') + @patch('apex.overcloud.deploy.os.path.isfile') @patch('builtins.open', mock_open()) - def test_prep_image_sdn_false(self, mock_os_path, mock_shutil, + def test_prep_image_sdn_false(self, mock_is_file, mock_shutil, mock_virt_utils): + mock_is_file.return_value = True ds_opts = {'dataplane': 'fdio', + 'vpn': False, 'sdn_controller': False} ds = {'deploy_options': MagicMock(), 'global_params': MagicMock()} ds['deploy_options'].__getitem__.side_effect = \ lambda i: ds_opts.get(i, MagicMock()) - prep_image(ds, 'undercloud.qcow2', '/tmp', root_pw='test') + ns = MagicMock() + prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test') mock_virt_utils.virt_customize.assert_called() + @patch('apex.builders.overcloud_builder.inject_ovs_nsh') + @patch('apex.overcloud.deploy.utils.fetch_upstream_and_unpack') + @patch('apex.builders.overcloud_builder.inject_opendaylight') @patch('apex.overcloud.deploy.virt_utils') @patch('apex.overcloud.deploy.shutil') - @patch('apex.overcloud.deploy.os.path') + @patch('apex.overcloud.deploy.os.path.isfile') @patch('builtins.open', mock_open()) - def test_prep_image_sdn_odl(self, mock_os_path, mock_shutil, - mock_virt_utils): + def test_prep_image_sdn_odl(self, mock_is_file, mock_shutil, + mock_virt_utils, mock_inject_odl, + mock_fetch, mock_ovs_nsh): + mock_is_file.return_value = True ds_opts = {'dataplane': 'ovs', 'sdn_controller': 'opendaylight', + 'vpn': False, + 'sfc': False, 'odl_version': con.DEFAULT_ODL_VERSION, 'odl_vpp_netvirt': True} ds = {'deploy_options': MagicMock(), @@ -178,15 +256,80 @@ class TestOvercloudDeploy(unittest.TestCase): lambda i: ds_opts.get(i, MagicMock()) ds['deploy_options'].__contains__.side_effect = \ lambda i: True if i in ds_opts else MagicMock() - prep_image(ds, 'undercloud.qcow2', '/tmp', root_pw='test') + ns = MagicMock() + prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test') mock_virt_utils.virt_customize.assert_called() + mock_inject_odl.assert_called() + # mock_ovs_nsh.assert_called() + @patch('apex.overcloud.deploy.c_builder') + @patch('apex.overcloud.deploy.oc_builder') @patch('apex.overcloud.deploy.virt_utils') @patch('apex.overcloud.deploy.shutil') - @patch('apex.overcloud.deploy.os.path') + @patch('apex.overcloud.deploy.os.path.isfile') @patch('builtins.open', mock_open()) - def test_prep_image_sdn_odl_not_def(self, mock_os_path, - mock_shutil, mock_virt_utils): + def test_prep_image_sdn_odl_upstream_containers_patches( + self, mock_is_file, mock_shutil, mock_virt_utils, + mock_oc_builder, mock_c_builder): + mock_is_file.return_value = True + ds_opts = {'dataplane': 'ovs', + 'sdn_controller': 'opendaylight', + 'odl_version': con.DEFAULT_ODL_VERSION, + 'odl_vpp_netvirt': True} + ds = {'deploy_options': MagicMock(), + 'global_params': MagicMock()} + ds['deploy_options'].__getitem__.side_effect = \ + lambda i: ds_opts.get(i, MagicMock()) + ds['deploy_options'].__contains__.side_effect = \ + lambda i: True if i in ds_opts else MagicMock() + ns = MagicMock() + mock_c_builder.add_upstream_patches.return_value = ['nova-api'] + patches = ['dummy_nova_patch'] + rv = prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test', + docker_tag='latest', patches=patches) + mock_oc_builder.inject_opendaylight.assert_called() + mock_virt_utils.virt_customize.assert_called() + mock_c_builder.add_upstream_patches.assert_called() + self.assertListEqual(sorted(rv), ['nova-api', 'opendaylight']) + + @patch('apex.overcloud.deploy.c_builder') + @patch('apex.overcloud.deploy.oc_builder') + @patch('apex.overcloud.deploy.virt_utils') + @patch('apex.overcloud.deploy.shutil') + @patch('apex.overcloud.deploy.os.path.isfile') + @patch('builtins.open', mock_open()) + def test_prep_image_nosdn_upstream_containers_patches( + self, mock_is_file, mock_shutil, mock_virt_utils, + mock_oc_builder, mock_c_builder): + mock_is_file.return_value = True + ds_opts = {'dataplane': 'ovs', + 'sdn_controller': False, + 'odl_version': con.DEFAULT_ODL_VERSION, + 'odl_vpp_netvirt': False} + ds = {'deploy_options': MagicMock(), + 'global_params': MagicMock()} + ds['deploy_options'].__getitem__.side_effect = \ + lambda i: ds_opts.get(i, MagicMock()) + ds['deploy_options'].__contains__.side_effect = \ + lambda i: True if i in ds_opts else MagicMock() + ns = MagicMock() + mock_c_builder.add_upstream_patches.return_value = ['nova-api'] + patches = ['dummy_nova_patch'] + rv = prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test', + docker_tag='latest', patches=patches) + mock_virt_utils.virt_customize.assert_called() + mock_c_builder.add_upstream_patches.assert_called() + self.assertListEqual(sorted(rv), ['nova-api']) + + @patch('apex.overcloud.deploy.oc_builder') + @patch('apex.overcloud.deploy.virt_utils') + @patch('apex.overcloud.deploy.shutil') + @patch('apex.overcloud.deploy.os.path.isfile') + @patch('builtins.open', mock_open()) + def test_prep_image_sdn_odl_not_def(self, mock_is_file, + mock_shutil, mock_virt_utils, + mock_oc_builder): + mock_is_file.return_value = True ds_opts = {'dataplane': 'ovs', 'sdn_controller': 'opendaylight', 'odl_version': 'uncommon'} @@ -194,71 +337,152 @@ class TestOvercloudDeploy(unittest.TestCase): 'global_params': MagicMock()} ds['deploy_options'].__getitem__.side_effect = \ lambda i: ds_opts.get(i, MagicMock()) - prep_image(ds, 'undercloud.qcow2', '/tmp', root_pw='test') + ns = MagicMock() + prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test') mock_virt_utils.virt_customize.assert_called() + mock_oc_builder.inject_opendaylight.assert_called() + @patch('apex.builders.overcloud_builder.inject_ovs_nsh') @patch('apex.overcloud.deploy.virt_utils') @patch('apex.overcloud.deploy.shutil') - @patch('apex.overcloud.deploy.os.path') + @patch('apex.overcloud.deploy.os.path.isfile') @patch('builtins.open', mock_open()) - def test_prep_image_sdn_ovn(self, mock_os_path, mock_shutil, - mock_virt_utils): + def test_prep_image_sdn_ovn(self, mock_is_file, mock_shutil, + mock_virt_utils, mock_ovs_nsh): + mock_is_file.return_value = True ds_opts = {'dataplane': 'ovs', + 'vpn': False, + 'sfc': False, 'sdn_controller': 'ovn'} ds = {'deploy_options': MagicMock(), 'global_params': MagicMock()} ds['deploy_options'].__getitem__.side_effect = \ lambda i: ds_opts.get(i, MagicMock()) - prep_image(ds, 'undercloud.qcow2', '/tmp', root_pw='test') + ns = MagicMock() + prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test') mock_virt_utils.virt_customize.assert_called() + # mock_ovs_nsh.assert_called() + + @patch('apex.builders.overcloud_builder.inject_ovs_nsh') + @patch('apex.overcloud.deploy.utils.fetch_upstream_and_unpack') + @patch('apex.builders.overcloud_builder.inject_quagga') + @patch('apex.builders.overcloud_builder.inject_opendaylight') + @patch('apex.overcloud.deploy.virt_utils') + @patch('apex.overcloud.deploy.shutil') + @patch('apex.overcloud.deploy.os.path.isfile') + @patch('builtins.open', mock_open()) + def test_prep_image_sdn_odl_vpn(self, mock_is_file, mock_shutil, + mock_virt_utils, mock_inject_odl, + mock_inject_quagga, mock_fetch, + mock_ovs_nsh): + mock_is_file.return_value = True + ds_opts = {'dataplane': 'ovs', + 'sdn_controller': 'opendaylight', + 'vpn': True, + 'sfc': False, + 'odl_version': con.DEFAULT_ODL_VERSION, + 'odl_vpp_netvirt': True} + ds = {'deploy_options': MagicMock(), + 'global_params': MagicMock()} + ds['deploy_options'].__getitem__.side_effect = \ + lambda i: ds_opts.get(i, MagicMock()) + ds['deploy_options'].__contains__.side_effect = \ + lambda i: True if i in ds_opts else MagicMock() + ns = MagicMock() + prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test') + mock_virt_utils.virt_customize.assert_called() + mock_inject_odl.assert_called() + mock_inject_quagga.assert_called() + # mock_ovs_nsh.assert_called() + + @patch('apex.builders.overcloud_builder.inject_ovs_nsh') + @patch('apex.builders.overcloud_builder.inject_opendaylight') + @patch('apex.overcloud.deploy.virt_utils') + @patch('apex.overcloud.deploy.shutil') + @patch('apex.overcloud.deploy.os.path.isfile') + @patch('builtins.open', mock_open()) + def test_prep_image_sdn_odl_sfc(self, mock_is_file, mock_shutil, + mock_virt_utils, mock_inject_odl, + mock_inject_ovs_nsh): + mock_is_file.return_value = True + ds_opts = {'dataplane': 'ovs', + 'sdn_controller': 'opendaylight', + 'vpn': False, + 'sfc': True, + 'odl_version': con.DEFAULT_ODL_VERSION, + 'odl_vpp_netvirt': True} + ds = {'deploy_options': MagicMock(), + 'global_params': MagicMock()} + ds['deploy_options'].__getitem__.side_effect = \ + lambda i: ds_opts.get(i, MagicMock()) + ds['deploy_options'].__contains__.side_effect = \ + lambda i: True if i in ds_opts else MagicMock() + ns = MagicMock() + prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test') + mock_virt_utils.virt_customize.assert_called() + mock_inject_odl.assert_called() + # mock_inject_ovs_nsh.assert_called() @patch('apex.overcloud.deploy.os.path.isfile') def test_prep_image_no_image(self, mock_isfile): mock_isfile.return_value = False assert_raises(ApexDeployException, prep_image, - {}, 'undercloud.qcow2', '/tmp') + {}, {}, 'undercloud.qcow2', '/tmp') def test_make_ssh_key(self): priv, pub = make_ssh_key() assert_in('-----BEGIN PRIVATE KEY-----', priv) assert_in('ssh-rsa', pub) + @patch('apex.overcloud.deploy.yaml') @patch('apex.overcloud.deploy.fileinput') @patch('apex.overcloud.deploy.shutil') - def test_prep_env(self, mock_shutil, mock_fileinput): + @patch('builtins.open', mock_open()) + def test_prep_env(self, mock_shutil, mock_fileinput, mock_yaml): mock_fileinput.input.return_value = \ ['CloudDomain', 'replace_private_key', 'replace_public_key', 'opendaylight::vpp_routing_node', 'ControllerExtraConfig', 'NovaComputeExtraConfig', 'ComputeKernelArgs', 'HostCpusList', 'ComputeExtraConfigPre', 'resource_registry', 'NovaSchedulerDefaultFilters'] - ds = {'deploy_options': + mock_yaml.safe_load.return_value = { + 'parameter_defaults': { + 'ControllerServices': [1, 2, 3], + 'ComputeServices': [3, 4, 5] + }} + ds = {'global_params': {'ha_enabled': False}, + 'deploy_options': {'sdn_controller': 'opendaylight', 'odl_vpp_routing_node': 'test', 'dataplane': 'ovs_dpdk', + 'sriov': 'xxx', 'performance': {'Compute': {'vpp': {'main-core': 'test', 'corelist-workers': 'test'}, 'ovs': {'dpdk_cores': 'test'}, 'kernel': {'test': 'test'}}, 'Controller': {'vpp': 'test'}}}} - ns = {'domain_name': 'test.domain', - 'networks': - {'tenant': - {'nic_mapping': {'controller': - {'members': ['tenant_nic']}, - 'compute': - {'members': ['tenant_nic']}}}, - 'external': - [{'nic_mapping': {'controller': - {'members': ['ext_nic']}, - 'compute': - {'members': ['ext_nic']}}}]}} - inv = None + ns_dict = {'domain_name': 'test.domain', + 'networks': + {'tenant': + {'nic_mapping': {'controller': + {'members': ['tenant_nic']}, + 'compute': + {'members': ['tenant_nic']}}}, + 'external': + [{'nic_mapping': {'controller': + {'members': ['ext_nic']}, + 'compute': + {'members': ['ext_nic']}}}]}} + inv = MagicMock() + inv.get_node_counts.return_value = (1, 0) try: # Swap stdout saved_stdout = sys.stdout out = StringIO() sys.stdout = out + ns = MagicMock() + ns.enabled_network_list = ['external', 'tenant'] + ns.__getitem__.side_effect = lambda i: ns_dict.get(i, MagicMock()) # run test prep_env(ds, ns, inv, 'opnfv-env.yml', '/net-env.yml', '/tmp') output = out.getvalue().strip() @@ -266,42 +490,56 @@ class TestOvercloudDeploy(unittest.TestCase): assert_in('ssh-rsa', output) assert_in('ComputeKernelArgs: \'test=test \'', output) assert_in('fdio::vpp_cpu_main_core: \'test\'', output) + mock_yaml.safe_dump.assert_called_with( + {'parameter_defaults': { + 'ControllerServices': [1, 2, 3, 4, 5], + }}, + mock.ANY, default_flow_style=False + ) finally: # put stdout back sys.stdout = saved_stdout @patch('apex.overcloud.deploy.fileinput') @patch('apex.overcloud.deploy.shutil') + @patch('builtins.open', mock_open()) def test_prep_env_round_two(self, mock_shutil, mock_fileinput): mock_fileinput.input.return_value = \ ['NeutronVPPAgentPhysnets'] - ds = {'deploy_options': + ds = {'global_params': {'ha_enabled': False}, + 'deploy_options': {'sdn_controller': False, 'dataplane': 'fdio', + 'sriov': 'xxx', 'performance': {'Compute': {}, 'Controller': {}}}} - ns = {'domain_name': 'test.domain', - 'networks': - {'tenant': - {'nic_mapping': {'controller': - {'members': ['tenant_nic']}, - 'compute': - {'members': ['tenant_nic']}}}, - 'external': - [{'nic_mapping': {'controller': - {'members': ['ext_nic']}, - 'compute': - {'members': ['ext_nic']}}}]}} - inv = None + ns_dict = {'domain_name': 'test.domain', + 'networks': + {'tenant': + {'nic_mapping': {'controller': + {'members': ['tenant_nic']}, + 'compute': + {'members': ['tenant_nic']}}}, + 'external': + [{'nic_mapping': {'controller': + {'members': ['ext_nic']}, + 'compute': + {'members': ['ext_nic']}}}]}} + inv = MagicMock() + inv.get_node_counts.return_value = (3, 2) try: # Swap stdout saved_stdout = sys.stdout out = StringIO() sys.stdout = out + ns = MagicMock() + ns.enabled_network_list = ['external', 'tenant'] + ns.__getitem__.side_effect = lambda i: ns_dict.get(i, MagicMock()) # run test prep_env(ds, ns, inv, 'opnfv-env.yml', '/net-env.yml', '/tmp') output = out.getvalue().strip() - assert_in('NeutronVPPAgentPhysnets: \'datacentre:tenant_nic\'', + assert_in('NeutronVPPAgentPhysnets: ' + '\'datacentre:tenant_nic,external:tap0\'', output) assert_in('NeutronVPPAgentPhysnets', output) finally: @@ -310,26 +548,29 @@ class TestOvercloudDeploy(unittest.TestCase): @patch('apex.overcloud.deploy.fileinput') @patch('apex.overcloud.deploy.shutil') + @patch('builtins.open', mock_open()) def test_prep_env_round_three(self, mock_shutil, mock_fileinput): mock_fileinput.input.return_value = \ ['OS::TripleO::Services::NeutronDhcpAgent', 'NeutronDhcpAgentsPerNetwork', 'ComputeServices'] - ds = {'deploy_options': + ds = {'global_params': {'ha_enabled': False}, + 'deploy_options': {'sdn_controller': 'opendaylight', 'dataplane': 'fdio', + 'sriov': 'xxx', 'dvr': True}} - ns = {'domain_name': 'test.domain', - 'networks': - {'tenant': - {'nic_mapping': {'controller': - {'members': ['tenant_nic']}, - 'compute': - {'members': ['tenant_nic']}}}, - 'external': - [{'nic_mapping': {'controller': - {'members': ['ext_nic']}, - 'compute': - {'members': ['ext_nic']}}}]}} + ns_dict = {'domain_name': 'test.domain', + 'networks': + {'tenant': + {'nic_mapping': {'controller': + {'members': ['tenant_nic']}, + 'compute': + {'members': ['tenant_nic']}}}, + 'external': + [{'nic_mapping': {'controller': + {'members': ['ext_nic']}, + 'compute': + {'members': ['ext_nic']}}}]}} inv = MagicMock() inv.get_node_counts.return_value = (3, 2) try: @@ -337,6 +578,9 @@ class TestOvercloudDeploy(unittest.TestCase): saved_stdout = sys.stdout out = StringIO() sys.stdout = out + ns = MagicMock() + ns.enabled_network_list = ['external', 'tenant'] + ns.__getitem__.side_effect = lambda i: ns_dict.get(i, MagicMock()) # run test prep_env(ds, ns, inv, 'opnfv-env.yml', '/net-env.yml', '/tmp') output = out.getvalue().strip() @@ -345,6 +589,111 @@ class TestOvercloudDeploy(unittest.TestCase): # put stdout back sys.stdout = saved_stdout + @patch('apex.overcloud.deploy.fileinput') + @patch('apex.overcloud.deploy.shutil') + @patch('builtins.open', mock_open()) + def test_prep_env_tenant_vlan(self, mock_shutil, mock_fileinput): + mock_fileinput.input.return_value = \ + ['NeutronNetworkVLANRanges', + 'NeutronNetworkType', 'NeutronBridgeMappings'] + ds = {'global_params': {'ha_enabled': False}, + 'deploy_options': + {'sdn_controller': False, + 'dataplane': 'ovs', + 'sriov': 'xxx', + 'dvr': True}} + ns_dict = {'domain_name': 'test.domain', + 'networks': + {'tenant': + {'nic_mapping': {'controller': + {'members': ['tenant_nic']}, + 'compute': + {'members': ['tenant_nic']}}, + 'segmentation_type': 'vlan', + 'overlay_id_range': 'vlan:500:600' + }, + 'external': + [{'nic_mapping': {'controller': + {'members': ['ext_nic']}, + 'compute': + {'members': ['ext_nic']}}}]}} + inv = MagicMock() + inv.get_node_counts.return_value = (3, 2) + try: + # Swap stdout + saved_stdout = sys.stdout + out = StringIO() + sys.stdout = out + ns = MagicMock() + ns.enabled_network_list = ['external', 'tenant'] + ns.__getitem__.side_effect = lambda i: ns_dict.get(i, MagicMock()) + # run test + prep_env(ds, ns, inv, 'opnfv-env.yml', '/net-env.yml', '/tmp') + output = out.getvalue().strip() + assert_in('NeutronNetworkVLANRanges: ' + 'vlan:500:600,datacentre:1:1000', output) + assert_in('NeutronNetworkType: vlan', output) + assert_in('NeutronBridgeMappings: ' + 'vlan:br-vlan,datacentre:br-ex', output) + assert_not_in('OpenDaylightProviderMappings', output) + finally: + # put stdout back + sys.stdout = saved_stdout + + @patch('apex.overcloud.deploy.fileinput') + @patch('apex.overcloud.deploy.shutil') + @patch('builtins.open', mock_open()) + def test_prep_env_tenant_vlan_odl(self, mock_shutil, mock_fileinput): + mock_fileinput.input.return_value = \ + ['NeutronNetworkVLANRanges', + 'NeutronNetworkType', + 'NeutronBridgeMappings', + 'OpenDaylightProviderMappings'] + ds = {'global_params': {'ha_enabled': False}, + 'deploy_options': + {'sdn_controller': 'opendaylight', + 'dataplane': 'ovs', + 'sriov': 'xxx', + 'dvr': True}} + ns_dict = {'domain_name': 'test.domain', + 'networks': + {'tenant': + {'nic_mapping': {'controller': + {'members': ['tenant_nic']}, + 'compute': + {'members': ['tenant_nic']}}, + 'segmentation_type': 'vlan', + 'overlay_id_range': 'vlan:500:600' + }, + 'external': + [{'nic_mapping': {'controller': + {'members': ['ext_nic']}, + 'compute': + {'members': ['ext_nic']}}}]}} + inv = MagicMock() + inv.get_node_counts.return_value = (3, 2) + try: + # Swap stdout + saved_stdout = sys.stdout + out = StringIO() + sys.stdout = out + ns = MagicMock() + ns.enabled_network_list = ['external', 'tenant'] + ns.__getitem__.side_effect = lambda i: ns_dict.get(i, MagicMock()) + # run test + prep_env(ds, ns, inv, 'opnfv-env.yml', '/net-env.yml', '/tmp') + output = out.getvalue().strip() + assert_in('NeutronNetworkVLANRanges: ' + 'vlan:500:600,datacentre:1:1000', output) + assert_in('NeutronNetworkType: vlan', output) + assert_in('NeutronBridgeMappings: ' + 'vlan:br-vlan,datacentre:br-ex', output) + assert_in('OpenDaylightProviderMappings: ' + 'vlan:br-vlan,datacentre:br-ex', output) + finally: + # put stdout back + sys.stdout = saved_stdout + def test_generate_ceph_key(self): assert_equal(len(generate_ceph_key()), 40) @@ -356,21 +705,109 @@ class TestOvercloudDeploy(unittest.TestCase): mock_ceph_key): mock_fileinput.input.return_value = \ ['CephClusterFSID', 'CephMonKey', 'CephAdminKey', 'random_key'] - ds = {'deploy_options': MagicMock()} - ds['deploy_options'].__getitem__.side_effect = \ - lambda i: '/dev/sdx' if i == 'ceph_device' else MagicMock() - ds['deploy_options'].__contains__.side_effect = \ - lambda i: True if i == 'ceph_device' else MagicMock() - prep_storage_env(ds, '/tmp') + ds = {'deploy_options': { + 'ceph_device': '/dev/sdx', + 'containers': False + }} + ns = {} + prep_storage_env(ds, ns, virtual=False, tmp_dir='/tmp') + + @patch('apex.overcloud.deploy.utils.edit_tht_env') + @patch('apex.overcloud.deploy.generate_ceph_key') + @patch('apex.overcloud.deploy.fileinput') + @patch('apex.overcloud.deploy.os.path.isfile') + @patch('builtins.open', mock_open()) + def test_prep_storage_env_containers(self, mock_isfile, mock_fileinput, + mock_ceph_key, mock_edit_tht): + mock_fileinput.input.return_value = \ + ['CephClusterFSID', 'CephMonKey', 'CephAdminKey', 'random_key'] + ds = {'deploy_options': { + 'ceph_device': '/dev/sdx', + 'containers': True, + 'os_version': 'master' + }, 'global_params': {'ha_enabled': False}} + ns = {'networks': {con.ADMIN_NETWORK: {'installer_vm': + {'ip': '192.0.2.1'}}} + } + prep_storage_env(ds, ns, virtual=True, tmp_dir='/tmp') + ceph_params = { + 'CephPoolDefaultSize': 2, + 'CephAnsibleExtraConfig': { + 'centos_package_dependencies': [], + 'ceph_osd_docker_memory_limit': '1g', + 'ceph_mds_docker_memory_limit': '1g' + }, + 'CephPoolDefaultPgNum': 32, + 'CephAnsibleDisksConfig': { + 'devices': ['/dev/sdx'], + 'journal_size': 512, + 'osd_scenario': 'collocated' + } + } + mock_edit_tht.assert_called_with('/tmp/storage-environment.yaml', + 'parameter_defaults', + ceph_params) @patch('apex.overcloud.deploy.os.path.isfile') @patch('builtins.open', mock_open()) def test_prep_storage_env_raises(self, mock_isfile): mock_isfile.return_value = False ds = {'deploy_options': MagicMock()} - assert_raises(ApexDeployException, prep_storage_env, ds, '/tmp') + ns = {} + assert_raises(ApexDeployException, prep_storage_env, ds, + ns, virtual=False, tmp_dir='/tmp') + + @patch('apex.overcloud.deploy.generate_ceph_key') + @patch('apex.overcloud.deploy.fileinput') + @patch('apex.overcloud.deploy.os.path.isfile') + @patch('builtins.open', mock_open()) + def test_prep_sriov_env(self, mock_isfile, mock_fileinput, mock_ceph_key): + ds = {'deploy_options': + {'sdn_controller': 'opendaylight', + 'sriov': 'xxx'}} + try: + # Swap stdout + saved_stdout = sys.stdout + out = StringIO() + sys.stdout = out + # Run tests + mock_fileinput.input.return_value = \ + ['# NovaSchedulerDefaultFilters', + '# NovaSchedulerAvailableFilters', + '#NeutronPhysicalDevMappings: "datacentre:ens20f2"', + '#NeutronSriovNumVFs: \"ens20f2:5\"', + '#NovaPCIPassthrough:', + '# - devname: \"ens20f2\"', + '# physical_network: \"datacentre\"'] + prep_sriov_env(ds, '/tmp') + output = out.getvalue().strip() + assert_in('NovaSchedulerDefaultFilters', output) + assert_in('NovaSchedulerAvailableFilters', output) + assert_in('NeutronPhysicalDevMappings: \"nfv_sriov:xxx\"', output) + assert_in('NeutronSriovNumVFs: \"xxx:8\"', output) + assert_in('NovaPCIPassthrough:', output) + assert_in('- devname: \"xxx\"', output) + assert_in('physical_network: \"nfv_sriov\"', output) + finally: + # put stdout back + sys.stdout = saved_stdout + + @patch('apex.overcloud.deploy.os.path.isfile') + @patch('builtins.open', mock_open()) + def test_prep_sriov_env_raises(self, mock_isfile): + ds_opts = {'sriov': True} + ds = {'deploy_options': MagicMock()} + ds['deploy_options'].__getitem__.side_effect = \ + lambda i: ds_opts.get(i, MagicMock()) + mock_isfile.return_value = False + ds = {'deploy_options': MagicMock()} + assert_raises(ApexDeployException, prep_sriov_env, ds, '/tmp') def test_external_network_cmds(self): + ds = {'deploy_options': + {'sdn_controller': 'opendaylight', + 'dataplane': 'ovs'}} + cidr = MagicMock() cidr.version = 6 ns_dict = {'networks': @@ -382,13 +819,41 @@ class TestOvercloudDeploy(unittest.TestCase): ns = MagicMock() ns.enabled_network_list = ['external'] ns.__getitem__.side_effect = lambda i: ns_dict.get(i, MagicMock()) - cmds = ' '.join(external_network_cmds(ns)) + cmds = ' '.join(external_network_cmds(ns, ds)) assert_in('--external', cmds) assert_in('--allocation-pool start=0,end=1', cmds) assert_in('--gateway gw', cmds) assert_in('--network external', cmds) + assert_in('--provider-physical-network datacentre', cmds) + + def test_external_network_cmds_nosdn_fdio(self): + ds = {'deploy_options': + {'sdn_controller': False, + 'dataplane': 'fdio'}} + + cidr = MagicMock() + cidr.version = 6 + ns_dict = {'networks': + {'external': [{'floating_ip_range': (0, 1), + 'nic_mapping': + {'compute': {'vlan': 'native'}}, + 'gateway': 'gw', + 'cidr': cidr}]}} + ns = MagicMock() + ns.enabled_network_list = ['external'] + ns.__getitem__.side_effect = lambda i: ns_dict.get(i, MagicMock()) + cmds = ' '.join(external_network_cmds(ns, ds)) + assert_in('--external', cmds) + assert_in('--allocation-pool start=0,end=1', cmds) + assert_in('--gateway gw', cmds) + assert_in('--network external', cmds) + assert_in('--provider-physical-network external', cmds) def test_external_network_cmds_no_ext(self): + ds = {'deploy_options': + {'sdn_controller': 'opendaylight', + 'dataplane': 'ovs'}} + cidr = MagicMock() cidr.version = 6 ns_dict = {'apex': @@ -402,8 +867,7 @@ class TestOvercloudDeploy(unittest.TestCase): ns = MagicMock() ns.enabled_network_list = ['admin'] ns.__getitem__.side_effect = lambda i: ns_dict.get(i, MagicMock()) - external_network_cmds(ns) - cmds = ' '.join(external_network_cmds(ns)) + cmds = ' '.join(external_network_cmds(ns, ds)) assert_in('--external', cmds) assert_in('--allocation-pool start=0,end=1', cmds) assert_in('--network external', cmds) @@ -417,3 +881,21 @@ class TestOvercloudDeploy(unittest.TestCase): def test_create_congress_cmds_raises(self, mock_parsers): mock_parsers.return_value.__getitem__.side_effect = KeyError() assert_raises(KeyError, create_congress_cmds, 'overcloud_file') + + def test_get_docker_sdn_files(self): + ds_opts = {'ha_enabled': True, + 'congress': True, + 'tacker': True, + 'containers': False, + 'barometer': True, + 'ceph': False, + 'vpn': True, + 'sdn_controller': 'opendaylight', + 'os_version': 'queens' + } + output = get_docker_sdn_files(ds_opts) + compare = ['/usr/share/openstack-tripleo-heat-templates/' + 'environments/services/neutron-opendaylight.yaml', + '/usr/share/openstack-tripleo-heat-templates/environments' + '/services/neutron-bgpvpn-opendaylight.yaml'] + self.assertEqual(output, compare) diff --git a/apex/tests/test_apex_overcloud_node.py b/apex/tests/test_apex_overcloud_node.py new file mode 100644 index 00000000..4c67b1d8 --- /dev/null +++ b/apex/tests/test_apex_overcloud_node.py @@ -0,0 +1,191 @@ +############################################################################## +# Copyright (c) 2018 Tim Rozet (trozet@redhat.com) (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 +############################################################################## + +from libvirt import libvirtError +from mock import patch +from mock import MagicMock +import os +import unittest +import urllib.request + +from apex.common import exceptions as exc +from apex.overcloud.node import OvercloudNode +from apex.settings.deploy_settings import DeploySettings +from apex.tests.constants import TEST_DUMMY_CONFIG + +DUMMY_SNAP_DIR = '/tmp/dummy_cache' + + +class TestSnapshotDeployment(unittest.TestCase): + @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('apex.overcloud.node.OvercloudNode.create') + @patch('apex.overcloud.node.os.path.isfile') + @patch('apex.overcloud.node.libvirt.open') + def test_init(self, mock_libvirt_open, mock_is_file, mock_node_create): + mock_is_file.return_value = True + OvercloudNode(role='controller', ip='123.123.123.123', + ovs_ctrlrs=None, ovs_mgrs=None, + name='dummy-controller-0', node_xml='dummynode.xml', + disk_img='dummy.qcow2') + mock_node_create.assert_called() + + @patch('apex.overcloud.node.OvercloudNode.create') + @patch('apex.overcloud.node.libvirt.open') + def test_init_invalid_files(self, mock_libvirt_open, mock_node_create): + self.assertRaises(exc.OvercloudNodeException, + OvercloudNode, 'controller', '123.123.123', + None, None, 'dummy-controller-0', 'dummynode.xml', + 'dummy.qcow2') + + @patch('apex.overcloud.node.shutil.copyfile') + @patch('apex.overcloud.node.OvercloudNode.create') + @patch('apex.overcloud.node.os.path.isfile') + @patch('apex.overcloud.node.libvirt.open') + def test_configure_disk(self, mock_libvirt_open, mock_is_file, + mock_node_create, mock_copy): + mock_is_file.return_value = True + node = OvercloudNode(role='controller', ip='123.123.123.123', + ovs_ctrlrs=None, ovs_mgrs=None, + name='dummy-controller-0', + node_xml='dummynode.xml', + disk_img='dummy.qcow2') + conn = mock_libvirt_open.return_value + conn.storagePoolLookupByName.return_value.XMLDesc.return_value = """ + <pool type='dir'> + <target> + <path>/var/lib/libvirt/images</path> + </target> + </pool> + """ + node._configure_disk('dummy.qcow2') + mock_copy.assert_called() + self.assertEqual(node.disk_img, '/var/lib/libvirt/images/dummy.qcow2') + + @patch('apex.overcloud.node.shutil.copyfile') + @patch('apex.overcloud.node.OvercloudNode.create') + @patch('apex.overcloud.node.os.path.isfile') + @patch('apex.overcloud.node.libvirt.open') + def test_configure_disk_bad_path(self, mock_libvirt_open, mock_is_file, + mock_node_create, mock_copy): + mock_is_file.return_value = True + node = OvercloudNode(role='controller', ip='123.123.123.123', + ovs_ctrlrs=None, ovs_mgrs=None, + name='dummy-controller-0', + node_xml='dummynode.xml', + disk_img='dummy.qcow2') + conn = mock_libvirt_open.return_value + conn.storagePoolLookupByName.return_value.XMLDesc.return_value = """ + <pool type='dir'> + <target> + </target> + </pool> + """ + self.assertRaises(exc.OvercloudNodeException, + node._configure_disk, 'dummy.qcow2') + + @patch('apex.overcloud.node.shutil.copyfile') + @patch('apex.overcloud.node.OvercloudNode.create') + @patch('apex.overcloud.node.os.path.isfile') + @patch('apex.overcloud.node.libvirt.open') + def test_configure_disk_no_pool(self, mock_libvirt_open, mock_is_file, + mock_node_create, mock_copy): + mock_is_file.return_value = True + node = OvercloudNode(role='controller', ip='123.123.123.123', + ovs_ctrlrs=None, ovs_mgrs=None, + name='dummy-controller-0', + node_xml='dummynode.xml', + disk_img='dummy.qcow2') + conn = mock_libvirt_open.return_value + conn.storagePoolLookupByName.return_value = None + self.assertRaises(exc.OvercloudNodeException, + node._configure_disk, 'dummy.qcow2') + + @patch('apex.overcloud.node.distro.linux_distribution') + def test_update_xml(self, mock_linux_distro): + mock_linux_distro.return_value = ['Fedora'] + xml_file = os.path.join(TEST_DUMMY_CONFIG, 'baremetal0.xml') + with open(xml_file, 'r') as fh: + xml = fh.read() + new_xml = OvercloudNode._update_xml( + xml=xml, disk_path='/dummy/disk/path/blah.qcow2') + self.assertIn('/dummy/disk/path/blah.qcow2', new_xml) + self.assertIn('/usr/bin/qemu-kvm', new_xml) + + @patch('apex.overcloud.node.distro.linux_distribution') + def test_update_xml_no_disk(self, mock_linux_distro): + mock_linux_distro.return_value = ['Fedora'] + xml_file = os.path.join(TEST_DUMMY_CONFIG, 'baremetal0.xml') + with open(xml_file, 'r') as fh: + xml = fh.read() + new_xml = OvercloudNode._update_xml(xml=xml) + self.assertIn('/home/images/baremetal0.qcow2', new_xml) + self.assertIn('/usr/bin/qemu-kvm', new_xml) + + @patch('apex.overcloud.node.OvercloudNode._update_xml') + @patch('apex.overcloud.node.OvercloudNode._configure_disk') + @patch('apex.overcloud.node.libvirt.open') + @patch('apex.overcloud.node.os.path.isfile') + def test_create(self, mock_isfile, mock_libvirt_conn, mock_configure_disk, + mock_update_xml): + mock_isfile.return_value = True + domain = mock_libvirt_conn.return_value.defineXML.return_value + node = OvercloudNode(role='controller', ip='123.123.123.123', + ovs_ctrlrs=None, ovs_mgrs=None, + name='dummy-controller-0', + node_xml=os.path.join(TEST_DUMMY_CONFIG, + 'baremetal0.xml'), + disk_img='dummy.qcow2') + self.assertIs(node.vm, domain) + + @patch('apex.overcloud.node.OvercloudNode._update_xml') + @patch('apex.overcloud.node.OvercloudNode._configure_disk') + @patch('apex.overcloud.node.libvirt.open') + @patch('apex.overcloud.node.os.path.isfile') + def test_start(self, mock_isfile, mock_libvirt_conn, mock_configure_disk, + mock_update_xml): + mock_isfile.return_value = True + domain = mock_libvirt_conn.return_value.defineXML.return_value + node = OvercloudNode(role='controller', ip='123.123.123.123', + ovs_ctrlrs=None, ovs_mgrs=None, + name='dummy-controller-0', + node_xml=os.path.join(TEST_DUMMY_CONFIG, + 'baremetal0.xml'), + disk_img='dummy.qcow2') + node.start() + domain.create.assert_called() + + @patch('apex.overcloud.node.OvercloudNode._update_xml') + @patch('apex.overcloud.node.OvercloudNode._configure_disk') + @patch('apex.overcloud.node.libvirt.open') + @patch('apex.overcloud.node.os.path.isfile') + def test_start_fail(self, mock_isfile, mock_libvirt_conn, + mock_configure_disk, mock_update_xml): + mock_isfile.return_value = True + domain = mock_libvirt_conn.return_value.defineXML.return_value + domain.create.side_effect = libvirtError('blah') + node = OvercloudNode(role='controller', ip='123.123.123.123', + ovs_ctrlrs=None, ovs_mgrs=None, + name='dummy-controller-0', + node_xml=os.path.join(TEST_DUMMY_CONFIG, + 'baremetal0.xml'), + disk_img='dummy.qcow2') + self.assertRaises(exc.OvercloudNodeException, node.start) diff --git a/apex/tests/test_apex_undercloud.py b/apex/tests/test_apex_undercloud.py index 9458bf9f..14586528 100644 --- a/apex/tests/test_apex_undercloud.py +++ b/apex/tests/test_apex_undercloud.py @@ -7,8 +7,10 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## +import ipaddress import libvirt import os +import platform import subprocess import unittest @@ -23,6 +25,7 @@ from nose.tools import ( assert_regexp_matches, assert_raises, assert_true, + assert_false, assert_equal) @@ -117,11 +120,111 @@ class TestUndercloud(unittest.TestCase): @patch.object(Undercloud, 'generate_config', return_value={}) @patch.object(Undercloud, '_get_vm', return_value=None) @patch.object(Undercloud, 'create') + def test_detect_nat_with_external(self, mock_create, mock_get_vm, + mock_generate_config, mock_utils): + ns = MagicMock() + ns.enabled_network_list = ['admin', 'external'] + ns_dict = { + 'apex': MagicMock(), + 'dns-domain': 'dns', + 'networks': {'admin': + {'cidr': ipaddress.ip_network('192.0.2.0/24'), + 'installer_vm': {'ip': '192.0.2.1', + 'vlan': 'native'}, + 'dhcp_range': ['192.0.2.15', '192.0.2.30'], + 'gateway': '192.1.1.1', + }, + 'external': + [{'enabled': True, + 'cidr': ipaddress.ip_network('192.168.0.0/24'), + 'installer_vm': {'ip': '192.168.0.1', + 'vlan': 'native'}, + 'gateway': '192.168.0.1' + }] + } + } + ns.__getitem__.side_effect = ns_dict.__getitem__ + ns.__contains__.side_effect = ns_dict.__contains__ + + uc = Undercloud('img_path', 'tplt_path', external_network=True) + assert_true(uc.detect_nat(ns)) + + @patch('apex.undercloud.undercloud.utils') + @patch.object(Undercloud, 'generate_config', return_value={}) + @patch.object(Undercloud, '_get_vm', return_value=None) + @patch.object(Undercloud, 'create') + def test_detect_nat_no_external(self, mock_create, mock_get_vm, + mock_generate_config, mock_utils): + ns = MagicMock() + ns.enabled_network_list = ['admin', 'external'] + ns_dict = { + 'apex': MagicMock(), + 'dns-domain': 'dns', + 'networks': {'admin': + {'cidr': ipaddress.ip_network('192.0.2.0/24'), + 'installer_vm': {'ip': '192.0.2.1', + 'vlan': 'native'}, + 'dhcp_range': ['192.0.2.15', '192.0.2.30'], + 'gateway': '192.0.2.1', + }, + 'external': + [{'enabled': False, + 'cidr': ipaddress.ip_network('192.168.0.0/24'), + 'installer_vm': {'ip': '192.168.0.1', + 'vlan': 'native'}, + 'gateway': '192.168.1.1' + }] + } + } + ns.__getitem__.side_effect = ns_dict.__getitem__ + ns.__contains__.side_effect = ns_dict.__contains__ + + uc = Undercloud('img_path', 'tplt_path', external_network=False) + assert_true(uc.detect_nat(ns)) + + @patch('apex.undercloud.undercloud.utils') + @patch.object(Undercloud, 'generate_config', return_value={}) + @patch.object(Undercloud, '_get_vm', return_value=None) + @patch.object(Undercloud, 'create') + def test_detect_no_nat_no_external(self, mock_create, mock_get_vm, + mock_generate_config, mock_utils): + ns = MagicMock() + ns.enabled_network_list = ['admin', 'external'] + ns_dict = { + 'apex': MagicMock(), + 'dns-domain': 'dns', + 'networks': {'admin': + {'cidr': ipaddress.ip_network('192.0.2.0/24'), + 'installer_vm': {'ip': '192.0.2.1', + 'vlan': 'native'}, + 'dhcp_range': ['192.0.2.15', '192.0.2.30'], + 'gateway': '192.0.2.3', + }, + 'external': + [{'enabled': False, + 'cidr': ipaddress.ip_network('192.168.0.0/24'), + 'installer_vm': {'ip': '192.168.0.1', + 'vlan': 'native'}, + 'gateway': '192.168.1.1' + }] + } + } + ns.__getitem__.side_effect = ns_dict.__getitem__ + ns.__contains__.side_effect = ns_dict.__contains__ + + uc = Undercloud('img_path', 'tplt_path', external_network=False) + assert_false(uc.detect_nat(ns)) + + @patch('apex.undercloud.undercloud.utils') + @patch.object(Undercloud, 'generate_config', return_value={}) + @patch.object(Undercloud, '_get_vm', return_value=None) + @patch.object(Undercloud, 'create') def test_configure(self, mock_create, mock_get_vm, mock_generate_config, mock_utils): uc = Undercloud('img_path', 'tplt_path', external_network=True) ns = MagicMock() - uc.configure(ns, 'playbook', '/tmp/dir') + ds = MagicMock() + uc.configure(ns, ds, 'playbook', '/tmp/dir') @patch('apex.undercloud.undercloud.utils') @patch.object(Undercloud, 'generate_config', return_value={}) @@ -131,18 +234,22 @@ class TestUndercloud(unittest.TestCase): mock_generate_config, mock_utils): uc = Undercloud('img_path', 'tplt_path', external_network=True) ns = MagicMock() + ds = MagicMock() subps_err = subprocess.CalledProcessError(1, 'cmd') mock_utils.run_ansible.side_effect = subps_err assert_raises(ApexUndercloudException, - uc.configure, ns, 'playbook', '/tmp/dir') + uc.configure, ns, ds, 'playbook', '/tmp/dir') + @patch('apex.undercloud.undercloud.virt_utils') + @patch('apex.undercloud.undercloud.uc_builder') @patch('apex.undercloud.undercloud.os.remove') @patch('apex.undercloud.undercloud.os.path') @patch('apex.undercloud.undercloud.shutil') @patch.object(Undercloud, '_get_vm', return_value=None) @patch.object(Undercloud, 'create') def test_setup_vols(self, mock_get_vm, mock_create, - mock_shutil, mock_os_path, mock_os_remove): + mock_shutil, mock_os_path, mock_os_remove, + mock_uc_builder, mock_virt_utils): uc = Undercloud('img_path', 'tplt_path', external_network=True) mock_os_path.isfile.return_value = True mock_os_path.exists.return_value = True @@ -152,6 +259,9 @@ class TestUndercloud(unittest.TestCase): src_img = os.path.join(uc.image_path, img_file) dest_img = os.path.join(constants.LIBVIRT_VOLUME_PATH, img_file) mock_shutil.copyfile.assert_called_with(src_img, dest_img) + if platform.machine() != 'aarch64': + mock_uc_builder.expand_disk.assert_called() + mock_virt_utils.virt_customize.assert_called() @patch('apex.undercloud.undercloud.os.path') @patch.object(Undercloud, '_get_vm', return_value=None) @@ -173,24 +283,64 @@ class TestUndercloud(unittest.TestCase): {'--upload': '/root/.ssh/id_rsa.pub:/root/.ssh/authorized_keys'}, {'--run-command': 'chmod 600 /root/.ssh/authorized_keys'}, - {'--run-command': 'restorecon /root/.ssh/authorized_keys'}, + {'--run-command': 'restorecon ' + '-R -v /root/.ssh'}, + {'--run-command': 'id -u stack || useradd -m stack'}, + {'--run-command': 'mkdir -p /home/stack/.ssh'}, + {'--run-command': 'chown stack:stack /home/stack/.ssh'}, {'--run-command': 'cp /root/.ssh/authorized_keys /home/stack/.ssh/'}, {'--run-command': 'chown stack:stack /home/stack/.ssh/authorized_keys'}, {'--run-command': - 'chmod 600 /home/stack/.ssh/authorized_keys'}] + 'chmod 600 /home/stack/.ssh/authorized_keys'}, + {'--run-command': + 'echo "stack ALL = (ALL) NOPASSWD: ALL" >> ' + '/etc/sudoers'}, + {'--run-command': 'touch /etc/cloud/cloud-init.disabled'}] mock_vutils.virt_customize.assert_called_with(test_ops, uc.volume) @patch.object(Undercloud, '_get_vm', return_value=None) @patch.object(Undercloud, 'create') def test_generate_config(self, mock_get_vm, mock_create): - ns_net = MagicMock() - ns_net.__getitem__.side_effect = \ - lambda i: '1234/24' if i is 'cidr' else MagicMock() - ns = {'apex': MagicMock(), - 'dns-domain': 'dns', - 'networks': {'admin': ns_net, - 'external': [ns_net]}} - - Undercloud('img_path', 'tplt_path').generate_config(ns) + ns = MagicMock() + ns.enabled_network_list = ['admin', 'external'] + ns_dict = { + 'apex': MagicMock(), + 'dns-domain': 'dns', + 'ntp': 'pool.ntp.org', + 'networks': {'admin': + {'cidr': ipaddress.ip_network('192.0.2.0/24'), + 'installer_vm': {'ip': '192.0.2.1', + 'vlan': 'native'}, + 'dhcp_range': ['192.0.2.15', '192.0.2.30'] + }, + 'external': + [{'enabled': True, + 'cidr': ipaddress.ip_network('192.168.0.0/24'), + 'installer_vm': {'ip': '192.168.0.1', + 'vlan': 'native'} + }] + } + } + ns.__getitem__.side_effect = ns_dict.__getitem__ + ns.__contains__.side_effect = ns_dict.__contains__ + ds = {'global_params': {}, + 'deploy_options': {}} + + Undercloud('img_path', 'tplt_path').generate_config(ns, ds) + + @patch.object(Undercloud, '_get_vm', return_value=None) + @patch.object(Undercloud, 'create') + @patch('apex.undercloud.undercloud.virt_utils') + def test_update_delorean(self, mock_vutils, mock_uc_create, mock_get_vm): + uc = Undercloud('img_path', 'tmplt_path', external_network=True) + uc._update_delorean_repo() + download_cmd = ( + "curl -L -f -o " + "/etc/yum.repos.d/deloran.repo " + "https://trunk.rdoproject.org/centos7-{}" + "/current-tripleo/delorean.repo".format( + constants.DEFAULT_OS_VERSION)) + test_ops = [{'--run-command': download_cmd}] + mock_vutils.virt_customize.assert_called_with(test_ops, uc.volume) diff --git a/apex/tests/test_apex_virtual_utils.py b/apex/tests/test_apex_virtual_utils.py index 643069f3..a9eb78dd 100644 --- a/apex/tests/test_apex_virtual_utils.py +++ b/apex/tests/test_apex_virtual_utils.py @@ -12,6 +12,7 @@ import unittest from mock import patch +from apex.virtual.exceptions import ApexVirtualException from apex.virtual.utils import DEFAULT_VIRT_IP from apex.virtual.utils import get_virt_ip from apex.virtual.utils import generate_inventory @@ -66,13 +67,30 @@ class TestVirtualUtils(unittest.TestCase): assert_is_instance(generate_inventory('target_file', ha_enabled=True), dict) + @patch('apex.virtual.utils.get_virt_ip') + @patch('apex.virtual.utils.subprocess.check_output') @patch('apex.virtual.utils.iptc') @patch('apex.virtual.utils.subprocess.check_call') @patch('apex.virtual.utils.vbmc_lib') - def test_host_setup(self, mock_vbmc_lib, mock_subprocess, mock_iptc): + def test_host_setup(self, mock_vbmc_lib, mock_subprocess, mock_iptc, + mock_check_output, mock_get_virt_ip): + mock_get_virt_ip.return_value = '192.168.122.1' + mock_check_output.return_value = b'blah |dummy \nstatus | running' host_setup({'test': 2468}) mock_subprocess.assert_called_with(['vbmc', 'start', 'test']) + @patch('apex.virtual.utils.get_virt_ip') + @patch('apex.virtual.utils.subprocess.check_output') + @patch('apex.virtual.utils.iptc') + @patch('apex.virtual.utils.subprocess.check_call') + @patch('apex.virtual.utils.vbmc_lib') + def test_host_setup_vbmc_fails(self, mock_vbmc_lib, mock_subprocess, + mock_iptc, mock_check_output, + mock_get_virt_ip): + mock_get_virt_ip.return_value = '192.168.122.1' + mock_check_output.return_value = b'blah |dummy \nstatus | stopped' + assert_raises(ApexVirtualException, host_setup, {'test': 2468}) + @patch('apex.virtual.utils.iptc') @patch('apex.virtual.utils.subprocess.check_call') @patch('apex.virtual.utils.vbmc_lib') |