68702bbb8f08635716eaed2cf3e34df727cdd940
[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 raise
908
909 return network_id
910
911
912 @rwstatus
913 def do_delete_virtual_link(self, account, link_id):
914 """Delete a virtual link
915
916 Arguments:
917 account - a cloud account
918 link_id - id for the virtual-link to be deleted
919
920 Returns:
921 None
922 """
923 drv = self._use_driver(account)
924 try:
925 port_list = drv.neutron_port_list(**{'network_id': link_id})
926 for port in port_list:
927 if ((port['device_owner'] == 'compute:None') or (port['device_owner'] == '')):
928 self.do_delete_port(account, port['id'], no_rwstatus=True)
929 self.do_delete_network(account, link_id, no_rwstatus=True)
930 except Exception as e:
931 self.log.exception("Exception %s occured during virtual-link deletion", str(e))
932 raise
933
934 @rwstatus(ret_on_failure=[None])
935 def do_get_virtual_link(self, account, link_id):
936 """Get information about virtual link.
937
938 Arguments:
939 account - a cloud account
940 link_id - id for the virtual-link
941
942 Returns:
943 Object of type RwcalYang.VirtualLinkInfoParams
944 """
945 drv = self._use_driver(account)
946 try:
947 network = drv.neutron_network_get(link_id)
948 if network:
949 port_list = drv.neutron_port_list(**{'network_id': network['id']})
950 if 'subnets' in network and network['subnets']:
951 subnet = drv.neutron_subnet_get(network['subnets'][0])
952 else:
953 subnet = None
954 virtual_link = drv.utils.network.parse_cloud_virtual_link_info(network, port_list, subnet)
955 except Exception as e:
956 self.log.exception("Exception %s occured during virtual-link-get", str(e))
957 raise
958 return virtual_link
959
960 @rwstatus(ret_on_failure=[None])
961 def do_get_virtual_link_list(self, account):
962 """Get information about all the virtual links
963
964 Arguments:
965 account - a cloud account
966
967 Returns:
968 A list of objects of type RwcalYang.VirtualLinkInfoParams
969 """
970 vnf_resources = RwcalYang.VNFResources()
971 drv = self._use_driver(account)
972 try:
973 networks = drv.neutron_network_list()
974 for network in networks:
975 port_list = drv.neutron_port_list(**{'network_id': network['id']})
976 if 'subnets' in network and network['subnets']:
977 subnet = drv.neutron_subnet_get(network['subnets'][0])
978 else:
979 subnet = None
980 virtual_link = drv.utils.network.parse_cloud_virtual_link_info(network, port_list, subnet)
981 vnf_resources.virtual_link_info_list.append(virtual_link)
982 except Exception as e:
983 self.log.exception("Exception %s occured during virtual-link-list-get", str(e))
984 raise
985 return vnf_resources
986
987
988
989 @rwcalstatus(ret_on_failure=[""])
990 def do_create_vdu(self, account, vdu_init):
991 """Create a new virtual deployment unit
992
993 Arguments:
994 account - a cloud account
995 vdu_init - information about VDU to create (RwcalYang.VDUInitParams)
996
997 Returns:
998 The vdu_id
999 """
1000 drv = self._use_driver(account)
1001 try:
1002 kwargs = drv.utils.compute.make_vdu_create_args(vdu_init, account)
1003 vm_id = drv.nova_server_create(**kwargs)
1004 self.prepare_vdu_on_boot(account, vm_id, vdu_init)
1005 except Exception as e:
1006 self.log.exception("Exception %s occured during create-vdu", str(e))
1007 raise
1008 return vm_id
1009
1010
1011 def prepare_vdu_on_boot(self, account, server_id, vdu_params):
1012 cmd = PREPARE_VM_CMD.format(auth_url = account.openstack.auth_url,
1013 username = account.openstack.key,
1014 password = account.openstack.secret,
1015 tenant_name = account.openstack.tenant,
1016 region = account.openstack.region,
1017 user_domain = account.openstack.user_domain,
1018 project_domain = account.openstack.project_domain,
1019 mgmt_network = account.openstack.mgmt_network,
1020 server_id = server_id)
1021 vol_list = list()
1022
1023 if vdu_params.has_field('allocate_public_address') and vdu_params.allocate_public_address:
1024 cmd += " --floating_ip"
1025 if account.openstack.has_field('floating_ip_pool'):
1026 cmd += (" --pool_name " + account.openstack.floating_ip_pool)
1027
1028 if vdu_params.has_field('volumes'):
1029 for volume in vdu_params.volumes:
1030 if volume.has_field('custom_meta_data'):
1031 vol_list.append(volume.as_dict())
1032
1033 if vol_list:
1034 with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp_file:
1035 yaml.dump(vol_list, tmp_file)
1036 cmd += (" --vol_metadata {}").format(tmp_file.name)
1037
1038 exec_path = 'python3 ' + os.path.dirname(openstack_drv.__file__)
1039 exec_cmd = exec_path + '/' + cmd
1040 self.log.info("Running command: %s" %(exec_cmd))
1041 subprocess.call(exec_cmd, shell=True)
1042
1043 @rwstatus
1044 def do_modify_vdu(self, account, vdu_modify):
1045 """Modify Properties of existing virtual deployment unit
1046
1047 Arguments:
1048 account - a cloud account
1049 vdu_modify - Information about VDU Modification (RwcalYang.VDUModifyParams)
1050 """
1051 drv = self._use_driver(account)
1052 ### First create required number of ports aka connection points
1053 port_list = []
1054 network_list = []
1055 for c_point in vdu_modify.connection_points_add:
1056 if c_point.virtual_link_id in network_list:
1057 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"
1058 else:
1059 network_list.append(c_point.virtual_link_id)
1060 port_id = self._create_connection_point(account, c_point)
1061 port_list.append(port_id)
1062
1063 drv = self._use_driver(account)
1064 ### Now add the ports to VM
1065 for port_id in port_list:
1066 drv.nova_server_add_port(vdu_modify.vdu_id, port_id)
1067
1068 ### Delete the requested connection_points
1069 for c_point in vdu_modify.connection_points_remove:
1070 self.do_delete_port(account, c_point.connection_point_id, no_rwstatus=True)
1071
1072 if vdu_modify.has_field('image_id'):
1073 drv.nova_server_rebuild(vdu_modify.vdu_id, vdu_modify.image_id)
1074
1075
1076 @rwstatus
1077 def do_delete_vdu(self, account, vdu_id):
1078 """Delete a virtual deployment unit
1079
1080 Arguments:
1081 account - a cloud account
1082 vdu_id - id for the vdu to be deleted
1083
1084 Returns:
1085 None
1086 """
1087 drv = self._use_driver(account)
1088 try:
1089 drv.utils.compute.perform_vdu_network_cleanup(vdu_id)
1090 drv.nova_server_delete(vdu_id)
1091 except Exception as e:
1092 self.log.exception("Exception %s occured during delete-vdu", str(e))
1093 raise
1094
1095
1096 @rwstatus(ret_on_failure=[None])
1097 def do_get_vdu(self, account, vdu_id):
1098 """Get information about a virtual deployment unit.
1099
1100 Arguments:
1101 account - a cloud account
1102 vdu_id - id for the vdu
1103
1104 Returns:
1105 Object of type RwcalYang.VDUInfoParams
1106 """
1107 drv = self._use_driver(account)
1108 try:
1109 vm_info = drv.nova_server_get(vdu_id)
1110 vdu_info = drv.utils.compute.parse_cloud_vdu_info(vm_info)
1111 except Exception as e:
1112 self.log.exception("Exception %s occured during get-vdu", str(e))
1113 raise
1114
1115 return vdu_info
1116
1117
1118 @rwstatus(ret_on_failure=[None])
1119 def do_get_vdu_list(self, account):
1120 """Get information about all the virtual deployment units
1121
1122 Arguments:
1123 account - a cloud account
1124
1125 Returns:
1126 A list of objects of type RwcalYang.VDUInfoParams
1127 """
1128 vnf_resources = RwcalYang.VNFResources()
1129 drv = self._use_driver(account)
1130 try:
1131 vms = drv.nova_server_list()
1132 for vm in vms:
1133 vdu = drv.utils.compute.parse_cloud_vdu_info(vm)
1134 vnf_resources.vdu_info_list.append(vdu)
1135 except Exception as e:
1136 self.log.exception("Exception %s occured during get-vdu-list", str(e))
1137 raise
1138 return vnf_resources
1139
1140