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