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