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 unittest
.mock
import MagicMock
, patch
44 from . import fixtures
as eg
45 from ...tests
.db_helpers
import (
46 TestCaseWithDatabasePerTest
,
50 from ..persistence
import WimPersistence
, preprocess_record
51 from ..wan_link_actions
import WanLinkCreate
, WanLinkDelete
52 from ..sdnconn
import SdnConnectorError
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__rules(self
):
122 db_state
= eg
.consistent_set(num_wims
=1, num_tenants
=1,
124 external_ports_config
=True)
126 instance_nets
= eg
.instance_nets(num_datacenters
=2, num_links
=1,
128 for i
, net
in enumerate(instance_nets
):
130 net
['vim_info']['provider:physical_network'] = 'provider'
131 net
['vim_info']['encapsulation_type'] = 'vlan'
132 net
['vim_info']['encapsulation_id'] = i
133 net
['sdn_net_id'] = uuid('sdn-net%d' % i
)
135 instance_action
= eg
.instance_action(action_id
='ACTION-000')
138 {'instance_wim_nets': eg
.instance_wim_nets()},
139 {'instance_nets': [preprocess_record(r
) for r
in instance_nets
]},
140 {'instance_actions': instance_action
}]
142 action
= WanLinkCreate(
143 eg
.wim_actions('CREATE', action_id
='ACTION-000')[0])
144 # --> ensure it is in the database for updates --> #
145 action_record
= action
.as_record()
146 action_record
['extra'] = json
.dumps(action_record
['extra'])
147 db_state
+= [{'vim_wim_actions': action_record
}]
149 return db_state
, action
151 @disable_foreign_keys
152 def test_process__rules(self
):
153 # Given we want 1 WAN link between 2 datacenters
154 # and the local network in each datacenter is already created
155 db_state
, action
= self
.prepare_create__rules()
156 self
.populate(db_state
)
158 instance_action
= self
.persist
.get_by_uuid(
159 'instance_actions', action
.instance_action_id
)
160 number_done
= instance_action
['number_done']
161 number_failed
= instance_action
['number_failed']
163 # If the connector works fine
164 with patch
.object(self
.connector
, 'create_connectivity_service',
165 lambda *_
, **__
: (uuid('random-id'), None)):
166 # When we try to process a CREATE action that refers to the same
167 # instance_scenario_id and sce_net_id
168 action
.process(self
.connector
, self
.persist
, self
.ovim
)
170 # Then the action should be succeeded
171 db_action
= self
.persist
.query_one('vim_wim_actions', WHERE
={
172 'instance_action_id': action
.instance_action_id
,
173 'task_index': action
.task_index
})
174 self
.assertEqual(db_action
['status'], 'DONE')
176 instance_action
= self
.persist
.get_by_uuid(
177 'instance_actions', action
.instance_action_id
)
178 self
.assertEqual(instance_action
['number_done'], number_done
+ 1)
179 self
.assertEqual(instance_action
['number_failed'], number_failed
)
181 @disable_foreign_keys
182 def test_process__rules_fail(self
):
183 # Given we want 1 WAN link between 2 datacenters
184 # and the local network in each datacenter is already created
185 db_state
, action
= self
.prepare_create__rules()
186 self
.populate(db_state
)
188 instance_action
= self
.persist
.get_by_uuid(
189 'instance_actions', action
.instance_action_id
)
190 number_done
= instance_action
['number_done']
191 number_failed
= instance_action
['number_failed']
193 # If the connector raises an error
194 with patch
.object(self
.connector
, 'create_connectivity_service',
195 MagicMock(side_effect
=SdnConnectorError('foobar'))):
196 # When we try to process a CREATE action that refers to the same
197 # instance_scenario_id and sce_net_id
198 action
.process(self
.connector
, self
.persist
, self
.ovim
)
200 # Then the action should be fail
201 db_action
= self
.persist
.query_one('vim_wim_actions', WHERE
={
202 'instance_action_id': action
.instance_action_id
,
203 'task_index': action
.task_index
})
204 self
.assertEqual(db_action
['status'], 'FAILED')
206 instance_action
= self
.persist
.get_by_uuid(
207 'instance_actions', action
.instance_action_id
)
208 self
.assertEqual(instance_action
['number_done'], number_done
)
209 self
.assertEqual(instance_action
['number_failed'], number_failed
+ 1)
211 def prepare_create__sdn(self
):
212 db_state
= eg
.consistent_set(num_wims
=1, num_tenants
=1,
214 external_ports_config
=False)
216 # Make sure all port_mappings are predictable
217 switch
= 'AA:AA:AA:AA:AA:AA:AA:AA'
219 port_mappings
= next(r
['wim_port_mappings']
220 for r
in db_state
if 'wim_port_mappings' in r
)
221 for mapping
in port_mappings
:
222 mapping
['device_id'] = switch
223 mapping
['device_interface_id'] = port
225 instance_action
= eg
.instance_action(action_id
='ACTION-000')
226 instance_nets
= eg
.instance_nets(num_datacenters
=2, num_links
=1,
228 for i
, net
in enumerate(instance_nets
):
229 net
['sdn_net_id'] = uuid('sdn-net%d' % i
)
231 db_state
+= [{'instance_nets': instance_nets
},
232 {'instance_wim_nets': eg
.instance_wim_nets()},
233 {'instance_actions': instance_action
}]
235 action
= WanLinkCreate(
236 eg
.wim_actions('CREATE', action_id
='ACTION-000')[0])
237 # --> ensure it is in the database for updates --> #
238 action_record
= action
.as_record()
239 action_record
['extra'] = json
.dumps(action_record
['extra'])
240 db_state
+= [{'vim_wim_actions': action_record
}]
242 ovim_patch
= patch
.object(
243 self
.ovim
, 'get_ports', MagicMock(return_value
=[{
244 'switch_dpid': switch
,
248 return db_state
, action
, ovim_patch
250 @disable_foreign_keys
251 def test_process__sdn(self
):
252 # Given we want 1 WAN link between 2 datacenters
253 # and the local network in each datacenter is already created
254 db_state
, action
, ovim_patch
= self
.prepare_create__sdn()
255 self
.populate(db_state
)
257 instance_action
= self
.persist
.get_by_uuid(
258 'instance_actions', action
.instance_action_id
)
259 number_done
= instance_action
['number_done']
260 number_failed
= instance_action
['number_failed']
262 connector_patch
= patch
.object(
263 self
.connector
, 'create_connectivity_service',
264 lambda *_
, **__
: (uuid('random-id'), None))
266 # If the connector works fine
267 with connector_patch
, ovim_patch
:
268 # When we try to process a CREATE action that refers to the same
269 # instance_scenario_id and sce_net_id
270 action
.process(self
.connector
, self
.persist
, self
.ovim
)
272 # Then the action should be succeeded
273 db_action
= self
.persist
.query_one('vim_wim_actions', WHERE
={
274 'instance_action_id': action
.instance_action_id
,
275 'task_index': action
.task_index
})
276 self
.assertEqual(db_action
['status'], 'DONE')
278 instance_action
= self
.persist
.get_by_uuid(
279 'instance_actions', action
.instance_action_id
)
280 self
.assertEqual(instance_action
['number_done'], number_done
+ 1)
281 self
.assertEqual(instance_action
['number_failed'], number_failed
)
283 @disable_foreign_keys
284 def test_process__sdn_fail(self
):
285 # Given we want 1 WAN link between 2 datacenters
286 # and the local network in each datacenter is already created
287 db_state
, action
, ovim_patch
= self
.prepare_create__sdn()
288 self
.populate(db_state
)
290 instance_action
= self
.persist
.get_by_uuid(
291 'instance_actions', action
.instance_action_id
)
292 number_done
= instance_action
['number_done']
293 number_failed
= instance_action
['number_failed']
295 connector_patch
= patch
.object(
296 self
.connector
, 'create_connectivity_service',
297 MagicMock(side_effect
=SdnConnectorError('foobar')))
299 # If the connector throws an error
300 with connector_patch
, ovim_patch
:
301 # When we try to process a CREATE action that refers to the same
302 # instance_scenario_id and sce_net_id
303 action
.process(self
.connector
, self
.persist
, self
.ovim
)
305 # Then the action should be fail
306 db_action
= self
.persist
.query_one('vim_wim_actions', WHERE
={
307 'instance_action_id': action
.instance_action_id
,
308 'task_index': action
.task_index
})
309 self
.assertEqual(db_action
['status'], 'FAILED')
311 instance_action
= self
.persist
.get_by_uuid(
312 'instance_actions', action
.instance_action_id
)
313 self
.assertEqual(instance_action
['number_done'], number_done
)
314 self
.assertEqual(instance_action
['number_failed'], number_failed
+ 1)
317 class TestDelete(TestActionsWithDb
):
318 @disable_foreign_keys
319 def test_process__no_internal_id(self
):
320 # Given no WAN link was created yet,
321 # when we try to process a DELETE action, with no wim_internal_id
322 action
= WanLinkDelete(eg
.wim_actions('DELETE')[0])
323 action
.wim_internal_id
= None
324 # -- ensure it is in the database for updates --> #
325 action_record
= action
.as_record()
326 action_record
['extra'] = json
.dumps(action_record
['extra'])
327 self
.populate([{'vim_wim_actions': action_record
,
328 'instance_wim_nets': eg
.instance_wim_nets()}])
330 action
.process(self
.connector
, self
.persist
, self
.ovim
)
332 # Then the action should succeed
333 assert action
.is_done
335 def prepare_delete(self
):
336 db_state
= eg
.consistent_set(num_wims
=1, num_tenants
=1,
338 external_ports_config
=True)
340 instance_nets
= eg
.instance_nets(num_datacenters
=2, num_links
=1,
342 for i
, net
in enumerate(instance_nets
):
344 net
['vim_info']['provider:physical_network'] = 'provider'
345 net
['vim_info']['encapsulation_type'] = 'vlan'
346 net
['vim_info']['encapsulation_id'] = i
347 net
['sdn_net_id'] = uuid('sdn-net%d' % i
)
349 instance_action
= eg
.instance_action(action_id
='ACTION-000')
352 {'instance_wim_nets': eg
.instance_wim_nets()},
353 {'instance_nets': [preprocess_record(r
) for r
in instance_nets
]},
354 {'instance_actions': instance_action
}]
356 action
= WanLinkDelete(
357 eg
.wim_actions('DELETE', action_id
='ACTION-000')[0])
358 # --> ensure it is in the database for updates --> #
359 action_record
= action
.as_record()
360 action_record
['extra'] = json
.dumps(action_record
['extra'])
361 db_state
+= [{'vim_wim_actions': action_record
}]
363 return db_state
, action
365 @disable_foreign_keys
366 def test_process(self
):
367 # Given we want to delete 1 WAN link between 2 datacenters
368 db_state
, action
= self
.prepare_delete()
369 self
.populate(db_state
)
371 instance_action
= self
.persist
.get_by_uuid(
372 'instance_actions', action
.instance_action_id
)
373 number_done
= instance_action
['number_done']
374 number_failed
= instance_action
['number_failed']
376 connector_patch
= patch
.object(
377 self
.connector
, 'delete_connectivity_service')
379 # If the connector works fine
380 with connector_patch
:
381 # When we try to process a DELETE action that refers to the same
382 # instance_scenario_id and sce_net_id
383 action
.process(self
.connector
, self
.persist
, self
.ovim
)
385 # Then the action should be succeeded
386 db_action
= self
.persist
.query_one('vim_wim_actions', WHERE
={
387 'instance_action_id': action
.instance_action_id
,
388 'task_index': action
.task_index
})
389 self
.assertEqual(db_action
['status'], 'DONE')
391 instance_action
= self
.persist
.get_by_uuid(
392 'instance_actions', action
.instance_action_id
)
393 self
.assertEqual(instance_action
['number_done'], number_done
+ 1)
394 self
.assertEqual(instance_action
['number_failed'], number_failed
)
396 @disable_foreign_keys
397 def test_process__wan_link_error(self
):
398 # Given we have a delete action that targets a wan link with an error
399 db_state
, action
= self
.prepare_delete()
400 wan_link
= [tables
for tables
in db_state
401 if tables
.get('instance_wim_nets')][0]['instance_wim_nets']
402 from pprint
import pprint
404 wan_link
[0]['status'] = 'ERROR'
405 self
.populate(db_state
)
407 # When we try to process it
408 action
.process(self
.connector
, self
.persist
, self
.ovim
)
410 # Then it should fail
411 assert action
.is_failed
413 def create_action(self
):
414 action
= WanLinkCreate(
415 eg
.wim_actions('CREATE', action_id
='ACTION-000')[0])
416 # --> ensure it is in the database for updates --> #
417 action_record
= action
.as_record()
418 action_record
['extra'] = json
.dumps(action_record
['extra'])
419 self
.populate([{'vim_wim_actions': action_record
}])
423 @disable_foreign_keys
424 def test_create_and_delete(self
):
425 # Given a CREATE action was well succeeded
426 db_state
, delete_action
= self
.prepare_delete()
427 self
.populate(db_state
)
429 delete_action
.save(self
.persist
, task_index
=1)
430 create_action
= self
.create_action()
432 connector_patch
= patch
.multiple(
434 delete_connectivity_service
=MagicMock(),
435 create_connectivity_service
=(
436 lambda *_
, **__
: (uuid('random-id'), None)))
438 with connector_patch
: # , ovim_patch:
439 create_action
.process(self
.connector
, self
.persist
, self
.ovim
)
441 # When we try to process a CREATE action that refers to the same
442 # instance_scenario_id and sce_net_id
443 with connector_patch
:
444 delete_action
.process(self
.connector
, self
.persist
, self
.ovim
)
446 # Then the DELETE action should be successful
447 db_action
= self
.persist
.query_one('vim_wim_actions', WHERE
={
448 'instance_action_id': delete_action
.instance_action_id
,
449 'task_index': delete_action
.task_index
})
450 self
.assertEqual(db_action
['status'], 'DONE')
453 if __name__
== '__main__':