Clean up openstack network after failed instantiate
[osm/SO.git] / rwcal / plugins / vala / rwcal_openstack / rwcal_openstack.py
1
2 #
3 # Copyright 2016 RIFT.IO Inc
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #
17
18 import logging
19 import os
20 import subprocess
21 import tempfile
22 import yaml
23
24 import gi
25 gi.require_version('RwCal', '1.0')
26 gi.require_version('RwcalYang', '1.0')
27
28 import rift.rwcal.openstack as openstack_drv
29
30
31 import rw_status
32 import rift.cal.rwcal_status as rwcal_status
33 import rwlogger
34
35
36 from gi.repository import (
37 GObject,
38 RwCal,
39 RwTypes,
40 RwcalYang)
41
42 PREPARE_VM_CMD = "prepare_vm.py --auth_url {auth_url} --username {username} --password {password} --tenant_name {tenant_name} --region {region} --user_domain {user_domain} --project_domain {project_domain} --mgmt_network {mgmt_network} --server_id {server_id} --port_metadata "
43
44 rwstatus_exception_map = {IndexError: RwTypes.RwStatus.NOTFOUND,
45 KeyError: RwTypes.RwStatus.NOTFOUND,
46 NotImplementedError: RwTypes.RwStatus.NOT_IMPLEMENTED, }
47
48 rwstatus = rw_status.rwstatus_from_exc_map(rwstatus_exception_map)
49 rwcalstatus = rwcal_status.rwcalstatus_from_exc_map(rwstatus_exception_map)
50
51
52 class OpenstackCALOperationFailure(Exception):
53 pass
54
55 class UninitializedPluginError(Exception):
56 pass
57
58
59 class OpenstackServerGroupError(Exception):
60 pass
61
62
63 class ImageUploadError(Exception):
64 pass
65
66
67 class RwcalAccountDriver(object):
68 """
69 Container class per cloud account
70 """
71 def __init__(self, logger, **kwargs):
72 self.log = logger
73 try:
74 self._driver = openstack_drv.OpenstackDriver(logger = self.log, **kwargs)
75 except Exception as e:
76 self.log.error("RwcalOpenstackPlugin: OpenstackDriver init failed. Exception: %s" %(str(e)))
77 raise
78
79 @property
80 def driver(self):
81 return self._driver
82
83 class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
84 """This class implements the CAL VALA methods for openstack."""
85
86 instance_num = 1
87
88 def __init__(self):
89 GObject.Object.__init__(self)
90 self.log = logging.getLogger('rwcal.openstack.%s' % RwcalOpenstackPlugin.instance_num)
91 self.log.setLevel(logging.DEBUG)
92 self._rwlog_handler = None
93 self._account_drivers = dict()
94 RwcalOpenstackPlugin.instance_num += 1
95
96 def _get_account_key(self, account):
97 key = str()
98 for f in account.openstack.fields:
99 try:
100 key+= str(getattr(account.openstack, f))
101 except:
102 pass
103 key += account.name
104 return key
105
106 def _use_driver(self, account):
107 if self._rwlog_handler is None:
108 raise UninitializedPluginError("Must call init() in CAL plugin before use.")
109
110 acct_key = self._get_account_key(account)
111
112 if acct_key not in self._account_drivers:
113 self.log.debug("Creating OpenstackDriver")
114 kwargs = dict(username = account.openstack.key,
115 password = account.openstack.secret,
116 auth_url = account.openstack.auth_url,
117 project = account.openstack.tenant,
118 mgmt_network = account.openstack.mgmt_network,
119 cert_validate = account.openstack.cert_validate,
120 user_domain = account.openstack.user_domain,
121 project_domain = account.openstack.project_domain,
122 region = account.openstack.region)
123 drv = RwcalAccountDriver(self.log, **kwargs)
124 self._account_drivers[account.name] = drv
125 return drv.driver
126 else:
127 return self._account_drivers[acct_key].driver
128
129
130 @rwstatus
131 def do_init(self, rwlog_ctx):
132 self._rwlog_handler = rwlogger.RwLogger(category="rw-cal-log",
133 subcategory="openstack",
134 log_hdl=rwlog_ctx,)
135 self.log.addHandler(self._rwlog_handler)
136 self.log.propagate = False
137
138 @rwstatus(ret_on_failure=[None])
139 def do_validate_cloud_creds(self, account):
140 """
141 Validates the cloud account credentials for the specified account.
142 Performs an access to the resources using Keystone API. If creds
143 are not valid, returns an error code & reason string
144 Arguments:
145 account - a cloud account to validate
146
147 Returns:
148 Validation Code and Details String
149 """
150 status = RwcalYang.CloudConnectionStatus()
151 try:
152 drv = self._use_driver(account)
153 drv.validate_account_creds()
154 except Exception as e:
155 msg = "RwcalOpenstackPlugin: Exception: %s" %(str(e))
156 self.log.error(msg)
157 status.status = "failure"
158 status.details = msg
159
160 else:
161 status.status = "success"
162 status.details = "Connection was successful"
163
164 return status
165
166 @rwstatus(ret_on_failure=[""])
167 def do_get_management_network(self, account):
168 """
169 Returns the management network associated with the specified account.
170 Arguments:
171 account - a cloud account
172
173 Returns:
174 The management network
175 """
176 return account.openstack.mgmt_network
177
178 @rwstatus(ret_on_failure=[""])
179 def do_create_tenant(self, account, name):
180 """Create a new tenant.
181
182 Arguments:
183 account - a cloud account
184 name - name of the tenant
185
186 Returns:
187 The tenant id
188 """
189 raise NotImplementedError
190
191 @rwstatus
192 def do_delete_tenant(self, account, tenant_id):
193 """delete a tenant.
194
195 Arguments:
196 account - a cloud account
197 tenant_id - id of the tenant
198 """
199 raise NotImplementedError
200
201 @rwstatus(ret_on_failure=[[]])
202 def do_get_tenant_list(self, account):
203 """List tenants.
204
205 Arguments:
206 account - a cloud account
207
208 Returns:
209 List of tenants
210 """
211 raise NotImplementedError
212
213 @rwstatus(ret_on_failure=[""])
214 def do_create_role(self, account, name):
215 """Create a new user.
216
217 Arguments:
218 account - a cloud account
219 name - name of the user
220
221 Returns:
222 The user id
223 """
224 raise NotImplementedError
225
226 @rwstatus
227 def do_delete_role(self, account, role_id):
228 """Delete a user.
229
230 Arguments:
231 account - a cloud account
232 role_id - id of the user
233 """
234 raise NotImplementedError
235
236 @rwstatus(ret_on_failure=[[]])
237 def do_get_role_list(self, account):
238 """List roles.
239
240 Arguments:
241 account - a cloud account
242
243 Returns:
244 List of roles
245 """
246 raise NotImplementedError
247
248 @rwstatus(ret_on_failure=[""])
249 def do_create_image(self, account, image):
250 """Create an image
251
252 Arguments:
253 account - a cloud account
254 image - a description of the image to create
255
256 Returns:
257 The image id
258 """
259 drv = self._use_driver(account)
260 fd = drv.utils.image.create_image_handle(image)
261 kwargs = drv.utils.image.make_image_args(image)
262
263 try:
264 # Create Image
265 image_id = drv.glance_image_create(**kwargs)
266 drv.glance_image_upload(image_id, fd)
267 except Exception as e:
268 self.log.exception("Exception %s occured during image create", str(e))
269 raise
270 finally:
271 fd.close()
272
273 # Update image properties, if they are provided
274 try:
275 if image.has_field("properties") and image.properties is not None:
276 for key in image.properties:
277 drv.glance_image_update(image_id, **{key.name: key.property_value})
278 except Exception as e:
279 self.log.exception("Exception %s occured during image update", str(e))
280 raise
281
282 if image.checksum:
283 try:
284 stored_image = drv.glance_image_get(image_id)
285 if stored_image.checksum != image.checksum:
286 drv.glance_image_delete(image_id=image_id)
287 raise ImageUploadError("image checksum did not match (actual: %s, expected: %s). Deleting." %
288 (stored_image.checksum, image.checksum))
289 except Exception as e:
290 self.log.exception("Exception %s occured during image checksum verification", str(e))
291 raise
292
293 return image_id
294
295 @rwstatus
296 def do_delete_image(self, account, image_id):
297 """Delete a vm image.
298
299 Arguments:
300 account - a cloud account
301 image_id - id of the image to delete
302 """
303 drv = self._use_driver(account)
304 try:
305 drv.glance_image_delete(image_id=image_id)
306 except Exception as e:
307 self.log.exception("Exception %s occured during image deletion", str(e))
308 raise
309
310
311 @rwstatus(ret_on_failure=[[]])
312 def do_get_image_list(self, account):
313 """Return a list of the names of all available images.
314
315 Arguments:
316 account - a cloud account
317
318 Returns:
319 The the list of images in VimResources object
320 """
321 response = RwcalYang.VimResources()
322 drv = self._use_driver(account)
323 try:
324 images = drv.glance_image_list()
325 for img in images:
326 response.imageinfo_list.append(drv.utils.image.parse_cloud_image_info(img))
327 except Exception as e:
328 self.log.exception("Exception %s occured during get-image-list", str(e))
329 raise
330 return response
331
332 @rwstatus(ret_on_failure=[None])
333 def do_get_image(self, account, image_id):
334 """Return a image information.
335
336 Arguments:
337 account - a cloud account
338 image_id - an id of the image
339
340 Returns:
341 ImageInfoItem object containing image information.
342 """
343 drv = self._use_driver(account)
344 try:
345 image_info = drv.glance_image_get(image_id)
346 image = drv.utils.image.parse_cloud_image_info(image_info)
347 except Exception as e:
348 self.log.exception("Exception %s occured during get-image", str(e))
349 raise
350 return image
351
352
353 # This is being deprecated. Please do not use for new SW development
354 @rwstatus(ret_on_failure=[""])
355 def do_create_vm(self, account, vminfo):
356 """Create a new virtual machine.
357
358 Arguments:
359 account - a cloud account
360 vminfo - information that defines the type of VM to create
361
362 Returns:
363 The image id
364 """
365 from warnings import warn
366 warn("This function is deprecated")
367 kwargs = {}
368 kwargs['name'] = vminfo.vm_name
369 kwargs['flavor_id'] = vminfo.flavor_id
370 if vminfo.has_field('image_id'):
371 kwargs['image_id'] = vminfo.image_id
372
373 ### If floating_ip is required and we don't have one, better fail before any further allocation
374 floating_ip = False
375 if vminfo.has_field('allocate_public_address') and vminfo.allocate_public_address:
376 floating_ip = True
377
378 if vminfo.has_field('cloud_init') and vminfo.cloud_init.has_field('userdata'):
379 kwargs['userdata'] = vminfo.cloud_init.userdata
380 else:
381 kwargs['userdata'] = ''
382
383 if account.openstack.security_groups:
384 kwargs['security_groups'] = account.openstack.security_groups
385
386 port_list = []
387 for port in vminfo.port_list:
388 port_list.append(port.port_id)
389
390 if port_list:
391 kwargs['port_list'] = port_list
392
393 network_list = []
394 for network in vminfo.network_list:
395 network_list.append(network.network_id)
396
397 if network_list:
398 kwargs['network_list'] = network_list
399
400 metadata = {}
401 for field in vminfo.user_tags.fields:
402 if vminfo.user_tags.has_field(field):
403 metadata[field] = getattr(vminfo.user_tags, field)
404 kwargs['metadata'] = metadata
405
406 if vminfo.has_field('availability_zone'):
407 kwargs['availability_zone'] = vminfo.availability_zone
408 else:
409 kwargs['availability_zone'] = None
410
411 if vminfo.has_field('server_group'):
412 kwargs['scheduler_hints'] = {'group': vminfo.server_group}
413 else:
414 kwargs['scheduler_hints'] = None
415
416 drv = self._use_driver(account)
417 vm_id = drv.nova_server_create(**kwargs)
418 if floating_ip:
419 self.prepare_vdu_on_boot(account, vm_id, floating_ip)
420
421 return vm_id
422
423 @rwstatus
424 def do_start_vm(self, account, vm_id):
425 """Start an existing virtual machine.
426
427 Arguments:
428 account - a cloud account
429 vm_id - an id of the VM
430 """
431 drv = self._use_driver(account)
432 drv.nova_server_start(vm_id)
433
434 @rwstatus
435 def do_stop_vm(self, account, vm_id):
436 """Stop a running virtual machine.
437
438 Arguments:
439 account - a cloud account
440 vm_id - an id of the VM
441 """
442 drv = self._use_driver(account)
443 drv.nova_server_stop(vm_id)
444
445 @rwstatus
446 def do_delete_vm(self, account, vm_id):
447 """Delete a virtual machine.
448
449 Arguments:
450 account - a cloud account
451 vm_id - an id of the VM
452 """
453 drv = self._use_driver(account)
454 drv.nova_server_delete(vm_id)
455
456 @rwstatus
457 def do_reboot_vm(self, account, vm_id):
458 """Reboot a virtual machine.
459
460 Arguments:
461 account - a cloud account
462 vm_id - an id of the VM
463 """
464 drv = self._use_driver(account)
465 drv.nova_server_reboot(vm_id)
466
467 @staticmethod
468 def _fill_vm_info(vm_info, mgmt_network):
469 """Create a GI object from vm info dictionary
470
471 Converts VM information dictionary object returned by openstack
472 driver into Protobuf Gi Object
473
474 Arguments:
475 vm_info - VM information from openstack
476 mgmt_network - Management network
477
478 Returns:
479 Protobuf Gi object for VM
480 """
481 vm = RwcalYang.VMInfoItem()
482 vm.vm_id = vm_info['id']
483 vm.vm_name = vm_info['name']
484 vm.image_id = vm_info['image']['id']
485 vm.flavor_id = vm_info['flavor']['id']
486 vm.state = vm_info['status']
487 for network_name, network_info in vm_info['addresses'].items():
488 if network_info:
489 if network_name == mgmt_network:
490 vm.public_ip = next((item['addr']
491 for item in network_info
492 if item['OS-EXT-IPS:type'] == 'floating'),
493 network_info[0]['addr'])
494 vm.management_ip = network_info[0]['addr']
495 else:
496 for interface in network_info:
497 addr = vm.private_ip_list.add()
498 addr.ip_address = interface['addr']
499
500 for network_name, network_info in vm_info['addresses'].items():
501 if network_info and network_name == mgmt_network and not vm.public_ip:
502 for interface in network_info:
503 if 'OS-EXT-IPS:type' in interface and interface['OS-EXT-IPS:type'] == 'floating':
504 vm.public_ip = interface['addr']
505
506 # Look for any metadata
507 for key, value in vm_info['metadata'].items():
508 if key in vm.user_tags.fields:
509 setattr(vm.user_tags, key, value)
510 if 'OS-EXT-SRV-ATTR:host' in vm_info:
511 if vm_info['OS-EXT-SRV-ATTR:host'] is not None:
512 vm.host_name = vm_info['OS-EXT-SRV-ATTR:host']
513 if 'OS-EXT-AZ:availability_zone' in vm_info:
514 if vm_info['OS-EXT-AZ:availability_zone'] is not None:
515 vm.availability_zone = vm_info['OS-EXT-AZ:availability_zone']
516 return vm
517
518 @rwstatus(ret_on_failure=[[]])
519 def do_get_vm_list(self, account):
520 """Return a list of the VMs as vala boxed objects
521
522 Arguments:
523 account - a cloud account
524
525 Returns:
526 List containing VM information
527 """
528 response = RwcalYang.VimResources()
529 drv = self._use_driver(account)
530 vms = drv.nova_server_list()
531 for vm in vms:
532 response.vminfo_list.append(RwcalOpenstackPlugin._fill_vm_info(vm, account.openstack.mgmt_network))
533 return response
534
535 @rwstatus(ret_on_failure=[None])
536 def do_get_vm(self, account, id):
537 """Return vm information.
538
539 Arguments:
540 account - a cloud account
541 id - an id for the VM
542
543 Returns:
544 VM information
545 """
546 drv = self._use_driver(account)
547 vm = drv.nova_server_get(id)
548 return RwcalOpenstackPlugin._fill_vm_info(vm, account.openstack.mgmt_network)
549
550
551 @rwstatus(ret_on_failure=[""])
552 def do_create_flavor(self, account, flavor):
553 """Create new flavor.
554
555 Arguments:
556 account - a cloud account
557 flavor - flavor of the VM
558
559 Returns:
560 flavor id
561 """
562 drv = self._use_driver(account)
563 try:
564 flavor_id = drv.nova_flavor_create(name = flavor.name,
565 ram = flavor.vm_flavor.memory_mb,
566 vcpus = flavor.vm_flavor.vcpu_count,
567 disk = flavor.vm_flavor.storage_gb,
568 epa_specs = drv.utils.flavor.get_extra_specs(flavor))
569 except Exception as e:
570 self.log.error("Encountered exceptions during Flavor creation. Exception: %s", str(e))
571 raise
572
573 return flavor_id
574
575 @rwstatus
576 def do_delete_flavor(self, account, flavor_id):
577 """Delete flavor.
578
579 Arguments:
580 account - a cloud account
581 flavor_id - id flavor of the VM
582 """
583 drv = self._use_driver(account)
584 try:
585 drv.nova_flavor_delete(flavor_id)
586 except Exception as e:
587 self.log.error("Encountered exceptions during Flavor deletion. Exception: %s", str(e))
588 raise
589
590
591 @rwstatus(ret_on_failure=[[]])
592 def do_get_flavor_list(self, account):
593 """Return flavor information.
594
595 Arguments:
596 account - a cloud account
597
598 Returns:
599 List of flavors
600 """
601 response = RwcalYang.VimResources()
602 drv = self._use_driver(account)
603 try:
604 flavors = drv.nova_flavor_list()
605 for flv in flavors:
606 response.flavorinfo_list.append(drv.utils.flavor.parse_flavor_info(flv))
607 except Exception as e:
608 self.log.error("Encountered exceptions during get-flavor-list. Exception: %s", str(e))
609 raise
610
611 return response
612
613 @rwstatus(ret_on_failure=[None])
614 def do_get_flavor(self, account, id):
615 """Return flavor information.
616
617 Arguments:
618 account - a cloud account
619 id - an id for the flavor
620
621 Returns:
622 Flavor info item
623 """
624 drv = self._use_driver(account)
625 try:
626 flavor = drv.nova_flavor_get(id)
627 response = drv.utils.flavor.parse_flavor_info(flavor)
628 except Exception as e:
629 self.log.error("Encountered exceptions during get-flavor. Exception: %s", str(e))
630 raise
631
632 return response
633
634
635 def _fill_network_info(self, network_info, account):
636 """Create a GI object from network info dictionary
637
638 Converts Network information dictionary object returned by openstack
639 driver into Protobuf Gi Object
640
641 Arguments:
642 network_info - Network information from openstack
643 account - a cloud account
644
645 Returns:
646 Network info item
647 """
648 network = RwcalYang.NetworkInfoItem()
649 network.network_name = network_info['name']
650 network.network_id = network_info['id']
651 if ('provider:network_type' in network_info) and (network_info['provider:network_type'] is not None):
652 network.provider_network.overlay_type = network_info['provider:network_type'].upper()
653 if ('provider:segmentation_id' in network_info) and (network_info['provider:segmentation_id']):
654 network.provider_network.segmentation_id = network_info['provider:segmentation_id']
655 if ('provider:physical_network' in network_info) and (network_info['provider:physical_network']):
656 network.provider_network.physical_network = network_info['provider:physical_network'].upper()
657
658 if 'subnets' in network_info and network_info['subnets']:
659 subnet_id = network_info['subnets'][0]
660 drv = self._use_driver(account)
661 subnet = drv.neutron_subnet_get(subnet_id)
662 network.subnet = subnet['cidr']
663 return network
664
665 @rwstatus(ret_on_failure=[[]])
666 def do_get_network_list(self, account):
667 """Return a list of networks
668
669 Arguments:
670 account - a cloud account
671
672 Returns:
673 List of networks
674 """
675 response = RwcalYang.VimResources()
676 drv = self._use_driver(account)
677 networks = drv.neutron_network_list()
678 for network in networks:
679 response.networkinfo_list.append(self._fill_network_info(network, account))
680 return response
681
682 @rwstatus(ret_on_failure=[None])
683 def do_get_network(self, account, id):
684 """Return a network
685
686 Arguments:
687 account - a cloud account
688 id - an id for the network
689
690 Returns:
691 Network info item
692 """
693 drv = self._use_driver(account)
694 network = drv.neutron_network_get(id)
695 return self._fill_network_info(network, account)
696
697 @rwstatus(ret_on_failure=[""])
698 def do_create_network(self, account, network):
699 """Create a new network
700
701 Arguments:
702 account - a cloud account
703 network - Network object
704
705 Returns:
706 Network id
707 """
708 from warnings import warn
709 warn("This function is deprecated")
710
711 kwargs = {}
712 kwargs['name'] = network.network_name
713 kwargs['admin_state_up'] = True
714 kwargs['external_router'] = False
715 kwargs['shared'] = False
716
717 if network.has_field('provider_network'):
718 if network.provider_network.has_field('physical_network'):
719 kwargs['physical_network'] = network.provider_network.physical_network
720 if network.provider_network.has_field('overlay_type'):
721 kwargs['network_type'] = network.provider_network.overlay_type.lower()
722 if network.provider_network.has_field('segmentation_id'):
723 kwargs['segmentation_id'] = network.provider_network.segmentation_id
724
725 drv = self._use_driver(account)
726 network_id = drv.neutron_network_create(**kwargs)
727 drv.neutron_subnet_create(network_id = network_id,
728 cidr = network.subnet)
729 return network_id
730
731 @rwstatus
732 def do_delete_network(self, account, network_id):
733 """Delete a network
734
735 Arguments:
736 account - a cloud account
737 network_id - an id for the network
738 """
739 drv = self._use_driver(account)
740 drv.neutron_network_delete(network_id)
741
742 @staticmethod
743 def _fill_port_info(port_info):
744 """Create a GI object from port info dictionary
745
746 Converts Port information dictionary object returned by openstack
747 driver into Protobuf Gi Object
748
749 Arguments:
750 port_info - Port information from openstack
751
752 Returns:
753 Port info item
754 """
755 port = RwcalYang.PortInfoItem()
756
757 port.port_name = port_info['name']
758 port.port_id = port_info['id']
759 port.network_id = port_info['network_id']
760 port.port_state = port_info['status']
761 if 'device_id' in port_info:
762 port.vm_id = port_info['device_id']
763 if 'fixed_ips' in port_info:
764 port.ip_address = port_info['fixed_ips'][0]['ip_address']
765 return port
766
767 @rwstatus(ret_on_failure=[None])
768 def do_get_port(self, account, port_id):
769 """Return a port
770
771 Arguments:
772 account - a cloud account
773 port_id - an id for the port
774
775 Returns:
776 Port info item
777 """
778 drv = self._use_driver(account)
779 port = drv.neutron_port_get(port_id)
780 return RwcalOpenstackPlugin._fill_port_info(port)
781
782 @rwstatus(ret_on_failure=[[]])
783 def do_get_port_list(self, account):
784 """Return a list of ports
785
786 Arguments:
787 account - a cloud account
788
789 Returns:
790 Port info list
791 """
792 response = RwcalYang.VimResources()
793 drv = self._use_driver(account)
794 ports = drv.neutron_port_list(*{})
795 for port in ports:
796 response.portinfo_list.append(RwcalOpenstackPlugin._fill_port_info(port))
797 return response
798
799 @rwstatus(ret_on_failure=[""])
800 def do_create_port(self, account, port):
801 """Create a new port
802
803 Arguments:
804 account - a cloud account
805 port - port object
806
807 Returns:
808 Port id
809 """
810 from warnings import warn
811 warn("This function is deprecated")
812
813 kwargs = {}
814 kwargs['name'] = port.port_name
815 kwargs['network_id'] = port.network_id
816 kwargs['admin_state_up'] = True
817 if port.has_field('vm_id'):
818 kwargs['vm_id'] = port.vm_id
819 if port.has_field('port_type'):
820 kwargs['port_type'] = port.port_type
821 else:
822 kwargs['port_type'] = "normal"
823
824 drv = self._use_driver(account)
825 return drv.neutron_port_create(**kwargs)
826
827 @rwstatus
828 def do_delete_port(self, account, port_id):
829 """Delete a port
830
831 Arguments:
832 account - a cloud account
833 port_id - an id for port
834 """
835 drv = self._use_driver(account)
836 drv.neutron_port_delete(port_id)
837
838 @rwstatus(ret_on_failure=[""])
839 def do_add_host(self, account, host):
840 """Add a new host
841
842 Arguments:
843 account - a cloud account
844 host - a host object
845
846 Returns:
847 An id for the host
848 """
849 raise NotImplementedError
850
851 @rwstatus
852 def do_remove_host(self, account, host_id):
853 """Remove a host
854
855 Arguments:
856 account - a cloud account
857 host_id - an id for the host
858 """
859 raise NotImplementedError
860
861 @rwstatus(ret_on_failure=[None])
862 def do_get_host(self, account, host_id):
863 """Return a host
864
865 Arguments:
866 account - a cloud account
867 host_id - an id for host
868
869 Returns:
870 Host info item
871 """
872 raise NotImplementedError
873
874 @rwstatus(ret_on_failure=[[]])
875 def do_get_host_list(self, account):
876 """Return a list of hosts
877
878 Arguments:
879 account - a cloud account
880
881 Returns:
882 List of hosts
883 """
884 raise NotImplementedError
885
886
887 @rwcalstatus(ret_on_failure=[""])
888 def do_create_virtual_link(self, account, link_params):
889 """Create a new virtual link
890
891 Arguments:
892 account - a cloud account
893 link_params - information that defines the type of VDU to create
894
895 Returns:
896 A kwargs dictionary for glance operation
897 """
898
899 drv = self._use_driver(account)
900 try:
901 kwargs = drv.utils.network.make_virtual_link_args(link_params)
902 network_id = drv.neutron_network_create(**kwargs)
903 kwargs = drv.utils.network.make_subnet_args(link_params, network_id)
904 drv.neutron_subnet_create(**kwargs)
905 except Exception as e:
906 self.log.error("Encountered exceptions during network creation. Exception: %s", str(e))
907 # This is to delete the network if neutron_subnet_create fails after creation of network
908 # Note:- Any subnet created will be implicitly deleted.
909 try:
910 drv.neutron_network_delete(network_id)
911 except Exception as delete_exception:
912 self.log.debug("Exception while deleting the network after failure of neutron_subnet_create or make_subnet_args: %s", str(delete_exception))
913 raise e
914 return network_id
915
916
917 @rwstatus
918 def do_delete_virtual_link(self, account, link_id):
919 """Delete a virtual link
920
921 Arguments:
922 account - a cloud account
923 link_id - id for the virtual-link to be deleted
924
925 Returns:
926 None
927 """
928 drv = self._use_driver(account)
929 try:
930 port_list = drv.neutron_port_list(**{'network_id': link_id})
931 for port in port_list:
932 if ((port['device_owner'] == 'compute:None') or (port['device_owner'] == '')):
933 self.do_delete_port(account, port['id'], no_rwstatus=True)
934 self.do_delete_network(account, link_id, no_rwstatus=True)
935 except Exception as e:
936 self.log.exception("Exception %s occured during virtual-link deletion", str(e))
937 raise
938
939 @rwstatus(ret_on_failure=[None])
940 def do_get_virtual_link(self, account, link_id):
941 """Get information about virtual link.
942
943 Arguments:
944 account - a cloud account
945 link_id - id for the virtual-link
946
947 Returns:
948 Object of type RwcalYang.VirtualLinkInfoParams
949 """
950 drv = self._use_driver(account)
951 try:
952 network = drv.neutron_network_get(link_id)
953 if network:
954 port_list = drv.neutron_port_list(**{'network_id': network['id']})
955 if 'subnets' in network and network['subnets']:
956 subnet = drv.neutron_subnet_get(network['subnets'][0])
957 else:
958 subnet = None
959 virtual_link = drv.utils.network.parse_cloud_virtual_link_info(network, port_list, subnet)
960 except Exception as e:
961 self.log.exception("Exception %s occured during virtual-link-get", str(e))
962 raise
963 return virtual_link
964
965 @rwstatus(ret_on_failure=[None])
966 def do_get_virtual_link_list(self, account):
967 """Get information about all the virtual links
968
969 Arguments:
970 account - a cloud account
971
972 Returns:
973 A list of objects of type RwcalYang.VirtualLinkInfoParams
974 """
975 vnf_resources = RwcalYang.VNFResources()
976 drv = self._use_driver(account)
977 try:
978 networks = drv.neutron_network_list()
979 for network in networks:
980 port_list = drv.neutron_port_list(**{'network_id': network['id']})
981 if 'subnets' in network and network['subnets']:
982 subnet = drv.neutron_subnet_get(network['subnets'][0])
983 else:
984 subnet = None
985 virtual_link = drv.utils.network.parse_cloud_virtual_link_info(network, port_list, subnet)
986 vnf_resources.virtual_link_info_list.append(virtual_link)
987 except Exception as e:
988 self.log.exception("Exception %s occured during virtual-link-list-get", str(e))
989 raise
990 return vnf_resources
991
992
993
994 @rwcalstatus(ret_on_failure=[""])
995 def do_create_vdu(self, account, vdu_init):
996 """Create a new virtual deployment unit
997
998 Arguments:
999 account - a cloud account
1000 vdu_init - information about VDU to create (RwcalYang.VDUInitParams)
1001
1002 Returns:
1003 The vdu_id
1004 """
1005 drv = self._use_driver(account)
1006 try:
1007 kwargs = drv.utils.compute.make_vdu_create_args(vdu_init, account)
1008 vm_id = drv.nova_server_create(**kwargs)
1009 self.prepare_vdu_on_boot(account, vm_id, vdu_init)
1010 except Exception as e:
1011 self.log.exception("Exception %s occured during create-vdu", str(e))
1012 raise
1013 return vm_id
1014
1015
1016 def prepare_vdu_on_boot(self, account, server_id, vdu_params):
1017 cmd = PREPARE_VM_CMD.format(auth_url = account.openstack.auth_url,
1018 username = account.openstack.key,
1019 password = account.openstack.secret,
1020 tenant_name = account.openstack.tenant,
1021 region = account.openstack.region,
1022 user_domain = account.openstack.user_domain,
1023 project_domain = account.openstack.project_domain,
1024 mgmt_network = account.openstack.mgmt_network,
1025 server_id = server_id)
1026 vol_list = list()
1027
1028 if vdu_params.has_field('allocate_public_address') and vdu_params.allocate_public_address:
1029 cmd += " --floating_ip"
1030 if account.openstack.has_field('floating_ip_pool'):
1031 cmd += (" --pool_name " + account.openstack.floating_ip_pool)
1032
1033 if vdu_params.has_field('volumes'):
1034 for volume in vdu_params.volumes:
1035 if volume.has_field('custom_meta_data'):
1036 vol_list.append(volume.as_dict())
1037
1038 if vol_list:
1039 with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp_file:
1040 yaml.dump(vol_list, tmp_file)
1041 cmd += (" --vol_metadata {}").format(tmp_file.name)
1042
1043 exec_path = 'python3 ' + os.path.dirname(openstack_drv.__file__)
1044 exec_cmd = exec_path + '/' + cmd
1045 self.log.info("Running command: %s" %(exec_cmd))
1046 subprocess.call(exec_cmd, shell=True)
1047
1048 @rwstatus
1049 def do_modify_vdu(self, account, vdu_modify):
1050 """Modify Properties of existing virtual deployment unit
1051
1052 Arguments:
1053 account - a cloud account
1054 vdu_modify - Information about VDU Modification (RwcalYang.VDUModifyParams)
1055 """
1056 drv = self._use_driver(account)
1057 ### First create required number of ports aka connection points
1058 port_list = []
1059 network_list = []
1060 for c_point in vdu_modify.connection_points_add:
1061 if c_point.virtual_link_id in network_list:
1062 assert False, "Only one port per network supported. Refer: http://specs.openstack.org/openstack/nova-specs/specs/juno/implemented/nfv-multiple-if-1-net.html"
1063 else:
1064 network_list.append(c_point.virtual_link_id)
1065 port_id = self._create_connection_point(account, c_point)
1066 port_list.append(port_id)
1067
1068 drv = self._use_driver(account)
1069 ### Now add the ports to VM
1070 for port_id in port_list:
1071 drv.nova_server_add_port(vdu_modify.vdu_id, port_id)
1072
1073 ### Delete the requested connection_points
1074 for c_point in vdu_modify.connection_points_remove:
1075 self.do_delete_port(account, c_point.connection_point_id, no_rwstatus=True)
1076
1077 if vdu_modify.has_field('image_id'):
1078 drv.nova_server_rebuild(vdu_modify.vdu_id, vdu_modify.image_id)
1079
1080
1081 @rwstatus
1082 def do_delete_vdu(self, account, vdu_id):
1083 """Delete a virtual deployment unit
1084
1085 Arguments:
1086 account - a cloud account
1087 vdu_id - id for the vdu to be deleted
1088
1089 Returns:
1090 None
1091 """
1092 drv = self._use_driver(account)
1093 try:
1094 drv.utils.compute.perform_vdu_network_cleanup(vdu_id)
1095 drv.nova_server_delete(vdu_id)
1096 except Exception as e:
1097 self.log.exception("Exception %s occured during delete-vdu", str(e))
1098 raise
1099
1100
1101 @rwstatus(ret_on_failure=[None])
1102 def do_get_vdu(self, account, vdu_id):
1103 """Get information about a virtual deployment unit.
1104
1105 Arguments:
1106 account - a cloud account
1107 vdu_id - id for the vdu
1108
1109 Returns:
1110 Object of type RwcalYang.VDUInfoParams
1111 """
1112 drv = self._use_driver(account)
1113 try:
1114 vm_info = drv.nova_server_get(vdu_id)
1115 vdu_info = drv.utils.compute.parse_cloud_vdu_info(vm_info)
1116 except Exception as e:
1117 self.log.exception("Exception %s occured during get-vdu", str(e))
1118 raise
1119
1120 return vdu_info
1121
1122
1123 @rwstatus(ret_on_failure=[None])
1124 def do_get_vdu_list(self, account):
1125 """Get information about all the virtual deployment units
1126
1127 Arguments:
1128 account - a cloud account
1129
1130 Returns:
1131 A list of objects of type RwcalYang.VDUInfoParams
1132 """
1133 vnf_resources = RwcalYang.VNFResources()
1134 drv = self._use_driver(account)
1135 try:
1136 vms = drv.nova_server_list()
1137 for vm in vms:
1138 vdu = drv.utils.compute.parse_cloud_vdu_info(vm)
1139 vnf_resources.vdu_info_list.append(vdu)
1140 except Exception as e:
1141 self.log.exception("Exception %s occured during get-vdu-list", str(e))
1142 raise
1143 return vnf_resources
1144
1145