1 # -*- coding: utf-8 -*-
3 # Copyright 2018 University of Bristol - High Performance Networks Research
7 # Contributors: Anderson Bravalheri, Dimitrios Gkounis, Abubakar Siddique
8 # Muqaddas, Navdeep Uniyal, Reza Nejabati and Dimitra Simeonidou
10 # Licensed under the Apache License, Version 2.0 (the "License"); you may
11 # not use this file except in compliance with the License. You may obtain
12 # a copy of the License at
14 # http://www.apache.org/licenses/LICENSE-2.0
16 # Unless required by applicable law or agreed to in writing, software
17 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
18 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
19 # License for the specific language governing permissions and limitations
22 # For those usages not covered by the Apache License, Version 2.0 please
23 # contact with: <highperformance-networks@bristol.ac.uk>
25 # Neither the name of the University of Bristol nor the names of its
26 # contributors may be used to endorse or promote products derived from
27 # this software without specific prior written permission.
29 # This work has been performed in the context of DCMS UK 5G Testbeds
30 # & Trials Programme and in the framework of the Metro-Haul project -
31 # funded by the European Commission under Grant number 761727 through the
32 # Horizon 2020 and 5G-PPP programmes.
34 # pylint: disable=E1101
36 from __future__
import unicode_literals
, print_function
42 from mock
import MagicMock
, patch
44 from . import fixtures
as eg
45 from ...tests
.db_helpers
import (
46 TestCaseWithDatabasePerTest
,
50 from ..persistence
import WimPersistence
51 from ..wan_link_actions
import WanLinkCreate
, WanLinkDelete
52 from ..wimconn
import WimConnectorError
55 class TestActionsWithDb(TestCaseWithDatabasePerTest
):
57 super(TestActionsWithDb
, self
).setUp()
58 self
.persist
= WimPersistence(self
.db
)
59 self
.connector
= MagicMock()
60 self
.ovim
= MagicMock()
63 class TestCreate(TestActionsWithDb
):
65 def test_process__instance_nets_on_build(self
):
66 # Given we want 1 WAN link between 2 datacenters
67 # and the local network in each datacenter is still being built
68 wan_link
= eg
.instance_wim_nets()
69 instance_nets
= eg
.instance_nets(num_datacenters
=2, num_links
=1)
70 for net
in instance_nets
:
71 net
['status'] = 'BUILD'
72 self
.populate([{'instance_nets': instance_nets
,
73 'instance_wim_nets': wan_link
}])
75 # When we try to process a CREATE action that refers to the same
76 # instance_scenario_id and sce_net_id
78 action
= WanLinkCreate(eg
.wim_actions('CREATE')[0])
79 action
.instance_scenario_id
= instance_nets
[0]['instance_scenario_id']
80 action
.sce_net_id
= instance_nets
[0]['sce_net_id']
81 # -- ensure it is in the database for updates --> #
82 action_record
= action
.as_record()
83 action_record
['extra'] = json
.dumps(action_record
['extra'])
84 self
.populate([{'vim_wim_actions': action_record
}])
86 action
.process(self
.connector
, self
.persist
, self
.ovim
)
88 # Then the action should be defered
89 assert action
.is_scheduled
90 self
.assertEqual(action
.extra
['attempts'], 1)
91 self
.assertGreater(action
.extra
['last_attempted_at'], now
)
94 def test_process__instance_nets_on_error(self
):
95 # Given we want 1 WAN link between 2 datacenters
96 # and at least one local network is in a not good state (error, or
98 instance_nets
= eg
.instance_nets(num_datacenters
=2, num_links
=1)
99 instance_nets
[1]['status'] = 'SCHEDULED_DELETION'
100 wan_link
= eg
.instance_wim_nets()
101 self
.populate([{'instance_nets': instance_nets
,
102 'instance_wim_nets': wan_link
}])
104 # When we try to process a CREATE action that refers to the same
105 # instance_scenario_id and sce_net_id
106 action
= WanLinkCreate(eg
.wim_actions('CREATE')[0])
107 action
.instance_scenario_id
= instance_nets
[0]['instance_scenario_id']
108 action
.sce_net_id
= instance_nets
[0]['sce_net_id']
109 # -- ensure it is in the database for updates --> #
110 action_record
= action
.as_record()
111 action_record
['extra'] = json
.dumps(action_record
['extra'])
112 self
.populate([{'vim_wim_actions': action_record
}])
114 action
.process(self
.connector
, self
.persist
, self
.ovim
)
116 # Then the action should fail
117 assert action
.is_failed
118 self
.assertIn('issue with the local networks', action
.error_msg
)
119 self
.assertIn('SCHEDULED_DELETION', action
.error_msg
)
121 def prepare_create__sdn(self
):
122 db_state
= [{'nfvo_tenants': eg
.tenant()}] + eg
.wim_set()
124 instance_nets
= eg
.instance_nets(num_datacenters
=2, num_links
=1)
126 eg
.wim_port_mapping(0, 0),
127 eg
.wim_port_mapping(0, 1)
129 instance_action
= eg
.instance_action(action_id
='ACTION-000')
130 for i
, net
in enumerate(instance_nets
):
131 net
['status'] = 'ACTIVE'
132 net
['sdn_net_id'] = uuid('sdn-net%d' % i
)
134 db_state
+= [{'instance_nets': instance_nets
},
135 {'instance_wim_nets': eg
.instance_wim_nets()},
136 {'wim_port_mappings': port_mappings
},
137 {'instance_actions': instance_action
}]
139 action
= WanLinkCreate(
140 eg
.wim_actions('CREATE', action_id
='ACTION-000')[0])
141 # --> ensure it is in the database for updates --> #
142 action_record
= action
.as_record()
143 action_record
['extra'] = json
.dumps(action_record
['extra'])
144 self
.populate([{'vim_wim_actions': action_record
}])
146 return db_state
, action
148 @disable_foreign_keys
149 def test_process__sdn(self
):
150 # Given we want 1 WAN link between 2 datacenters
151 # and the local network in each datacenter is already created
152 db_state
, action
= self
.prepare_create__sdn()
153 self
.populate(db_state
)
155 instance_action
= self
.persist
.get_by_uuid(
156 'instance_actions', action
.instance_action_id
)
157 number_done
= instance_action
['number_done']
158 number_failed
= instance_action
['number_failed']
160 connector_patch
= patch
.object(
161 self
.connector
, 'create_connectivity_service',
162 lambda *_
, **__
: (uuid('random-id'), None))
164 ovim_patch
= patch
.object(
165 self
.ovim
, 'get_ports', MagicMock(return_value
=[{
166 'switch_dpid': 'AA:AA:AA:AA:AA:AA:AA:AA',
170 # If the connector works fine
171 with connector_patch
, ovim_patch
:
172 # When we try to process a CREATE action that refers to the same
173 # instance_scenario_id and sce_net_id
174 action
.process(self
.connector
, self
.persist
, self
.ovim
)
176 # Then the action should be succeeded
177 db_action
= self
.persist
.query_one('vim_wim_actions', WHERE
={
178 'instance_action_id': action
.instance_action_id
,
179 'task_index': action
.task_index
})
180 self
.assertEqual(db_action
['status'], 'DONE')
182 instance_action
= self
.persist
.get_by_uuid(
183 'instance_actions', action
.instance_action_id
)
184 self
.assertEqual(instance_action
['number_done'], number_done
+ 1)
185 self
.assertEqual(instance_action
['number_failed'], number_failed
)
187 @disable_foreign_keys
188 def test_process__sdn_fail(self
):
189 # Given we want 1 WAN link between 2 datacenters
190 # and the local network in each datacenter is already created
191 db_state
, action
= self
.prepare_create__sdn()
192 self
.populate(db_state
)
194 instance_action
= self
.persist
.get_by_uuid(
195 'instance_actions', action
.instance_action_id
)
196 number_done
= instance_action
['number_done']
197 number_failed
= instance_action
['number_failed']
199 connector_patch
= patch
.object(
200 self
.connector
, 'create_connectivity_service',
201 MagicMock(side_effect
=WimConnectorError('foobar')))
203 ovim_patch
= patch
.object(
204 self
.ovim
, 'get_ports', MagicMock(return_value
=[{
205 'switch_dpid': 'AA:AA:AA:AA:AA:AA:AA:AA',
209 # If the connector throws an error
210 with connector_patch
, ovim_patch
:
211 # When we try to process a CREATE action that refers to the same
212 # instance_scenario_id and sce_net_id
213 action
.process(self
.connector
, self
.persist
, self
.ovim
)
215 # Then the action should be fail
216 db_action
= self
.persist
.query_one('vim_wim_actions', WHERE
={
217 'instance_action_id': action
.instance_action_id
,
218 'task_index': action
.task_index
})
219 self
.assertEqual(db_action
['status'], 'FAILED')
221 instance_action
= self
.persist
.get_by_uuid(
222 'instance_actions', action
.instance_action_id
)
223 self
.assertEqual(instance_action
['number_done'], number_done
)
224 self
.assertEqual(instance_action
['number_failed'], number_failed
+ 1)
227 class TestDelete(TestActionsWithDb
):
228 @disable_foreign_keys
229 def test_process__no_internal_id(self
):
230 # Given no WAN link was created yet,
231 # when we try to process a DELETE action, with no wim_internal_id
232 action
= WanLinkDelete(eg
.wim_actions('DELETE')[0])
233 action
.wim_internal_id
= None
234 # -- ensure it is in the database for updates --> #
235 action_record
= action
.as_record()
236 action_record
['extra'] = json
.dumps(action_record
['extra'])
237 self
.populate([{'vim_wim_actions': action_record
,
238 'instance_wim_nets': eg
.instance_wim_nets()}])
240 action
.process(self
.connector
, self
.persist
, self
.ovim
)
242 # Then the action should succeed
243 assert action
.is_done
245 def prepare_delete(self
):
246 db_state
= [{'nfvo_tenants': eg
.tenant()}] + eg
.wim_set()
248 instance_nets
= eg
.instance_nets(num_datacenters
=2, num_links
=1)
250 eg
.wim_port_mapping(0, 0),
251 eg
.wim_port_mapping(0, 1)
253 instance_action
= eg
.instance_action(action_id
='ACTION-000')
254 for i
, net
in enumerate(instance_nets
):
255 net
['status'] = 'ACTIVE'
256 net
['sdn_net_id'] = uuid('sdn-net%d' % i
)
258 db_state
+= [{'instance_nets': instance_nets
},
259 {'instance_wim_nets': eg
.instance_wim_nets()},
260 {'wim_port_mappings': port_mappings
},
261 {'instance_actions': instance_action
}]
263 action
= WanLinkDelete(
264 eg
.wim_actions('DELETE', action_id
='ACTION-000')[0])
265 # --> ensure it is in the database for updates --> #
266 action_record
= action
.as_record()
267 action_record
['extra'] = json
.dumps(action_record
['extra'])
268 self
.populate([{'vim_wim_actions': action_record
}])
270 return db_state
, action
272 @disable_foreign_keys
273 def test_process(self
):
274 # Given we want to delete 1 WAN link between 2 datacenters
275 db_state
, action
= self
.prepare_delete()
276 self
.populate(db_state
)
278 instance_action
= self
.persist
.get_by_uuid(
279 'instance_actions', action
.instance_action_id
)
280 number_done
= instance_action
['number_done']
281 number_failed
= instance_action
['number_failed']
283 connector_patch
= patch
.object(
284 self
.connector
, 'delete_connectivity_service')
286 # If the connector works fine
287 with connector_patch
:
288 # When we try to process a DELETE action that refers to the same
289 # instance_scenario_id and sce_net_id
290 action
.process(self
.connector
, self
.persist
, self
.ovim
)
292 # Then the action should be succeeded
293 db_action
= self
.persist
.query_one('vim_wim_actions', WHERE
={
294 'instance_action_id': action
.instance_action_id
,
295 'task_index': action
.task_index
})
296 self
.assertEqual(db_action
['status'], 'DONE')
298 instance_action
= self
.persist
.get_by_uuid(
299 'instance_actions', action
.instance_action_id
)
300 self
.assertEqual(instance_action
['number_done'], number_done
+ 1)
301 self
.assertEqual(instance_action
['number_failed'], number_failed
)
303 @disable_foreign_keys
304 def test_process__wan_link_error(self
):
305 # Given we have a delete action that targets a wan link with an error
306 db_state
, action
= self
.prepare_delete()
307 wan_link
= [tables
for tables
in db_state
308 if tables
.get('instance_wim_nets')][0]['instance_wim_nets']
309 from pprint
import pprint
311 wan_link
[0]['status'] = 'ERROR'
312 self
.populate(db_state
)
314 # When we try to process it
315 action
.process(self
.connector
, self
.persist
, self
.ovim
)
317 # Then it should fail
318 assert action
.is_failed
320 def create_action(self
):
321 action
= WanLinkCreate(
322 eg
.wim_actions('CREATE', action_id
='ACTION-000')[0])
323 # --> ensure it is in the database for updates --> #
324 action_record
= action
.as_record()
325 action_record
['extra'] = json
.dumps(action_record
['extra'])
326 self
.populate([{'vim_wim_actions': action_record
}])
330 @disable_foreign_keys
331 def test_create_and_delete(self
):
332 # Given a CREATE action was well succeeded
333 db_state
, delete_action
= self
.prepare_delete()
334 delete_action
.save(self
.persist
, task_index
=1)
335 self
.populate(db_state
)
336 create_action
= self
.create_action()
338 connector_patch
= patch
.multiple(
340 delete_connectivity_service
=MagicMock(),
341 create_connectivity_service
=(
342 lambda *_
, **__
: (uuid('random-id'), None)))
344 ovim_patch
= patch
.object(
345 self
.ovim
, 'get_ports', MagicMock(return_value
=[{
346 'switch_dpid': 'AA:AA:AA:AA:AA:AA:AA:AA',
350 with connector_patch
, ovim_patch
:
351 create_action
.process(self
.connector
, self
.persist
, self
.ovim
)
353 # When we try to process a CREATE action that refers to the same
354 # instance_scenario_id and sce_net_id
355 with connector_patch
:
356 delete_action
.process(self
.connector
, self
.persist
, self
.ovim
)
358 # Then the DELETE action should be successful
359 db_action
= self
.persist
.query_one('vim_wim_actions', WHERE
={
360 'instance_action_id': delete_action
.instance_action_id
,
361 'task_index': delete_action
.task_index
})
362 self
.assertEqual(db_action
['status'], 'DONE')
365 if __name__
== '__main__':