1 ##############################################################################
2 # Copyright (c) 2016 Dan Radez (dradez@redhat.com) (Red Hat)
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
14 from mock import patch
15 from mock import MagicMock
16 from mock import mock_open
17 from io import StringIO
19 from apex.common import constants as con
20 from apex.common.exceptions import ApexDeployException
21 from apex.overcloud.deploy import build_sdn_env_list
22 from apex.overcloud.deploy import create_deploy_cmd
23 from apex.overcloud.deploy import prep_image
24 from apex.overcloud.deploy import make_ssh_key
25 from apex.overcloud.deploy import prep_env
26 from apex.overcloud.deploy import generate_ceph_key
27 from apex.overcloud.deploy import prep_storage_env
28 from apex.overcloud.deploy import prep_sriov_env
29 from apex.overcloud.deploy import external_network_cmds
30 from apex.overcloud.deploy import create_congress_cmds
31 from apex.overcloud.deploy import SDN_FILE_MAP
33 from nose.tools import (
34 assert_regexp_matches,
42 class TestOvercloudDeploy(unittest.TestCase):
45 """This method is run once for each class before any tests are run"""
48 def teardown_class(cls):
49 """This method is run once for each class _after_ all tests are run"""
52 """This method is run once before _each_ test method is executed"""
55 """This method is run once after _each_ test method is executed"""
57 def test_build_sdn_env_list(self):
58 ds = {'sdn_controller': 'opendaylight'}
59 sdn_map = {'opendaylight': 'test'}
60 res = '/usr/share/openstack-tripleo-heat-templates/environments/test'
61 assert_equal(build_sdn_env_list(ds, sdn_map), [res])
63 def test_build_sdn_env_list_dict(self):
64 ds = {'opendaylight': True,
65 'sdn_controller': None}
66 sdn_map = {'opendaylight': {}}
67 assert_equal(build_sdn_env_list(ds, sdn_map), [])
69 def test_build_sdn_env_list_tuple(self):
70 ds = {'opendaylight': 'test',
71 'sdn_controller': None}
72 sdn_map = {'opendaylight': ('test', 'test')}
73 res = '/usr/share/openstack-tripleo-heat-templates/environments/test'
74 assert_equal(build_sdn_env_list(ds, sdn_map), [res])
76 def test_build_sdn_env_list_with_default(self):
77 ds = {'sdn_controller': 'opendaylight',
79 prefix = '/usr/share/openstack-tripleo-heat-templates/environments'
80 res = [os.path.join(prefix, 'neutron-opendaylight.yaml'),
81 os.path.join(prefix, 'neutron-bgpvpn-opendaylight.yaml')]
82 assert_equal(build_sdn_env_list(ds, SDN_FILE_MAP), res)
84 @patch('apex.overcloud.deploy.prep_sriov_env')
85 @patch('apex.overcloud.deploy.prep_storage_env')
86 @patch('apex.overcloud.deploy.build_sdn_env_list')
87 @patch('builtins.open', mock_open())
88 def test_create_deploy_cmd(self, mock_sdn_list, mock_prep_storage,
90 mock_sdn_list.return_value = []
91 ds = {'deploy_options': MagicMock(),
92 'global_params': MagicMock()}
93 ds['global_params'].__getitem__.side_effect = \
94 lambda i: True if i == 'ha_enabled' else MagicMock()
95 ds['deploy_options'].__getitem__.side_effect = \
96 lambda i: True if i == 'congress' else MagicMock()
97 ds['deploy_options'].__contains__.side_effect = \
98 lambda i: True if i == 'congress' else MagicMock()
101 inv.get_node_counts.return_value = (3, 2)
103 result_cmd = create_deploy_cmd(ds, ns, inv, '/tmp', virt)
104 assert_in('--ntp-server ntp', result_cmd)
105 assert_in('enable_tacker.yaml', result_cmd)
106 assert_in('enable_congress.yaml', result_cmd)
107 assert_in('enable_barometer.yaml', result_cmd)
108 assert_in('virtual-environment.yaml', result_cmd)
109 assert_in('--control-scale 3', result_cmd)
110 assert_in('--compute-scale 2', result_cmd)
112 @patch('apex.overcloud.deploy.prep_sriov_env')
113 @patch('apex.overcloud.deploy.prep_storage_env')
114 @patch('apex.overcloud.deploy.build_sdn_env_list')
115 @patch('builtins.open', mock_open())
116 def test_create_deploy_cmd_no_ha_bm(self, mock_sdn_list,
117 mock_prep_storage, mock_prep_sriov):
118 mock_sdn_list.return_value = []
119 ds = {'deploy_options': MagicMock(),
120 'global_params': MagicMock()}
121 ds['global_params'].__getitem__.side_effect = \
122 lambda i: False if i == 'ha_enabled' else MagicMock()
123 ns = {'ntp': ['ntp']}
125 inv.get_node_counts.return_value = (3, 2)
127 result_cmd = create_deploy_cmd(ds, ns, inv, '/tmp', virt)
128 assert_in('--ntp-server ntp', result_cmd)
129 assert_in('--control-scale 1', result_cmd)
130 assert_in('--compute-scale 2', result_cmd)
131 assert_in('baremetal-environment.yaml', result_cmd)
132 assert_not_in('enable_tacker.yaml', result_cmd)
133 assert_not_in('enable_congress.yaml', result_cmd)
134 assert_not_in('enable_barometer.yaml', result_cmd)
136 @patch('apex.overcloud.deploy.prep_sriov_env')
137 @patch('apex.overcloud.deploy.prep_storage_env')
138 @patch('apex.overcloud.deploy.build_sdn_env_list')
139 def test_create_deploy_cmd_raises(self, mock_sdn_list, mock_prep_storage,
141 mock_sdn_list.return_value = []
142 ds = {'deploy_options': MagicMock(),
143 'global_params': MagicMock()}
146 inv.get_node_counts.return_value = (0, 0)
148 assert_raises(ApexDeployException, create_deploy_cmd,
149 ds, ns, inv, '/tmp', virt)
151 @patch('apex.overcloud.deploy.virt_utils')
152 @patch('apex.overcloud.deploy.shutil')
153 @patch('apex.overcloud.deploy.os.path')
154 @patch('builtins.open', mock_open())
155 def test_prep_image(self, mock_os_path, mock_shutil, mock_virt_utils):
156 ds_opts = {'dataplane': 'fdio',
157 'sdn_controller': 'opendaylight',
158 'odl_version': 'master'}
159 ds = {'deploy_options': MagicMock(),
160 'global_params': MagicMock()}
161 ds['deploy_options'].__getitem__.side_effect = \
162 lambda i: ds_opts.get(i, MagicMock())
164 prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test')
165 mock_virt_utils.virt_customize.assert_called()
167 @patch('apex.overcloud.deploy.virt_utils')
168 @patch('apex.overcloud.deploy.shutil')
169 @patch('apex.overcloud.deploy.os.path')
170 @patch('builtins.open', mock_open())
171 def test_prep_image_sdn_false(self, mock_os_path, mock_shutil,
173 ds_opts = {'dataplane': 'fdio',
174 'sdn_controller': False}
175 ds = {'deploy_options': MagicMock(),
176 'global_params': MagicMock()}
177 ds['deploy_options'].__getitem__.side_effect = \
178 lambda i: ds_opts.get(i, MagicMock())
180 prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test')
181 mock_virt_utils.virt_customize.assert_called()
183 @patch('apex.overcloud.deploy.virt_utils')
184 @patch('apex.overcloud.deploy.shutil')
185 @patch('apex.overcloud.deploy.os.path')
186 @patch('builtins.open', mock_open())
187 def test_prep_image_sdn_odl(self, mock_os_path, mock_shutil,
189 ds_opts = {'dataplane': 'ovs',
190 'sdn_controller': 'opendaylight',
191 'odl_version': con.DEFAULT_ODL_VERSION,
192 'odl_vpp_netvirt': True}
193 ds = {'deploy_options': MagicMock(),
194 'global_params': MagicMock()}
195 ds['deploy_options'].__getitem__.side_effect = \
196 lambda i: ds_opts.get(i, MagicMock())
197 ds['deploy_options'].__contains__.side_effect = \
198 lambda i: True if i in ds_opts else MagicMock()
200 prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test')
201 mock_virt_utils.virt_customize.assert_called()
203 @patch('apex.overcloud.deploy.virt_utils')
204 @patch('apex.overcloud.deploy.shutil')
205 @patch('apex.overcloud.deploy.os.path')
206 @patch('builtins.open', mock_open())
207 def test_prep_image_sdn_odl_not_def(self, mock_os_path,
208 mock_shutil, mock_virt_utils):
209 ds_opts = {'dataplane': 'ovs',
210 'sdn_controller': 'opendaylight',
211 'odl_version': 'uncommon'}
212 ds = {'deploy_options': MagicMock(),
213 'global_params': MagicMock()}
214 ds['deploy_options'].__getitem__.side_effect = \
215 lambda i: ds_opts.get(i, MagicMock())
217 prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test')
218 mock_virt_utils.virt_customize.assert_called()
220 @patch('apex.overcloud.deploy.virt_utils')
221 @patch('apex.overcloud.deploy.shutil')
222 @patch('apex.overcloud.deploy.os.path')
223 @patch('builtins.open', mock_open())
224 def test_prep_image_sdn_ovn(self, mock_os_path, mock_shutil,
226 ds_opts = {'dataplane': 'ovs',
227 'sdn_controller': 'ovn'}
228 ds = {'deploy_options': MagicMock(),
229 'global_params': MagicMock()}
230 ds['deploy_options'].__getitem__.side_effect = \
231 lambda i: ds_opts.get(i, MagicMock())
233 prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test')
234 mock_virt_utils.virt_customize.assert_called()
236 @patch('apex.overcloud.deploy.os.path.isfile')
237 def test_prep_image_no_image(self, mock_isfile):
238 mock_isfile.return_value = False
239 assert_raises(ApexDeployException, prep_image,
240 {}, {}, 'undercloud.qcow2', '/tmp')
242 def test_make_ssh_key(self):
243 priv, pub = make_ssh_key()
244 assert_in('-----BEGIN PRIVATE KEY-----', priv)
245 assert_in('ssh-rsa', pub)
247 @patch('apex.overcloud.deploy.fileinput')
248 @patch('apex.overcloud.deploy.shutil')
249 def test_prep_env(self, mock_shutil, mock_fileinput):
250 mock_fileinput.input.return_value = \
251 ['CloudDomain', 'replace_private_key', 'replace_public_key',
252 'opendaylight::vpp_routing_node', 'ControllerExtraConfig',
253 'NovaComputeExtraConfig', 'ComputeKernelArgs', 'HostCpusList',
254 'ComputeExtraConfigPre', 'resource_registry',
255 'NovaSchedulerDefaultFilters']
256 ds = {'deploy_options':
257 {'sdn_controller': 'opendaylight',
258 'odl_vpp_routing_node': 'test',
259 'dataplane': 'ovs_dpdk',
261 'performance': {'Compute': {'vpp': {'main-core': 'test',
262 'corelist-workers': 'test'},
263 'ovs': {'dpdk_cores': 'test'},
264 'kernel': {'test': 'test'}},
265 'Controller': {'vpp': 'test'}}}}
266 ns = {'domain_name': 'test.domain',
269 {'nic_mapping': {'controller':
270 {'members': ['tenant_nic']},
272 {'members': ['tenant_nic']}}},
274 [{'nic_mapping': {'controller':
275 {'members': ['ext_nic']},
277 {'members': ['ext_nic']}}}]}}
281 saved_stdout = sys.stdout
285 prep_env(ds, ns, inv, 'opnfv-env.yml', '/net-env.yml', '/tmp')
286 output = out.getvalue().strip()
287 assert_in('CloudDomain: test.domain', output)
288 assert_in('ssh-rsa', output)
289 assert_in('ComputeKernelArgs: \'test=test \'', output)
290 assert_in('fdio::vpp_cpu_main_core: \'test\'', output)
293 sys.stdout = saved_stdout
295 @patch('apex.overcloud.deploy.fileinput')
296 @patch('apex.overcloud.deploy.shutil')
297 def test_prep_env_round_two(self, mock_shutil, mock_fileinput):
298 mock_fileinput.input.return_value = \
299 ['NeutronVPPAgentPhysnets']
300 ds = {'deploy_options':
301 {'sdn_controller': False,
304 'performance': {'Compute': {},
306 ns = {'domain_name': 'test.domain',
309 {'nic_mapping': {'controller':
310 {'members': ['tenant_nic']},
312 {'members': ['tenant_nic']}}},
314 [{'nic_mapping': {'controller':
315 {'members': ['ext_nic']},
317 {'members': ['ext_nic']}}}]}}
321 saved_stdout = sys.stdout
325 prep_env(ds, ns, inv, 'opnfv-env.yml', '/net-env.yml', '/tmp')
326 output = out.getvalue().strip()
327 assert_in('NeutronVPPAgentPhysnets: \'datacentre:tenant_nic\'',
329 assert_in('NeutronVPPAgentPhysnets', output)
332 sys.stdout = saved_stdout
334 @patch('apex.overcloud.deploy.fileinput')
335 @patch('apex.overcloud.deploy.shutil')
336 def test_prep_env_round_three(self, mock_shutil, mock_fileinput):
337 mock_fileinput.input.return_value = \
338 ['OS::TripleO::Services::NeutronDhcpAgent',
339 'NeutronDhcpAgentsPerNetwork', 'ComputeServices']
340 ds = {'deploy_options':
341 {'sdn_controller': 'opendaylight',
345 ns = {'domain_name': 'test.domain',
348 {'nic_mapping': {'controller':
349 {'members': ['tenant_nic']},
351 {'members': ['tenant_nic']}}},
353 [{'nic_mapping': {'controller':
354 {'members': ['ext_nic']},
356 {'members': ['ext_nic']}}}]}}
358 inv.get_node_counts.return_value = (3, 2)
361 saved_stdout = sys.stdout
365 prep_env(ds, ns, inv, 'opnfv-env.yml', '/net-env.yml', '/tmp')
366 output = out.getvalue().strip()
367 assert_in('NeutronDhcpAgentsPerNetwork: 2', output)
370 sys.stdout = saved_stdout
372 def test_generate_ceph_key(self):
373 assert_equal(len(generate_ceph_key()), 40)
375 @patch('apex.overcloud.deploy.generate_ceph_key')
376 @patch('apex.overcloud.deploy.fileinput')
377 @patch('apex.overcloud.deploy.os.path.isfile')
378 @patch('builtins.open', mock_open())
379 def test_prep_storage_env(self, mock_isfile, mock_fileinput,
381 mock_fileinput.input.return_value = \
382 ['CephClusterFSID', 'CephMonKey', 'CephAdminKey', 'random_key']
383 ds = {'deploy_options': MagicMock()}
384 ds['deploy_options'].__getitem__.side_effect = \
385 lambda i: '/dev/sdx' if i == 'ceph_device' else MagicMock()
386 ds['deploy_options'].__contains__.side_effect = \
387 lambda i: True if i == 'ceph_device' else MagicMock()
388 prep_storage_env(ds, '/tmp')
390 @patch('apex.overcloud.deploy.os.path.isfile')
391 @patch('builtins.open', mock_open())
392 def test_prep_storage_env_raises(self, mock_isfile):
393 mock_isfile.return_value = False
394 ds = {'deploy_options': MagicMock()}
395 assert_raises(ApexDeployException, prep_storage_env, ds, '/tmp')
397 @patch('apex.overcloud.deploy.generate_ceph_key')
398 @patch('apex.overcloud.deploy.fileinput')
399 @patch('apex.overcloud.deploy.os.path.isfile')
400 @patch('builtins.open', mock_open())
401 def test_prep_sriov_env(self, mock_isfile, mock_fileinput, mock_ceph_key):
402 ds = {'deploy_options':
403 {'sdn_controller': 'opendaylight',
407 saved_stdout = sys.stdout
411 mock_fileinput.input.return_value = \
412 ['# NovaSchedulerDefaultFilters',
413 '# NovaSchedulerAvailableFilters',
414 '#NeutronPhysicalDevMappings: "datacentre:ens20f2"',
415 '#NeutronSriovNumVFs: \"ens20f2:5\"',
416 '#NovaPCIPassthrough:',
417 '# - devname: \"ens20f2\"',
418 '# physical_network: \"datacentre\"']
419 prep_sriov_env(ds, '/tmp')
420 output = out.getvalue().strip()
421 assert_in('NovaSchedulerDefaultFilters', output)
422 assert_in('NovaSchedulerAvailableFilters', output)
423 assert_in('NeutronPhysicalDevMappings: \"nfv_sriov:xxx\"', output)
424 assert_in('NeutronSriovNumVFs: \"xxx:8\"', output)
425 assert_in('NovaPCIPassthrough:', output)
426 assert_in('- devname: \"xxx\"', output)
427 assert_in('physical_network: \"nfv_sriov\"', output)
430 sys.stdout = saved_stdout
432 @patch('apex.overcloud.deploy.os.path.isfile')
433 @patch('builtins.open', mock_open())
434 def test_prep_sriov_env_raises(self, mock_isfile):
435 ds_opts = {'sriov': True}
436 ds = {'deploy_options': MagicMock()}
437 ds['deploy_options'].__getitem__.side_effect = \
438 lambda i: ds_opts.get(i, MagicMock())
439 mock_isfile.return_value = False
440 ds = {'deploy_options': MagicMock()}
441 assert_raises(ApexDeployException, prep_sriov_env, ds, '/tmp')
443 def test_external_network_cmds(self):
446 ns_dict = {'networks':
447 {'external': [{'floating_ip_range': (0, 1),
449 {'compute': {'vlan': 'native'}},
453 ns.enabled_network_list = ['external']
454 ns.__getitem__.side_effect = lambda i: ns_dict.get(i, MagicMock())
455 cmds = ' '.join(external_network_cmds(ns))
456 assert_in('--external', cmds)
457 assert_in('--allocation-pool start=0,end=1', cmds)
458 assert_in('--gateway gw', cmds)
459 assert_in('--network external', cmds)
461 def test_external_network_cmds_no_ext(self):
467 {'introspection_range': (0, 1),
469 {'compute': {'vlan': '123'}},
473 ns.enabled_network_list = ['admin']
474 ns.__getitem__.side_effect = lambda i: ns_dict.get(i, MagicMock())
475 external_network_cmds(ns)
476 cmds = ' '.join(external_network_cmds(ns))
477 assert_in('--external', cmds)
478 assert_in('--allocation-pool start=0,end=1', cmds)
479 assert_in('--network external', cmds)
480 assert_in('--provider-network-type vlan', cmds)
482 @patch('apex.overcloud.deploy.parsers')
483 def test_create_congress_cmds(self, mock_parsers):
484 assert_greater(len(create_congress_cmds('overcloud_file')), 0)
486 @patch('apex.overcloud.deploy.parsers.parse_overcloudrc')
487 def test_create_congress_cmds_raises(self, mock_parsers):
488 mock_parsers.return_value.__getitem__.side_effect = KeyError()
489 assert_raises(KeyError, create_congress_cmds, 'overcloud_file')