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.
26 gi
.require_version('RwSdn', '1.0')
27 gi
.require_version('RwCal', '1.0')
28 gi
.require_version('RwcalYang', '1.0')
30 import rift
.rwcal
.openstack
as openstack_drv
34 import rift
.cal
.rwcal_status
as rwcal_status
36 import neutronclient
.common
.exceptions
as NeutronException
37 import keystoneclient
.exceptions
as KeystoneExceptions
40 from gi
.repository
import (
48 PREPARE_VM_CMD
= "prepare_vm.py --auth_url {auth_url} --username {username} --password {password} --tenant_name {tenant_name} --region {region} --user_domain {user_domain} --project_domain {project_domain} --mgmt_network {mgmt_network} --server_id {server_id} --port_metadata "
50 rwstatus_exception_map
= { IndexError: RwTypes
.RwStatus
.NOTFOUND
,
51 KeyError: RwTypes
.RwStatus
.NOTFOUND
,
52 NotImplementedError: RwTypes
.RwStatus
.NOT_IMPLEMENTED
,}
54 rwstatus
= rw_status
.rwstatus_from_exc_map(rwstatus_exception_map
)
55 rwcalstatus
= rwcal_status
.rwcalstatus_from_exc_map(rwstatus_exception_map
)
58 class OpenstackCALOperationFailure(Exception):
61 class UninitializedPluginError(Exception):
65 class OpenstackServerGroupError(Exception):
69 class ImageUploadError(Exception):
73 class RwcalAccountDriver(object):
75 Container class per cloud account
77 def __init__(self
, logger
, **kwargs
):
80 self
._driver
= openstack_drv
.OpenstackDriver(logger
= self
.log
, **kwargs
)
81 except (KeystoneExceptions
.Unauthorized
, KeystoneExceptions
.AuthorizationFailure
,
82 NeutronException
.NotFound
) as e
:
84 except Exception as e
:
85 self
.log
.error("RwcalOpenstackPlugin: OpenstackDriver init failed. Exception: %s" %(str(e
)))
92 class RwcalOpenstackPlugin(GObject
.Object
, RwCal
.Cloud
):
93 """This class implements the CAL VALA methods for openstack."""
98 GObject
.Object
.__init
__(self
)
99 self
._driver
_class
= openstack_drv
.OpenstackDriver
100 self
.log
= logging
.getLogger('rwcal.openstack.%s' % RwcalOpenstackPlugin
.instance_num
)
101 self
.log
.setLevel(logging
.DEBUG
)
102 self
._rwlog
_handler
= None
103 self
._account
_drivers
= dict()
104 RwcalOpenstackPlugin
.instance_num
+= 1
106 def _use_driver(self
, account
):
107 if self
._rwlog
_handler
is None:
108 raise UninitializedPluginError("Must call init() in CAL plugin before use.")
110 if account
.name
not in self
._account
_drivers
:
111 self
.log
.debug("Creating OpenstackDriver")
112 kwargs
= dict(username
= account
.openstack
.key
,
113 password
= account
.openstack
.secret
,
114 auth_url
= account
.openstack
.auth_url
,
115 project
= account
.openstack
.tenant
,
116 mgmt_network
= account
.openstack
.mgmt_network
,
117 cert_validate
= account
.openstack
.cert_validate
,
118 user_domain
= account
.openstack
.user_domain
,
119 project_domain
= account
.openstack
.project_domain
,
120 region
= account
.openstack
.region
)
121 drv
= RwcalAccountDriver(self
.log
, **kwargs
)
122 self
._account
_drivers
[account
.name
] = drv
125 return self
._account
_drivers
[account
.name
].driver
129 def do_init(self
, rwlog_ctx
):
130 self
._rwlog
_handler
= rwlogger
.RwLogger(category
="rw-cal-log",
131 subcategory
="openstack",
133 self
.log
.addHandler(self
._rwlog
_handler
)
134 self
.log
.propagate
= False
136 @rwstatus(ret_on_failure
=[None])
137 def do_validate_cloud_creds(self
, account
):
139 Validates the cloud account credentials for the specified account.
140 Performs an access to the resources using Keystone API. If creds
141 are not valid, returns an error code & reason string
143 account - a cloud account to validate
146 Validation Code and Details String
148 status
= RwcalYang
.CloudConnectionStatus()
149 drv
= self
._use
_driver
(account
)
151 drv
.validate_account_creds()
152 except KeystoneExceptions
.Unauthorized
as e
:
153 self
.log
.error("Invalid credentials given for VIM account %s", account
.name
)
154 status
.status
= "failure"
155 status
.details
= "Invalid Credentials: %s" % str(e
)
157 except KeystoneExceptions
.AuthorizationFailure
as e
:
158 self
.log
.error("Bad authentication URL given for VIM account %s. Given auth url: %s",
159 account
.name
, account
.openstack
.auth_url
)
160 status
.status
= "failure"
161 status
.details
= "Invalid auth url: %s" % str(e
)
163 except NeutronException
.NotFound
as e
:
164 self
.log
.error("Given management network %s could not be found for VIM account %s",
165 account
.openstack
.mgmt_network
,
167 status
.status
= "failure"
168 status
.details
= "mgmt network does not exist: %s" % str(e
)
170 except openstack_drv
.ValidationError
as e
:
171 self
.log
.error("RwcalOpenstackPlugin: OpenstackDriver credential validation failed. Exception: %s", str(e
))
172 status
.status
= "failure"
173 status
.details
= "Invalid Credentials: %s" % str(e
)
175 except Exception as e
:
176 msg
= "RwcalOpenstackPlugin: OpenstackDriver connection failed. Exception: %s" %(str(e
))
178 status
.status
= "failure"
182 status
.status
= "success"
183 status
.details
= "Connection was successful"
187 @rwstatus(ret_on_failure
=[""])
188 def do_get_management_network(self
, account
):
190 Returns the management network associated with the specified account.
192 account - a cloud account
195 The management network
197 return account
.openstack
.mgmt_network
199 @rwstatus(ret_on_failure
=[""])
200 def do_create_tenant(self
, account
, name
):
201 """Create a new tenant.
204 account - a cloud account
205 name - name of the tenant
210 raise NotImplementedError
213 def do_delete_tenant(self
, account
, tenant_id
):
217 account - a cloud account
218 tenant_id - id of the tenant
220 raise NotImplementedError
222 @rwstatus(ret_on_failure
=[[]])
223 def do_get_tenant_list(self
, account
):
227 account - a cloud account
232 raise NotImplementedError
234 @rwstatus(ret_on_failure
=[""])
235 def do_create_role(self
, account
, name
):
236 """Create a new user.
239 account - a cloud account
240 name - name of the user
245 raise NotImplementedError
248 def do_delete_role(self
, account
, role_id
):
252 account - a cloud account
253 role_id - id of the user
255 raise NotImplementedError
257 @rwstatus(ret_on_failure
=[[]])
258 def do_get_role_list(self
, account
):
262 account - a cloud account
267 raise NotImplementedError
269 @rwstatus(ret_on_failure
=[""])
270 def do_create_image(self
, account
, image
):
274 account - a cloud account
275 image - a description of the image to create
280 drv
= self
._use
_driver
(account
)
281 fd
= drv
.utils
.image
.create_image_handle(image
)
282 kwargs
= drv
.utils
.image
.make_image_args(image
)
286 image_id
= drv
.glance_image_create(**kwargs
)
287 drv
.glance_image_upload(image_id
, fd
)
288 except Exception as e
:
289 self
.log
.exception("Exception %s occured during image create", str(e
))
294 # Update image properties, if they are provided
296 if image
.has_field("properties") and image
.properties
is not None:
297 for key
in image
.properties
:
298 drv
.glance_image_update(image_id
, **{key
.name
: key
.property_value
})
299 except Exception as e
:
300 self
.log
.exception("Exception %s occured during image update", str(e
))
305 stored_image
= drv
.glance_image_get(image_id
)
306 if stored_image
.checksum
!= image
.checksum
:
307 drv
.glance_image_delete(image_id
=image_id
)
308 raise ImageUploadError("image checksum did not match (actual: %s, expected: %s). Deleting." %
309 (stored_image
.checksum
, image
.checksum
))
310 except Exception as e
:
311 self
.log
.exception("Exception %s occured during image checksum verification", str(e
))
317 def do_delete_image(self
, account
, image_id
):
318 """Delete a vm image.
321 account - a cloud account
322 image_id - id of the image to delete
324 drv
= self
._use
_driver
(account
)
326 drv
.glance_image_delete(image_id
=image_id
)
327 except Exception as e
:
328 self
.log
.exception("Exception %s occured during image deletion", str(e
))
332 @rwstatus(ret_on_failure
=[[]])
333 def do_get_image_list(self
, account
):
334 """Return a list of the names of all available images.
337 account - a cloud account
340 The the list of images in VimResources object
342 response
= RwcalYang
.VimResources()
343 drv
= self
._use
_driver
(account
)
345 images
= drv
.glance_image_list()
347 response
.imageinfo_list
.append(drv
.utils
.image
.parse_cloud_image_info(img
))
348 except Exception as e
:
349 self
.log
.exception("Exception %s occured during get-image-list", str(e
))
353 @rwstatus(ret_on_failure
=[None])
354 def do_get_image(self
, account
, image_id
):
355 """Return a image information.
358 account - a cloud account
359 image_id - an id of the image
362 ImageInfoItem object containing image information.
364 drv
= self
._use
_driver
(account
)
366 image_info
= drv
.glance_image_get(image_id
)
367 image
= drv
.utils
.image
.parse_cloud_image_info(image_info
)
368 except Exception as e
:
369 self
.log
.exception("Exception %s occured during get-image", str(e
))
374 # This is being deprecated. Please do not use for new SW development
375 @rwstatus(ret_on_failure
=[""])
376 def do_create_vm(self
, account
, vminfo
):
377 """Create a new virtual machine.
380 account - a cloud account
381 vminfo - information that defines the type of VM to create
386 from warnings
import warn
387 warn("This function is deprecated")
389 kwargs
['name'] = vminfo
.vm_name
390 kwargs
['flavor_id'] = vminfo
.flavor_id
391 if vminfo
.has_field('image_id'):
392 kwargs
['image_id'] = vminfo
.image_id
394 ### If floating_ip is required and we don't have one, better fail before any further allocation
397 if vminfo
.has_field('allocate_public_address') and vminfo
.allocate_public_address
:
398 if account
.openstack
.has_field('floating_ip_pool'):
399 pool_name
= account
.openstack
.floating_ip_pool
402 if vminfo
.has_field('cloud_init') and vminfo
.cloud_init
.has_field('userdata'):
403 kwargs
['userdata'] = vminfo
.cloud_init
.userdata
405 kwargs
['userdata'] = ''
407 if account
.openstack
.security_groups
:
408 kwargs
['security_groups'] = account
.openstack
.security_groups
411 for port
in vminfo
.port_list
:
412 port_list
.append(port
.port_id
)
415 kwargs
['port_list'] = port_list
418 for network
in vminfo
.network_list
:
419 network_list
.append(network
.network_id
)
422 kwargs
['network_list'] = network_list
425 for field
in vminfo
.user_tags
.fields
:
426 if vminfo
.user_tags
.has_field(field
):
427 metadata
[field
] = getattr(vminfo
.user_tags
, field
)
428 kwargs
['metadata'] = metadata
430 if vminfo
.has_field('availability_zone'):
431 kwargs
['availability_zone'] = vminfo
.availability_zone
433 kwargs
['availability_zone'] = None
435 if vminfo
.has_field('server_group'):
436 kwargs
['scheduler_hints'] = {'group': vminfo
.server_group
}
438 kwargs
['scheduler_hints'] = None
440 drv
= self
._use
_driver
(account
)
441 vm_id
= drv
.nova_server_create(**kwargs
)
443 self
.prepare_vdu_on_boot(account
, vm_id
, floating_ip
)
448 def do_start_vm(self
, account
, vm_id
):
449 """Start an existing virtual machine.
452 account - a cloud account
453 vm_id - an id of the VM
455 drv
= self
._use
_driver
(account
)
456 drv
.nova_server_start(vm_id
)
459 def do_stop_vm(self
, account
, vm_id
):
460 """Stop a running virtual machine.
463 account - a cloud account
464 vm_id - an id of the VM
466 drv
= self
._use
_driver
(account
)
467 drv
.nova_server_stop(vm_id
)
470 def do_delete_vm(self
, account
, vm_id
):
471 """Delete a virtual machine.
474 account - a cloud account
475 vm_id - an id of the VM
477 drv
= self
._use
_driver
(account
)
478 drv
.nova_server_delete(vm_id
)
481 def do_reboot_vm(self
, account
, vm_id
):
482 """Reboot a virtual machine.
485 account - a cloud account
486 vm_id - an id of the VM
488 drv
= self
._use
_driver
(account
)
489 drv
.nova_server_reboot(vm_id
)
492 def _fill_vm_info(vm_info
, mgmt_network
):
493 """Create a GI object from vm info dictionary
495 Converts VM information dictionary object returned by openstack
496 driver into Protobuf Gi Object
499 vm_info - VM information from openstack
500 mgmt_network - Management network
503 Protobuf Gi object for VM
505 vm
= RwcalYang
.VMInfoItem()
506 vm
.vm_id
= vm_info
['id']
507 vm
.vm_name
= vm_info
['name']
508 vm
.image_id
= vm_info
['image']['id']
509 vm
.flavor_id
= vm_info
['flavor']['id']
510 vm
.state
= vm_info
['status']
511 for network_name
, network_info
in vm_info
['addresses'].items():
513 if network_name
== mgmt_network
:
514 vm
.public_ip
= next((item
['addr']
515 for item
in network_info
516 if item
['OS-EXT-IPS:type'] == 'floating'),
517 network_info
[0]['addr'])
518 vm
.management_ip
= network_info
[0]['addr']
520 for interface
in network_info
:
521 addr
= vm
.private_ip_list
.add()
522 addr
.ip_address
= interface
['addr']
524 for network_name
, network_info
in vm_info
['addresses'].items():
525 if network_info
and network_name
== mgmt_network
and not vm
.public_ip
:
526 for interface
in network_info
:
527 if 'OS-EXT-IPS:type' in interface
and interface
['OS-EXT-IPS:type'] == 'floating':
528 vm
.public_ip
= interface
['addr']
530 # Look for any metadata
531 for key
, value
in vm_info
['metadata'].items():
532 if key
in vm
.user_tags
.fields
:
533 setattr(vm
.user_tags
, key
, value
)
534 if 'OS-EXT-SRV-ATTR:host' in vm_info
:
535 if vm_info
['OS-EXT-SRV-ATTR:host'] != None:
536 vm
.host_name
= vm_info
['OS-EXT-SRV-ATTR:host']
537 if 'OS-EXT-AZ:availability_zone' in vm_info
:
538 if vm_info
['OS-EXT-AZ:availability_zone'] != None:
539 vm
.availability_zone
= vm_info
['OS-EXT-AZ:availability_zone']
542 @rwstatus(ret_on_failure
=[[]])
543 def do_get_vm_list(self
, account
):
544 """Return a list of the VMs as vala boxed objects
547 account - a cloud account
550 List containing VM information
552 response
= RwcalYang
.VimResources()
553 drv
= self
._use
_driver
(account
)
554 vms
= drv
.nova_server_list()
556 response
.vminfo_list
.append(RwcalOpenstackPlugin
._fill
_vm
_info
(vm
, account
.openstack
.mgmt_network
))
559 @rwstatus(ret_on_failure
=[None])
560 def do_get_vm(self
, account
, id):
561 """Return vm information.
564 account - a cloud account
565 id - an id for the VM
570 drv
= self
._use
_driver
(account
)
571 vm
= drv
.nova_server_get(id)
572 return RwcalOpenstackPlugin
._fill
_vm
_info
(vm
, account
.openstack
.mgmt_network
)
575 @rwstatus(ret_on_failure
=[""])
576 def do_create_flavor(self
, account
, flavor
):
577 """Create new flavor.
580 account - a cloud account
581 flavor - flavor of the VM
586 drv
= self
._use
_driver
(account
)
587 return drv
.nova_flavor_create(name
= flavor
.name
,
588 ram
= flavor
.vm_flavor
.memory_mb
,
589 vcpus
= flavor
.vm_flavor
.vcpu_count
,
590 disk
= flavor
.vm_flavor
.storage_gb
,
591 epa_specs
= drv
.utils
.flavor
.get_extra_specs(flavor
))
595 def do_delete_flavor(self
, account
, flavor_id
):
599 account - a cloud account
600 flavor_id - id flavor of the VM
602 drv
= self
._use
_driver
(account
)
603 drv
.nova_flavor_delete(flavor_id
)
606 @rwstatus(ret_on_failure
=[[]])
607 def do_get_flavor_list(self
, account
):
608 """Return flavor information.
611 account - a cloud account
616 response
= RwcalYang
.VimResources()
617 drv
= self
._use
_driver
(account
)
618 flavors
= drv
.nova_flavor_list()
620 response
.flavorinfo_list
.append(drv
.utils
.flavor
.parse_flavor_info(flv
))
623 @rwstatus(ret_on_failure
=[None])
624 def do_get_flavor(self
, account
, id):
625 """Return flavor information.
628 account - a cloud account
629 id - an id for the flavor
634 drv
= self
._use
_driver
(account
)
635 flavor
= drv
.nova_flavor_get(id)
636 return drv
.utils
.flavor
.parse_flavor_info(flavor
)
639 def _fill_network_info(self
, network_info
, account
):
640 """Create a GI object from network info dictionary
642 Converts Network information dictionary object returned by openstack
643 driver into Protobuf Gi Object
646 network_info - Network information from openstack
647 account - a cloud account
652 network
= RwcalYang
.NetworkInfoItem()
653 network
.network_name
= network_info
['name']
654 network
.network_id
= network_info
['id']
655 if ('provider:network_type' in network_info
) and (network_info
['provider:network_type'] != None):
656 network
.provider_network
.overlay_type
= network_info
['provider:network_type'].upper()
657 if ('provider:segmentation_id' in network_info
) and (network_info
['provider:segmentation_id']):
658 network
.provider_network
.segmentation_id
= network_info
['provider:segmentation_id']
659 if ('provider:physical_network' in network_info
) and (network_info
['provider:physical_network']):
660 network
.provider_network
.physical_network
= network_info
['provider:physical_network'].upper()
662 if 'subnets' in network_info
and network_info
['subnets']:
663 subnet_id
= network_info
['subnets'][0]
664 drv
= self
._use
_driver
(account
)
665 subnet
= drv
.neutron_subnet_get(subnet_id
)
666 network
.subnet
= subnet
['cidr']
669 @rwstatus(ret_on_failure
=[[]])
670 def do_get_network_list(self
, account
):
671 """Return a list of networks
674 account - a cloud account
679 response
= RwcalYang
.VimResources()
680 drv
= self
._use
_driver
(account
)
681 networks
= drv
.neutron_network_list()
682 for network
in networks
:
683 response
.networkinfo_list
.append(self
._fill
_network
_info
(network
, account
))
686 @rwstatus(ret_on_failure
=[None])
687 def do_get_network(self
, account
, id):
691 account - a cloud account
692 id - an id for the network
697 drv
= self
._use
_driver
(account
)
698 network
= drv
.neutron_network_get(id)
699 return self
._fill
_network
_info
(network
, account
)
701 @rwstatus(ret_on_failure
=[""])
702 def do_create_network(self
, account
, network
):
703 """Create a new network
706 account - a cloud account
707 network - Network object
712 from warnings
import warn
713 warn("This function is deprecated")
716 kwargs
['name'] = network
.network_name
717 kwargs
['admin_state_up'] = True
718 kwargs
['external_router'] = False
719 kwargs
['shared'] = False
721 if network
.has_field('provider_network'):
722 if network
.provider_network
.has_field('physical_network'):
723 kwargs
['physical_network'] = network
.provider_network
.physical_network
724 if network
.provider_network
.has_field('overlay_type'):
725 kwargs
['network_type'] = network
.provider_network
.overlay_type
.lower()
726 if network
.provider_network
.has_field('segmentation_id'):
727 kwargs
['segmentation_id'] = network
.provider_network
.segmentation_id
729 drv
= self
._use
_driver
(account
)
730 network_id
= drv
.neutron_network_create(**kwargs
)
731 drv
.neutron_subnet_create(network_id
= network_id
,
732 cidr
= network
.subnet
)
736 def do_delete_network(self
, account
, network_id
):
740 account - a cloud account
741 network_id - an id for the network
743 drv
= self
._use
_driver
(account
)
744 drv
.neutron_network_delete(network_id
)
747 def _fill_port_info(port_info
):
748 """Create a GI object from port info dictionary
750 Converts Port information dictionary object returned by openstack
751 driver into Protobuf Gi Object
754 port_info - Port information from openstack
759 port
= RwcalYang
.PortInfoItem()
761 port
.port_name
= port_info
['name']
762 port
.port_id
= port_info
['id']
763 port
.network_id
= port_info
['network_id']
764 port
.port_state
= port_info
['status']
765 if 'device_id' in port_info
:
766 port
.vm_id
= port_info
['device_id']
767 if 'fixed_ips' in port_info
:
768 port
.ip_address
= port_info
['fixed_ips'][0]['ip_address']
771 @rwstatus(ret_on_failure
=[None])
772 def do_get_port(self
, account
, port_id
):
776 account - a cloud account
777 port_id - an id for the port
782 drv
= self
._use
_driver
(account
)
783 port
= drv
.neutron_port_get(port_id
)
784 return RwcalOpenstackPlugin
._fill
_port
_info
(port
)
786 @rwstatus(ret_on_failure
=[[]])
787 def do_get_port_list(self
, account
):
788 """Return a list of ports
791 account - a cloud account
796 response
= RwcalYang
.VimResources()
797 drv
= self
._use
_driver
(account
)
798 ports
= drv
.neutron_port_list(*{})
800 response
.portinfo_list
.append(RwcalOpenstackPlugin
._fill
_port
_info
(port
))
803 @rwstatus(ret_on_failure
=[""])
804 def do_create_port(self
, account
, port
):
808 account - a cloud account
814 from warnings
import warn
815 warn("This function is deprecated")
818 kwargs
['name'] = port
.port_name
819 kwargs
['network_id'] = port
.network_id
820 kwargs
['admin_state_up'] = True
821 if port
.has_field('vm_id'):
822 kwargs
['vm_id'] = port
.vm_id
823 if port
.has_field('port_type'):
824 kwargs
['port_type'] = port
.port_type
826 kwargs
['port_type'] = "normal"
828 drv
= self
._use
_driver
(account
)
829 return drv
.neutron_port_create(**kwargs
)
832 def do_delete_port(self
, account
, port_id
):
836 account - a cloud account
837 port_id - an id for port
839 drv
= self
._use
_driver
(account
)
840 drv
.neutron_port_delete(port_id
)
842 @rwstatus(ret_on_failure
=[""])
843 def do_add_host(self
, account
, host
):
847 account - a cloud account
853 raise NotImplementedError
856 def do_remove_host(self
, account
, host_id
):
860 account - a cloud account
861 host_id - an id for the host
863 raise NotImplementedError
865 @rwstatus(ret_on_failure
=[None])
866 def do_get_host(self
, account
, host_id
):
870 account - a cloud account
871 host_id - an id for host
876 raise NotImplementedError
878 @rwstatus(ret_on_failure
=[[]])
879 def do_get_host_list(self
, account
):
880 """Return a list of hosts
883 account - a cloud account
888 raise NotImplementedError
891 @rwcalstatus(ret_on_failure
=[""])
892 def do_create_virtual_link(self
, account
, link_params
):
893 """Create a new virtual link
896 account - a cloud account
897 link_params - information that defines the type of VDU to create
900 A kwargs dictionary for glance operation
903 drv
= self
._use
_driver
(account
)
905 kwargs
= drv
.utils
.network
.make_virtual_link_args(link_params
)
906 network_id
= drv
.neutron_network_create(**kwargs
)
907 except Exception as e
:
908 self
.log
.error("Encountered exceptions during network creation. Exception: %s", str(e
))
911 kwargs
= drv
.utils
.network
.make_subnet_args(link_params
, network_id
)
912 drv
.neutron_subnet_create(**kwargs
)
917 def do_delete_virtual_link(self
, account
, link_id
):
918 """Delete a virtual link
921 account - a cloud account
922 link_id - id for the virtual-link to be deleted
927 drv
= self
._use
_driver
(account
)
929 port_list
= drv
.neutron_port_list(**{'network_id': link_id
})
930 for port
in port_list
:
931 if ((port
['device_owner'] == 'compute:None') or (port
['device_owner'] == '')):
932 self
.do_delete_port(account
, port
['id'], no_rwstatus
=True)
933 self
.do_delete_network(account
, link_id
, no_rwstatus
=True)
934 except Exception as e
:
935 self
.log
.exception("Exception %s occured during virtual-link deletion", str(e
))
938 @rwstatus(ret_on_failure
=[None])
939 def do_get_virtual_link(self
, account
, link_id
):
940 """Get information about virtual link.
943 account - a cloud account
944 link_id - id for the virtual-link
947 Object of type RwcalYang.VirtualLinkInfoParams
949 drv
= self
._use
_driver
(account
)
951 network
= drv
.neutron_network_get(link_id
)
953 port_list
= drv
.neutron_port_list(**{'network_id': network
['id']})
954 if 'subnets' in network
and network
['subnets']:
955 subnet
= drv
.neutron_subnet_get(network
['subnets'][0])
958 virtual_link
= drv
.utils
.network
.parse_cloud_virtual_link_info(network
, port_list
, subnet
)
959 except Exception as e
:
960 self
.log
.exception("Exception %s occured during virtual-link-get", str(e
))
964 @rwstatus(ret_on_failure
=[None])
965 def do_get_virtual_link_list(self
, account
):
966 """Get information about all the virtual links
969 account - a cloud account
972 A list of objects of type RwcalYang.VirtualLinkInfoParams
974 vnf_resources
= RwcalYang
.VNFResources()
975 drv
= self
._use
_driver
(account
)
977 networks
= drv
.neutron_network_list()
978 for network
in networks
:
979 port_list
= drv
.neutron_port_list(**{'network_id': network
['id']})
980 if 'subnets' in network
and network
['subnets']:
981 subnet
= drv
.neutron_subnet_get(network
['subnets'][0])
984 virtual_link
= drv
.utils
.network
.parse_cloud_virtual_link_info(network
, port_list
, subnet
)
985 vnf_resources
.virtual_link_info_list
.append(virtual_link
)
986 except Exception as e
:
987 self
.log
.exception("Exception %s occured during virtual-link-list-get", str(e
))
993 @rwcalstatus(ret_on_failure
=[""])
994 def do_create_vdu(self
, account
, vdu_init
):
995 """Create a new virtual deployment unit
998 account - a cloud account
999 vdu_init - information about VDU to create (RwcalYang.VDUInitParams)
1004 drv
= self
._use
_driver
(account
)
1006 kwargs
= drv
.utils
.compute
.make_vdu_create_args(vdu_init
, account
)
1007 vm_id
= drv
.nova_server_create(**kwargs
)
1008 self
.prepare_vdu_on_boot(account
, vm_id
, vdu_init
)
1009 except Exception as e
:
1010 self
.log
.exception("Exception %s occured during create-vdu", str(e
))
1015 def prepare_vdu_on_boot(self
, account
, server_id
, vdu_params
):
1016 cmd
= PREPARE_VM_CMD
.format(auth_url
= account
.openstack
.auth_url
,
1017 username
= account
.openstack
.key
,
1018 password
= account
.openstack
.secret
,
1019 tenant_name
= account
.openstack
.tenant
,
1020 region
= account
.openstack
.region
,
1021 user_domain
= account
.openstack
.user_domain
,
1022 project_domain
= account
.openstack
.project_domain
,
1023 mgmt_network
= account
.openstack
.mgmt_network
,
1024 server_id
= server_id
)
1027 if vdu_params
.has_field('allocate_public_address') and vdu_params
.allocate_public_address
:
1028 cmd
+= " --floating_ip"
1029 if account
.openstack
.has_field('floating_ip_pool'):
1030 cmd
+= (" --pool_name " + account
.openstack
.floating_ip_pool
)
1032 if vdu_params
.has_field('volumes'):
1033 for volume
in vdu_params
.volumes
:
1034 if volume
.has_field('custom_meta_data'):
1035 vol_list
.append(volume
.as_dict())
1038 with tempfile
.NamedTemporaryFile(mode
='w', delete
=False) as tmp_file
:
1039 yaml
.dump(vol_list
, tmp_file
)
1040 cmd
+= (" --vol_metadata {}").format(tmp_file
.name
)
1042 exec_path
= 'python3 ' + os
.path
.dirname(openstack_drv
.__file
__)
1043 exec_cmd
= exec_path
+'/'+cmd
1044 self
.log
.info("Running command: %s" %(exec_cmd))
1045 subprocess
.call(exec_cmd
, shell
=True)
1048 def do_modify_vdu(self
, account
, vdu_modify
):
1049 """Modify Properties of existing virtual deployment unit
1052 account - a cloud account
1053 vdu_modify - Information about VDU Modification (RwcalYang.VDUModifyParams)
1055 drv
= self
._use
_driver
(account
)
1056 ### First create required number of ports aka connection points
1059 for c_point
in vdu_modify
.connection_points_add
:
1060 if c_point
.virtual_link_id
in network_list
:
1061 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"
1063 network_list
.append(c_point
.virtual_link_id
)
1064 port_id
= self
._create
_connection
_point
(account
, c_point
)
1065 port_list
.append(port_id
)
1067 drv
= self
._use
_driver
(account
)
1068 ### Now add the ports to VM
1069 for port_id
in port_list
:
1070 drv
.nova_server_add_port(vdu_modify
.vdu_id
, port_id
)
1072 ### Delete the requested connection_points
1073 for c_point
in vdu_modify
.connection_points_remove
:
1074 self
.do_delete_port(account
, c_point
.connection_point_id
, no_rwstatus
=True)
1076 if vdu_modify
.has_field('image_id'):
1077 drv
.nova_server_rebuild(vdu_modify
.vdu_id
, vdu_modify
.image_id
)
1081 def do_delete_vdu(self
, account
, vdu_id
):
1082 """Delete a virtual deployment unit
1085 account - a cloud account
1086 vdu_id - id for the vdu to be deleted
1091 drv
= self
._use
_driver
(account
)
1093 drv
.utils
.compute
.perform_vdu_network_cleanup(vdu_id
)
1094 drv
.nova_server_delete(vdu_id
)
1095 except Exception as e
:
1096 self
.log
.exception("Exception %s occured during delete-vdu", str(e
))
1100 @rwstatus(ret_on_failure
=[None])
1101 def do_get_vdu(self
, account
, vdu_id
):
1102 """Get information about a virtual deployment unit.
1105 account - a cloud account
1106 vdu_id - id for the vdu
1109 Object of type RwcalYang.VDUInfoParams
1111 drv
= self
._use
_driver
(account
)
1113 vm_info
= drv
.nova_server_get(vdu_id
)
1114 vdu_info
= drv
.utils
.compute
.parse_cloud_vdu_info(vm_info
)
1115 except Exception as e
:
1116 self
.log
.exception("Exception %s occured during get-vdu", str(e
))
1122 @rwstatus(ret_on_failure
=[None])
1123 def do_get_vdu_list(self
, account
):
1124 """Get information about all the virtual deployment units
1127 account - a cloud account
1130 A list of objects of type RwcalYang.VDUInfoParams
1132 vnf_resources
= RwcalYang
.VNFResources()
1133 drv
= self
._use
_driver
(account
)
1135 vms
= drv
.nova_server_list()
1137 vdu
= drv
.utils
.compute
.parse_cloud_vdu_info(vm
)
1138 vnf_resources
.vdu_info_list
.append(vdu
)
1139 except Exception as e
:
1140 self
.log
.exception("Exception %s occured during get-vdu-list", str(e
))
1142 return vnf_resources
1145 class SdnOpenstackPlugin(GObject
.Object
, RwSdn
.Topology
):
1148 GObject
.Object
.__init
__(self
)
1149 self
._driver
_class
= openstack_drv
.OpenstackDriver
1150 self
.log
= logging
.getLogger('rwsdn.openstack.%s' % SdnOpenstackPlugin
.instance_num
)
1151 self
.log
.setLevel(logging
.DEBUG
)
1153 self
._rwlog
_handler
= None
1154 SdnOpenstackPlugin
.instance_num
+= 1
1156 @contextlib.contextmanager
1157 def _use_driver(self
, account
):
1158 if self
._rwlog
_handler
is None:
1159 raise UninitializedPluginError("Must call init() in CAL plugin before use.")
1161 with rwlogger
.rwlog_root_handler(self
._rwlog
_handler
):
1163 drv
= self
._driver
_class
(username
= account
.openstack
.key
,
1164 password
= account
.openstack
.secret
,
1165 auth_url
= account
.openstack
.auth_url
,
1166 tenant_name
= account
.openstack
.tenant
,
1167 mgmt_network
= account
.openstack
.mgmt_network
,
1168 cert_validate
= account
.openstack
.cert_validate
)
1169 except Exception as e
:
1170 self
.log
.error("SdnOpenstackPlugin: OpenstackDriver init failed. Exception: %s" %(str(e
)))
1176 def do_init(self
, rwlog_ctx
):
1177 self
._rwlog
_handler
= rwlogger
.RwLogger(
1178 category
="rw-cal-log",
1179 subcategory
="openstack",
1182 self
.log
.addHandler(self
._rwlog
_handler
)
1183 self
.log
.propagate
= False
1185 @rwstatus(ret_on_failure
=[None])
1186 def do_validate_sdn_creds(self
, account
):
1188 Validates the sdn account credentials for the specified account.
1189 Performs an access to the resources using Keystone API. If creds
1190 are not valid, returns an error code & reason string
1192 @param account - a SDN account
1195 Validation Code and Details String
1197 status
= RwsdnYang
.SdnConnectionStatus()
1199 with self
._use
_driver
(account
) as drv
:
1200 drv
.validate_account_creds()
1202 except openstack_drv
.ValidationError
as e
:
1203 self
.log
.error("SdnOpenstackPlugin: OpenstackDriver credential validation failed. Exception: %s", str(e
))
1204 status
.status
= "failure"
1205 status
.details
= "Invalid Credentials: %s" % str(e
)
1207 except Exception as e
:
1208 msg
= "SdnOpenstackPlugin: OpenstackDriver connection failed. Exception: %s" %(str(e
))
1210 status
.status
= "failure"
1211 status
.details
= msg
1214 status
.status
= "success"
1215 status
.details
= "Connection was successful"
1219 @rwstatus(ret_on_failure
=[""])
1220 def do_create_vnffg_chain(self
, account
,vnffg
):
1222 Creates Service Function chain in ODL
1224 @param account - a SDN account
1227 self
.log
.debug('Received Create VNFFG chain for account {}, chain {}'.format(account
,vnffg
))
1228 with self
._use
_driver
(account
) as drv
:
1230 vnf_chain_list
= sorted(vnffg
.vnf_chain_path
, key
= lambda x
: x
.order
)
1232 for path
in vnf_chain_list
:
1233 if prev_vm_id
and path
.vnfr_ids
[0].vdu_list
[0].vm_id
== prev_vm_id
:
1234 prev_entry
= port_list
.pop()
1235 port_list
.append((prev_entry
[0],path
.vnfr_ids
[0].vdu_list
[0].port_id
))
1238 prev_vm_id
= path
.vnfr_ids
[0].vdu_list
[0].vm_id
1239 port_list
.append((path
.vnfr_ids
[0].vdu_list
[0].port_id
,path
.vnfr_ids
[0].vdu_list
[0].port_id
))
1240 vnffg_id
= drv
.create_port_chain(vnffg
.name
,port_list
)
1244 def do_terminate_vnffg_chain(self
, account
,vnffg_id
):
1246 Terminate Service Function chain in ODL
1248 @param account - a SDN account
1250 self
.log
.debug('Received terminate VNFFG chain for id %s ', vnffg_id
)
1251 with self
._use
_driver
(account
) as drv
:
1252 drv
.delete_port_chain(vnffg_id
)
1254 @rwstatus(ret_on_failure
=[None])
1255 def do_create_vnffg_classifier(self
, account
, vnffg_classifier
):
1257 Add VNFFG Classifier
1259 @param account - a SDN account
1261 self
.log
.debug('Received Create VNFFG classifier for account {}, classifier {}'.format(account
,vnffg_classifier
))
1262 protocol_map
= {1:'ICMP',6:'TCP',17:'UDP'}
1263 flow_classifier_list
= list()
1264 with self
._use
_driver
(account
) as drv
:
1265 for rule
in vnffg_classifier
.match_attributes
:
1266 classifier_name
= vnffg_classifier
.name
+ '_' + rule
.name
1268 for field
, value
in rule
.as_dict().items():
1269 if field
== 'ip_proto':
1270 flow_dict
['protocol'] = protocol_map
.get(value
,None)
1271 elif field
== 'source_ip_address':
1272 flow_dict
['source_ip_prefix'] = value
1273 elif field
== 'destination_ip_address':
1274 flow_dict
['destination_ip_prefix'] = value
1275 elif field
== 'source_port':
1276 flow_dict
['source_port_range_min'] = value
1277 flow_dict
['source_port_range_max'] = value
1278 elif field
== 'destination_port':
1279 flow_dict
['destination_port_range_min'] = value
1280 flow_dict
['destination_port_range_max'] = value
1281 if vnffg_classifier
.has_field('port_id'):
1282 flow_dict
['logical_source_port'] = vnffg_classifier
.port_id
1283 flow_classifier_id
= drv
.create_flow_classifer(classifier_name
, flow_dict
)
1284 flow_classifier_list
.append(flow_classifier_id
)
1285 drv
.update_port_chain(vnffg_classifier
.rsp_id
,flow_classifier_list
)
1286 return flow_classifier_list
1288 @rwstatus(ret_on_failure
=[None])
1289 def do_terminate_vnffg_classifier(self
, account
, vnffg_classifier_list
):
1291 Add VNFFG Classifier
1293 @param account - a SDN account
1295 self
.log
.debug('Received terminate VNFFG classifier for id %s ', vnffg_classifier_list
)
1296 with self
._use
_driver
(account
) as drv
:
1297 for classifier_id
in vnffg_classifier_list
:
1298 drv
.delete_flow_classifier(classifier_id
)
1300 @rwstatus(ret_on_failure
=[None])
1301 def do_get_vnffg_rendered_paths(self
, account
):
1303 Get ODL Rendered Service Path List (SFC)
1305 @param account - a SDN account
1307 self
.log
.debug('Received get VNFFG rendered path for account %s ', account
)
1308 vnffg_rsps
= RwsdnYang
.VNFFGRenderedPaths()
1309 with self
._use
_driver
(account
) as drv
:
1310 port_chain_list
= drv
.get_port_chain_list()
1311 for port_chain
in port_chain_list
:
1312 #rsp = vnffg_rsps.vnffg_rendered_path.add()
1313 #rsp.name = port_chain['name']