Merge "Adding SRIOV scenario"
[apex.git] / apex / tests / test_apex_overcloud_deploy.py
1 ##############################################################################
2 # Copyright (c) 2016 Dan Radez (dradez@redhat.com) (Red Hat)
3 #
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 ##############################################################################
9
10 import os
11 import sys
12 import unittest
13
14 from mock import patch
15 from mock import MagicMock
16 from mock import mock_open
17 from io import StringIO
18
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
32
33 from nose.tools import (
34     assert_regexp_matches,
35     assert_raises,
36     assert_in,
37     assert_not_in,
38     assert_greater,
39     assert_equal)
40
41
42 class TestOvercloudDeploy(unittest.TestCase):
43     @classmethod
44     def setup_class(cls):
45         """This method is run once for each class before any tests are run"""
46
47     @classmethod
48     def teardown_class(cls):
49         """This method is run once for each class _after_ all tests are run"""
50
51     def setup(self):
52         """This method is run once before _each_ test method is executed"""
53
54     def teardown(self):
55         """This method is run once after _each_ test method is executed"""
56
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])
62
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), [])
68
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])
75
76     def test_build_sdn_env_list_with_default(self):
77         ds = {'sdn_controller': 'opendaylight',
78               'vpn': True}
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)
83
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,
89                                mock_prep_sriov):
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()
99         ns = {'ntp': ['ntp']}
100         inv = MagicMock()
101         inv.get_node_counts.return_value = (3, 2)
102         virt = True
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)
111
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']}
124         inv = MagicMock()
125         inv.get_node_counts.return_value = (3, 2)
126         virt = False
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)
135
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,
140                                       mock_prep_sriov):
141         mock_sdn_list.return_value = []
142         ds = {'deploy_options': MagicMock(),
143               'global_params': MagicMock()}
144         ns = {}
145         inv = MagicMock()
146         inv.get_node_counts.return_value = (0, 0)
147         virt = False
148         assert_raises(ApexDeployException, create_deploy_cmd,
149                       ds, ns, inv, '/tmp', virt)
150
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())
163         ns = MagicMock()
164         prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test')
165         mock_virt_utils.virt_customize.assert_called()
166
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,
172                                   mock_virt_utils):
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())
179         ns = MagicMock()
180         prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test')
181         mock_virt_utils.virt_customize.assert_called()
182
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,
188                                 mock_virt_utils):
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()
199         ns = MagicMock()
200         prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test')
201         mock_virt_utils.virt_customize.assert_called()
202
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())
216         ns = MagicMock()
217         prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test')
218         mock_virt_utils.virt_customize.assert_called()
219
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,
225                                 mock_virt_utils):
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())
232         ns = MagicMock()
233         prep_image(ds, ns, 'undercloud.qcow2', '/tmp', root_pw='test')
234         mock_virt_utils.virt_customize.assert_called()
235
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')
241
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)
246
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',
260                'sriov': 'xxx',
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',
267               'networks':
268               {'tenant':
269                {'nic_mapping': {'controller':
270                                 {'members': ['tenant_nic']},
271                                 'compute':
272                                 {'members': ['tenant_nic']}}},
273                'external':
274                [{'nic_mapping': {'controller':
275                                  {'members': ['ext_nic']},
276                                  'compute':
277                                  {'members': ['ext_nic']}}}]}}
278         inv = None
279         try:
280             # Swap stdout
281             saved_stdout = sys.stdout
282             out = StringIO()
283             sys.stdout = out
284             # run test
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)
291         finally:
292             # put stdout back
293             sys.stdout = saved_stdout
294
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,
302                'dataplane': 'fdio',
303                'sriov': 'xxx',
304                'performance': {'Compute': {},
305                                'Controller': {}}}}
306         ns = {'domain_name': 'test.domain',
307               'networks':
308               {'tenant':
309                {'nic_mapping': {'controller':
310                                 {'members': ['tenant_nic']},
311                                 'compute':
312                                 {'members': ['tenant_nic']}}},
313                'external':
314                [{'nic_mapping': {'controller':
315                                  {'members': ['ext_nic']},
316                                  'compute':
317                                  {'members': ['ext_nic']}}}]}}
318         inv = None
319         try:
320             # Swap stdout
321             saved_stdout = sys.stdout
322             out = StringIO()
323             sys.stdout = out
324             # run test
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\'',
328                       output)
329             assert_in('NeutronVPPAgentPhysnets', output)
330         finally:
331             # put stdout back
332             sys.stdout = saved_stdout
333
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',
342                'dataplane': 'fdio',
343                'sriov': 'xxx',
344                'dvr': True}}
345         ns = {'domain_name': 'test.domain',
346               'networks':
347               {'tenant':
348                {'nic_mapping': {'controller':
349                                 {'members': ['tenant_nic']},
350                                 'compute':
351                                 {'members': ['tenant_nic']}}},
352                'external':
353                [{'nic_mapping': {'controller':
354                                  {'members': ['ext_nic']},
355                                  'compute':
356                                  {'members': ['ext_nic']}}}]}}
357         inv = MagicMock()
358         inv.get_node_counts.return_value = (3, 2)
359         try:
360             # Swap stdout
361             saved_stdout = sys.stdout
362             out = StringIO()
363             sys.stdout = out
364             # run test
365             prep_env(ds, ns, inv, 'opnfv-env.yml', '/net-env.yml', '/tmp')
366             output = out.getvalue().strip()
367             assert_in('NeutronDhcpAgentsPerNetwork: 2', output)
368         finally:
369             # put stdout back
370             sys.stdout = saved_stdout
371
372     def test_generate_ceph_key(self):
373         assert_equal(len(generate_ceph_key()), 40)
374
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,
380                               mock_ceph_key):
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')
389
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')
396
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',
404                'sriov': 'xxx'}}
405         try:
406             # Swap stdout
407             saved_stdout = sys.stdout
408             out = StringIO()
409             sys.stdout = out
410             # Run tests
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)
428         finally:
429             # put stdout back
430             sys.stdout = saved_stdout
431
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')
442
443     def test_external_network_cmds(self):
444         cidr = MagicMock()
445         cidr.version = 6
446         ns_dict = {'networks':
447                    {'external': [{'floating_ip_range': (0, 1),
448                                   'nic_mapping':
449                                   {'compute': {'vlan': 'native'}},
450                                   'gateway': 'gw',
451                                   'cidr': cidr}]}}
452         ns = MagicMock()
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)
460
461     def test_external_network_cmds_no_ext(self):
462         cidr = MagicMock()
463         cidr.version = 6
464         ns_dict = {'apex':
465                    {'networks':
466                     {'admin':
467                      {'introspection_range': (0, 1),
468                       'nic_mapping':
469                       {'compute': {'vlan': '123'}},
470                       'gateway': 'gw',
471                       'cidr': cidr}}}}
472         ns = MagicMock()
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)
481
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)
485
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')