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
29 import keystoneclient
.exceptions
as KeystoneExceptions
31 from gi
.repository
import (
37 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"
39 rwstatus_exception_map
= { IndexError: RwTypes
.RwStatus
.NOTFOUND
,
40 KeyError: RwTypes
.RwStatus
.NOTFOUND
,
41 NotImplementedError: RwTypes
.RwStatus
.NOT_IMPLEMENTED
,}
43 rwstatus
= rw_status
.rwstatus_from_exc_map(rwstatus_exception_map
)
44 rwcalstatus
= rwcal_status
.rwcalstatus_from_exc_map(rwstatus_exception_map
)
47 espec_utils
= openstack_drv
.OpenstackExtraSpecUtils()
49 class OpenstackCALOperationFailure(Exception):
52 class UninitializedPluginError(Exception):
56 class OpenstackServerGroupError(Exception):
60 class ImageUploadError(Exception):
64 class RwcalOpenstackPlugin(GObject
.Object
, RwCal
.Cloud
):
65 """This class implements the CAL VALA methods for openstack."""
70 GObject
.Object
.__init
__(self
)
71 self
._driver
_class
= openstack_drv
.OpenstackDriver
72 self
.log
= logging
.getLogger('rwcal.openstack.%s' % RwcalOpenstackPlugin
.instance_num
)
73 self
.log
.setLevel(logging
.DEBUG
)
75 self
._rwlog
_handler
= None
76 RwcalOpenstackPlugin
.instance_num
+= 1
79 @contextlib.contextmanager
80 def _use_driver(self
, account
):
81 if self
._rwlog
_handler
is None:
82 raise UninitializedPluginError("Must call init() in CAL plugin before use.")
84 with rwlogger
.rwlog_root_handler(self
._rwlog
_handler
):
86 drv
= self
._driver
_class
(username
= account
.openstack
.key
,
87 password
= account
.openstack
.secret
,
88 auth_url
= account
.openstack
.auth_url
,
89 tenant_name
= account
.openstack
.tenant
,
90 mgmt_network
= account
.openstack
.mgmt_network
,
91 cert_validate
= account
.openstack
.cert_validate
)
92 except (KeystoneExceptions
.Unauthorized
, KeystoneExceptions
.AuthorizationFailure
,
93 NeutronException
.NotFound
) as e
:
95 except Exception as e
:
96 self
.log
.error("RwcalOpenstackPlugin: OpenstackDriver init failed. Exception: %s" %(str(e
)))
103 def do_init(self
, rwlog_ctx
):
104 self
._rwlog
_handler
= rwlogger
.RwLogger(
105 category
="rw-cal-log",
106 subcategory
="openstack",
109 self
.log
.addHandler(self
._rwlog
_handler
)
110 self
.log
.propagate
= False
112 @rwstatus(ret_on_failure
=[None])
113 def do_validate_cloud_creds(self
, account
):
115 Validates the cloud account credentials for the specified account.
116 Performs an access to the resources using Keystone API. If creds
117 are not valid, returns an error code & reason string
119 account - a cloud account to validate
122 Validation Code and Details String
124 status
= RwcalYang
.CloudConnectionStatus()
126 with self
._use
_driver
(account
) as drv
:
127 drv
.validate_account_creds()
129 except KeystoneExceptions
.Unauthorized
as e
:
130 self
.log
.error("Invalid credentials given for VIM account %s" %account
.name
)
131 status
.status
= "failure"
132 status
.details
= "Invalid Credentials: %s" % str(e
)
134 except KeystoneExceptions
.AuthorizationFailure
as e
:
135 self
.log
.error("Bad authentication URL given for VIM account %s. Given auth url: %s" % (
136 account
.name
, account
.openstack
.auth_url
))
137 status
.status
= "failure"
138 status
.details
= "Invalid auth url: %s" % str(e
)
140 except NeutronException
.NotFound
as e
:
141 self
.log
.error("Given management network %s could not be found for VIM account %s" % (
142 account
.openstack
.mgmt_network
, account
.name
))
143 status
.status
= "failure"
144 status
.details
= "mgmt network does not exist: %s" % str(e
)
146 except openstack_drv
.ValidationError
as e
:
147 self
.log
.error("RwcalOpenstackPlugin: OpenstackDriver credential validation failed. Exception: %s", str(e
))
148 status
.status
= "failure"
149 status
.details
= "Invalid Credentials: %s" % str(e
)
151 except Exception as e
:
152 msg
= "RwcalOpenstackPlugin: OpenstackDriver connection failed. Exception: %s" %(str(e
))
154 status
.status
= "failure"
158 status
.status
= "success"
159 status
.details
= "Connection was successful"
163 @rwstatus(ret_on_failure
=[""])
164 def do_get_management_network(self
, account
):
166 Returns the management network associated with the specified account.
168 account - a cloud account
171 The management network
173 return account
.openstack
.mgmt_network
175 @rwstatus(ret_on_failure
=[""])
176 def do_create_tenant(self
, account
, name
):
177 """Create a new tenant.
180 account - a cloud account
181 name - name of the tenant
186 raise NotImplementedError
189 def do_delete_tenant(self
, account
, tenant_id
):
193 account - a cloud account
194 tenant_id - id of the tenant
196 raise NotImplementedError
198 @rwstatus(ret_on_failure
=[[]])
199 def do_get_tenant_list(self
, account
):
203 account - a cloud account
208 raise NotImplementedError
210 @rwstatus(ret_on_failure
=[""])
211 def do_create_role(self
, account
, name
):
212 """Create a new user.
215 account - a cloud account
216 name - name of the user
221 raise NotImplementedError
224 def do_delete_role(self
, account
, role_id
):
228 account - a cloud account
229 role_id - id of the user
231 raise NotImplementedError
233 @rwstatus(ret_on_failure
=[[]])
234 def do_get_role_list(self
, account
):
238 account - a cloud account
243 raise NotImplementedError
245 @rwstatus(ret_on_failure
=[""])
246 def do_create_image(self
, account
, image
):
250 account - a cloud account
251 image - a description of the image to create
258 # If the use passed in a file descriptor, use that to
260 if image
.has_field("fileno"):
261 new_fileno
= os
.dup(image
.fileno
)
262 hdl
= os
.fdopen(new_fileno
, 'rb')
264 hdl
= open(image
.location
, "rb")
265 except Exception as e
:
266 self
.log
.error("Could not open file for upload. Exception received: %s", str(e
))
271 kwargs
['name'] = image
.name
273 if image
.disk_format
:
274 kwargs
['disk_format'] = image
.disk_format
275 if image
.container_format
:
276 kwargs
['container_format'] = image
.container_format
278 with self
._use
_driver
(account
) as drv
:
280 image_id
= drv
.glance_image_create(**kwargs
)
282 drv
.glance_image_upload(image_id
, fd
)
285 stored_image
= drv
.glance_image_get(image_id
)
286 if stored_image
.checksum
!= image
.checksum
:
287 drv
.glance_image_delete(image_id
=image_id
)
288 raise ImageUploadError(
289 "image checksum did not match (actual: %s, expected: %s). Deleting." %
290 (stored_image
.checksum
, image
.checksum
)
296 def do_delete_image(self
, account
, image_id
):
297 """Delete a vm image.
300 account - a cloud account
301 image_id - id of the image to delete
303 with self
._use
_driver
(account
) as drv
:
304 drv
.glance_image_delete(image_id
=image_id
)
308 def _fill_image_info(img_info
):
309 """Create a GI object from image info dictionary
311 Converts image information dictionary object returned by openstack
312 driver into Protobuf Gi Object
315 account - a cloud account
316 img_info - image information dictionary object from openstack
321 img
= RwcalYang
.ImageInfoItem()
322 img
.name
= img_info
['name']
323 img
.id = img_info
['id']
324 img
.checksum
= img_info
['checksum']
325 img
.disk_format
= img_info
['disk_format']
326 img
.container_format
= img_info
['container_format']
327 if img_info
['status'] == 'active':
330 img
.state
= 'inactive'
333 @rwstatus(ret_on_failure
=[[]])
334 def do_get_image_list(self
, account
):
335 """Return a list of the names of all available images.
338 account - a cloud account
341 The the list of images in VimResources object
343 response
= RwcalYang
.VimResources()
344 with self
._use
_driver
(account
) as drv
:
345 images
= drv
.glance_image_list()
347 response
.imageinfo_list
.append(RwcalOpenstackPlugin
._fill
_image
_info
(img
))
350 @rwstatus(ret_on_failure
=[None])
351 def do_get_image(self
, account
, image_id
):
352 """Return a image information.
355 account - a cloud account
356 image_id - an id of the image
359 ImageInfoItem object containing image information.
361 with self
._use
_driver
(account
) as drv
:
362 image
= drv
.glance_image_get(image_id
)
363 return RwcalOpenstackPlugin
._fill
_image
_info
(image
)
365 @rwstatus(ret_on_failure
=[""])
366 def do_create_vm(self
, account
, vminfo
):
367 """Create a new virtual machine.
370 account - a cloud account
371 vminfo - information that defines the type of VM to create
377 kwargs
['name'] = vminfo
.vm_name
378 kwargs
['flavor_id'] = vminfo
.flavor_id
379 if vminfo
.has_field('image_id'):
380 kwargs
['image_id'] = vminfo
.image_id
382 with self
._use
_driver
(account
) as drv
:
383 ### If floating_ip is required and we don't have one, better fail before any further allocation
384 if vminfo
.has_field('allocate_public_address') and vminfo
.allocate_public_address
:
385 if account
.openstack
.has_field('floating_ip_pool'):
386 pool_name
= account
.openstack
.floating_ip_pool
389 floating_ip
= self
._allocate
_floating
_ip
(drv
, pool_name
)
393 if vminfo
.has_field('cloud_init') and vminfo
.cloud_init
.has_field('userdata'):
394 kwargs
['userdata'] = vminfo
.cloud_init
.userdata
396 kwargs
['userdata'] = ''
398 if account
.openstack
.security_groups
:
399 kwargs
['security_groups'] = account
.openstack
.security_groups
402 for port
in vminfo
.port_list
:
403 port_list
.append(port
.port_id
)
406 kwargs
['port_list'] = port_list
409 for network
in vminfo
.network_list
:
410 network_list
.append(network
.network_id
)
413 kwargs
['network_list'] = network_list
416 for field
in vminfo
.user_tags
.fields
:
417 if vminfo
.user_tags
.has_field(field
):
418 metadata
[field
] = getattr(vminfo
.user_tags
, field
)
419 kwargs
['metadata'] = metadata
421 if vminfo
.has_field('availability_zone'):
422 kwargs
['availability_zone'] = vminfo
.availability_zone
424 kwargs
['availability_zone'] = None
426 if vminfo
.has_field('server_group'):
427 kwargs
['scheduler_hints'] = {'group': vminfo
.server_group
}
429 kwargs
['scheduler_hints'] = None
431 with self
._use
_driver
(account
) as drv
:
432 vm_id
= drv
.nova_server_create(**kwargs
)
434 self
.prepare_vdu_on_boot(account
, vm_id
, floating_ip
, mgmt_network
= None)
438 def do_start_vm(self
, account
, vm_id
):
439 """Start an existing virtual machine.
442 account - a cloud account
443 vm_id - an id of the VM
445 with self
._use
_driver
(account
) as drv
:
446 drv
.nova_server_start(vm_id
)
449 def do_stop_vm(self
, account
, vm_id
):
450 """Stop a running virtual machine.
453 account - a cloud account
454 vm_id - an id of the VM
456 with self
._use
_driver
(account
) as drv
:
457 drv
.nova_server_stop(vm_id
)
460 def do_delete_vm(self
, account
, vm_id
):
461 """Delete a virtual machine.
464 account - a cloud account
465 vm_id - an id of the VM
467 with self
._use
_driver
(account
) as drv
:
468 drv
.nova_server_delete(vm_id
)
471 def do_reboot_vm(self
, account
, vm_id
):
472 """Reboot a virtual machine.
475 account - a cloud account
476 vm_id - an id of the VM
478 with self
._use
_driver
(account
) as drv
:
479 drv
.nova_server_reboot(vm_id
)
482 def _fill_vm_info(vm_info
, mgmt_network
):
483 """Create a GI object from vm info dictionary
485 Converts VM information dictionary object returned by openstack
486 driver into Protobuf Gi Object
489 vm_info - VM information from openstack
490 mgmt_network - Management network
493 Protobuf Gi object for VM
495 vm
= RwcalYang
.VMInfoItem()
496 vm
.vm_id
= vm_info
['id']
497 vm
.vm_name
= vm_info
['name']
498 vm
.image_id
= vm_info
['image']['id']
499 vm
.flavor_id
= vm_info
['flavor']['id']
500 vm
.state
= vm_info
['status']
501 for network_name
, network_info
in vm_info
['addresses'].items():
503 if network_name
== mgmt_network
:
504 vm
.public_ip
= next((item
['addr']
505 for item
in network_info
506 if item
['OS-EXT-IPS:type'] == 'floating'),
507 network_info
[0]['addr'])
508 vm
.management_ip
= network_info
[0]['addr']
510 for interface
in network_info
:
511 addr
= vm
.private_ip_list
.add()
512 addr
.ip_address
= interface
['addr']
514 for network_name
, network_info
in vm_info
['addresses'].items():
515 if network_info
and network_name
== mgmt_network
and not vm
.public_ip
:
516 for interface
in network_info
:
517 if 'OS-EXT-IPS:type' in interface
and interface
['OS-EXT-IPS:type'] == 'floating':
518 vm
.public_ip
= interface
['addr']
520 # Look for any metadata
521 for key
, value
in vm_info
['metadata'].items():
522 if key
in vm
.user_tags
.fields
:
523 setattr(vm
.user_tags
, key
, value
)
524 if 'OS-EXT-SRV-ATTR:host' in vm_info
:
525 if vm_info
['OS-EXT-SRV-ATTR:host'] != None:
526 vm
.host_name
= vm_info
['OS-EXT-SRV-ATTR:host']
527 if 'OS-EXT-AZ:availability_zone' in vm_info
:
528 if vm_info
['OS-EXT-AZ:availability_zone'] != None:
529 vm
.availability_zone
= vm_info
['OS-EXT-AZ:availability_zone']
532 @rwstatus(ret_on_failure
=[[]])
533 def do_get_vm_list(self
, account
):
534 """Return a list of the VMs as vala boxed objects
537 account - a cloud account
540 List containing VM information
542 response
= RwcalYang
.VimResources()
543 with self
._use
_driver
(account
) as drv
:
544 vms
= drv
.nova_server_list()
546 response
.vminfo_list
.append(RwcalOpenstackPlugin
._fill
_vm
_info
(vm
, account
.openstack
.mgmt_network
))
549 @rwstatus(ret_on_failure
=[None])
550 def do_get_vm(self
, account
, id):
551 """Return vm information.
554 account - a cloud account
555 id - an id for the VM
560 with self
._use
_driver
(account
) as drv
:
561 vm
= drv
.nova_server_get(id)
562 return RwcalOpenstackPlugin
._fill
_vm
_info
(vm
, account
.openstack
.mgmt_network
)
565 def _get_guest_epa_specs(guest_epa
):
567 Returns EPA Specs dictionary for guest_epa attributes
570 if guest_epa
.has_field('mempage_size'):
571 mempage_size
= espec_utils
.guest
.mano_to_extra_spec_mempage_size(guest_epa
.mempage_size
)
572 if mempage_size
is not None:
573 epa_specs
['hw:mem_page_size'] = mempage_size
575 if guest_epa
.has_field('cpu_pinning_policy'):
576 cpu_pinning_policy
= espec_utils
.guest
.mano_to_extra_spec_cpu_pinning_policy(guest_epa
.cpu_pinning_policy
)
577 if cpu_pinning_policy
is not None:
578 epa_specs
['hw:cpu_policy'] = cpu_pinning_policy
580 if guest_epa
.has_field('cpu_thread_pinning_policy'):
581 cpu_thread_pinning_policy
= espec_utils
.guest
.mano_to_extra_spec_cpu_thread_pinning_policy(guest_epa
.cpu_thread_pinning_policy
)
582 if cpu_thread_pinning_policy
is None:
583 epa_specs
['hw:cpu_threads_policy'] = cpu_thread_pinning_policy
585 if guest_epa
.has_field('trusted_execution'):
586 trusted_execution
= espec_utils
.guest
.mano_to_extra_spec_trusted_execution(guest_epa
.trusted_execution
)
587 if trusted_execution
is not None:
588 epa_specs
['trust:trusted_host'] = trusted_execution
590 if guest_epa
.has_field('numa_node_policy'):
591 if guest_epa
.numa_node_policy
.has_field('node_cnt'):
592 numa_node_count
= espec_utils
.guest
.mano_to_extra_spec_numa_node_count(guest_epa
.numa_node_policy
.node_cnt
)
593 if numa_node_count
is not None:
594 epa_specs
['hw:numa_nodes'] = numa_node_count
596 if guest_epa
.numa_node_policy
.has_field('mem_policy'):
597 numa_memory_policy
= espec_utils
.guest
.mano_to_extra_spec_numa_memory_policy(guest_epa
.numa_node_policy
.mem_policy
)
598 if numa_memory_policy
is not None:
599 epa_specs
['hw:numa_mempolicy'] = numa_memory_policy
601 if guest_epa
.numa_node_policy
.has_field('node'):
602 for node
in guest_epa
.numa_node_policy
.node
:
603 if node
.has_field('vcpu') and node
.vcpu
:
604 epa_specs
['hw:numa_cpus.'+str(node
.id)] = ','.join([str(j
.id) for j
in node
.vcpu
])
606 epa_specs
['hw:numa_mem.'+str(node
.id)] = str(node
.memory_mb
)
608 if guest_epa
.has_field('pcie_device'):
610 for device
in guest_epa
.pcie_device
:
611 pci_devices
.append(device
.device_id
+':'+str(device
.count
))
612 epa_specs
['pci_passthrough:alias'] = ','.join(pci_devices
)
617 def _get_host_epa_specs(host_epa
):
619 Returns EPA Specs dictionary for host_epa attributes
624 if host_epa
.has_field('cpu_model'):
625 cpu_model
= espec_utils
.host
.mano_to_extra_spec_cpu_model(host_epa
.cpu_model
)
626 if cpu_model
is not None:
627 epa_specs
['capabilities:cpu_info:model'] = cpu_model
629 if host_epa
.has_field('cpu_arch'):
630 cpu_arch
= espec_utils
.host
.mano_to_extra_spec_cpu_arch(host_epa
.cpu_arch
)
631 if cpu_arch
is not None:
632 epa_specs
['capabilities:cpu_info:arch'] = cpu_arch
634 if host_epa
.has_field('cpu_vendor'):
635 cpu_vendor
= espec_utils
.host
.mano_to_extra_spec_cpu_vendor(host_epa
.cpu_vendor
)
636 if cpu_vendor
is not None:
637 epa_specs
['capabilities:cpu_info:vendor'] = cpu_vendor
639 if host_epa
.has_field('cpu_socket_count'):
640 cpu_socket_count
= espec_utils
.host
.mano_to_extra_spec_cpu_socket_count(host_epa
.cpu_socket_count
)
641 if cpu_socket_count
is not None:
642 epa_specs
['capabilities:cpu_info:topology:sockets'] = cpu_socket_count
644 if host_epa
.has_field('cpu_core_count'):
645 cpu_core_count
= espec_utils
.host
.mano_to_extra_spec_cpu_core_count(host_epa
.cpu_core_count
)
646 if cpu_core_count
is not None:
647 epa_specs
['capabilities:cpu_info:topology:cores'] = cpu_core_count
649 if host_epa
.has_field('cpu_core_thread_count'):
650 cpu_core_thread_count
= espec_utils
.host
.mano_to_extra_spec_cpu_core_thread_count(host_epa
.cpu_core_thread_count
)
651 if cpu_core_thread_count
is not None:
652 epa_specs
['capabilities:cpu_info:topology:threads'] = cpu_core_thread_count
654 if host_epa
.has_field('cpu_feature'):
656 espec_cpu_features
= []
657 for feature
in host_epa
.cpu_feature
:
658 cpu_features
.append(feature
.feature
)
659 espec_cpu_features
= espec_utils
.host
.mano_to_extra_spec_cpu_features(cpu_features
)
660 if espec_cpu_features
is not None:
661 epa_specs
['capabilities:cpu_info:features'] = espec_cpu_features
665 def _get_hypervisor_epa_specs(guest_epa
):
667 Returns EPA Specs dictionary for hypervisor_epa attributes
670 return hypervisor_epa
673 def _get_vswitch_epa_specs(guest_epa
):
675 Returns EPA Specs dictionary for vswitch_epa attributes
681 def _get_host_aggregate_epa_specs(host_aggregate
):
683 Returns EPA Specs dictionary for host aggregates
686 for aggregate
in host_aggregate
:
687 epa_specs
['aggregate_instance_extra_specs:'+aggregate
.metadata_key
] = aggregate
.metadata_value
692 def _get_epa_specs(flavor
):
694 Returns epa_specs dictionary based on flavor information
697 if flavor
.has_field('guest_epa'):
698 guest_epa
= RwcalOpenstackPlugin
._get
_guest
_epa
_specs
(flavor
.guest_epa
)
699 epa_specs
.update(guest_epa
)
700 if flavor
.has_field('host_epa'):
701 host_epa
= RwcalOpenstackPlugin
._get
_host
_epa
_specs
(flavor
.host_epa
)
702 epa_specs
.update(host_epa
)
703 if flavor
.has_field('hypervisor_epa'):
704 hypervisor_epa
= RwcalOpenstackPlugin
._get
_hypervisor
_epa
_specs
(flavor
.hypervisor_epa
)
705 epa_specs
.update(hypervisor_epa
)
706 if flavor
.has_field('vswitch_epa'):
707 vswitch_epa
= RwcalOpenstackPlugin
._get
_vswitch
_epa
_specs
(flavor
.vswitch_epa
)
708 epa_specs
.update(vswitch_epa
)
709 if flavor
.has_field('host_aggregate'):
710 host_aggregate
= RwcalOpenstackPlugin
._get
_host
_aggregate
_epa
_specs
(flavor
.host_aggregate
)
711 epa_specs
.update(host_aggregate
)
714 @rwstatus(ret_on_failure
=[""])
715 def do_create_flavor(self
, account
, flavor
):
716 """Create new flavor.
719 account - a cloud account
720 flavor - flavor of the VM
725 epa_specs
= RwcalOpenstackPlugin
._get
_epa
_specs
(flavor
)
726 with self
._use
_driver
(account
) as drv
:
727 return drv
.nova_flavor_create(name
= flavor
.name
,
728 ram
= flavor
.vm_flavor
.memory_mb
,
729 vcpus
= flavor
.vm_flavor
.vcpu_count
,
730 disk
= flavor
.vm_flavor
.storage_gb
,
731 epa_specs
= epa_specs
)
735 def do_delete_flavor(self
, account
, flavor_id
):
739 account - a cloud account
740 flavor_id - id flavor of the VM
742 with self
._use
_driver
(account
) as drv
:
743 drv
.nova_flavor_delete(flavor_id
)
746 def _fill_epa_attributes(flavor
, flavor_info
):
747 """Helper function to populate the EPA attributes
750 flavor : Object with EPA attributes
751 flavor_info: A dictionary of flavor_info received from openstack
755 getattr(flavor
, 'vm_flavor').vcpu_count
= flavor_info
['vcpus']
756 getattr(flavor
, 'vm_flavor').memory_mb
= flavor_info
['ram']
757 getattr(flavor
, 'vm_flavor').storage_gb
= flavor_info
['disk']
759 ### If extra_specs in flavor_info
760 if not 'extra_specs' in flavor_info
:
763 for attr
in flavor_info
['extra_specs']:
764 if attr
== 'hw:cpu_policy':
765 cpu_pinning_policy
= espec_utils
.guest
.extra_spec_to_mano_cpu_pinning_policy(flavor_info
['extra_specs']['hw:cpu_policy'])
766 if cpu_pinning_policy
is not None:
767 getattr(flavor
, 'guest_epa').cpu_pinning_policy
= cpu_pinning_policy
769 elif attr
== 'hw:cpu_threads_policy':
770 cpu_thread_pinning_policy
= espec_utils
.guest
.extra_spec_to_mano_cpu_thread_pinning_policy(flavor_info
['extra_specs']['hw:cpu_threads_policy'])
771 if cpu_thread_pinning_policy
is not None:
772 getattr(flavor
, 'guest_epa').cpu_thread_pinning_policy
= cpu_thread_pinning_policy
774 elif attr
== 'hw:mem_page_size':
775 mempage_size
= espec_utils
.guest
.extra_spec_to_mano_mempage_size(flavor_info
['extra_specs']['hw:mem_page_size'])
776 if mempage_size
is not None:
777 getattr(flavor
, 'guest_epa').mempage_size
= mempage_size
780 elif attr
== 'hw:numa_nodes':
781 numa_node_count
= espec_utils
.guest
.extra_specs_to_mano_numa_node_count(flavor_info
['extra_specs']['hw:numa_nodes'])
782 if numa_node_count
is not None:
783 getattr(flavor
,'guest_epa').numa_node_policy
.node_cnt
= numa_node_count
785 elif attr
.startswith('hw:numa_cpus.'):
786 node_id
= attr
.split('.')[1]
787 nodes
= [ n
for n
in flavor
.guest_epa
.numa_node_policy
.node
if n
.id == int(node_id
) ]
791 numa_node
= getattr(flavor
,'guest_epa').numa_node_policy
.node
.add()
792 numa_node
.id = int(node_id
)
794 for x
in flavor_info
['extra_specs'][attr
].split(','):
795 numa_node_vcpu
= numa_node
.vcpu
.add()
796 numa_node_vcpu
.id = int(x
)
798 elif attr
.startswith('hw:numa_mem.'):
799 node_id
= attr
.split('.')[1]
800 nodes
= [ n
for n
in flavor
.guest_epa
.numa_node_policy
.node
if n
.id == int(node_id
) ]
804 numa_node
= getattr(flavor
,'guest_epa').numa_node_policy
.node
.add()
805 numa_node
.id = int(node_id
)
807 numa_node
.memory_mb
= int(flavor_info
['extra_specs'][attr
])
809 elif attr
== 'hw:numa_mempolicy':
810 numa_memory_policy
= espec_utils
.guest
.extra_to_mano_spec_numa_memory_policy(flavor_info
['extra_specs']['hw:numa_mempolicy'])
811 if numa_memory_policy
is not None:
812 getattr(flavor
,'guest_epa').numa_node_policy
.mem_policy
= numa_memory_policy
814 elif attr
== 'trust:trusted_host':
815 trusted_execution
= espec_utils
.guest
.extra_spec_to_mano_trusted_execution(flavor_info
['extra_specs']['trust:trusted_host'])
816 if trusted_execution
is not None:
817 getattr(flavor
,'guest_epa').trusted_execution
= trusted_execution
819 elif attr
== 'pci_passthrough:alias':
820 device_types
= flavor_info
['extra_specs']['pci_passthrough:alias']
821 for device
in device_types
.split(','):
822 dev
= getattr(flavor
,'guest_epa').pcie_device
.add()
823 dev
.device_id
= device
.split(':')[0]
824 dev
.count
= int(device
.split(':')[1])
826 elif attr
== 'capabilities:cpu_info:model':
827 cpu_model
= espec_utils
.host
.extra_specs_to_mano_cpu_model(flavor_info
['extra_specs']['capabilities:cpu_info:model'])
828 if cpu_model
is not None:
829 getattr(flavor
, 'host_epa').cpu_model
= cpu_model
831 elif attr
== 'capabilities:cpu_info:arch':
832 cpu_arch
= espec_utils
.host
.extra_specs_to_mano_cpu_arch(flavor_info
['extra_specs']['capabilities:cpu_info:arch'])
833 if cpu_arch
is not None:
834 getattr(flavor
, 'host_epa').cpu_arch
= cpu_arch
836 elif attr
== 'capabilities:cpu_info:vendor':
837 cpu_vendor
= espec_utils
.host
.extra_spec_to_mano_cpu_vendor(flavor_info
['extra_specs']['capabilities:cpu_info:vendor'])
838 if cpu_vendor
is not None:
839 getattr(flavor
, 'host_epa').cpu_vendor
= cpu_vendor
841 elif attr
== 'capabilities:cpu_info:topology:sockets':
842 cpu_sockets
= espec_utils
.host
.extra_spec_to_mano_cpu_socket_count(flavor_info
['extra_specs']['capabilities:cpu_info:topology:sockets'])
843 if cpu_sockets
is not None:
844 getattr(flavor
, 'host_epa').cpu_socket_count
= cpu_sockets
846 elif attr
== 'capabilities:cpu_info:topology:cores':
847 cpu_cores
= espec_utils
.host
.extra_spec_to_mano_cpu_core_count(flavor_info
['extra_specs']['capabilities:cpu_info:topology:cores'])
848 if cpu_cores
is not None:
849 getattr(flavor
, 'host_epa').cpu_core_count
= cpu_cores
851 elif attr
== 'capabilities:cpu_info:topology:threads':
852 cpu_threads
= espec_utils
.host
.extra_spec_to_mano_cpu_core_thread_count(flavor_info
['extra_specs']['capabilities:cpu_info:topology:threads'])
853 if cpu_threads
is not None:
854 getattr(flavor
, 'host_epa').cpu_core_thread_count
= cpu_threads
856 elif attr
== 'capabilities:cpu_info:features':
857 cpu_features
= espec_utils
.host
.extra_spec_to_mano_cpu_features(flavor_info
['extra_specs']['capabilities:cpu_info:features'])
858 if cpu_features
is not None:
859 for feature
in cpu_features
:
860 getattr(flavor
, 'host_epa').cpu_feature
.append(feature
)
861 elif attr
.startswith('aggregate_instance_extra_specs:'):
862 aggregate
= getattr(flavor
, 'host_aggregate').add()
863 aggregate
.metadata_key
= ":".join(attr
.split(':')[1::])
864 aggregate
.metadata_value
= flavor_info
['extra_specs'][attr
]
867 def _fill_flavor_info(flavor_info
):
868 """Create a GI object from flavor info dictionary
870 Converts Flavor information dictionary object returned by openstack
871 driver into Protobuf Gi Object
874 flavor_info: Flavor information from openstack
877 Object of class FlavorInfoItem
879 flavor
= RwcalYang
.FlavorInfoItem()
880 flavor
.name
= flavor_info
['name']
881 flavor
.id = flavor_info
['id']
882 RwcalOpenstackPlugin
._fill
_epa
_attributes
(flavor
, flavor_info
)
886 @rwstatus(ret_on_failure
=[[]])
887 def do_get_flavor_list(self
, account
):
888 """Return flavor information.
891 account - a cloud account
896 response
= RwcalYang
.VimResources()
897 with self
._use
_driver
(account
) as drv
:
898 flavors
= drv
.nova_flavor_list()
900 response
.flavorinfo_list
.append(RwcalOpenstackPlugin
._fill
_flavor
_info
(flv
))
903 @rwstatus(ret_on_failure
=[None])
904 def do_get_flavor(self
, account
, id):
905 """Return flavor information.
908 account - a cloud account
909 id - an id for the flavor
914 with self
._use
_driver
(account
) as drv
:
915 flavor
= drv
.nova_flavor_get(id)
916 return RwcalOpenstackPlugin
._fill
_flavor
_info
(flavor
)
919 def _fill_network_info(self
, network_info
, account
):
920 """Create a GI object from network info dictionary
922 Converts Network information dictionary object returned by openstack
923 driver into Protobuf Gi Object
926 network_info - Network information from openstack
927 account - a cloud account
932 network
= RwcalYang
.NetworkInfoItem()
933 network
.network_name
= network_info
['name']
934 network
.network_id
= network_info
['id']
935 if ('provider:network_type' in network_info
) and (network_info
['provider:network_type'] != None):
936 network
.provider_network
.overlay_type
= network_info
['provider:network_type'].upper()
937 if ('provider:segmentation_id' in network_info
) and (network_info
['provider:segmentation_id']):
938 network
.provider_network
.segmentation_id
= network_info
['provider:segmentation_id']
939 if ('provider:physical_network' in network_info
) and (network_info
['provider:physical_network']):
940 network
.provider_network
.physical_network
= network_info
['provider:physical_network'].upper()
942 if 'subnets' in network_info
and network_info
['subnets']:
943 subnet_id
= network_info
['subnets'][0]
944 with self
._use
_driver
(account
) as drv
:
945 subnet
= drv
.neutron_subnet_get(subnet_id
)
946 network
.subnet
= subnet
['cidr']
949 @rwstatus(ret_on_failure
=[[]])
950 def do_get_network_list(self
, account
):
951 """Return a list of networks
954 account - a cloud account
959 response
= RwcalYang
.VimResources()
960 with self
._use
_driver
(account
) as drv
:
961 networks
= drv
.neutron_network_list()
962 for network
in networks
:
963 response
.networkinfo_list
.append(self
._fill
_network
_info
(network
, account
))
966 @rwstatus(ret_on_failure
=[None])
967 def do_get_network(self
, account
, id):
971 account - a cloud account
972 id - an id for the network
977 with self
._use
_driver
(account
) as drv
:
978 network
= drv
.neutron_network_get(id)
979 return self
._fill
_network
_info
(network
, account
)
981 @rwstatus(ret_on_failure
=[""])
982 def do_create_network(self
, account
, network
):
983 """Create a new network
986 account - a cloud account
987 network - Network object
993 kwargs
['name'] = network
.network_name
994 kwargs
['admin_state_up'] = True
995 kwargs
['external_router'] = False
996 kwargs
['shared'] = False
998 if network
.has_field('provider_network'):
999 if network
.provider_network
.has_field('physical_network'):
1000 kwargs
['physical_network'] = network
.provider_network
.physical_network
1001 if network
.provider_network
.has_field('overlay_type'):
1002 kwargs
['network_type'] = network
.provider_network
.overlay_type
.lower()
1003 if network
.provider_network
.has_field('segmentation_id'):
1004 kwargs
['segmentation_id'] = network
.provider_network
.segmentation_id
1006 with self
._use
_driver
(account
) as drv
:
1007 network_id
= drv
.neutron_network_create(**kwargs
)
1008 drv
.neutron_subnet_create(network_id
= network_id
,
1009 cidr
= network
.subnet
)
1013 def do_delete_network(self
, account
, network_id
):
1017 account - a cloud account
1018 network_id - an id for the network
1020 with self
._use
_driver
(account
) as drv
:
1021 drv
.neutron_network_delete(network_id
)
1024 def _fill_port_info(port_info
):
1025 """Create a GI object from port info dictionary
1027 Converts Port information dictionary object returned by openstack
1028 driver into Protobuf Gi Object
1031 port_info - Port information from openstack
1036 port
= RwcalYang
.PortInfoItem()
1038 port
.port_name
= port_info
['name']
1039 port
.port_id
= port_info
['id']
1040 port
.network_id
= port_info
['network_id']
1041 port
.port_state
= port_info
['status']
1042 if 'device_id' in port_info
:
1043 port
.vm_id
= port_info
['device_id']
1044 if 'fixed_ips' in port_info
:
1045 port
.ip_address
= port_info
['fixed_ips'][0]['ip_address']
1048 @rwstatus(ret_on_failure
=[None])
1049 def do_get_port(self
, account
, port_id
):
1053 account - a cloud account
1054 port_id - an id for the port
1059 with self
._use
_driver
(account
) as drv
:
1060 port
= drv
.neutron_port_get(port_id
)
1062 return RwcalOpenstackPlugin
._fill
_port
_info
(port
)
1064 @rwstatus(ret_on_failure
=[[]])
1065 def do_get_port_list(self
, account
):
1066 """Return a list of ports
1069 account - a cloud account
1074 response
= RwcalYang
.VimResources()
1075 with self
._use
_driver
(account
) as drv
:
1076 ports
= drv
.neutron_port_list(*{})
1078 response
.portinfo_list
.append(RwcalOpenstackPlugin
._fill
_port
_info
(port
))
1081 @rwstatus(ret_on_failure
=[""])
1082 def do_create_port(self
, account
, port
):
1083 """Create a new port
1086 account - a cloud account
1093 kwargs
['name'] = port
.port_name
1094 kwargs
['network_id'] = port
.network_id
1095 kwargs
['admin_state_up'] = True
1096 if port
.has_field('vm_id'):
1097 kwargs
['vm_id'] = port
.vm_id
1098 if port
.has_field('port_type'):
1099 kwargs
['port_type'] = port
.port_type
1101 kwargs
['port_type'] = "normal"
1103 with self
._use
_driver
(account
) as drv
:
1104 return drv
.neutron_port_create(**kwargs
)
1107 def do_delete_port(self
, account
, port_id
):
1111 account - a cloud account
1112 port_id - an id for port
1114 with self
._use
_driver
(account
) as drv
:
1115 drv
.neutron_port_delete(port_id
)
1117 @rwstatus(ret_on_failure
=[""])
1118 def do_add_host(self
, account
, host
):
1122 account - a cloud account
1123 host - a host object
1128 raise NotImplementedError
1131 def do_remove_host(self
, account
, host_id
):
1135 account - a cloud account
1136 host_id - an id for the host
1138 raise NotImplementedError
1140 @rwstatus(ret_on_failure
=[None])
1141 def do_get_host(self
, account
, host_id
):
1145 account - a cloud account
1146 host_id - an id for host
1151 raise NotImplementedError
1153 @rwstatus(ret_on_failure
=[[]])
1154 def do_get_host_list(self
, account
):
1155 """Return a list of hosts
1158 account - a cloud account
1163 raise NotImplementedError
1166 def _fill_connection_point_info(c_point
, port_info
):
1167 """Create a GI object for RwcalYang.VDUInfoParams_ConnectionPoints()
1169 Converts Port information dictionary object returned by openstack
1170 driver into Protobuf Gi Object
1173 port_info - Port information from openstack
1175 Protobuf Gi object for RwcalYang.VDUInfoParams_ConnectionPoints
1177 c_point
.name
= port_info
['name']
1178 c_point
.connection_point_id
= port_info
['id']
1179 if ('fixed_ips' in port_info
) and (len(port_info
['fixed_ips']) >= 1):
1180 if 'ip_address' in port_info
['fixed_ips'][0]:
1181 c_point
.ip_address
= port_info
['fixed_ips'][0]['ip_address']
1182 if port_info
['status'] == 'ACTIVE':
1183 c_point
.state
= 'active'
1185 c_point
.state
= 'inactive'
1186 if 'network_id' in port_info
:
1187 c_point
.virtual_link_id
= port_info
['network_id']
1188 if ('device_id' in port_info
) and (port_info
['device_id']):
1189 c_point
.vdu_id
= port_info
['device_id']
1192 def _fill_virtual_link_info(network_info
, port_list
, subnet
):
1193 """Create a GI object for VirtualLinkInfoParams
1195 Converts Network and Port information dictionary object
1196 returned by openstack driver into Protobuf Gi Object
1199 network_info - Network information from openstack
1200 port_list - A list of port information from openstack
1201 subnet: Subnet information from openstack
1203 Protobuf Gi object for VirtualLinkInfoParams
1205 link
= RwcalYang
.VirtualLinkInfoParams()
1206 link
.name
= network_info
['name']
1207 if network_info
['status'] == 'ACTIVE':
1208 link
.state
= 'active'
1210 link
.state
= 'inactive'
1211 link
.virtual_link_id
= network_info
['id']
1212 for port
in port_list
:
1213 if port
['device_owner'] == 'compute:None':
1214 c_point
= link
.connection_points
.add()
1215 RwcalOpenstackPlugin
._fill
_connection
_point
_info
(c_point
, port
)
1218 link
.subnet
= subnet
['cidr']
1220 if ('provider:network_type' in network_info
) and (network_info
['provider:network_type'] != None):
1221 link
.provider_network
.overlay_type
= network_info
['provider:network_type'].upper()
1222 if ('provider:segmentation_id' in network_info
) and (network_info
['provider:segmentation_id']):
1223 link
.provider_network
.segmentation_id
= network_info
['provider:segmentation_id']
1224 if ('provider:physical_network' in network_info
) and (network_info
['provider:physical_network']):
1225 link
.provider_network
.physical_network
= network_info
['provider:physical_network'].upper()
1230 def _fill_vdu_info(vm_info
, flavor_info
, mgmt_network
, port_list
, server_group
, volume_list
= None, overridden_mgmt_network
= None):
1231 """Create a GI object for VDUInfoParams
1233 Converts VM information dictionary object returned by openstack
1234 driver into Protobuf Gi Object
1237 vm_info - VM information from openstack
1238 flavor_info - VM Flavor information from openstack
1239 mgmt_network - Management network
1240 port_list - A list of port information from openstack
1241 server_group - A list (with one element or empty list) of server group to which this VM belongs
1243 Protobuf Gi object for VDUInfoParams
1245 vdu
= RwcalYang
.VDUInfoParams()
1246 vdu
.name
= vm_info
['name']
1247 vdu
.vdu_id
= vm_info
['id']
1248 for network_name
, network_info
in vm_info
['addresses'].items():
1249 if network_info
and network_name
== mgmt_network
or network_name
== overridden_mgmt_network
:
1250 for interface
in network_info
:
1251 for interface
in network_info
:
1252 if 'OS-EXT-IPS:type' in interface
:
1253 if interface
['OS-EXT-IPS:type'] == 'fixed':
1254 vdu
.management_ip
= interface
['addr']
1255 elif interface
['OS-EXT-IPS:type'] == 'floating':
1256 vdu
.public_ip
= interface
['addr']
1258 # Look for any metadata
1259 for key
, value
in vm_info
['metadata'].items():
1260 if key
== 'node_id':
1262 if ('image' in vm_info
) and ('id' in vm_info
['image']):
1263 vdu
.image_id
= vm_info
['image']['id']
1264 if ('flavor' in vm_info
) and ('id' in vm_info
['flavor']):
1265 vdu
.flavor_id
= vm_info
['flavor']['id']
1267 if vm_info
['status'] == 'ACTIVE':
1268 vdu
.state
= 'active'
1269 elif vm_info
['status'] == 'ERROR':
1270 vdu
.state
= 'failed'
1272 vdu
.state
= 'inactive'
1274 if 'availability_zone' in vm_info
:
1275 vdu
.availability_zone
= vm_info
['availability_zone']
1278 vdu
.server_group
.name
= server_group
[0]
1280 vdu
.cloud_type
= 'openstack'
1281 # Fill the port information
1282 for port
in port_list
:
1283 c_point
= vdu
.connection_points
.add()
1284 RwcalOpenstackPlugin
._fill
_connection
_point
_info
(c_point
, port
)
1286 if flavor_info
is not None:
1287 RwcalOpenstackPlugin
._fill
_epa
_attributes
(vdu
, flavor_info
)
1289 # Fill the volume information
1290 if volume_list
is not None:
1291 for os_volume
in volume_list
:
1292 volr
= vdu
.volumes
.add()
1294 " Device name is of format /dev/vda"
1295 vol_name
= (os_volume
['device']).split('/')[2]
1298 volr
.name
= vol_name
1299 volr
.volume_id
= os_volume
['volumeId']
1303 @rwcalstatus(ret_on_failure
=[""])
1304 def do_create_virtual_link(self
, account
, link_params
):
1305 """Create a new virtual link
1308 account - a cloud account
1309 link_params - information that defines the type of VDU to create
1315 kwargs
['name'] = link_params
.name
1316 kwargs
['admin_state_up'] = True
1317 kwargs
['external_router'] = False
1318 kwargs
['shared'] = False
1320 if link_params
.has_field('provider_network'):
1321 if link_params
.provider_network
.has_field('physical_network'):
1322 kwargs
['physical_network'] = link_params
.provider_network
.physical_network
1323 if link_params
.provider_network
.has_field('overlay_type'):
1324 kwargs
['network_type'] = link_params
.provider_network
.overlay_type
.lower()
1325 if link_params
.provider_network
.has_field('segmentation_id'):
1326 kwargs
['segmentation_id'] = link_params
.provider_network
.segmentation_id
1329 with self
._use
_driver
(account
) as drv
:
1331 network_id
= drv
.neutron_network_create(**kwargs
)
1332 except Exception as e
:
1333 self
.log
.error("Encountered exceptions during network creation. Exception: %s", str(e
))
1336 kwargs
= {'network_id' : network_id
,
1337 'dhcp_params': {'enable_dhcp': True},
1338 'gateway_ip' : None,}
1340 if link_params
.ip_profile_params
.has_field('ip_version'):
1341 kwargs
['ip_version'] = 6 if link_params
.ip_profile_params
.ip_version
== 'ipv6' else 4
1343 kwargs
['ip_version'] = 4
1345 if link_params
.ip_profile_params
.has_field('subnet_address'):
1346 kwargs
['cidr'] = link_params
.ip_profile_params
.subnet_address
1347 elif link_params
.ip_profile_params
.has_field('subnet_prefix_pool'):
1348 subnet_pool
= drv
.netruon_subnetpool_by_name(link_params
.ip_profile_params
.subnet_prefix_pool
)
1349 if subnet_pool
is None:
1350 self
.log
.error("Could not find subnet pool with name :%s to be used for network: %s",
1351 link_params
.ip_profile_params
.subnet_prefix_pool
,
1353 raise NeutronException
.NotFound("SubnetPool with name %s not found"%(link_params
.ip_profile_params
.subnet_prefix_pool
))
1355 kwargs
['subnetpool_id'] = subnet_pool
['id']
1356 elif link_params
.has_field('subnet'):
1357 kwargs
['cidr'] = link_params
.subnet
1359 assert 0, "No IP Prefix or Pool name specified"
1361 if link_params
.ip_profile_params
.has_field('dhcp_params'):
1362 if link_params
.ip_profile_params
.dhcp_params
.has_field('enabled'):
1363 kwargs
['dhcp_params']['enable_dhcp'] = link_params
.ip_profile_params
.dhcp_params
.enabled
1364 if link_params
.ip_profile_params
.dhcp_params
.has_field('start_address'):
1365 kwargs
['dhcp_params']['start_address'] = link_params
.ip_profile_params
.dhcp_params
.start_address
1366 if link_params
.ip_profile_params
.dhcp_params
.has_field('count'):
1367 kwargs
['dhcp_params']['count'] = link_params
.ip_profile_params
.dhcp_params
.count
1369 if link_params
.ip_profile_params
.has_field('dns_server'):
1370 kwargs
['dns_server'] = []
1371 for server
in link_params
.ip_profile_params
.dns_server
:
1372 kwargs
['dns_server'].append(server
.address
)
1374 if link_params
.ip_profile_params
.has_field('gateway_address'):
1375 kwargs
['gateway_ip'] = link_params
.ip_profile_params
.gateway_address
1377 drv
.neutron_subnet_create(**kwargs
)
1383 def do_delete_virtual_link(self
, account
, link_id
):
1384 """Delete a virtual link
1387 account - a cloud account
1388 link_id - id for the virtual-link to be deleted
1394 self
.log
.error("Empty link_id during the virtual link deletion")
1395 raise Exception("Empty link_id during the virtual link deletion")
1397 with self
._use
_driver
(account
) as drv
:
1398 port_list
= drv
.neutron_port_list(**{'network_id': link_id
})
1400 for port
in port_list
:
1401 if ((port
['device_owner'] == 'compute:None') or (port
['device_owner'] == '')):
1402 self
.do_delete_port(account
, port
['id'], no_rwstatus
=True)
1403 self
.do_delete_network(account
, link_id
, no_rwstatus
=True)
1405 @rwstatus(ret_on_failure
=[None])
1406 def do_get_virtual_link(self
, account
, link_id
):
1407 """Get information about virtual link.
1410 account - a cloud account
1411 link_id - id for the virtual-link
1414 Object of type RwcalYang.VirtualLinkInfoParams
1417 self
.log
.error("Empty link_id during the virtual link get request")
1418 raise Exception("Empty link_id during the virtual link get request")
1420 with self
._use
_driver
(account
) as drv
:
1421 network
= drv
.neutron_network_get(link_id
)
1423 port_list
= drv
.neutron_port_list(**{'network_id': network
['id']})
1424 if 'subnets' in network
:
1425 subnet
= drv
.neutron_subnet_get(network
['subnets'][0])
1428 virtual_link
= RwcalOpenstackPlugin
._fill
_virtual
_link
_info
(network
, port_list
, subnet
)
1433 @rwstatus(ret_on_failure
=[None])
1434 def do_get_virtual_link_list(self
, account
):
1435 """Get information about all the virtual links
1438 account - a cloud account
1441 A list of objects of type RwcalYang.VirtualLinkInfoParams
1443 vnf_resources
= RwcalYang
.VNFResources()
1444 with self
._use
_driver
(account
) as drv
:
1445 networks
= drv
.neutron_network_list()
1446 for network
in networks
:
1447 port_list
= drv
.neutron_port_list(**{'network_id': network
['id']})
1448 if ('subnets' in network
) and (network
['subnets']):
1449 subnet
= drv
.neutron_subnet_get(network
['subnets'][0])
1452 virtual_link
= RwcalOpenstackPlugin
._fill
_virtual
_link
_info
(network
, port_list
, subnet
)
1453 vnf_resources
.virtual_link_info_list
.append(virtual_link
)
1454 return vnf_resources
1456 def _create_connection_point(self
, account
, c_point
):
1458 Create a connection point
1460 account - a cloud account
1461 c_point - connection_points
1465 kwargs
['name'] = c_point
.name
1466 kwargs
['network_id'] = c_point
.virtual_link_id
1467 kwargs
['admin_state_up'] = True
1469 if c_point
.type_yang
== 'VIRTIO' or c_point
.type_yang
== 'E1000':
1470 kwargs
['port_type'] = 'normal'
1471 elif c_point
.type_yang
== 'SR_IOV':
1472 kwargs
['port_type'] = 'direct'
1474 raise NotImplementedError("Port Type: %s not supported" %(c_point
.type_yang
))
1476 if c_point
.static_ip_address
:
1477 kwargs
["ip_address"] = c_point
.static_ip_address
1479 with self
._use
_driver
(account
) as drv
:
1480 if c_point
.has_field('security_group'):
1481 group
= drv
.neutron_security_group_by_name(c_point
.security_group
)
1482 if group
is not None:
1483 kwargs
['security_groups'] = [group
['id']]
1484 self
.log
.debug("Create connection point port : {}".
1486 return drv
.neutron_port_create(**kwargs
)
1488 def _allocate_floating_ip(self
, drv
, pool_name
):
1490 Allocate a floating_ip. If unused floating_ip exists then its reused.
1492 drv: OpenstackDriver instance
1493 pool_name: Floating IP pool name
1496 An object of floating IP nova class (novaclient.v2.floating_ips.FloatingIP)
1499 # available_ip = [ ip for ip in drv.nova_floating_ip_list() if ip.instance_id == None ]
1501 # if pool_name is not None:
1502 # ### Filter further based on IP address
1503 # available_ip = [ ip for ip in available_ip if ip.pool == pool_name ]
1505 # if not available_ip:
1506 # floating_ip = drv.nova_floating_ip_create(pool_name)
1508 # floating_ip = available_ip[0]
1510 floating_ip
= drv
.nova_floating_ip_create(pool_name
)
1513 def _match_vm_flavor(self
, required
, available
):
1514 self
.log
.info("Matching VM Flavor attributes")
1515 if available
.vcpu_count
!= required
.vcpu_count
:
1516 self
.log
.debug("VCPU requirement mismatch. Required: %d, Available: %d",
1517 required
.vcpu_count
,
1518 available
.vcpu_count
)
1520 if available
.memory_mb
!= required
.memory_mb
:
1521 self
.log
.debug("Memory requirement mismatch. Required: %d MB, Available: %d MB",
1523 available
.memory_mb
)
1525 if available
.storage_gb
!= required
.storage_gb
:
1526 self
.log
.debug("Storage requirement mismatch. Required: %d GB, Available: %d GB",
1527 required
.storage_gb
,
1528 available
.storage_gb
)
1530 self
.log
.debug("VM Flavor match found")
1533 def _match_guest_epa(self
, required
, available
):
1534 self
.log
.info("Matching Guest EPA attributes")
1535 if required
.has_field('pcie_device'):
1536 self
.log
.debug("Matching pcie_device")
1537 if available
.has_field('pcie_device') == False:
1538 self
.log
.debug("Matching pcie_device failed. Not available in flavor")
1541 for dev
in required
.pcie_device
:
1542 if not [ d
for d
in available
.pcie_device
1543 if ((d
.device_id
== dev
.device_id
) and (d
.count
== dev
.count
)) ]:
1544 self
.log
.debug("Matching pcie_device failed. Required: %s, Available: %s", required
.pcie_device
, available
.pcie_device
)
1546 elif available
.has_field('pcie_device'):
1547 self
.log
.debug("Rejecting available flavor because pcie_device not required but available")
1551 if required
.has_field('mempage_size'):
1552 self
.log
.debug("Matching mempage_size")
1553 if available
.has_field('mempage_size') == False:
1554 self
.log
.debug("Matching mempage_size failed. Not available in flavor")
1557 if required
.mempage_size
!= available
.mempage_size
:
1558 self
.log
.debug("Matching mempage_size failed. Required: %s, Available: %s", required
.mempage_size
, available
.mempage_size
)
1560 elif available
.has_field('mempage_size'):
1561 self
.log
.debug("Rejecting available flavor because mempage_size not required but available")
1564 if required
.has_field('cpu_pinning_policy'):
1565 self
.log
.debug("Matching cpu_pinning_policy")
1566 if required
.cpu_pinning_policy
!= 'ANY':
1567 if available
.has_field('cpu_pinning_policy') == False:
1568 self
.log
.debug("Matching cpu_pinning_policy failed. Not available in flavor")
1571 if required
.cpu_pinning_policy
!= available
.cpu_pinning_policy
:
1572 self
.log
.debug("Matching cpu_pinning_policy failed. Required: %s, Available: %s", required
.cpu_pinning_policy
, available
.cpu_pinning_policy
)
1574 elif available
.has_field('cpu_pinning_policy'):
1575 self
.log
.debug("Rejecting available flavor because cpu_pinning_policy not required but available")
1578 if required
.has_field('cpu_thread_pinning_policy'):
1579 self
.log
.debug("Matching cpu_thread_pinning_policy")
1580 if available
.has_field('cpu_thread_pinning_policy') == False:
1581 self
.log
.debug("Matching cpu_thread_pinning_policy failed. Not available in flavor")
1584 if required
.cpu_thread_pinning_policy
!= available
.cpu_thread_pinning_policy
:
1585 self
.log
.debug("Matching cpu_thread_pinning_policy failed. Required: %s, Available: %s", required
.cpu_thread_pinning_policy
, available
.cpu_thread_pinning_policy
)
1587 elif available
.has_field('cpu_thread_pinning_policy'):
1588 self
.log
.debug("Rejecting available flavor because cpu_thread_pinning_policy not required but available")
1591 if required
.has_field('trusted_execution'):
1592 self
.log
.debug("Matching trusted_execution")
1593 if required
.trusted_execution
== True:
1594 if available
.has_field('trusted_execution') == False:
1595 self
.log
.debug("Matching trusted_execution failed. Not available in flavor")
1598 if required
.trusted_execution
!= available
.trusted_execution
:
1599 self
.log
.debug("Matching trusted_execution failed. Required: %s, Available: %s", required
.trusted_execution
, available
.trusted_execution
)
1601 elif available
.has_field('trusted_execution'):
1602 self
.log
.debug("Rejecting available flavor because trusted_execution not required but available")
1605 if required
.has_field('numa_node_policy'):
1606 self
.log
.debug("Matching numa_node_policy")
1607 if available
.has_field('numa_node_policy') == False:
1608 self
.log
.debug("Matching numa_node_policy failed. Not available in flavor")
1611 if required
.numa_node_policy
.has_field('node_cnt'):
1612 self
.log
.debug("Matching numa_node_policy node_cnt")
1613 if available
.numa_node_policy
.has_field('node_cnt') == False:
1614 self
.log
.debug("Matching numa_node_policy node_cnt failed. Not available in flavor")
1617 if required
.numa_node_policy
.node_cnt
!= available
.numa_node_policy
.node_cnt
:
1618 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
)
1620 elif available
.numa_node_policy
.has_field('node_cnt'):
1621 self
.log
.debug("Rejecting available flavor because numa node count not required but available")
1624 if required
.numa_node_policy
.has_field('mem_policy'):
1625 self
.log
.debug("Matching numa_node_policy mem_policy")
1626 if available
.numa_node_policy
.has_field('mem_policy') == False:
1627 self
.log
.debug("Matching numa_node_policy mem_policy failed. Not available in flavor")
1630 if required
.numa_node_policy
.mem_policy
!= available
.numa_node_policy
.mem_policy
:
1631 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
)
1633 elif available
.numa_node_policy
.has_field('mem_policy'):
1634 self
.log
.debug("Rejecting available flavor because num node mem_policy not required but available")
1637 if required
.numa_node_policy
.has_field('node'):
1638 self
.log
.debug("Matching numa_node_policy nodes configuration")
1639 if available
.numa_node_policy
.has_field('node') == False:
1640 self
.log
.debug("Matching numa_node_policy nodes configuration failed. Not available in flavor")
1642 for required_node
in required
.numa_node_policy
.node
:
1643 self
.log
.debug("Matching numa_node_policy nodes configuration for node %s", required_node
)
1645 for available_node
in available
.numa_node_policy
.node
:
1646 if required_node
.id != available_node
.id:
1647 self
.log
.debug("Matching numa_node_policy nodes configuration failed. Required: %s, Available: %s", required_node
, available_node
)
1649 if required_node
.vcpu
!= available_node
.vcpu
:
1650 self
.log
.debug("Matching numa_node_policy nodes configuration failed. Required: %s, Available: %s", required_node
, available_node
)
1652 if required_node
.memory_mb
!= available_node
.memory_mb
:
1653 self
.log
.debug("Matching numa_node_policy nodes configuration failed. Required: %s, Available: %s", required_node
, available_node
)
1656 if numa_match
== False:
1658 elif available
.numa_node_policy
.has_field('node'):
1659 self
.log
.debug("Rejecting available flavor because numa nodes not required but available")
1661 elif available
.has_field('numa_node_policy'):
1662 self
.log
.debug("Rejecting available flavor because numa_node_policy not required but available")
1664 self
.log
.info("Successful match for Guest EPA attributes")
1667 def _match_vswitch_epa(self
, required
, available
):
1668 self
.log
.debug("VSwitch EPA match found")
1671 def _match_hypervisor_epa(self
, required
, available
):
1672 self
.log
.debug("Hypervisor EPA match found")
1675 def _match_host_epa(self
, required
, available
):
1676 self
.log
.info("Matching Host EPA attributes")
1677 if required
.has_field('cpu_model'):
1678 self
.log
.debug("Matching CPU model")
1679 if available
.has_field('cpu_model') == False:
1680 self
.log
.debug("Matching CPU model failed. Not available in flavor")
1683 #### Convert all PREFER to REQUIRE since flavor will only have REQUIRE attributes
1684 if required
.cpu_model
.replace('PREFER', 'REQUIRE') != available
.cpu_model
:
1685 self
.log
.debug("Matching CPU model failed. Required: %s, Available: %s", required
.cpu_model
, available
.cpu_model
)
1687 elif available
.has_field('cpu_model'):
1688 self
.log
.debug("Rejecting available flavor because cpu_model not required but available")
1691 if required
.has_field('cpu_arch'):
1692 self
.log
.debug("Matching CPU architecture")
1693 if available
.has_field('cpu_arch') == False:
1694 self
.log
.debug("Matching CPU architecture failed. Not available in flavor")
1697 #### Convert all PREFER to REQUIRE since flavor will only have REQUIRE attributes
1698 if required
.cpu_arch
.replace('PREFER', 'REQUIRE') != available
.cpu_arch
:
1699 self
.log
.debug("Matching CPU architecture failed. Required: %s, Available: %s", required
.cpu_arch
, available
.cpu_arch
)
1701 elif available
.has_field('cpu_arch'):
1702 self
.log
.debug("Rejecting available flavor because cpu_arch not required but available")
1705 if required
.has_field('cpu_vendor'):
1706 self
.log
.debug("Matching CPU vendor")
1707 if available
.has_field('cpu_vendor') == False:
1708 self
.log
.debug("Matching CPU vendor failed. Not available in flavor")
1711 #### Convert all PREFER to REQUIRE since flavor will only have REQUIRE attributes
1712 if required
.cpu_vendor
.replace('PREFER', 'REQUIRE') != available
.cpu_vendor
:
1713 self
.log
.debug("Matching CPU vendor failed. Required: %s, Available: %s", required
.cpu_vendor
, available
.cpu_vendor
)
1715 elif available
.has_field('cpu_vendor'):
1716 self
.log
.debug("Rejecting available flavor because cpu_vendor not required but available")
1719 if required
.has_field('cpu_socket_count'):
1720 self
.log
.debug("Matching CPU socket count")
1721 if available
.has_field('cpu_socket_count') == False:
1722 self
.log
.debug("Matching CPU socket count failed. Not available in flavor")
1725 if required
.cpu_socket_count
!= available
.cpu_socket_count
:
1726 self
.log
.debug("Matching CPU socket count failed. Required: %s, Available: %s", required
.cpu_socket_count
, available
.cpu_socket_count
)
1728 elif available
.has_field('cpu_socket_count'):
1729 self
.log
.debug("Rejecting available flavor because cpu_socket_count not required but available")
1732 if required
.has_field('cpu_core_count'):
1733 self
.log
.debug("Matching CPU core count")
1734 if available
.has_field('cpu_core_count') == False:
1735 self
.log
.debug("Matching CPU core count failed. Not available in flavor")
1738 if required
.cpu_core_count
!= available
.cpu_core_count
:
1739 self
.log
.debug("Matching CPU core count failed. Required: %s, Available: %s", required
.cpu_core_count
, available
.cpu_core_count
)
1741 elif available
.has_field('cpu_core_count'):
1742 self
.log
.debug("Rejecting available flavor because cpu_core_count not required but available")
1745 if required
.has_field('cpu_core_thread_count'):
1746 self
.log
.debug("Matching CPU core thread count")
1747 if available
.has_field('cpu_core_thread_count') == False:
1748 self
.log
.debug("Matching CPU core thread count failed. Not available in flavor")
1751 if required
.cpu_core_thread_count
!= available
.cpu_core_thread_count
:
1752 self
.log
.debug("Matching CPU core thread count failed. Required: %s, Available: %s", required
.cpu_core_thread_count
, available
.cpu_core_thread_count
)
1754 elif available
.has_field('cpu_core_thread_count'):
1755 self
.log
.debug("Rejecting available flavor because cpu_core_thread_count not required but available")
1758 if required
.has_field('cpu_feature'):
1759 self
.log
.debug("Matching CPU feature list")
1760 if available
.has_field('cpu_feature') == False:
1761 self
.log
.debug("Matching CPU feature list failed. Not available in flavor")
1764 for feature
in required
.cpu_feature
:
1765 if feature
not in available
.cpu_feature
:
1766 self
.log
.debug("Matching CPU feature list failed. Required feature: %s is not present. Available features: %s", feature
, available
.cpu_feature
)
1768 elif available
.has_field('cpu_feature'):
1769 self
.log
.debug("Rejecting available flavor because cpu_feature not required but available")
1771 self
.log
.info("Successful match for Host EPA attributes")
1775 def _match_placement_group_inputs(self
, required
, available
):
1776 self
.log
.info("Matching Host aggregate attributes")
1778 if not required
and not available
:
1779 # Host aggregate not required and not available => success
1780 self
.log
.info("Successful match for Host Aggregate attributes")
1782 if required
and available
:
1783 # Host aggregate requested and available => Do a match and decide
1784 xx
= [ x
.as_dict() for x
in required
]
1785 yy
= [ y
.as_dict() for y
in available
]
1788 self
.log
.debug("Rejecting available flavor because host Aggregate mismatch. Required: %s, Available: %s ", required
, available
)
1790 self
.log
.info("Successful match for Host Aggregate attributes")
1793 # Either of following conditions => Failure
1794 # - Host aggregate required but not available
1795 # - Host aggregate not required but available
1796 self
.log
.debug("Rejecting available flavor because host Aggregate mismatch. Required: %s, Available: %s ", required
, available
)
1799 def match_epa_params(self
, resource_info
, request_params
):
1800 result
= self
._match
_vm
_flavor
(getattr(request_params
, 'vm_flavor'),
1801 getattr(resource_info
, 'vm_flavor'))
1803 self
.log
.debug("VM Flavor mismatched")
1806 result
= self
._match
_guest
_epa
(getattr(request_params
, 'guest_epa'),
1807 getattr(resource_info
, 'guest_epa'))
1809 self
.log
.debug("Guest EPA mismatched")
1812 result
= self
._match
_vswitch
_epa
(getattr(request_params
, 'vswitch_epa'),
1813 getattr(resource_info
, 'vswitch_epa'))
1815 self
.log
.debug("Vswitch EPA mismatched")
1818 result
= self
._match
_hypervisor
_epa
(getattr(request_params
, 'hypervisor_epa'),
1819 getattr(resource_info
, 'hypervisor_epa'))
1821 self
.log
.debug("Hypervisor EPA mismatched")
1824 result
= self
._match
_host
_epa
(getattr(request_params
, 'host_epa'),
1825 getattr(resource_info
, 'host_epa'))
1827 self
.log
.debug("Host EPA mismatched")
1830 result
= self
._match
_placement
_group
_inputs
(getattr(request_params
, 'host_aggregate'),
1831 getattr(resource_info
, 'host_aggregate'))
1834 self
.log
.debug("Host Aggregate mismatched")
1839 def _select_resource_flavor(self
, account
, vdu_init
):
1841 Select a existing flavor if it matches the request or create new flavor
1843 flavor
= RwcalYang
.FlavorInfoItem()
1844 flavor
.name
= str(uuid
.uuid4())
1845 epa_types
= ['vm_flavor', 'guest_epa', 'host_epa', 'host_aggregate', 'hypervisor_epa', 'vswitch_epa']
1846 epa_dict
= {k
: v
for k
, v
in vdu_init
.as_dict().items() if k
in epa_types
}
1847 flavor
.from_dict(epa_dict
)
1849 rc
, response
= self
.do_get_flavor_list(account
)
1850 if rc
!= RwTypes
.RwStatus
.SUCCESS
:
1851 self
.log
.error("Get-flavor-info-list operation failed for cloud account: %s",
1853 raise OpenstackCALOperationFailure("Get-flavor-info-list operation failed for cloud account: %s" %(account
.name
))
1856 flavor_list
= response
.flavorinfo_list
1857 self
.log
.debug("Received %d flavor information from RW.CAL", len(flavor_list
))
1858 for flv
in flavor_list
:
1859 self
.log
.info("Attempting to match compute requirement for VDU: %s with flavor %s",
1861 if self
.match_epa_params(flv
, vdu_init
):
1862 self
.log
.info("Flavor match found for compute requirements for VDU: %s with flavor name: %s, flavor-id: %s",
1863 vdu_init
.name
, flv
.name
, flv
.id)
1866 if account
.openstack
.dynamic_flavor_support
is False:
1867 self
.log
.error("Unable to create flavor for compute requirement for VDU: %s. VDU instantiation failed", vdu_init
.name
)
1868 raise OpenstackCALOperationFailure("No resource available with matching EPA attributes")
1870 rc
,flavor_id
= self
.do_create_flavor(account
,flavor
)
1871 if rc
!= RwTypes
.RwStatus
.SUCCESS
:
1872 self
.log
.error("Create-flavor operation failed for cloud account: %s",
1874 raise OpenstackCALOperationFailure("Create-flavor operation failed for cloud account: %s" %(account
.name
))
1877 def _create_vm(self
, account
, vduinfo
, pci_assignement
=None, server_group
=None, port_list
=None, network_list
=None, imageinfo_list
=None, mgmt_network
= None):
1878 """Create a new virtual machine.
1881 account - a cloud account
1882 vminfo - information that defines the type of VM to create
1888 kwargs
['name'] = vduinfo
.name
1889 kwargs
['flavor_id'] = vduinfo
.flavor_id
1890 if vduinfo
.has_field('image_id'):
1891 kwargs
['image_id'] = vduinfo
.image_id
1893 kwargs
['image_id'] = ""
1895 with self
._use
_driver
(account
) as drv
:
1896 ### If floating_ip is required and we don't have one, better fail before any further allocation
1897 if vduinfo
.has_field('allocate_public_address') and vduinfo
.allocate_public_address
:
1898 if account
.openstack
.has_field('floating_ip_pool'):
1899 pool_name
= account
.openstack
.floating_ip_pool
1902 floating_ip
= self
._allocate
_floating
_ip
(drv
, pool_name
)
1906 if vduinfo
.has_field('vdu_init') and vduinfo
.vdu_init
.has_field('userdata'):
1907 kwargs
['userdata'] = vduinfo
.vdu_init
.userdata
1909 kwargs
['userdata'] = ''
1911 if account
.openstack
.security_groups
:
1912 kwargs
['security_groups'] = account
.openstack
.security_groups
1914 kwargs
['port_list'] = port_list
1915 kwargs
['network_list'] = network_list
1918 # Add all metadata related fields
1919 if vduinfo
.has_field('node_id'):
1920 metadata
['node_id'] = vduinfo
.node_id
1921 if pci_assignement
is not None:
1922 metadata
['pci_assignement'] = pci_assignement
1923 kwargs
['metadata'] = metadata
1925 if vduinfo
.has_field('availability_zone') and vduinfo
.availability_zone
.has_field('name'):
1926 kwargs
['availability_zone'] = vduinfo
.availability_zone
1928 kwargs
['availability_zone'] = None
1930 if server_group
is not None:
1931 kwargs
['scheduler_hints'] = {'group': server_group
}
1933 kwargs
['scheduler_hints'] = None
1935 kwargs
['block_device_mapping_v2'] = None
1936 if vduinfo
.has_field('volumes') :
1937 kwargs
['block_device_mapping_v2'] = []
1938 with self
._use
_driver
(account
) as drv
:
1939 # Only support image->volume
1940 for volume
in vduinfo
.volumes
:
1942 block_map
['boot_index'] = volume
.boot_params
.boot_priority
1943 if "image" in volume
:
1944 # Support image->volume
1945 # Match retrived image info with volume based image name and checksum
1946 if volume
.image
is not None:
1947 matching_images
= [img
for img
in imageinfo_list
if img
['name'] == volume
.image
]
1948 if volume
.image_checksum
is not None:
1949 matching_images
= [img
for img
in matching_images
if img
['checksum'] == volume
.image_checksum
]
1950 img_id
= matching_images
[0]['id']
1952 raise OpenstackCALOperationFailure("Create-vdu operation failed. Volume image not found for name {} checksum {}".format(volume
.name
, volume
.checksum
))
1953 block_map
['uuid'] = img_id
1954 block_map
['source_type'] = "image"
1956 block_map
['source_type'] = "blank"
1958 block_map
['device_name'] = volume
.name
1959 block_map
['destination_type'] = "volume"
1960 block_map
['volume_size'] = volume
.size
1961 block_map
['delete_on_termination'] = True
1962 if volume
.guest_params
.has_field('device_type') and volume
.guest_params
.device_type
== 'cdrom':
1963 block_map
['device_type'] = 'cdrom'
1964 if volume
.guest_params
.has_field('device_bus') and volume
.guest_params
.device_bus
== 'ide':
1965 block_map
['disk_bus'] = 'ide'
1966 kwargs
['block_device_mapping_v2'].append(block_map
)
1969 with self
._use
_driver
(account
) as drv
:
1970 vm_id
= drv
.nova_server_create(**kwargs
)
1972 self
.prepare_vdu_on_boot(account
, vm_id
, floating_ip
, mgmt_network
)
1976 def get_openstack_image_info(self
, account
, image_name
, image_checksum
=None):
1977 self
.log
.debug("Looking up image id for image name %s and checksum %s on cloud account: %s",
1978 image_name
, image_checksum
, account
.name
1982 with self
._use
_driver
(account
) as drv
:
1983 image_list
= drv
.glance_image_list()
1984 matching_images
= [img
for img
in image_list
if img
['name'] == image_name
]
1986 # If the image checksum was filled in then further filter the images by the checksum
1987 if image_checksum
is not None:
1988 matching_images
= [img
for img
in matching_images
if img
['checksum'] == image_checksum
]
1990 self
.log
.warning("Image checksum not provided. Lookup using image name (%s) only.",
1993 if len(matching_images
) == 0:
1994 raise ResMgrCALOperationFailure("Could not find image name {} (using checksum: {}) for cloud account: {}".format(
1995 image_name
, image_checksum
, account
.name
1998 elif len(matching_images
) > 1:
1999 unique_checksums
= {i
.checksum
for i
in matching_images
}
2000 if len(unique_checksums
) > 1:
2001 msg
= ("Too many images with different checksums matched "
2002 "image name of %s for cloud account: %s" % (image_name
, account
.name
))
2003 raise ResMgrCALOperationFailure(msg
)
2005 return matching_images
[0]
2007 @rwcalstatus(ret_on_failure
=[""])
2008 def do_create_vdu(self
, account
, vdu_init
):
2009 """Create a new virtual deployment unit
2012 account - a cloud account
2013 vdu_init - information about VDU to create (RwcalYang.VDUInitParams)
2018 ### First create required number of ports aka connection points
2019 # Add the mgmt_ntwk by default.
2021 mgmt_network_id
= None
2022 with self
._use
_driver
(account
) as drv
:
2023 mgmt_network_id
= drv
._mgmt
_network
_id
2024 ### If floating_ip is required and we don't have one, better fail before any further allocation
2025 if vdu_init
.has_field('allocate_public_address') and vdu_init
.allocate_public_address
:
2026 if account
.openstack
.has_field('floating_ip_pool'):
2027 pool_name
= account
.openstack
.floating_ip_pool
2030 floating_ip
= self
._allocate
_floating
_ip
(drv
, pool_name
)
2036 explicit_mgmt_network
= None
2037 if vdu_init
.get_mgmt_network() is not None:
2038 explicit_mgmt_network
= vdu_init
.get_mgmt_network()
2040 for c_point
in reversed(vdu_init
.connection_points
):
2041 # if the user has specified explicit mgmt_network connection point
2042 # then remove the mgmt_network from the VM list
2043 if c_point
.virtual_link_id
in network_list
:
2044 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"
2046 network_list
.append(c_point
.virtual_link_id
)
2047 port_id
= self
._create
_connection
_point
(account
, c_point
)
2048 port_list
.append(port_id
)
2050 if not vdu_init
.has_field('flavor_id'):
2051 vdu_init
.flavor_id
= self
._select
_resource
_flavor
(account
,vdu_init
)
2053 ### Obtain all images for volumes and perform validations
2054 if vdu_init
.has_field('volumes'):
2055 for volume
in vdu_init
.volumes
:
2056 if "image" in volume
:
2057 image_checksum
= volume
.image_checksum
if volume
.has_field("image_checksum") else None
2058 image_info
= self
.get_openstack_image_info(account
, volume
.image
, image_checksum
)
2059 imageinfo_list
.append(image_info
)
2060 elif vdu_init
.has_field('image_id'):
2061 with self
._use
_driver
(account
) as drv
:
2062 image_info
= drv
.glance_image_get(vdu_init
.image_id
)
2063 imageinfo_list
.append(image_info
)
2065 if not imageinfo_list
:
2066 err_str
= ("VDU has no image information")
2067 self
.log
.error(err_str
)
2068 raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str
)
2070 ### Check VDU Virtual Interface type and make sure VM with property exists
2071 if vdu_init
.connection_points
:
2072 ### All virtual interfaces need to be of the same type for Openstack Accounts
2073 if not (all(cp
.type_yang
== 'E1000' for cp
in vdu_init
.connection_points
) or all(cp
.type_yang
!= 'E1000' for cp
in vdu_init
.connection_points
)):
2074 ### We have a mix of E1000 & VIRTIO/SR_IPOV virtual interface types in the VDU, abort instantiation.
2075 assert False, "Only one type of Virtual Intefaces supported for Openstack accounts. Found a mix of VIRTIO/SR_IOV & E1000."
2077 ## It is not clear if all the images need to checked for HW properties. In the absence of model info describing each im age's properties,
2078 ### we shall assume that all images need to have similar properties
2079 for img_info
in imageinfo_list
:
2081 virt_intf_type
= vdu_init
.connection_points
[0].type_yang
2082 if virt_intf_type
== 'E1000':
2083 if 'hw_vif_model' in img_info
and img_info
.hw_vif_model
== 'e1000':
2084 self
.log
.debug("VDU has Virtual Interface E1000, found matching image with property hw_vif_model=e1000")
2086 err_str
= ("VDU has Virtual Interface E1000, but image '%s' does not have property hw_vif_model=e1000" % img_info
.name
)
2087 self
.log
.error(err_str
)
2088 raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str
)
2089 elif virt_intf_type
== 'VIRTIO' or virt_intf_type
== 'SR_IOV':
2090 if 'hw_vif_model' in img_info
:
2091 err_str
= ("VDU has Virtual Interface %s, but image '%s' has hw_vif_model mismatch" % virt_intf_type
,img_info
.name
)
2092 self
.log
.error(err_str
)
2093 raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str
)
2095 self
.log
.debug("VDU has Virtual Interface %s, found matching image" % virt_intf_type
)
2097 err_str
= ("VDU Virtual Interface '%s' not supported yet" % virt_intf_type
)
2098 self
.log
.error(err_str
)
2099 raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str
)
2101 with self
._use
_driver
(account
) as drv
:
2103 vm_network_list
= []
2104 if explicit_mgmt_network
is None:
2105 vm_network_list
.append(drv
._mgmt
_network
_id
)
2107 if vdu_init
.has_field('volumes'):
2108 # Only combination supported: Image->Volume
2109 for volume
in vdu_init
.volumes
:
2110 if "volume" in volume
:
2111 err_str
= ("VDU Volume source not supported yet")
2112 self
.log
.error(err_str
)
2113 raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str
)
2114 if "guest_params" not in volume
:
2115 err_str
= ("VDU Volume destination parameters '%s' not defined")
2116 self
.log
.error(err_str
)
2117 raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str
)
2118 if not volume
.guest_params
.has_field('device_type'):
2119 err_str
= ("VDU Volume destination type '%s' not defined")
2120 self
.log
.error(err_str
)
2121 raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str
)
2122 if volume
.guest_params
.device_type
not in ['disk', 'cdrom'] :
2123 err_str
= ("VDU Volume destination type '%s' not supported" % volume
.guest_params
.device_type
)
2124 self
.log
.error(err_str
)
2125 raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str
)
2129 if vdu_init
.has_field('server_group'):
2130 ### Get list of server group in openstack for name->id mapping
2131 openstack_group_list
= drv
.nova_server_group_list()
2132 group_id
= [ i
['id'] for i
in openstack_group_list
if i
['name'] == vdu_init
.server_group
.name
]
2133 if len(group_id
) != 1:
2134 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
]))
2135 server_group
= group_id
[0]
2137 pci_assignement
= self
.prepare_vpci_metadata(drv
, vdu_init
)
2138 if pci_assignement
!= '':
2139 vm
.user_tags
.pci_assignement
= pci_assignement
2141 vm_id
= self
._create
_vm
(account
, vdu_init
, pci_assignement
=pci_assignement
, server_group
=server_group
, port_list
=port_list
, network_list
=vm_network_list
, imageinfo_list
= imageinfo_list
, mgmt_network
= explicit_mgmt_network
)
2144 def prepare_vpci_metadata(self
, drv
, vdu_init
):
2145 pci_assignement
= ''
2146 ### TEF specific metadata creation for
2151 ### For MGMT interface
2152 if vdu_init
.has_field('mgmt_vpci'):
2153 xx
= 'u\''+ drv
._mgmt
_network
_id
+ '\' :[[u\'' + vdu_init
.mgmt_vpci
+ '\', ' + '\'\']]'
2154 virtio_vpci
.append(xx
)
2156 for c_point
in vdu_init
.connection_points
:
2157 if c_point
.has_field('vpci'):
2158 if c_point
.has_field('vpci') and c_point
.type_yang
== 'VIRTIO':
2159 xx
= 'u\''+c_point
.virtual_link_id
+ '\' :[[u\'' + c_point
.vpci
+ '\', ' + '\'\']]'
2160 virtio_vpci
.append(xx
)
2161 elif c_point
.has_field('vpci') and c_point
.type_yang
== 'SR_IOV':
2162 xx
= '[u\'' + c_point
.vpci
+ '\', ' + '\'\']'
2163 sriov_vpci
.append(xx
)
2166 virtio_meta
+= ','.join(virtio_vpci
)
2169 sriov_meta
= 'u\'VF\': ['
2170 sriov_meta
+= ','.join(sriov_vpci
)
2173 if virtio_meta
!= '':
2174 pci_assignement
+= virtio_meta
2175 pci_assignement
+= ','
2177 if sriov_meta
!= '':
2178 pci_assignement
+= sriov_meta
2180 if pci_assignement
!= '':
2181 pci_assignement
= '{' + pci_assignement
+ '}'
2183 return pci_assignement
2187 def prepare_vdu_on_boot(self
, account
, server_id
, floating_ip
, mgmt_network
= None):
2189 if(mgmt_network
is None):
2190 cmd
= PREPARE_VM_CMD
.format(auth_url
= account
.openstack
.auth_url
,
2191 username
= account
.openstack
.key
,
2192 password
= account
.openstack
.secret
,
2193 tenant_name
= account
.openstack
.tenant
,
2194 mgmt_network
= account
.openstack
.mgmt_network
,
2195 server_id
= server_id
)
2197 cmd
= PREPARE_VM_CMD
.format(auth_url
= account
.openstack
.auth_url
,
2198 username
= account
.openstack
.key
,
2199 password
= account
.openstack
.secret
,
2200 tenant_name
= account
.openstack
.tenant
,
2201 mgmt_network
= mgmt_network
,
2202 server_id
= server_id
)
2204 if floating_ip
is not None:
2205 cmd
+= (" --floating_ip "+ floating_ip
.ip
)
2207 exec_path
= 'python3 ' + os
.path
.dirname(openstack_drv
.__file
__)
2208 exec_cmd
= exec_path
+'/'+cmd
2209 self
.log
.info("Running command: %s" %(exec_cmd))
2210 subprocess
.call(exec_cmd
, shell
=True)
2213 def do_modify_vdu(self
, account
, vdu_modify
):
2214 """Modify Properties of existing virtual deployment unit
2217 account - a cloud account
2218 vdu_modify - Information about VDU Modification (RwcalYang.VDUModifyParams)
2220 ### First create required number of ports aka connection points
2223 for c_point
in vdu_modify
.connection_points_add
:
2224 if c_point
.virtual_link_id
in network_list
:
2225 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"
2227 network_list
.append(c_point
.virtual_link_id
)
2228 port_id
= self
._create
_connection
_point
(account
, c_point
)
2229 port_list
.append(port_id
)
2231 ### Now add the ports to VM
2232 for port_id
in port_list
:
2233 with self
._use
_driver
(account
) as drv
:
2234 drv
.nova_server_add_port(vdu_modify
.vdu_id
, port_id
)
2236 ### Delete the requested connection_points
2237 for c_point
in vdu_modify
.connection_points_remove
:
2238 self
.do_delete_port(account
, c_point
.connection_point_id
, no_rwstatus
=True)
2240 if vdu_modify
.has_field('image_id'):
2241 with self
._use
_driver
(account
) as drv
:
2242 drv
.nova_server_rebuild(vdu_modify
.vdu_id
, vdu_modify
.image_id
)
2246 def do_delete_vdu(self
, account
, vdu_id
):
2247 """Delete a virtual deployment unit
2250 account - a cloud account
2251 vdu_id - id for the vdu to be deleted
2257 self
.log
.error("empty vdu_id during the vdu deletion")
2260 with self
._use
_driver
(account
) as drv
:
2261 ### Get list of floating_ips associated with this instance and delete them
2262 floating_ips
= [ f
for f
in drv
.nova_floating_ip_list() if f
.instance_id
== vdu_id
]
2263 for f
in floating_ips
:
2264 drv
.nova_drv
.floating_ip_delete(f
)
2266 ### Get list of port on VM and delete them.
2267 port_list
= drv
.neutron_port_list(**{'device_id': vdu_id
})
2269 for port
in port_list
:
2270 if ((port
['device_owner'] == 'compute:None') or (port
['device_owner'] == '')):
2271 self
.do_delete_port(account
, port
['id'], no_rwstatus
=True)
2273 self
.do_delete_vm(account
, vdu_id
, no_rwstatus
=True)
2276 @rwstatus(ret_on_failure
=[None])
2277 def do_get_vdu(self
, account
, vdu_id
, mgmt_network
= None):
2278 """Get information about a virtual deployment unit.
2281 account - a cloud account
2282 vdu_id - id for the vdu
2285 Object of type RwcalYang.VDUInfoParams
2287 with self
._use
_driver
(account
) as drv
:
2288 port_list
= drv
.neutron_port_list(**{'device_id': vdu_id
})
2290 vm
= drv
.nova_server_get(vdu_id
)
2293 if ('flavor' in vm
) and ('id' in vm
['flavor']):
2295 flavor_info
= drv
.nova_flavor_get(vm
['flavor']['id'])
2296 except Exception as e
:
2297 self
.log
.critical("Exception encountered while attempting to get flavor info for flavor_id: %s. Exception: %s" %(vm
['flavor']['id'], str(e
)))
2299 openstack_group_list
= drv
.nova_server_group_list()
2300 server_group
= [ i
['name'] for i
in openstack_group_list
if vm
['id'] in i
['members']]
2301 openstack_srv_volume_list
= drv
.nova_volume_list(vm
['id'])
2302 vdu_info
= RwcalOpenstackPlugin
._fill
_vdu
_info
(vm
,
2304 account
.openstack
.mgmt_network
,
2307 volume_list
= openstack_srv_volume_list
,
2308 overridden_mgmt_network
= mgmt_network
)
2309 if vdu_info
.state
== 'active':
2311 console_info
= drv
.nova_server_console(vdu_info
.vdu_id
)
2312 except Exception as e
:
2315 vdu_info
.console_url
= console_info
['console']['url']
2321 @rwstatus(ret_on_failure
=[None])
2322 def do_get_vdu_list(self
, account
):
2323 """Get information about all the virtual deployment units
2326 account - a cloud account
2329 A list of objects of type RwcalYang.VDUInfoParams
2331 vnf_resources
= RwcalYang
.VNFResources()
2332 with self
._use
_driver
(account
) as drv
:
2333 vms
= drv
.nova_server_list()
2335 port_list
= drv
.neutron_port_list(**{'device_id': vm
['id']})
2339 if ('flavor' in vm
) and ('id' in vm
['flavor']):
2341 flavor_info
= drv
.nova_flavor_get(vm
['flavor']['id'])
2342 except Exception as e
:
2343 self
.log
.critical("Exception encountered while attempting to get flavor info for flavor_id: %s. Exception: %s" %(vm
['flavor']['id'], str(e
)))
2348 openstack_group_list
= drv
.nova_server_group_list()
2349 server_group
= [ i
['name'] for i
in openstack_group_list
if vm
['id'] in i
['members']]
2351 openstack_srv_volume_list
= drv
.nova_volume_list(vm
['id'])
2352 vdu
= RwcalOpenstackPlugin
._fill
_vdu
_info
(vm
,
2354 account
.openstack
.mgmt_network
,
2357 volume_list
= openstack_srv_volume_list
)
2358 if vdu
.state
== 'active':
2360 console_info
= drv
.nova_server_console(vdu
.vdu_id
)
2361 except Exception as e
:
2364 vdu
.console_url
= console_info
['console']['url']
2366 vnf_resources
.vdu_info_list
.append(vdu
)
2367 return vnf_resources