3 # Copyright 2016 RIFT.IO Inc
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
24 import rift
.rwcal
.openstack
as openstack_drv
26 import rift
.cal
.rwcal_status
as rwcal_status
28 import neutronclient
.common
.exceptions
as NeutronException
30 from gi
.repository
import (
36 PREPARE_VM_CMD
= "prepare_vm.py --auth_url {auth_url} --username {username} --password {password} --tenant_name {tenant_name} --mgmt_network {mgmt_network} --server_id {server_id} --port_metadata"
38 rwstatus_exception_map
= { IndexError: RwTypes
.RwStatus
.NOTFOUND
,
39 KeyError: RwTypes
.RwStatus
.NOTFOUND
,
40 NotImplementedError: RwTypes
.RwStatus
.NOT_IMPLEMENTED
,}
42 rwstatus
= rw_status
.rwstatus_from_exc_map(rwstatus_exception_map
)
43 rwcalstatus
= rwcal_status
.rwcalstatus_from_exc_map(rwstatus_exception_map
)
46 espec_utils
= openstack_drv
.OpenstackExtraSpecUtils()
48 class OpenstackCALOperationFailure(Exception):
51 class UninitializedPluginError(Exception):
55 class OpenstackServerGroupError(Exception):
59 class ImageUploadError(Exception):
63 class RwcalOpenstackPlugin(GObject
.Object
, RwCal
.Cloud
):
64 """This class implements the CAL VALA methods for openstack."""
69 GObject
.Object
.__init
__(self
)
70 self
._driver
_class
= openstack_drv
.OpenstackDriver
71 self
.log
= logging
.getLogger('rwcal.openstack.%s' % RwcalOpenstackPlugin
.instance_num
)
72 self
.log
.setLevel(logging
.DEBUG
)
74 self
._rwlog
_handler
= None
75 RwcalOpenstackPlugin
.instance_num
+= 1
78 @contextlib.contextmanager
79 def _use_driver(self
, account
):
80 if self
._rwlog
_handler
is None:
81 raise UninitializedPluginError("Must call init() in CAL plugin before use.")
83 with rwlogger
.rwlog_root_handler(self
._rwlog
_handler
):
85 drv
= self
._driver
_class
(username
= account
.openstack
.key
,
86 password
= account
.openstack
.secret
,
87 auth_url
= account
.openstack
.auth_url
,
88 tenant_name
= account
.openstack
.tenant
,
89 mgmt_network
= account
.openstack
.mgmt_network
,
90 cert_validate
= account
.openstack
.cert_validate
)
91 except Exception as e
:
92 self
.log
.error("RwcalOpenstackPlugin: OpenstackDriver init failed. Exception: %s" %(str(e
)))
99 def do_init(self
, rwlog_ctx
):
100 self
._rwlog
_handler
= rwlogger
.RwLogger(
101 category
="rw-cal-log",
102 subcategory
="openstack",
105 self
.log
.addHandler(self
._rwlog
_handler
)
106 self
.log
.propagate
= False
108 @rwstatus(ret_on_failure
=[None])
109 def do_validate_cloud_creds(self
, account
):
111 Validates the cloud account credentials for the specified account.
112 Performs an access to the resources using Keystone API. If creds
113 are not valid, returns an error code & reason string
115 account - a cloud account to validate
118 Validation Code and Details String
120 status
= RwcalYang
.CloudConnectionStatus()
123 with self
._use
_driver
(account
) as drv
:
124 drv
.validate_account_creds()
126 except openstack_drv
.ValidationError
as e
:
127 self
.log
.error("RwcalOpenstackPlugin: OpenstackDriver credential validation failed. Exception: %s", str(e
))
128 status
.status
= "failure"
129 status
.details
= "Invalid Credentials: %s" % str(e
)
131 except Exception as e
:
132 msg
= "RwcalOpenstackPlugin: OpenstackDriver connection failed. Exception: %s" %(str(e
))
134 status
.status
= "failure"
138 status
.status
= "success"
139 status
.details
= "Connection was successful"
143 @rwstatus(ret_on_failure
=[""])
144 def do_get_management_network(self
, account
):
146 Returns the management network associated with the specified account.
148 account - a cloud account
151 The management network
153 return account
.openstack
.mgmt_network
155 @rwstatus(ret_on_failure
=[""])
156 def do_create_tenant(self
, account
, name
):
157 """Create a new tenant.
160 account - a cloud account
161 name - name of the tenant
166 raise NotImplementedError
169 def do_delete_tenant(self
, account
, tenant_id
):
173 account - a cloud account
174 tenant_id - id of the tenant
176 raise NotImplementedError
178 @rwstatus(ret_on_failure
=[[]])
179 def do_get_tenant_list(self
, account
):
183 account - a cloud account
188 raise NotImplementedError
190 @rwstatus(ret_on_failure
=[""])
191 def do_create_role(self
, account
, name
):
192 """Create a new user.
195 account - a cloud account
196 name - name of the user
201 raise NotImplementedError
204 def do_delete_role(self
, account
, role_id
):
208 account - a cloud account
209 role_id - id of the user
211 raise NotImplementedError
213 @rwstatus(ret_on_failure
=[[]])
214 def do_get_role_list(self
, account
):
218 account - a cloud account
223 raise NotImplementedError
225 @rwstatus(ret_on_failure
=[""])
226 def do_create_image(self
, account
, image
):
230 account - a cloud account
231 image - a description of the image to create
238 # If the use passed in a file descriptor, use that to
240 if image
.has_field("fileno"):
241 new_fileno
= os
.dup(image
.fileno
)
242 hdl
= os
.fdopen(new_fileno
, 'rb')
244 hdl
= open(image
.location
, "rb")
245 except Exception as e
:
246 self
.log
.error("Could not open file for upload. Exception received: %s", str(e
))
251 kwargs
['name'] = image
.name
253 if image
.disk_format
:
254 kwargs
['disk_format'] = image
.disk_format
255 if image
.container_format
:
256 kwargs
['container_format'] = image
.container_format
258 with self
._use
_driver
(account
) as drv
:
260 image_id
= drv
.glance_image_create(**kwargs
)
262 drv
.glance_image_upload(image_id
, fd
)
265 stored_image
= drv
.glance_image_get(image_id
)
266 if stored_image
.checksum
!= image
.checksum
:
267 drv
.glance_image_delete(image_id
=image_id
)
268 raise ImageUploadError(
269 "image checksum did not match (actual: %s, expected: %s). Deleting." %
270 (stored_image
.checksum
, image
.checksum
)
276 def do_delete_image(self
, account
, image_id
):
277 """Delete a vm image.
280 account - a cloud account
281 image_id - id of the image to delete
283 with self
._use
_driver
(account
) as drv
:
284 drv
.glance_image_delete(image_id
=image_id
)
288 def _fill_image_info(img_info
):
289 """Create a GI object from image info dictionary
291 Converts image information dictionary object returned by openstack
292 driver into Protobuf Gi Object
295 account - a cloud account
296 img_info - image information dictionary object from openstack
301 img
= RwcalYang
.ImageInfoItem()
302 img
.name
= img_info
['name']
303 img
.id = img_info
['id']
304 img
.checksum
= img_info
['checksum']
305 img
.disk_format
= img_info
['disk_format']
306 img
.container_format
= img_info
['container_format']
307 if img_info
['status'] == 'active':
310 img
.state
= 'inactive'
313 @rwstatus(ret_on_failure
=[[]])
314 def do_get_image_list(self
, account
):
315 """Return a list of the names of all available images.
318 account - a cloud account
321 The the list of images in VimResources object
323 response
= RwcalYang
.VimResources()
324 with self
._use
_driver
(account
) as drv
:
325 images
= drv
.glance_image_list()
327 response
.imageinfo_list
.append(RwcalOpenstackPlugin
._fill
_image
_info
(img
))
330 @rwstatus(ret_on_failure
=[None])
331 def do_get_image(self
, account
, image_id
):
332 """Return a image information.
335 account - a cloud account
336 image_id - an id of the image
339 ImageInfoItem object containing image information.
341 with self
._use
_driver
(account
) as drv
:
342 image
= drv
.glance_image_get(image_id
)
343 return RwcalOpenstackPlugin
._fill
_image
_info
(image
)
345 @rwstatus(ret_on_failure
=[""])
346 def do_create_vm(self
, account
, vminfo
):
347 """Create a new virtual machine.
350 account - a cloud account
351 vminfo - information that defines the type of VM to create
357 kwargs
['name'] = vminfo
.vm_name
358 kwargs
['flavor_id'] = vminfo
.flavor_id
359 kwargs
['image_id'] = vminfo
.image_id
361 with self
._use
_driver
(account
) as drv
:
362 ### If floating_ip is required and we don't have one, better fail before any further allocation
363 if vminfo
.has_field('allocate_public_address') and vminfo
.allocate_public_address
:
364 if account
.openstack
.has_field('floating_ip_pool'):
365 pool_name
= account
.openstack
.floating_ip_pool
368 floating_ip
= self
._allocate
_floating
_ip
(drv
, pool_name
)
372 if vminfo
.has_field('cloud_init') and vminfo
.cloud_init
.has_field('userdata'):
373 kwargs
['userdata'] = vminfo
.cloud_init
.userdata
375 kwargs
['userdata'] = ''
377 if account
.openstack
.security_groups
:
378 kwargs
['security_groups'] = account
.openstack
.security_groups
381 for port
in vminfo
.port_list
:
382 port_list
.append(port
.port_id
)
385 kwargs
['port_list'] = port_list
388 for network
in vminfo
.network_list
:
389 network_list
.append(network
.network_id
)
392 kwargs
['network_list'] = network_list
395 for field
in vminfo
.user_tags
.fields
:
396 if vminfo
.user_tags
.has_field(field
):
397 metadata
[field
] = getattr(vminfo
.user_tags
, field
)
398 kwargs
['metadata'] = metadata
400 if vminfo
.has_field('availability_zone'):
401 kwargs
['availability_zone'] = vminfo
.availability_zone
403 kwargs
['availability_zone'] = None
405 if vminfo
.has_field('server_group'):
406 kwargs
['scheduler_hints'] = {'group': vminfo
.server_group
}
408 kwargs
['scheduler_hints'] = None
410 with self
._use
_driver
(account
) as drv
:
411 vm_id
= drv
.nova_server_create(**kwargs
)
413 self
.prepare_vdu_on_boot(account
, vm_id
, floating_ip
)
418 def do_start_vm(self
, account
, vm_id
):
419 """Start an existing virtual machine.
422 account - a cloud account
423 vm_id - an id of the VM
425 with self
._use
_driver
(account
) as drv
:
426 drv
.nova_server_start(vm_id
)
429 def do_stop_vm(self
, account
, vm_id
):
430 """Stop a running virtual machine.
433 account - a cloud account
434 vm_id - an id of the VM
436 with self
._use
_driver
(account
) as drv
:
437 drv
.nova_server_stop(vm_id
)
440 def do_delete_vm(self
, account
, vm_id
):
441 """Delete a virtual machine.
444 account - a cloud account
445 vm_id - an id of the VM
447 with self
._use
_driver
(account
) as drv
:
448 drv
.nova_server_delete(vm_id
)
451 def do_reboot_vm(self
, account
, vm_id
):
452 """Reboot a virtual machine.
455 account - a cloud account
456 vm_id - an id of the VM
458 with self
._use
_driver
(account
) as drv
:
459 drv
.nova_server_reboot(vm_id
)
462 def _fill_vm_info(vm_info
, mgmt_network
):
463 """Create a GI object from vm info dictionary
465 Converts VM information dictionary object returned by openstack
466 driver into Protobuf Gi Object
469 vm_info - VM information from openstack
470 mgmt_network - Management network
473 Protobuf Gi object for VM
475 vm
= RwcalYang
.VMInfoItem()
476 vm
.vm_id
= vm_info
['id']
477 vm
.vm_name
= vm_info
['name']
478 vm
.image_id
= vm_info
['image']['id']
479 vm
.flavor_id
= vm_info
['flavor']['id']
480 vm
.state
= vm_info
['status']
481 for network_name
, network_info
in vm_info
['addresses'].items():
483 if network_name
== mgmt_network
:
484 vm
.public_ip
= next((item
['addr']
485 for item
in network_info
486 if item
['OS-EXT-IPS:type'] == 'floating'),
487 network_info
[0]['addr'])
488 vm
.management_ip
= network_info
[0]['addr']
490 for interface
in network_info
:
491 addr
= vm
.private_ip_list
.add()
492 addr
.ip_address
= interface
['addr']
494 for network_name
, network_info
in vm_info
['addresses'].items():
495 if network_info
and network_name
== mgmt_network
and not vm
.public_ip
:
496 for interface
in network_info
:
497 if 'OS-EXT-IPS:type' in interface
and interface
['OS-EXT-IPS:type'] == 'floating':
498 vm
.public_ip
= interface
['addr']
500 # Look for any metadata
501 for key
, value
in vm_info
['metadata'].items():
502 if key
in vm
.user_tags
.fields
:
503 setattr(vm
.user_tags
, key
, value
)
504 if 'OS-EXT-SRV-ATTR:host' in vm_info
:
505 if vm_info
['OS-EXT-SRV-ATTR:host'] != None:
506 vm
.host_name
= vm_info
['OS-EXT-SRV-ATTR:host']
507 if 'OS-EXT-AZ:availability_zone' in vm_info
:
508 if vm_info
['OS-EXT-AZ:availability_zone'] != None:
509 vm
.availability_zone
= vm_info
['OS-EXT-AZ:availability_zone']
512 @rwstatus(ret_on_failure
=[[]])
513 def do_get_vm_list(self
, account
):
514 """Return a list of the VMs as vala boxed objects
517 account - a cloud account
520 List containing VM information
522 response
= RwcalYang
.VimResources()
523 with self
._use
_driver
(account
) as drv
:
524 vms
= drv
.nova_server_list()
526 response
.vminfo_list
.append(RwcalOpenstackPlugin
._fill
_vm
_info
(vm
, account
.openstack
.mgmt_network
))
529 @rwstatus(ret_on_failure
=[None])
530 def do_get_vm(self
, account
, id):
531 """Return vm information.
534 account - a cloud account
535 id - an id for the VM
540 with self
._use
_driver
(account
) as drv
:
541 vm
= drv
.nova_server_get(id)
542 return RwcalOpenstackPlugin
._fill
_vm
_info
(vm
, account
.openstack
.mgmt_network
)
545 def _get_guest_epa_specs(guest_epa
):
547 Returns EPA Specs dictionary for guest_epa attributes
550 if guest_epa
.has_field('mempage_size'):
551 mempage_size
= espec_utils
.guest
.mano_to_extra_spec_mempage_size(guest_epa
.mempage_size
)
552 if mempage_size
is not None:
553 epa_specs
['hw:mem_page_size'] = mempage_size
555 if guest_epa
.has_field('cpu_pinning_policy'):
556 cpu_pinning_policy
= espec_utils
.guest
.mano_to_extra_spec_cpu_pinning_policy(guest_epa
.cpu_pinning_policy
)
557 if cpu_pinning_policy
is not None:
558 epa_specs
['hw:cpu_policy'] = cpu_pinning_policy
560 if guest_epa
.has_field('cpu_thread_pinning_policy'):
561 cpu_thread_pinning_policy
= espec_utils
.guest
.mano_to_extra_spec_cpu_thread_pinning_policy(guest_epa
.cpu_thread_pinning_policy
)
562 if cpu_thread_pinning_policy
is None:
563 epa_specs
['hw:cpu_threads_policy'] = cpu_thread_pinning_policy
565 if guest_epa
.has_field('trusted_execution'):
566 trusted_execution
= espec_utils
.guest
.mano_to_extra_spec_trusted_execution(guest_epa
.trusted_execution
)
567 if trusted_execution
is not None:
568 epa_specs
['trust:trusted_host'] = trusted_execution
570 if guest_epa
.has_field('numa_node_policy'):
571 if guest_epa
.numa_node_policy
.has_field('node_cnt'):
572 numa_node_count
= espec_utils
.guest
.mano_to_extra_spec_numa_node_count(guest_epa
.numa_node_policy
.node_cnt
)
573 if numa_node_count
is not None:
574 epa_specs
['hw:numa_nodes'] = numa_node_count
576 if guest_epa
.numa_node_policy
.has_field('mem_policy'):
577 numa_memory_policy
= espec_utils
.guest
.mano_to_extra_spec_numa_memory_policy(guest_epa
.numa_node_policy
.mem_policy
)
578 if numa_memory_policy
is not None:
579 epa_specs
['hw:numa_mempolicy'] = numa_memory_policy
581 if guest_epa
.numa_node_policy
.has_field('node'):
582 for node
in guest_epa
.numa_node_policy
.node
:
583 if node
.has_field('vcpu') and node
.vcpu
:
584 epa_specs
['hw:numa_cpus.'+str(node
.id)] = ','.join([str(j
.id) for j
in node
.vcpu
])
586 epa_specs
['hw:numa_mem.'+str(node
.id)] = str(node
.memory_mb
)
588 if guest_epa
.has_field('pcie_device'):
590 for device
in guest_epa
.pcie_device
:
591 pci_devices
.append(device
.device_id
+':'+str(device
.count
))
592 epa_specs
['pci_passthrough:alias'] = ','.join(pci_devices
)
597 def _get_host_epa_specs(host_epa
):
599 Returns EPA Specs dictionary for host_epa attributes
604 if host_epa
.has_field('cpu_model'):
605 cpu_model
= espec_utils
.host
.mano_to_extra_spec_cpu_model(host_epa
.cpu_model
)
606 if cpu_model
is not None:
607 epa_specs
['capabilities:cpu_info:model'] = cpu_model
609 if host_epa
.has_field('cpu_arch'):
610 cpu_arch
= espec_utils
.host
.mano_to_extra_spec_cpu_arch(host_epa
.cpu_arch
)
611 if cpu_arch
is not None:
612 epa_specs
['capabilities:cpu_info:arch'] = cpu_arch
614 if host_epa
.has_field('cpu_vendor'):
615 cpu_vendor
= espec_utils
.host
.mano_to_extra_spec_cpu_vendor(host_epa
.cpu_vendor
)
616 if cpu_vendor
is not None:
617 epa_specs
['capabilities:cpu_info:vendor'] = cpu_vendor
619 if host_epa
.has_field('cpu_socket_count'):
620 cpu_socket_count
= espec_utils
.host
.mano_to_extra_spec_cpu_socket_count(host_epa
.cpu_socket_count
)
621 if cpu_socket_count
is not None:
622 epa_specs
['capabilities:cpu_info:topology:sockets'] = cpu_socket_count
624 if host_epa
.has_field('cpu_core_count'):
625 cpu_core_count
= espec_utils
.host
.mano_to_extra_spec_cpu_core_count(host_epa
.cpu_core_count
)
626 if cpu_core_count
is not None:
627 epa_specs
['capabilities:cpu_info:topology:cores'] = cpu_core_count
629 if host_epa
.has_field('cpu_core_thread_count'):
630 cpu_core_thread_count
= espec_utils
.host
.mano_to_extra_spec_cpu_core_thread_count(host_epa
.cpu_core_thread_count
)
631 if cpu_core_thread_count
is not None:
632 epa_specs
['capabilities:cpu_info:topology:threads'] = cpu_core_thread_count
634 if host_epa
.has_field('cpu_feature'):
636 espec_cpu_features
= []
637 for feature
in host_epa
.cpu_feature
:
638 cpu_features
.append(feature
.feature
)
639 espec_cpu_features
= espec_utils
.host
.mano_to_extra_spec_cpu_features(cpu_features
)
640 if espec_cpu_features
is not None:
641 epa_specs
['capabilities:cpu_info:features'] = espec_cpu_features
645 def _get_hypervisor_epa_specs(guest_epa
):
647 Returns EPA Specs dictionary for hypervisor_epa attributes
650 return hypervisor_epa
653 def _get_vswitch_epa_specs(guest_epa
):
655 Returns EPA Specs dictionary for vswitch_epa attributes
661 def _get_host_aggregate_epa_specs(host_aggregate
):
663 Returns EPA Specs dictionary for host aggregates
666 for aggregate
in host_aggregate
:
667 epa_specs
['aggregate_instance_extra_specs:'+aggregate
.metadata_key
] = aggregate
.metadata_value
672 def _get_epa_specs(flavor
):
674 Returns epa_specs dictionary based on flavor information
677 if flavor
.has_field('guest_epa'):
678 guest_epa
= RwcalOpenstackPlugin
._get
_guest
_epa
_specs
(flavor
.guest_epa
)
679 epa_specs
.update(guest_epa
)
680 if flavor
.has_field('host_epa'):
681 host_epa
= RwcalOpenstackPlugin
._get
_host
_epa
_specs
(flavor
.host_epa
)
682 epa_specs
.update(host_epa
)
683 if flavor
.has_field('hypervisor_epa'):
684 hypervisor_epa
= RwcalOpenstackPlugin
._get
_hypervisor
_epa
_specs
(flavor
.hypervisor_epa
)
685 epa_specs
.update(hypervisor_epa
)
686 if flavor
.has_field('vswitch_epa'):
687 vswitch_epa
= RwcalOpenstackPlugin
._get
_vswitch
_epa
_specs
(flavor
.vswitch_epa
)
688 epa_specs
.update(vswitch_epa
)
689 if flavor
.has_field('host_aggregate'):
690 host_aggregate
= RwcalOpenstackPlugin
._get
_host
_aggregate
_epa
_specs
(flavor
.host_aggregate
)
691 epa_specs
.update(host_aggregate
)
694 @rwstatus(ret_on_failure
=[""])
695 def do_create_flavor(self
, account
, flavor
):
696 """Create new flavor.
699 account - a cloud account
700 flavor - flavor of the VM
705 epa_specs
= RwcalOpenstackPlugin
._get
_epa
_specs
(flavor
)
706 with self
._use
_driver
(account
) as drv
:
707 return drv
.nova_flavor_create(name
= flavor
.name
,
708 ram
= flavor
.vm_flavor
.memory_mb
,
709 vcpus
= flavor
.vm_flavor
.vcpu_count
,
710 disk
= flavor
.vm_flavor
.storage_gb
,
711 epa_specs
= epa_specs
)
715 def do_delete_flavor(self
, account
, flavor_id
):
719 account - a cloud account
720 flavor_id - id flavor of the VM
722 with self
._use
_driver
(account
) as drv
:
723 drv
.nova_flavor_delete(flavor_id
)
726 def _fill_epa_attributes(flavor
, flavor_info
):
727 """Helper function to populate the EPA attributes
730 flavor : Object with EPA attributes
731 flavor_info: A dictionary of flavor_info received from openstack
735 getattr(flavor
, 'vm_flavor').vcpu_count
= flavor_info
['vcpus']
736 getattr(flavor
, 'vm_flavor').memory_mb
= flavor_info
['ram']
737 getattr(flavor
, 'vm_flavor').storage_gb
= flavor_info
['disk']
739 ### If extra_specs in flavor_info
740 if not 'extra_specs' in flavor_info
:
743 for attr
in flavor_info
['extra_specs']:
744 if attr
== 'hw:cpu_policy':
745 cpu_pinning_policy
= espec_utils
.guest
.extra_spec_to_mano_cpu_pinning_policy(flavor_info
['extra_specs']['hw:cpu_policy'])
746 if cpu_pinning_policy
is not None:
747 getattr(flavor
, 'guest_epa').cpu_pinning_policy
= cpu_pinning_policy
749 elif attr
== 'hw:cpu_threads_policy':
750 cpu_thread_pinning_policy
= espec_utils
.guest
.extra_spec_to_mano_cpu_thread_pinning_policy(flavor_info
['extra_specs']['hw:cpu_threads_policy'])
751 if cpu_thread_pinning_policy
is not None:
752 getattr(flavor
, 'guest_epa').cpu_thread_pinning_policy
= cpu_thread_pinning_policy
754 elif attr
== 'hw:mem_page_size':
755 mempage_size
= espec_utils
.guest
.extra_spec_to_mano_mempage_size(flavor_info
['extra_specs']['hw:mem_page_size'])
756 if mempage_size
is not None:
757 getattr(flavor
, 'guest_epa').mempage_size
= mempage_size
760 elif attr
== 'hw:numa_nodes':
761 numa_node_count
= espec_utils
.guest
.extra_specs_to_mano_numa_node_count(flavor_info
['extra_specs']['hw:numa_nodes'])
762 if numa_node_count
is not None:
763 getattr(flavor
,'guest_epa').numa_node_policy
.node_cnt
= numa_node_count
765 elif attr
.startswith('hw:numa_cpus.'):
766 node_id
= attr
.split('.')[1]
767 nodes
= [ n
for n
in flavor
.guest_epa
.numa_node_policy
.node
if n
.id == int(node_id
) ]
771 numa_node
= getattr(flavor
,'guest_epa').numa_node_policy
.node
.add()
772 numa_node
.id = int(node_id
)
774 for x
in flavor_info
['extra_specs'][attr
].split(','):
775 numa_node_vcpu
= numa_node
.vcpu
.add()
776 numa_node_vcpu
.id = int(x
)
778 elif attr
.startswith('hw:numa_mem.'):
779 node_id
= attr
.split('.')[1]
780 nodes
= [ n
for n
in flavor
.guest_epa
.numa_node_policy
.node
if n
.id == int(node_id
) ]
784 numa_node
= getattr(flavor
,'guest_epa').numa_node_policy
.node
.add()
785 numa_node
.id = int(node_id
)
787 numa_node
.memory_mb
= int(flavor_info
['extra_specs'][attr
])
789 elif attr
== 'hw:numa_mempolicy':
790 numa_memory_policy
= espec_utils
.guest
.extra_to_mano_spec_numa_memory_policy(flavor_info
['extra_specs']['hw:numa_mempolicy'])
791 if numa_memory_policy
is not None:
792 getattr(flavor
,'guest_epa').numa_node_policy
.mem_policy
= numa_memory_policy
794 elif attr
== 'trust:trusted_host':
795 trusted_execution
= espec_utils
.guest
.extra_spec_to_mano_trusted_execution(flavor_info
['extra_specs']['trust:trusted_host'])
796 if trusted_execution
is not None:
797 getattr(flavor
,'guest_epa').trusted_execution
= trusted_execution
799 elif attr
== 'pci_passthrough:alias':
800 device_types
= flavor_info
['extra_specs']['pci_passthrough:alias']
801 for device
in device_types
.split(','):
802 dev
= getattr(flavor
,'guest_epa').pcie_device
.add()
803 dev
.device_id
= device
.split(':')[0]
804 dev
.count
= int(device
.split(':')[1])
806 elif attr
== 'capabilities:cpu_info:model':
807 cpu_model
= espec_utils
.host
.extra_specs_to_mano_cpu_model(flavor_info
['extra_specs']['capabilities:cpu_info:model'])
808 if cpu_model
is not None:
809 getattr(flavor
, 'host_epa').cpu_model
= cpu_model
811 elif attr
== 'capabilities:cpu_info:arch':
812 cpu_arch
= espec_utils
.host
.extra_specs_to_mano_cpu_arch(flavor_info
['extra_specs']['capabilities:cpu_info:arch'])
813 if cpu_arch
is not None:
814 getattr(flavor
, 'host_epa').cpu_arch
= cpu_arch
816 elif attr
== 'capabilities:cpu_info:vendor':
817 cpu_vendor
= espec_utils
.host
.extra_spec_to_mano_cpu_vendor(flavor_info
['extra_specs']['capabilities:cpu_info:vendor'])
818 if cpu_vendor
is not None:
819 getattr(flavor
, 'host_epa').cpu_vendor
= cpu_vendor
821 elif attr
== 'capabilities:cpu_info:topology:sockets':
822 cpu_sockets
= espec_utils
.host
.extra_spec_to_mano_cpu_socket_count(flavor_info
['extra_specs']['capabilities:cpu_info:topology:sockets'])
823 if cpu_sockets
is not None:
824 getattr(flavor
, 'host_epa').cpu_socket_count
= cpu_sockets
826 elif attr
== 'capabilities:cpu_info:topology:cores':
827 cpu_cores
= espec_utils
.host
.extra_spec_to_mano_cpu_core_count(flavor_info
['extra_specs']['capabilities:cpu_info:topology:cores'])
828 if cpu_cores
is not None:
829 getattr(flavor
, 'host_epa').cpu_core_count
= cpu_cores
831 elif attr
== 'capabilities:cpu_info:topology:threads':
832 cpu_threads
= espec_utils
.host
.extra_spec_to_mano_cpu_core_thread_count(flavor_info
['extra_specs']['capabilities:cpu_info:topology:threads'])
833 if cpu_threads
is not None:
834 getattr(flavor
, 'host_epa').cpu_core_thread_count
= cpu_threads
836 elif attr
== 'capabilities:cpu_info:features':
837 cpu_features
= espec_utils
.host
.extra_spec_to_mano_cpu_features(flavor_info
['extra_specs']['capabilities:cpu_info:features'])
838 if cpu_features
is not None:
839 for feature
in cpu_features
:
840 getattr(flavor
, 'host_epa').cpu_feature
.append(feature
)
841 elif attr
.startswith('aggregate_instance_extra_specs:'):
842 aggregate
= getattr(flavor
, 'host_aggregate').add()
843 aggregate
.metadata_key
= ":".join(attr
.split(':')[1::])
844 aggregate
.metadata_value
= flavor_info
['extra_specs'][attr
]
847 def _fill_flavor_info(flavor_info
):
848 """Create a GI object from flavor info dictionary
850 Converts Flavor information dictionary object returned by openstack
851 driver into Protobuf Gi Object
854 flavor_info: Flavor information from openstack
857 Object of class FlavorInfoItem
859 flavor
= RwcalYang
.FlavorInfoItem()
860 flavor
.name
= flavor_info
['name']
861 flavor
.id = flavor_info
['id']
862 RwcalOpenstackPlugin
._fill
_epa
_attributes
(flavor
, flavor_info
)
866 @rwstatus(ret_on_failure
=[[]])
867 def do_get_flavor_list(self
, account
):
868 """Return flavor information.
871 account - a cloud account
876 response
= RwcalYang
.VimResources()
877 with self
._use
_driver
(account
) as drv
:
878 flavors
= drv
.nova_flavor_list()
880 response
.flavorinfo_list
.append(RwcalOpenstackPlugin
._fill
_flavor
_info
(flv
))
883 @rwstatus(ret_on_failure
=[None])
884 def do_get_flavor(self
, account
, id):
885 """Return flavor information.
888 account - a cloud account
889 id - an id for the flavor
894 with self
._use
_driver
(account
) as drv
:
895 flavor
= drv
.nova_flavor_get(id)
896 return RwcalOpenstackPlugin
._fill
_flavor
_info
(flavor
)
899 def _fill_network_info(self
, network_info
, account
):
900 """Create a GI object from network info dictionary
902 Converts Network information dictionary object returned by openstack
903 driver into Protobuf Gi Object
906 network_info - Network information from openstack
907 account - a cloud account
912 network
= RwcalYang
.NetworkInfoItem()
913 network
.network_name
= network_info
['name']
914 network
.network_id
= network_info
['id']
915 if ('provider:network_type' in network_info
) and (network_info
['provider:network_type'] != None):
916 network
.provider_network
.overlay_type
= network_info
['provider:network_type'].upper()
917 if ('provider:segmentation_id' in network_info
) and (network_info
['provider:segmentation_id']):
918 network
.provider_network
.segmentation_id
= network_info
['provider:segmentation_id']
919 if ('provider:physical_network' in network_info
) and (network_info
['provider:physical_network']):
920 network
.provider_network
.physical_network
= network_info
['provider:physical_network'].upper()
922 if 'subnets' in network_info
and network_info
['subnets']:
923 subnet_id
= network_info
['subnets'][0]
924 with self
._use
_driver
(account
) as drv
:
925 subnet
= drv
.neutron_subnet_get(subnet_id
)
926 network
.subnet
= subnet
['cidr']
929 @rwstatus(ret_on_failure
=[[]])
930 def do_get_network_list(self
, account
):
931 """Return a list of networks
934 account - a cloud account
939 response
= RwcalYang
.VimResources()
940 with self
._use
_driver
(account
) as drv
:
941 networks
= drv
.neutron_network_list()
942 for network
in networks
:
943 response
.networkinfo_list
.append(self
._fill
_network
_info
(network
, account
))
946 @rwstatus(ret_on_failure
=[None])
947 def do_get_network(self
, account
, id):
951 account - a cloud account
952 id - an id for the network
957 with self
._use
_driver
(account
) as drv
:
958 network
= drv
.neutron_network_get(id)
959 return self
._fill
_network
_info
(network
, account
)
961 @rwstatus(ret_on_failure
=[""])
962 def do_create_network(self
, account
, network
):
963 """Create a new network
966 account - a cloud account
967 network - Network object
973 kwargs
['name'] = network
.network_name
974 kwargs
['admin_state_up'] = True
975 kwargs
['external_router'] = False
976 kwargs
['shared'] = False
978 if network
.has_field('provider_network'):
979 if network
.provider_network
.has_field('physical_network'):
980 kwargs
['physical_network'] = network
.provider_network
.physical_network
981 if network
.provider_network
.has_field('overlay_type'):
982 kwargs
['network_type'] = network
.provider_network
.overlay_type
.lower()
983 if network
.provider_network
.has_field('segmentation_id'):
984 kwargs
['segmentation_id'] = network
.provider_network
.segmentation_id
986 with self
._use
_driver
(account
) as drv
:
987 network_id
= drv
.neutron_network_create(**kwargs
)
988 drv
.neutron_subnet_create(network_id
= network_id
,
989 cidr
= network
.subnet
)
993 def do_delete_network(self
, account
, network_id
):
997 account - a cloud account
998 network_id - an id for the network
1000 with self
._use
_driver
(account
) as drv
:
1001 drv
.neutron_network_delete(network_id
)
1004 def _fill_port_info(port_info
):
1005 """Create a GI object from port info dictionary
1007 Converts Port information dictionary object returned by openstack
1008 driver into Protobuf Gi Object
1011 port_info - Port information from openstack
1016 port
= RwcalYang
.PortInfoItem()
1018 port
.port_name
= port_info
['name']
1019 port
.port_id
= port_info
['id']
1020 port
.network_id
= port_info
['network_id']
1021 port
.port_state
= port_info
['status']
1022 if 'device_id' in port_info
:
1023 port
.vm_id
= port_info
['device_id']
1024 if 'fixed_ips' in port_info
:
1025 port
.ip_address
= port_info
['fixed_ips'][0]['ip_address']
1028 @rwstatus(ret_on_failure
=[None])
1029 def do_get_port(self
, account
, port_id
):
1033 account - a cloud account
1034 port_id - an id for the port
1039 with self
._use
_driver
(account
) as drv
:
1040 port
= drv
.neutron_port_get(port_id
)
1042 return RwcalOpenstackPlugin
._fill
_port
_info
(port
)
1044 @rwstatus(ret_on_failure
=[[]])
1045 def do_get_port_list(self
, account
):
1046 """Return a list of ports
1049 account - a cloud account
1054 response
= RwcalYang
.VimResources()
1055 with self
._use
_driver
(account
) as drv
:
1056 ports
= drv
.neutron_port_list(*{})
1058 response
.portinfo_list
.append(RwcalOpenstackPlugin
._fill
_port
_info
(port
))
1061 @rwstatus(ret_on_failure
=[""])
1062 def do_create_port(self
, account
, port
):
1063 """Create a new port
1066 account - a cloud account
1073 kwargs
['name'] = port
.port_name
1074 kwargs
['network_id'] = port
.network_id
1075 kwargs
['admin_state_up'] = True
1076 if port
.has_field('vm_id'):
1077 kwargs
['vm_id'] = port
.vm_id
1078 if port
.has_field('port_type'):
1079 kwargs
['port_type'] = port
.port_type
1081 kwargs
['port_type'] = "normal"
1083 with self
._use
_driver
(account
) as drv
:
1084 return drv
.neutron_port_create(**kwargs
)
1087 def do_delete_port(self
, account
, port_id
):
1091 account - a cloud account
1092 port_id - an id for port
1094 with self
._use
_driver
(account
) as drv
:
1095 drv
.neutron_port_delete(port_id
)
1097 @rwstatus(ret_on_failure
=[""])
1098 def do_add_host(self
, account
, host
):
1102 account - a cloud account
1103 host - a host object
1108 raise NotImplementedError
1111 def do_remove_host(self
, account
, host_id
):
1115 account - a cloud account
1116 host_id - an id for the host
1118 raise NotImplementedError
1120 @rwstatus(ret_on_failure
=[None])
1121 def do_get_host(self
, account
, host_id
):
1125 account - a cloud account
1126 host_id - an id for host
1131 raise NotImplementedError
1133 @rwstatus(ret_on_failure
=[[]])
1134 def do_get_host_list(self
, account
):
1135 """Return a list of hosts
1138 account - a cloud account
1143 raise NotImplementedError
1146 def _fill_connection_point_info(c_point
, port_info
):
1147 """Create a GI object for RwcalYang.VDUInfoParams_ConnectionPoints()
1149 Converts Port information dictionary object returned by openstack
1150 driver into Protobuf Gi Object
1153 port_info - Port information from openstack
1155 Protobuf Gi object for RwcalYang.VDUInfoParams_ConnectionPoints
1157 c_point
.name
= port_info
['name']
1158 c_point
.connection_point_id
= port_info
['id']
1159 if ('fixed_ips' in port_info
) and (len(port_info
['fixed_ips']) >= 1):
1160 if 'ip_address' in port_info
['fixed_ips'][0]:
1161 c_point
.ip_address
= port_info
['fixed_ips'][0]['ip_address']
1162 if 'mac_address' in port_info
:
1163 c_point
.mac_addr
= port_info
['mac_address']
1164 if port_info
['status'] == 'ACTIVE':
1165 c_point
.state
= 'active'
1167 c_point
.state
= 'inactive'
1168 if 'network_id' in port_info
:
1169 c_point
.virtual_link_id
= port_info
['network_id']
1170 if ('device_id' in port_info
) and (port_info
['device_id']):
1171 c_point
.vdu_id
= port_info
['device_id']
1174 def _fill_virtual_link_info(network_info
, port_list
, subnet
):
1175 """Create a GI object for VirtualLinkInfoParams
1177 Converts Network and Port information dictionary object
1178 returned by openstack driver into Protobuf Gi Object
1181 network_info - Network information from openstack
1182 port_list - A list of port information from openstack
1183 subnet: Subnet information from openstack
1185 Protobuf Gi object for VirtualLinkInfoParams
1187 link
= RwcalYang
.VirtualLinkInfoParams()
1188 link
.name
= network_info
['name']
1189 if network_info
['status'] == 'ACTIVE':
1190 link
.state
= 'active'
1192 link
.state
= 'inactive'
1193 link
.virtual_link_id
= network_info
['id']
1194 for port
in port_list
:
1195 if port
['device_owner'] == 'compute:None':
1196 c_point
= link
.connection_points
.add()
1197 RwcalOpenstackPlugin
._fill
_connection
_point
_info
(c_point
, port
)
1200 link
.subnet
= subnet
['cidr']
1202 if ('provider:network_type' in network_info
) and (network_info
['provider:network_type'] != None):
1203 link
.provider_network
.overlay_type
= network_info
['provider:network_type'].upper()
1204 if ('provider:segmentation_id' in network_info
) and (network_info
['provider:segmentation_id']):
1205 link
.provider_network
.segmentation_id
= network_info
['provider:segmentation_id']
1206 if ('provider:physical_network' in network_info
) and (network_info
['provider:physical_network']):
1207 link
.provider_network
.physical_network
= network_info
['provider:physical_network'].upper()
1212 def _fill_vdu_info(vm_info
, flavor_info
, mgmt_network
, port_list
, server_group
):
1213 """Create a GI object for VDUInfoParams
1215 Converts VM information dictionary object returned by openstack
1216 driver into Protobuf Gi Object
1219 vm_info - VM information from openstack
1220 flavor_info - VM Flavor information from openstack
1221 mgmt_network - Management network
1222 port_list - A list of port information from openstack
1223 server_group - A list (with one element or empty list) of server group to which this VM belongs
1225 Protobuf Gi object for VDUInfoParams
1227 vdu
= RwcalYang
.VDUInfoParams()
1228 vdu
.name
= vm_info
['name']
1229 vdu
.vdu_id
= vm_info
['id']
1230 for network_name
, network_info
in vm_info
['addresses'].items():
1231 if network_info
and network_name
== mgmt_network
:
1232 for interface
in network_info
:
1233 if 'OS-EXT-IPS:type' in interface
:
1234 if interface
['OS-EXT-IPS:type'] == 'fixed':
1235 vdu
.management_ip
= interface
['addr']
1236 elif interface
['OS-EXT-IPS:type'] == 'floating':
1237 vdu
.public_ip
= interface
['addr']
1239 # Look for any metadata
1240 for key
, value
in vm_info
['metadata'].items():
1241 if key
== 'node_id':
1243 if ('image' in vm_info
) and ('id' in vm_info
['image']):
1244 vdu
.image_id
= vm_info
['image']['id']
1245 if ('flavor' in vm_info
) and ('id' in vm_info
['flavor']):
1246 vdu
.flavor_id
= vm_info
['flavor']['id']
1248 if vm_info
['status'] == 'ACTIVE':
1249 vdu
.state
= 'active'
1250 elif vm_info
['status'] == 'ERROR':
1251 vdu
.state
= 'failed'
1253 vdu
.state
= 'inactive'
1255 if 'availability_zone' in vm_info
:
1256 vdu
.availability_zone
= vm_info
['availability_zone']
1259 vdu
.server_group
.name
= server_group
[0]
1261 vdu
.cloud_type
= 'openstack'
1262 # Fill the port information
1263 for port
in port_list
:
1264 c_point
= vdu
.connection_points
.add()
1265 RwcalOpenstackPlugin
._fill
_connection
_point
_info
(c_point
, port
)
1267 if flavor_info
is not None:
1268 RwcalOpenstackPlugin
._fill
_epa
_attributes
(vdu
, flavor_info
)
1271 @rwcalstatus(ret_on_failure
=[""])
1272 def do_create_virtual_link(self
, account
, link_params
):
1273 """Create a new virtual link
1276 account - a cloud account
1277 link_params - information that defines the type of VDU to create
1283 kwargs
['name'] = link_params
.name
1284 kwargs
['admin_state_up'] = True
1285 kwargs
['external_router'] = False
1286 kwargs
['shared'] = False
1288 if link_params
.has_field('provider_network'):
1289 if link_params
.provider_network
.has_field('physical_network'):
1290 kwargs
['physical_network'] = link_params
.provider_network
.physical_network
1291 if link_params
.provider_network
.has_field('overlay_type'):
1292 kwargs
['network_type'] = link_params
.provider_network
.overlay_type
.lower()
1293 if link_params
.provider_network
.has_field('segmentation_id'):
1294 kwargs
['segmentation_id'] = link_params
.provider_network
.segmentation_id
1297 with self
._use
_driver
(account
) as drv
:
1299 network_id
= drv
.neutron_network_create(**kwargs
)
1300 except Exception as e
:
1301 self
.log
.error("Encountered exceptions during network creation. Exception: %s", str(e
))
1304 kwargs
= {'network_id' : network_id
,
1305 'dhcp_params': {'enable_dhcp': True},
1306 'gateway_ip' : None,}
1308 if link_params
.ip_profile_params
.has_field('ip_version'):
1309 kwargs
['ip_version'] = 6 if link_params
.ip_profile_params
.ip_version
== 'ipv6' else 4
1311 kwargs
['ip_version'] = 4
1313 if link_params
.ip_profile_params
.has_field('subnet_address'):
1314 kwargs
['cidr'] = link_params
.ip_profile_params
.subnet_address
1315 elif link_params
.ip_profile_params
.has_field('subnet_prefix_pool'):
1316 subnet_pool
= drv
.netruon_subnetpool_by_name(link_params
.ip_profile_params
.subnet_prefix_pool
)
1317 if subnet_pool
is None:
1318 self
.log
.error("Could not find subnet pool with name :%s to be used for network: %s",
1319 link_params
.ip_profile_params
.subnet_prefix_pool
,
1321 raise NeutronException
.NotFound("SubnetPool with name %s not found"%(link_params
.ip_profile_params
.subnet_prefix_pool
))
1323 kwargs
['subnetpool_id'] = subnet_pool
['id']
1324 elif link_params
.has_field('subnet'):
1325 kwargs
['cidr'] = link_params
.subnet
1327 assert 0, "No IP Prefix or Pool name specified"
1329 if link_params
.ip_profile_params
.has_field('dhcp_params'):
1330 if link_params
.ip_profile_params
.dhcp_params
.has_field('enabled'):
1331 kwargs
['dhcp_params']['enable_dhcp'] = link_params
.ip_profile_params
.dhcp_params
.enabled
1332 if link_params
.ip_profile_params
.dhcp_params
.has_field('start_address'):
1333 kwargs
['dhcp_params']['start_address'] = link_params
.ip_profile_params
.dhcp_params
.start_address
1334 if link_params
.ip_profile_params
.dhcp_params
.has_field('count'):
1335 kwargs
['dhcp_params']['count'] = link_params
.ip_profile_params
.dhcp_params
.count
1337 if link_params
.ip_profile_params
.has_field('dns_server'):
1338 kwargs
['dns_server'] = []
1339 for server
in link_params
.ip_profile_params
.dns_server
:
1340 kwargs
['dns_server'].append(server
.address
)
1342 if link_params
.ip_profile_params
.has_field('gateway_address'):
1343 kwargs
['gateway_ip'] = link_params
.ip_profile_params
.gateway_address
1345 drv
.neutron_subnet_create(**kwargs
)
1351 def do_delete_virtual_link(self
, account
, link_id
):
1352 """Delete a virtual link
1355 account - a cloud account
1356 link_id - id for the virtual-link to be deleted
1362 self
.log
.error("Empty link_id during the virtual link deletion")
1363 raise Exception("Empty link_id during the virtual link deletion")
1365 with self
._use
_driver
(account
) as drv
:
1366 port_list
= drv
.neutron_port_list(**{'network_id': link_id
})
1368 for port
in port_list
:
1369 if ((port
['device_owner'] == 'compute:None') or (port
['device_owner'] == '')):
1370 self
.do_delete_port(account
, port
['id'], no_rwstatus
=True)
1371 self
.do_delete_network(account
, link_id
, no_rwstatus
=True)
1373 @rwstatus(ret_on_failure
=[None])
1374 def do_get_virtual_link(self
, account
, link_id
):
1375 """Get information about virtual link.
1378 account - a cloud account
1379 link_id - id for the virtual-link
1382 Object of type RwcalYang.VirtualLinkInfoParams
1385 self
.log
.error("Empty link_id during the virtual link get request")
1386 raise Exception("Empty link_id during the virtual link get request")
1388 with self
._use
_driver
(account
) as drv
:
1389 network
= drv
.neutron_network_get(link_id
)
1391 port_list
= drv
.neutron_port_list(**{'network_id': network
['id']})
1392 if 'subnets' in network
:
1393 subnet
= drv
.neutron_subnet_get(network
['subnets'][0])
1396 virtual_link
= RwcalOpenstackPlugin
._fill
_virtual
_link
_info
(network
, port_list
, subnet
)
1401 @rwstatus(ret_on_failure
=[None])
1402 def do_get_virtual_link_list(self
, account
):
1403 """Get information about all the virtual links
1406 account - a cloud account
1409 A list of objects of type RwcalYang.VirtualLinkInfoParams
1411 vnf_resources
= RwcalYang
.VNFResources()
1412 with self
._use
_driver
(account
) as drv
:
1413 networks
= drv
.neutron_network_list()
1414 for network
in networks
:
1415 port_list
= drv
.neutron_port_list(**{'network_id': network
['id']})
1416 if ('subnets' in network
) and (network
['subnets']):
1417 subnet
= drv
.neutron_subnet_get(network
['subnets'][0])
1420 virtual_link
= RwcalOpenstackPlugin
._fill
_virtual
_link
_info
(network
, port_list
, subnet
)
1421 vnf_resources
.virtual_link_info_list
.append(virtual_link
)
1422 return vnf_resources
1424 def _create_connection_point(self
, account
, c_point
):
1426 Create a connection point
1428 account - a cloud account
1429 c_point - connection_points
1432 kwargs
['name'] = c_point
.name
1433 kwargs
['network_id'] = c_point
.virtual_link_id
1434 kwargs
['admin_state_up'] = True
1436 if c_point
.type_yang
== 'VIRTIO' or c_point
.type_yang
== 'E1000':
1437 kwargs
['port_type'] = 'normal'
1438 elif c_point
.type_yang
== 'SR_IOV':
1439 kwargs
['port_type'] = 'direct'
1441 raise NotImplementedError("Port Type: %s not supported" %(c_point
.type_yang
))
1443 with self
._use
_driver
(account
) as drv
:
1444 if c_point
.has_field('security_group'):
1445 group
= drv
.neutron_security_group_by_name(c_point
.security_group
)
1446 if group
is not None:
1447 kwargs
['security_groups'] = [group
['id']]
1448 return drv
.neutron_port_create(**kwargs
)
1450 def _allocate_floating_ip(self
, drv
, pool_name
):
1452 Allocate a floating_ip. If unused floating_ip exists then its reused.
1454 drv: OpenstackDriver instance
1455 pool_name: Floating IP pool name
1458 An object of floating IP nova class (novaclient.v2.floating_ips.FloatingIP)
1461 # available_ip = [ ip for ip in drv.nova_floating_ip_list() if ip.instance_id == None ]
1463 # if pool_name is not None:
1464 # ### Filter further based on IP address
1465 # available_ip = [ ip for ip in available_ip if ip.pool == pool_name ]
1467 # if not available_ip:
1468 # floating_ip = drv.nova_floating_ip_create(pool_name)
1470 # floating_ip = available_ip[0]
1472 floating_ip
= drv
.nova_floating_ip_create(pool_name
)
1475 def _match_vm_flavor(self
, required
, available
):
1476 self
.log
.info("Matching VM Flavor attributes")
1477 if available
.vcpu_count
!= required
.vcpu_count
:
1478 self
.log
.debug("VCPU requirement mismatch. Required: %d, Available: %d",
1479 required
.vcpu_count
,
1480 available
.vcpu_count
)
1482 if available
.memory_mb
!= required
.memory_mb
:
1483 self
.log
.debug("Memory requirement mismatch. Required: %d MB, Available: %d MB",
1485 available
.memory_mb
)
1487 if available
.storage_gb
!= required
.storage_gb
:
1488 self
.log
.debug("Storage requirement mismatch. Required: %d GB, Available: %d GB",
1489 required
.storage_gb
,
1490 available
.storage_gb
)
1492 self
.log
.debug("VM Flavor match found")
1495 def _match_guest_epa(self
, required
, available
):
1496 self
.log
.info("Matching Guest EPA attributes")
1497 if required
.has_field('pcie_device'):
1498 self
.log
.debug("Matching pcie_device")
1499 if available
.has_field('pcie_device') == False:
1500 self
.log
.debug("Matching pcie_device failed. Not available in flavor")
1503 for dev
in required
.pcie_device
:
1504 if not [ d
for d
in available
.pcie_device
1505 if ((d
.device_id
== dev
.device_id
) and (d
.count
== dev
.count
)) ]:
1506 self
.log
.debug("Matching pcie_device failed. Required: %s, Available: %s", required
.pcie_device
, available
.pcie_device
)
1508 elif available
.has_field('pcie_device'):
1509 self
.log
.debug("Rejecting available flavor because pcie_device not required but available")
1513 if required
.has_field('mempage_size'):
1514 self
.log
.debug("Matching mempage_size")
1515 if available
.has_field('mempage_size') == False:
1516 self
.log
.debug("Matching mempage_size failed. Not available in flavor")
1519 if required
.mempage_size
!= available
.mempage_size
:
1520 self
.log
.debug("Matching mempage_size failed. Required: %s, Available: %s", required
.mempage_size
, available
.mempage_size
)
1522 elif available
.has_field('mempage_size'):
1523 self
.log
.debug("Rejecting available flavor because mempage_size not required but available")
1526 if required
.has_field('cpu_pinning_policy'):
1527 self
.log
.debug("Matching cpu_pinning_policy")
1528 if required
.cpu_pinning_policy
!= 'ANY':
1529 if available
.has_field('cpu_pinning_policy') == False:
1530 self
.log
.debug("Matching cpu_pinning_policy failed. Not available in flavor")
1533 if required
.cpu_pinning_policy
!= available
.cpu_pinning_policy
:
1534 self
.log
.debug("Matching cpu_pinning_policy failed. Required: %s, Available: %s", required
.cpu_pinning_policy
, available
.cpu_pinning_policy
)
1536 elif available
.has_field('cpu_pinning_policy'):
1537 self
.log
.debug("Rejecting available flavor because cpu_pinning_policy not required but available")
1540 if required
.has_field('cpu_thread_pinning_policy'):
1541 self
.log
.debug("Matching cpu_thread_pinning_policy")
1542 if available
.has_field('cpu_thread_pinning_policy') == False:
1543 self
.log
.debug("Matching cpu_thread_pinning_policy failed. Not available in flavor")
1546 if required
.cpu_thread_pinning_policy
!= available
.cpu_thread_pinning_policy
:
1547 self
.log
.debug("Matching cpu_thread_pinning_policy failed. Required: %s, Available: %s", required
.cpu_thread_pinning_policy
, available
.cpu_thread_pinning_policy
)
1549 elif available
.has_field('cpu_thread_pinning_policy'):
1550 self
.log
.debug("Rejecting available flavor because cpu_thread_pinning_policy not required but available")
1553 if required
.has_field('trusted_execution'):
1554 self
.log
.debug("Matching trusted_execution")
1555 if required
.trusted_execution
== True:
1556 if available
.has_field('trusted_execution') == False:
1557 self
.log
.debug("Matching trusted_execution failed. Not available in flavor")
1560 if required
.trusted_execution
!= available
.trusted_execution
:
1561 self
.log
.debug("Matching trusted_execution failed. Required: %s, Available: %s", required
.trusted_execution
, available
.trusted_execution
)
1563 elif available
.has_field('trusted_execution'):
1564 self
.log
.debug("Rejecting available flavor because trusted_execution not required but available")
1567 if required
.has_field('numa_node_policy'):
1568 self
.log
.debug("Matching numa_node_policy")
1569 if available
.has_field('numa_node_policy') == False:
1570 self
.log
.debug("Matching numa_node_policy failed. Not available in flavor")
1573 if required
.numa_node_policy
.has_field('node_cnt'):
1574 self
.log
.debug("Matching numa_node_policy node_cnt")
1575 if available
.numa_node_policy
.has_field('node_cnt') == False:
1576 self
.log
.debug("Matching numa_node_policy node_cnt failed. Not available in flavor")
1579 if required
.numa_node_policy
.node_cnt
!= available
.numa_node_policy
.node_cnt
:
1580 self
.log
.debug("Matching numa_node_policy node_cnt failed. Required: %s, Available: %s",required
.numa_node_policy
.node_cnt
, available
.numa_node_policy
.node_cnt
)
1582 elif available
.numa_node_policy
.has_field('node_cnt'):
1583 self
.log
.debug("Rejecting available flavor because numa node count not required but available")
1586 if required
.numa_node_policy
.has_field('mem_policy'):
1587 self
.log
.debug("Matching numa_node_policy mem_policy")
1588 if available
.numa_node_policy
.has_field('mem_policy') == False:
1589 self
.log
.debug("Matching numa_node_policy mem_policy failed. Not available in flavor")
1592 if required
.numa_node_policy
.mem_policy
!= available
.numa_node_policy
.mem_policy
:
1593 self
.log
.debug("Matching numa_node_policy mem_policy failed. Required: %s, Available: %s", required
.numa_node_policy
.mem_policy
, available
.numa_node_policy
.mem_policy
)
1595 elif available
.numa_node_policy
.has_field('mem_policy'):
1596 self
.log
.debug("Rejecting available flavor because num node mem_policy not required but available")
1599 if required
.numa_node_policy
.has_field('node'):
1600 self
.log
.debug("Matching numa_node_policy nodes configuration")
1601 if available
.numa_node_policy
.has_field('node') == False:
1602 self
.log
.debug("Matching numa_node_policy nodes configuration failed. Not available in flavor")
1604 for required_node
in required
.numa_node_policy
.node
:
1605 self
.log
.debug("Matching numa_node_policy nodes configuration for node %s", required_node
)
1607 for available_node
in available
.numa_node_policy
.node
:
1608 if required_node
.id != available_node
.id:
1609 self
.log
.debug("Matching numa_node_policy nodes configuration failed. Required: %s, Available: %s", required_node
, available_node
)
1611 if required_node
.vcpu
!= available_node
.vcpu
:
1612 self
.log
.debug("Matching numa_node_policy nodes configuration failed. Required: %s, Available: %s", required_node
, available_node
)
1614 if required_node
.memory_mb
!= available_node
.memory_mb
:
1615 self
.log
.debug("Matching numa_node_policy nodes configuration failed. Required: %s, Available: %s", required_node
, available_node
)
1618 if numa_match
== False:
1620 elif available
.numa_node_policy
.has_field('node'):
1621 self
.log
.debug("Rejecting available flavor because numa nodes not required but available")
1623 elif available
.has_field('numa_node_policy'):
1624 self
.log
.debug("Rejecting available flavor because numa_node_policy not required but available")
1626 self
.log
.info("Successful match for Guest EPA attributes")
1629 def _match_vswitch_epa(self
, required
, available
):
1630 self
.log
.debug("VSwitch EPA match found")
1633 def _match_hypervisor_epa(self
, required
, available
):
1634 self
.log
.debug("Hypervisor EPA match found")
1637 def _match_host_epa(self
, required
, available
):
1638 self
.log
.info("Matching Host EPA attributes")
1639 if required
.has_field('cpu_model'):
1640 self
.log
.debug("Matching CPU model")
1641 if available
.has_field('cpu_model') == False:
1642 self
.log
.debug("Matching CPU model failed. Not available in flavor")
1645 #### Convert all PREFER to REQUIRE since flavor will only have REQUIRE attributes
1646 if required
.cpu_model
.replace('PREFER', 'REQUIRE') != available
.cpu_model
:
1647 self
.log
.debug("Matching CPU model failed. Required: %s, Available: %s", required
.cpu_model
, available
.cpu_model
)
1649 elif available
.has_field('cpu_model'):
1650 self
.log
.debug("Rejecting available flavor because cpu_model not required but available")
1653 if required
.has_field('cpu_arch'):
1654 self
.log
.debug("Matching CPU architecture")
1655 if available
.has_field('cpu_arch') == False:
1656 self
.log
.debug("Matching CPU architecture failed. Not available in flavor")
1659 #### Convert all PREFER to REQUIRE since flavor will only have REQUIRE attributes
1660 if required
.cpu_arch
.replace('PREFER', 'REQUIRE') != available
.cpu_arch
:
1661 self
.log
.debug("Matching CPU architecture failed. Required: %s, Available: %s", required
.cpu_arch
, available
.cpu_arch
)
1663 elif available
.has_field('cpu_arch'):
1664 self
.log
.debug("Rejecting available flavor because cpu_arch not required but available")
1667 if required
.has_field('cpu_vendor'):
1668 self
.log
.debug("Matching CPU vendor")
1669 if available
.has_field('cpu_vendor') == False:
1670 self
.log
.debug("Matching CPU vendor failed. Not available in flavor")
1673 #### Convert all PREFER to REQUIRE since flavor will only have REQUIRE attributes
1674 if required
.cpu_vendor
.replace('PREFER', 'REQUIRE') != available
.cpu_vendor
:
1675 self
.log
.debug("Matching CPU vendor failed. Required: %s, Available: %s", required
.cpu_vendor
, available
.cpu_vendor
)
1677 elif available
.has_field('cpu_vendor'):
1678 self
.log
.debug("Rejecting available flavor because cpu_vendor not required but available")
1681 if required
.has_field('cpu_socket_count'):
1682 self
.log
.debug("Matching CPU socket count")
1683 if available
.has_field('cpu_socket_count') == False:
1684 self
.log
.debug("Matching CPU socket count failed. Not available in flavor")
1687 if required
.cpu_socket_count
!= available
.cpu_socket_count
:
1688 self
.log
.debug("Matching CPU socket count failed. Required: %s, Available: %s", required
.cpu_socket_count
, available
.cpu_socket_count
)
1690 elif available
.has_field('cpu_socket_count'):
1691 self
.log
.debug("Rejecting available flavor because cpu_socket_count not required but available")
1694 if required
.has_field('cpu_core_count'):
1695 self
.log
.debug("Matching CPU core count")
1696 if available
.has_field('cpu_core_count') == False:
1697 self
.log
.debug("Matching CPU core count failed. Not available in flavor")
1700 if required
.cpu_core_count
!= available
.cpu_core_count
:
1701 self
.log
.debug("Matching CPU core count failed. Required: %s, Available: %s", required
.cpu_core_count
, available
.cpu_core_count
)
1703 elif available
.has_field('cpu_core_count'):
1704 self
.log
.debug("Rejecting available flavor because cpu_core_count not required but available")
1707 if required
.has_field('cpu_core_thread_count'):
1708 self
.log
.debug("Matching CPU core thread count")
1709 if available
.has_field('cpu_core_thread_count') == False:
1710 self
.log
.debug("Matching CPU core thread count failed. Not available in flavor")
1713 if required
.cpu_core_thread_count
!= available
.cpu_core_thread_count
:
1714 self
.log
.debug("Matching CPU core thread count failed. Required: %s, Available: %s", required
.cpu_core_thread_count
, available
.cpu_core_thread_count
)
1716 elif available
.has_field('cpu_core_thread_count'):
1717 self
.log
.debug("Rejecting available flavor because cpu_core_thread_count not required but available")
1720 if required
.has_field('cpu_feature'):
1721 self
.log
.debug("Matching CPU feature list")
1722 if available
.has_field('cpu_feature') == False:
1723 self
.log
.debug("Matching CPU feature list failed. Not available in flavor")
1726 for feature
in required
.cpu_feature
:
1727 if feature
not in available
.cpu_feature
:
1728 self
.log
.debug("Matching CPU feature list failed. Required feature: %s is not present. Available features: %s", feature
, available
.cpu_feature
)
1730 elif available
.has_field('cpu_feature'):
1731 self
.log
.debug("Rejecting available flavor because cpu_feature not required but available")
1733 self
.log
.info("Successful match for Host EPA attributes")
1737 def _match_placement_group_inputs(self
, required
, available
):
1738 self
.log
.info("Matching Host aggregate attributes")
1740 if not required
and not available
:
1741 # Host aggregate not required and not available => success
1742 self
.log
.info("Successful match for Host Aggregate attributes")
1744 if required
and available
:
1745 # Host aggregate requested and available => Do a match and decide
1746 xx
= [ x
.as_dict() for x
in required
]
1747 yy
= [ y
.as_dict() for y
in available
]
1750 self
.log
.debug("Rejecting available flavor because host Aggregate mismatch. Required: %s, Available: %s ", required
, available
)
1752 self
.log
.info("Successful match for Host Aggregate attributes")
1755 # Either of following conditions => Failure
1756 # - Host aggregate required but not available
1757 # - Host aggregate not required but available
1758 self
.log
.debug("Rejecting available flavor because host Aggregate mismatch. Required: %s, Available: %s ", required
, available
)
1761 def match_epa_params(self
, resource_info
, request_params
):
1762 result
= self
._match
_vm
_flavor
(getattr(request_params
, 'vm_flavor'),
1763 getattr(resource_info
, 'vm_flavor'))
1765 self
.log
.debug("VM Flavor mismatched")
1768 result
= self
._match
_guest
_epa
(getattr(request_params
, 'guest_epa'),
1769 getattr(resource_info
, 'guest_epa'))
1771 self
.log
.debug("Guest EPA mismatched")
1774 result
= self
._match
_vswitch
_epa
(getattr(request_params
, 'vswitch_epa'),
1775 getattr(resource_info
, 'vswitch_epa'))
1777 self
.log
.debug("Vswitch EPA mismatched")
1780 result
= self
._match
_hypervisor
_epa
(getattr(request_params
, 'hypervisor_epa'),
1781 getattr(resource_info
, 'hypervisor_epa'))
1783 self
.log
.debug("Hypervisor EPA mismatched")
1786 result
= self
._match
_host
_epa
(getattr(request_params
, 'host_epa'),
1787 getattr(resource_info
, 'host_epa'))
1789 self
.log
.debug("Host EPA mismatched")
1792 result
= self
._match
_placement
_group
_inputs
(getattr(request_params
, 'host_aggregate'),
1793 getattr(resource_info
, 'host_aggregate'))
1796 self
.log
.debug("Host Aggregate mismatched")
1801 def _select_resource_flavor(self
, account
, vdu_init
):
1803 Select a existing flavor if it matches the request or create new flavor
1805 flavor
= RwcalYang
.FlavorInfoItem()
1806 flavor
.name
= str(uuid
.uuid4())
1807 epa_types
= ['vm_flavor', 'guest_epa', 'host_epa', 'host_aggregate', 'hypervisor_epa', 'vswitch_epa']
1808 epa_dict
= {k
: v
for k
, v
in vdu_init
.as_dict().items() if k
in epa_types
}
1809 flavor
.from_dict(epa_dict
)
1811 rc
, response
= self
.do_get_flavor_list(account
)
1812 if rc
!= RwTypes
.RwStatus
.SUCCESS
:
1813 self
.log
.error("Get-flavor-info-list operation failed for cloud account: %s",
1815 raise OpenstackCALOperationFailure("Get-flavor-info-list operation failed for cloud account: %s" %(account
.name
))
1818 flavor_list
= response
.flavorinfo_list
1819 self
.log
.debug("Received %d flavor information from RW.CAL", len(flavor_list
))
1820 for flv
in flavor_list
:
1821 self
.log
.info("Attempting to match compute requirement for VDU: %s with flavor %s",
1823 if self
.match_epa_params(flv
, vdu_init
):
1824 self
.log
.info("Flavor match found for compute requirements for VDU: %s with flavor name: %s, flavor-id: %s",
1825 vdu_init
.name
, flv
.name
, flv
.id)
1828 if account
.openstack
.dynamic_flavor_support
is False:
1829 self
.log
.error("Unable to create flavor for compute requirement for VDU: %s. VDU instantiation failed", vdu_init
.name
)
1830 raise OpenstackCALOperationFailure("No resource available with matching EPA attributes")
1832 rc
,flavor_id
= self
.do_create_flavor(account
,flavor
)
1833 if rc
!= RwTypes
.RwStatus
.SUCCESS
:
1834 self
.log
.error("Create-flavor operation failed for cloud account: %s",
1836 raise OpenstackCALOperationFailure("Create-flavor operation failed for cloud account: %s" %(account
.name
))
1839 @rwcalstatus(ret_on_failure
=[""])
1840 def do_create_vdu(self
, account
, vdu_init
):
1841 """Create a new virtual deployment unit
1844 account - a cloud account
1845 vdu_init - information about VDU to create (RwcalYang.VDUInitParams)
1850 ### First create required number of ports aka connection points
1851 with self
._use
_driver
(account
) as drv
:
1852 ### If floating_ip is required and we don't have one, better fail before any further allocation
1853 if vdu_init
.has_field('allocate_public_address') and vdu_init
.allocate_public_address
:
1854 if account
.openstack
.has_field('floating_ip_pool'):
1855 pool_name
= account
.openstack
.floating_ip_pool
1858 floating_ip
= self
._allocate
_floating
_ip
(drv
, pool_name
)
1864 for c_point
in vdu_init
.connection_points
:
1865 if c_point
.virtual_link_id
in network_list
:
1866 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"
1868 network_list
.append(c_point
.virtual_link_id
)
1869 port_id
= self
._create
_connection
_point
(account
, c_point
)
1870 port_list
.append(port_id
)
1872 if not vdu_init
.has_field('flavor_id'):
1873 vdu_init
.flavor_id
= self
._select
_resource
_flavor
(account
,vdu_init
)
1875 ### Check VDU Virtual Interface type and make sure VM with property exists
1876 if vdu_init
.connection_points
is not None:
1877 ### All virtual interfaces need to be of the same type for Openstack Accounts
1878 if not all(cp
.type_yang
== vdu_init
.connection_points
[0].type_yang
for cp
in vdu_init
.connection_points
):
1879 ### We have a mix of E1000 & VIRTIO virtual interface types in the VDU, abort instantiation.
1880 assert False, "Only one type of Virtual Intefaces supported for Openstack accounts. Found a mix of VIRTIO & E1000."
1882 with self
._use
_driver
(account
) as drv
:
1883 img_info
= drv
.glance_image_get(vdu_init
.image_id
)
1885 virt_intf_type
= vdu_init
.connection_points
[0].type_yang
1886 if virt_intf_type
== 'E1000':
1887 if 'hw_vif_model' in img_info
and img_info
.hw_vif_model
== 'e1000':
1888 self
.log
.debug("VDU has Virtual Interface E1000, found matching image with property hw_vif_model=e1000")
1890 err_str
= ("VDU has Virtual Interface E1000, but image '%s' does not have property hw_vif_model=e1000" % img_info
.name
)
1891 self
.log
.error(err_str
)
1892 raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str
)
1893 elif virt_intf_type
== 'VIRTIO':
1894 if 'hw_vif_model' in img_info
:
1895 err_str
= ("VDU has Virtual Interface VIRTIO, but image '%s' has hw_vif_model mismatch" % img_info
.name
)
1896 self
.log
.error(err_str
)
1897 raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str
)
1899 self
.log
.debug("VDU has Virtual Interface VIRTIO, found matching image")
1901 err_str
= ("VDU Virtual Interface '%s' not supported yet" % virt_intf_type
)
1902 self
.log
.error(err_str
)
1903 raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str
)
1905 with self
._use
_driver
(account
) as drv
:
1907 vm
= RwcalYang
.VMInfoItem()
1908 vm
.vm_name
= vdu_init
.name
1909 vm
.flavor_id
= vdu_init
.flavor_id
1910 vm
.image_id
= vdu_init
.image_id
1911 vm_network
= vm
.network_list
.add()
1912 vm_network
.network_id
= drv
._mgmt
_network
_id
1913 if vdu_init
.has_field('vdu_init') and vdu_init
.vdu_init
.has_field('userdata'):
1914 vm
.cloud_init
.userdata
= vdu_init
.vdu_init
.userdata
1916 if vdu_init
.has_field('node_id'):
1917 vm
.user_tags
.node_id
= vdu_init
.node_id
;
1919 if vdu_init
.has_field('availability_zone') and vdu_init
.availability_zone
.has_field('name'):
1920 vm
.availability_zone
= vdu_init
.availability_zone
.name
1922 if vdu_init
.has_field('server_group'):
1923 ### Get list of server group in openstack for name->id mapping
1924 openstack_group_list
= drv
.nova_server_group_list()
1925 group_id
= [ i
['id'] for i
in openstack_group_list
if i
['name'] == vdu_init
.server_group
.name
]
1926 if len(group_id
) != 1:
1927 raise OpenstackServerGroupError("VM placement failed. Server Group %s not found in openstack. Available groups" %(vdu_init
.server_group
.name
, [i
['name'] for i
in openstack_group_list
]))
1928 vm
.server_group
= group_id
[0]
1930 for port_id
in port_list
:
1931 port
= vm
.port_list
.add()
1932 port
.port_id
= port_id
1934 pci_assignement
= self
.prepare_vpci_metadata(drv
, vdu_init
)
1935 if pci_assignement
!= '':
1936 vm
.user_tags
.pci_assignement
= pci_assignement
1938 vm_id
= self
.do_create_vm(account
, vm
, no_rwstatus
=True)
1939 self
.prepare_vdu_on_boot(account
, vm_id
, floating_ip
)
1942 def prepare_vpci_metadata(self
, drv
, vdu_init
):
1943 pci_assignement
= ''
1944 ### TEF specific metadata creation for
1949 ### For MGMT interface
1950 if vdu_init
.has_field('mgmt_vpci'):
1951 xx
= 'u\''+ drv
._mgmt
_network
_id
+ '\' :[[u\'' + vdu_init
.mgmt_vpci
+ '\', ' + '\'\']]'
1952 virtio_vpci
.append(xx
)
1954 for c_point
in vdu_init
.connection_points
:
1955 if c_point
.has_field('vpci'):
1956 if c_point
.has_field('vpci') and c_point
.type_yang
== 'VIRTIO':
1957 xx
= 'u\''+c_point
.virtual_link_id
+ '\' :[[u\'' + c_point
.vpci
+ '\', ' + '\'\']]'
1958 virtio_vpci
.append(xx
)
1959 elif c_point
.has_field('vpci') and c_point
.type_yang
== 'SR_IOV':
1960 xx
= '[u\'' + c_point
.vpci
+ '\', ' + '\'\']'
1961 sriov_vpci
.append(xx
)
1964 virtio_meta
+= ','.join(virtio_vpci
)
1967 sriov_meta
= 'u\'VF\': ['
1968 sriov_meta
+= ','.join(sriov_vpci
)
1971 if virtio_meta
!= '':
1972 pci_assignement
+= virtio_meta
1973 pci_assignement
+= ','
1975 if sriov_meta
!= '':
1976 pci_assignement
+= sriov_meta
1978 if pci_assignement
!= '':
1979 pci_assignement
= '{' + pci_assignement
+ '}'
1981 return pci_assignement
1985 def prepare_vdu_on_boot(self
, account
, server_id
, floating_ip
):
1986 cmd
= PREPARE_VM_CMD
.format(auth_url
= account
.openstack
.auth_url
,
1987 username
= account
.openstack
.key
,
1988 password
= account
.openstack
.secret
,
1989 tenant_name
= account
.openstack
.tenant
,
1990 mgmt_network
= account
.openstack
.mgmt_network
,
1991 server_id
= server_id
)
1993 if floating_ip
is not None:
1994 cmd
+= (" --floating_ip "+ floating_ip
.ip
)
1996 exec_path
= 'python3 ' + os
.path
.dirname(openstack_drv
.__file
__)
1997 exec_cmd
= exec_path
+'/'+cmd
1998 self
.log
.info("Running command: %s" %(exec_cmd))
1999 subprocess
.call(exec_cmd
, shell
=True)
2002 def do_modify_vdu(self
, account
, vdu_modify
):
2003 """Modify Properties of existing virtual deployment unit
2006 account - a cloud account
2007 vdu_modify - Information about VDU Modification (RwcalYang.VDUModifyParams)
2009 ### First create required number of ports aka connection points
2012 for c_point
in vdu_modify
.connection_points_add
:
2013 if c_point
.virtual_link_id
in network_list
:
2014 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"
2016 network_list
.append(c_point
.virtual_link_id
)
2017 port_id
= self
._create
_connection
_point
(account
, c_point
)
2018 port_list
.append(port_id
)
2020 ### Now add the ports to VM
2021 for port_id
in port_list
:
2022 with self
._use
_driver
(account
) as drv
:
2023 drv
.nova_server_add_port(vdu_modify
.vdu_id
, port_id
)
2025 ### Delete the requested connection_points
2026 for c_point
in vdu_modify
.connection_points_remove
:
2027 self
.do_delete_port(account
, c_point
.connection_point_id
, no_rwstatus
=True)
2029 if vdu_modify
.has_field('image_id'):
2030 with self
._use
_driver
(account
) as drv
:
2031 drv
.nova_server_rebuild(vdu_modify
.vdu_id
, vdu_modify
.image_id
)
2035 def do_delete_vdu(self
, account
, vdu_id
):
2036 """Delete a virtual deployment unit
2039 account - a cloud account
2040 vdu_id - id for the vdu to be deleted
2046 self
.log
.error("empty vdu_id during the vdu deletion")
2049 with self
._use
_driver
(account
) as drv
:
2050 ### Get list of floating_ips associated with this instance and delete them
2051 floating_ips
= [ f
for f
in drv
.nova_floating_ip_list() if f
.instance_id
== vdu_id
]
2052 for f
in floating_ips
:
2053 drv
.nova_drv
.floating_ip_delete(f
)
2055 ### Get list of port on VM and delete them.
2056 port_list
= drv
.neutron_port_list(**{'device_id': vdu_id
})
2058 for port
in port_list
:
2059 if ((port
['device_owner'] == 'compute:None') or (port
['device_owner'] == '')):
2060 self
.do_delete_port(account
, port
['id'], no_rwstatus
=True)
2062 self
.do_delete_vm(account
, vdu_id
, no_rwstatus
=True)
2065 @rwstatus(ret_on_failure
=[None])
2066 def do_get_vdu(self
, account
, vdu_id
):
2067 """Get information about a virtual deployment unit.
2070 account - a cloud account
2071 vdu_id - id for the vdu
2074 Object of type RwcalYang.VDUInfoParams
2076 with self
._use
_driver
(account
) as drv
:
2078 ### Get list of ports excluding the one for management network
2079 port_list
= [p
for p
in drv
.neutron_port_list(**{'device_id': vdu_id
}) if p
['network_id'] != drv
.get_mgmt_network_id()]
2081 vm
= drv
.nova_server_get(vdu_id
)
2084 if ('flavor' in vm
) and ('id' in vm
['flavor']):
2086 flavor_info
= drv
.nova_flavor_get(vm
['flavor']['id'])
2087 except Exception as e
:
2088 self
.log
.critical("Exception encountered while attempting to get flavor info for flavor_id: %s. Exception: %s" %(vm
['flavor']['id'], str(e
)))
2090 openstack_group_list
= drv
.nova_server_group_list()
2091 server_group
= [ i
['name'] for i
in openstack_group_list
if vm
['id'] in i
['members']]
2092 vdu_info
= RwcalOpenstackPlugin
._fill
_vdu
_info
(vm
,
2094 account
.openstack
.mgmt_network
,
2097 if vdu_info
.state
== 'active':
2099 console_info
= drv
.nova_server_console(vdu_info
.vdu_id
)
2100 except Exception as e
:
2103 vdu_info
.console_url
= console_info
['console']['url']
2109 @rwstatus(ret_on_failure
=[None])
2110 def do_get_vdu_list(self
, account
):
2111 """Get information about all the virtual deployment units
2114 account - a cloud account
2117 A list of objects of type RwcalYang.VDUInfoParams
2119 vnf_resources
= RwcalYang
.VNFResources()
2120 with self
._use
_driver
(account
) as drv
:
2121 vms
= drv
.nova_server_list()
2123 ### Get list of ports excluding one for management network
2124 port_list
= [p
for p
in drv
.neutron_port_list(**{'device_id': vm
['id']}) if p
['network_id'] != drv
.get_mgmt_network_id()]
2128 if ('flavor' in vm
) and ('id' in vm
['flavor']):
2130 flavor_info
= drv
.nova_flavor_get(vm
['flavor']['id'])
2131 except Exception as e
:
2132 self
.log
.critical("Exception encountered while attempting to get flavor info for flavor_id: %s. Exception: %s" %(vm
['flavor']['id'], str(e
)))
2137 openstack_group_list
= drv
.nova_server_group_list()
2138 server_group
= [ i
['name'] for i
in openstack_group_list
if vm
['id'] in i
['members']]
2140 vdu
= RwcalOpenstackPlugin
._fill
_vdu
_info
(vm
,
2142 account
.openstack
.mgmt_network
,
2145 if vdu
.state
== 'active':
2147 console_info
= drv
.nova_server_console(vdu
.vdu_id
)
2148 except Exception as e
:
2151 vdu
.console_url
= console_info
['console']['url']
2153 vnf_resources
.vdu_info_list
.append(vdu
)
2154 return vnf_resources