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.
22 import rift
.rwcal
.aws
as aws_drv
24 import rift
.cal
.rwcal_status
as rwcal_status
26 import rift
.rwcal
.aws
.exceptions
as exceptions
27 from gi
import require_version
28 require_version('RwCal', '1.0')
30 from gi
.repository
import (
37 PREPARE_VM_CMD
= "prepare_vm.py --aws_key {key} --aws_secret {secret} --aws_region {region} --server_id {server_id}"
38 DELETE_VM_CMD
= "delete_vm.py --aws_key {key} --aws_secret {secret} --aws_region {region} --server_id {server_id}"
40 rwstatus_exception_map
= {IndexError: RwTypes
.RwStatus
.NOTFOUND
,
41 KeyError: RwTypes
.RwStatus
.NOTFOUND
,
42 NotImplementedError: RwTypes
.RwStatus
.NOT_IMPLEMENTED
,
43 AttributeError: RwTypes
.RwStatus
.FAILURE
,
44 exceptions
.RWErrorNotFound
: RwTypes
.RwStatus
.NOTFOUND
,
45 exceptions
.RWErrorDuplicate
: RwTypes
.RwStatus
.DUPLICATE
,
46 exceptions
.RWErrorExists
: RwTypes
.RwStatus
.EXISTS
,
47 exceptions
.RWErrorNotConnected
: RwTypes
.RwStatus
.NOTCONNECTED
,
50 rwstatus
= rw_status
.rwstatus_from_exc_map(rwstatus_exception_map
)
51 rwcalstatus
= rwcal_status
.rwcalstatus_from_exc_map(rwstatus_exception_map
)
53 class RwcalAWSPlugin(GObject
.Object
, RwCal
.Cloud
):
54 """This class implements the CAL VALA methods for AWS."""
59 GObject
.Object
.__init
__(self
)
60 self
._driver
_class
= aws_drv
.AWSDriver
61 self
._flavor
_list
= []
62 self
.log
= logging
.getLogger('rwcal.aws.%s' % RwcalAWSPlugin
.instance_num
)
63 self
.log
.setLevel(logging
.DEBUG
)
65 RwcalAWSPlugin
.instance_num
+= 1
67 def _get_driver(self
, account
):
68 return self
._driver
_class
(key
= account
.aws
.key
,
69 secret
= account
.aws
.secret
,
70 region
= account
.aws
.region
,
71 ssh_key
= account
.aws
.ssh_key
,
72 vpcid
= account
.aws
.vpcid
,
73 availability_zone
= account
.aws
.availability_zone
,
74 default_subnet_id
= account
.aws
.default_subnet_id
)
77 def do_init(self
, rwlog_ctx
):
80 category
="rw-cal-log",
86 @rwstatus(ret_on_failure
=[None])
87 def do_validate_cloud_creds(self
, account
):
89 Validates the cloud account credentials for the specified account.
90 Performs an access to the resources using underlying API. If creds
91 are not valid, returns an error code & reason string
93 account - a cloud account to validate
96 Validation Code and Details String
98 status
= RwcalYang
.CloudConnectionStatus(
100 details
="AWS Cloud Account validation not implemented yet"
105 @rwstatus(ret_on_failure
=[""])
106 def do_get_management_network(self
, account
):
108 Returns the management network associated with the specified account.
110 account - a cloud account
113 The management network
115 raise NotImplementedError
117 @rwstatus(ret_on_failure
=[""])
118 def do_create_tenant(self
, account
, name
):
119 """Create a new tenant.
122 account - a cloud account
123 name - name of the tenant
128 raise NotImplementedError
131 def do_delete_tenant(self
, account
, tenant_id
):
135 account - a cloud account
136 tenant_id - id of the tenant
138 raise NotImplementedError
140 @rwstatus(ret_on_failure
=[[]])
141 def do_get_tenant_list(self
, account
):
145 account - a cloud account
150 raise NotImplementedError
152 @rwstatus(ret_on_failure
=[""])
153 def do_create_role(self
, account
, name
):
154 """Create a new user.
157 account - a cloud account
158 name - name of the user
163 raise NotImplementedError
166 def do_delete_role(self
, account
, role_id
):
170 account - a cloud account
171 role_id - id of the user
173 raise NotImplementedError
175 @rwstatus(ret_on_failure
=[[]])
176 def do_get_role_list(self
, account
):
180 account - a cloud account
185 raise NotImplementedError
187 @rwstatus(ret_on_failure
=[""])
188 def do_create_image(self
, account
, image
):
192 account - a cloud account
193 image - a description of the image to create
198 raise NotImplementedError
201 def do_delete_image(self
, account
, image_id
):
202 """Delete a vm image.
205 account - a cloud account
206 image_id - id of the image to delete
208 raise NotImplementedError
211 def _fill_image_info(img_info
):
212 """Create a GI object from image info dictionary
214 Converts image information dictionary object returned by AWS
215 driver into Protobuf Gi Object
218 account - a cloud account
219 img_info - image information dictionary object from AWS
224 img
= RwcalYang
.ImageInfoItem()
225 img
.name
= img_info
.name
228 #tag_fields = ['checksum']
229 # Look for any properties
231 for tag
in img_info
.tags
:
232 if tag
['Key'] == 'checksum':
233 setattr(img
, tag
['Key'], tag
['Value'])
234 img
.disk_format
= 'ami'
235 if img_info
.state
== 'available':
238 img
.state
= 'inactive'
241 @rwstatus(ret_on_failure
=[[]])
242 def do_get_image_list(self
, account
):
243 """Return a list of the names of all available images.
246 account - a cloud account
249 The the list of images in VimResources object
251 response
= RwcalYang
.VimResources()
252 images
= self
._get
_driver
(account
).list_images()
254 response
.imageinfo_list
.append(RwcalAWSPlugin
._fill
_image
_info
(img
))
257 @rwstatus(ret_on_failure
=[None])
258 def do_get_image(self
, account
, image_id
):
259 """Return a image information.
262 account - a cloud account
263 image_id - an id of the image
266 ImageInfoItem object containing image information.
268 image
= self
._get
_driver
(account
).get_image(image_id
)
269 return RwcalAWSPlugin
._fill
_image
_info
(image
)
271 @rwstatus(ret_on_failure
=[""])
272 def do_create_vm(self
, account
, vminfo
):
273 """Create a new virtual machine.
276 account - a cloud account
277 vminfo - information that defines the type of VM to create
282 raise NotImplementedError
285 def do_start_vm(self
, account
, vm_id
):
286 """Start an existing virtual machine.
289 account - a cloud account
290 vm_id - an id of the VM
292 raise NotImplementedError
295 def do_stop_vm(self
, account
, vm_id
):
296 """Stop a running virtual machine.
299 account - a cloud account
300 vm_id - an id of the VM
302 raise NotImplementedError
305 def do_delete_vm(self
, account
, vm_id
):
306 """Delete a virtual machine.
309 account - a cloud account
310 vm_id - an id of the VM
312 raise NotImplementedError
315 def do_reboot_vm(self
, account
, vm_id
):
316 """Reboot a virtual machine.
319 account - a cloud account
320 vm_id - an id of the VM
322 raise NotImplementedError
325 def _fill_vm_info(vm_info
):
326 """Create a GI object from vm info dictionary
328 Converts VM information dictionary object returned by openstack
329 driver into Protobuf Gi Object
332 vm_info - VM information from AWS
335 Protobuf Gi object for VM
337 vm
= RwcalYang
.VMInfoItem()
338 vm
.vm_id
= vm_info
.id
339 vm
.image_id
= vm_info
.image_id
340 vm
.flavor_id
= vm_info
.instance_type
341 if vm_info
.state
['Name'] == 'running':
344 vm
.state
= 'inactive'
345 for network_intf
in vm_info
.network_interfaces
:
346 if 'Attachment' in network_intf
and network_intf
['Attachment']['DeviceIndex'] == 0:
347 if 'Association' in network_intf
and 'PublicIp' in network_intf
['Association']:
348 vm
.public_ip
= network_intf
['Association']['PublicIp']
349 vm
.management_ip
= network_intf
['PrivateIpAddress']
351 addr
= vm
.private_ip_list
.add()
352 addr
.ip_address
= network_intf
['PrivateIpAddress']
353 if 'Association' in network_intf
and 'PublicIp' in network_intf
['Association']:
354 addr
= vm
.public_ip_list
.add()
355 addr
.ip_address
= network_intf
['Association']['PublicIp']
357 if vm_info
.placement
and 'AvailabilityZone' in vm_info
.placement
:
358 vm
.availability_zone
= vm_info
.placement
['AvailabilityZone']
360 for tag
in vm_info
.tags
:
361 if tag
['Key'] == 'Name':
362 vm
.vm_name
= tag
['Value']
363 elif tag
['Key'] in vm
.user_tags
.fields
:
364 setattr(vm
.user_tags
,tag
['Key'],tag
['Value'])
367 @rwstatus(ret_on_failure
=[[]])
368 def do_get_vm_list(self
, account
):
369 """Return a list of the VMs as vala boxed objects
372 account - a cloud account
375 List containing VM information
377 response
= RwcalYang
.VimResources()
378 vms
= self
._get
_driver
(account
).list_instances()
380 response
.vminfo_list
.append(RwcalAWSPlugin
._fill
_vm
_info
(vm
))
383 @rwstatus(ret_on_failure
=[None])
384 def do_get_vm(self
, account
, id):
385 """Return vm information.
388 account - a cloud account
389 id - an id for the VM
394 vm
= self
._get
_driver
(account
).get_instance(id)
395 return RwcalAWSPlugin
._fill
_vm
_info
(vm
)
397 @rwstatus(ret_on_failure
=[""])
398 def do_create_flavor(self
, account
, flavor
):
399 """Create new flavor.
400 AWS has fixed set of AWS types and so we map flavor to existing instance type
401 and create local flavor for the same.
404 account - a cloud account
405 flavor - flavor of the VM
408 flavor id (with EC2 instance type included in id)
410 drv
= self
._get
_driver
(account
)
411 inst_type
= drv
.map_flavor_to_instance_type(ram
= flavor
.vm_flavor
.memory_mb
,
412 vcpus
= flavor
.vm_flavor
.vcpu_count
,
413 disk
= flavor
.vm_flavor
.storage_gb
)
415 new_flavor
= RwcalYang
.FlavorInfoItem()
416 new_flavor
.name
= flavor
.name
417 new_flavor
.vm_flavor
.memory_mb
= flavor
.vm_flavor
.memory_mb
418 new_flavor
.vm_flavor
.vcpu_count
= flavor
.vm_flavor
.vcpu_count
419 new_flavor
.vm_flavor
.storage_gb
= flavor
.vm_flavor
.storage_gb
420 new_flavor
.id = inst_type
+ '-' + str(RwcalAWSPlugin
.flavor_id
)
421 RwcalAWSPlugin
.flavor_id
= RwcalAWSPlugin
.flavor_id
+1
422 self
._flavor
_list
.append(new_flavor
)
426 def do_delete_flavor(self
, account
, flavor_id
):
430 account - a cloud account
431 flavor_id - id flavor of the VM
434 flavor
= [flav
for flav
in self
._flavor
_list
if flav
.id == flavor_id
]
435 self
._flavor
_list
.delete(flavor
[0])
438 def _fill_flavor_info(flavor_info
):
439 """Create a GI object from flavor info dictionary
441 Converts Flavor information dictionary object returned by openstack
442 driver into Protobuf Gi Object
445 flavor_info: Flavor information from openstack
448 Object of class FlavorInfoItem
450 flavor
= RwcalYang
.FlavorInfoItem()
451 flavor
.name
= flavor_info
.name
452 flavor
.id = flavor_info
.id
453 flavor
.vm_flavor
.memory_mb
= flavor_info
.vm_flavor
.memory_mb
454 flavor
.vm_flavor
.vcpu_count
= flavor_info
.vm_flavor
.vcpu_count
455 flavor
.vm_flavor
.storage_gb
= flavor_info
.vm_flavor
.storage_gb
458 @rwstatus(ret_on_failure
=[[]])
459 def do_get_flavor_list(self
, account
):
460 """Return flavor information.
463 account - a cloud account
468 response
= RwcalYang
.VimResources()
469 for flv
in self
._flavor
_list
:
470 response
.flavorinfo_list
.append(RwcalAWSPlugin
._fill
_flavor
_info
(flv
))
474 @rwstatus(ret_on_failure
=[None])
475 def do_get_flavor(self
, account
, id):
476 """Return flavor information.
479 account - a cloud account
480 id - an id for the flavor
485 flavor
= [flav
for flav
in self
._flavor
_list
if flav
.id == id]
486 return (RwcalAWSPlugin
._fill
_flavor
_info
(flavor
[0]))
488 def _fill_network_info(self
, network_info
, account
):
489 """Create a GI object from network info dictionary
491 Converts Network information dictionary object returned by AWS
492 driver into Protobuf Gi Object
495 network_info - Network information from AWS
496 account - a cloud account
501 network
= RwcalYang
.NetworkInfoItem()
502 network
.network_id
= network_info
.subnet_id
503 network
.subnet
= network_info
.cidr_block
504 if network_info
.tags
:
505 for tag
in network_info
.tags
:
506 if tag
['Key'] == 'Name':
507 network
.network_name
= tag
['Value']
510 @rwstatus(ret_on_failure
=[[]])
511 def do_get_network_list(self
, account
):
512 """Return a list of networks
515 account - a cloud account
520 response
= RwcalYang
.VimResources()
521 networks
= self
._get
_driver
(account
).get_subnet_list()
522 for network
in networks
:
523 response
.networkinfo_list
.append(self
._fill
_network
_info
(network
, account
))
526 @rwstatus(ret_on_failure
=[None])
527 def do_get_network(self
, account
, id):
531 account - a cloud account
532 id - an id for the network
537 network
= self
._get
_driver
(account
).get_subnet(id)
538 return self
._fill
_network
_info
(network
, account
)
540 @rwstatus(ret_on_failure
=[""])
541 def do_create_network(self
, account
, network
):
542 """Create a new network
545 account - a cloud account
546 network - Network object
551 raise NotImplementedError
554 def do_delete_network(self
, account
, network_id
):
558 account - a cloud account
559 network_id - an id for the network
561 raise NotImplementedError
564 def _fill_port_info(port_info
):
565 """Create a GI object from port info dictionary
567 Converts Port information dictionary object returned by AWS
568 driver into Protobuf Gi Object
571 port_info - Port/Network interface information from AWS
576 port
= RwcalYang
.PortInfoItem()
578 port
.port_id
= port_info
.id
579 port
.network_id
= port_info
.subnet_id
580 if port_info
.attachment
and 'InstanceId' in port_info
.attachment
:
581 port
.vm_id
= port_info
.attachment
['InstanceId']
582 port
.ip_address
= port_info
.private_ip_address
583 if port_info
.status
== 'in-use':
584 port
.port_state
= 'active'
585 elif port_info
.status
== 'available':
586 port
.port_state
= 'inactive'
588 port
.port_state
= 'unknown'
589 if port_info
.tag_set
:
590 for tag
in port_info
.tag_set
:
591 if tag
['Key'] == 'Name':
592 port
.port_name
= tag
['Value']
596 @rwstatus(ret_on_failure
=[None])
597 def do_get_port(self
, account
, port_id
):
601 account - a cloud account
602 port_id - an id for the port
607 port
= self
._get
_driver
(account
).get_network_interface(port_id
)
608 return RwcalAWSPlugin
._fill
_port
_info
(port
)
610 @rwstatus(ret_on_failure
=[[]])
611 def do_get_port_list(self
, account
):
612 """Return a list of ports
615 account - a cloud account
620 response
= RwcalYang
.VimResources()
621 ports
= self
._get
_driver
(account
).get_network_interface_list()
623 response
.portinfo_list
.append(RwcalAWSPlugin
._fill
_port
_info
(port
))
626 @rwstatus(ret_on_failure
=[""])
627 def do_create_port(self
, account
, port
):
631 account - a cloud account
637 raise NotImplementedError
640 def do_delete_port(self
, account
, port_id
):
644 account - a cloud account
645 port_id - an id for port
647 raise NotImplementedError
649 @rwstatus(ret_on_failure
=[""])
650 def do_add_host(self
, account
, host
):
654 account - a cloud account
660 raise NotImplementedError
663 def do_remove_host(self
, account
, host_id
):
667 account - a cloud account
668 host_id - an id for the host
670 raise NotImplementedError
672 @rwstatus(ret_on_failure
=[None])
673 def do_get_host(self
, account
, host_id
):
677 account - a cloud account
678 host_id - an id for host
683 raise NotImplementedError
685 @rwstatus(ret_on_failure
=[[]])
686 def do_get_host_list(self
, account
):
687 """Return a list of hosts
690 account - a cloud account
695 raise NotImplementedError
697 @rwcalstatus(ret_on_failure
=[""])
698 def do_create_virtual_link(self
, account
, link_params
):
699 """Create a new virtual link
702 account - a cloud account
703 link_params - information that defines the type of VDU to create
708 drv
= self
._get
_driver
(account
)
710 kwargs
['CidrBlock'] = link_params
.subnet
712 subnet
= drv
.create_subnet(**kwargs
)
714 subnet
.create_tags(Tags
=[{'Key': 'Name','Value':link_params
.name
}])
715 if link_params
.associate_public_ip
:
716 drv
.modify_subnet(SubnetId
=subnet
.id,MapPublicIpOnLaunch
=link_params
.associate_public_ip
)
720 def do_delete_virtual_link(self
, account
, link_id
):
721 """Delete a virtual link
724 account - a cloud account
725 link_id - id for the virtual-link to be deleted
730 drv
= self
._get
_driver
(account
)
731 port_list
= drv
.get_network_interface_list(SubnetId
=link_id
)
732 for port
in port_list
:
733 if port
and port
.association
and 'AssociationId' in port
.association
:
734 drv
.disassociate_public_ip_from_network_interface(NetworkInterfaceId
=port
.id)
735 if port
and port
.attachment
and 'AttachmentId' in port
.attachment
:
736 drv
.detach_network_interface(AttachmentId
= port
.attachment
['AttachmentId'],Force
=True) #force detach as otherwise delete fails
737 #detach instance takes time; so poll to check port is not in-use
738 port
= drv
.get_network_interface(NetworkInterfaceId
=port
.id)
740 while port
.status
== 'in-use' and retries
< 10:
742 port
= drv
.get_network_interface(NetworkInterfaceId
=port
.id)
743 drv
.delete_network_interface(NetworkInterfaceId
=port
.id)
744 drv
.delete_subnet(link_id
)
747 def _fill_connection_point_info(c_point
, port_info
):
748 """Create a GI object for RwcalYang.VDUInfoParams_ConnectionPoints()
750 Converts EC2.NetworkInterface object returned by AWS driver into
754 port_info - Network Interface information from AWS
756 Protobuf Gi object for RwcalYang.VDUInfoParams_ConnectionPoints
758 c_point
.virtual_link_id
= port_info
.subnet_id
759 c_point
.connection_point_id
= port_info
.id
760 if port_info
.attachment
:
761 c_point
.vdu_id
= port_info
.attachment
['InstanceId']
762 c_point
.ip_address
= port_info
.private_ip_address
763 if port_info
.association
and 'PublicIp' in port_info
.association
:
764 c_point
.public_ip
= port_info
.association
['PublicIp']
765 if port_info
.tag_set
:
766 for tag
in port_info
.tag_set
:
767 if tag
['Key'] == 'Name':
768 c_point
.name
= tag
['Value']
769 if port_info
.status
== 'in-use':
770 c_point
.state
= 'active'
771 elif port_info
.status
== 'available':
772 c_point
.state
= 'inactive'
774 c_point
.state
= 'unknown'
777 def _fill_virtual_link_info(network_info
, port_list
):
778 """Create a GI object for VirtualLinkInfoParams
780 Converts Subnet and NetworkInterface object
781 returned by AWS driver into Protobuf Gi Object
784 network_info - Subnet information from AWS
785 port_list - A list of network interface information from openstack
787 Protobuf Gi object for VirtualLinkInfoParams
789 link
= RwcalYang
.VirtualLinkInfoParams()
790 if network_info
.state
== 'available':
791 link
.state
= 'active'
793 link
.state
= 'inactive'
794 link
.virtual_link_id
= network_info
.subnet_id
795 link
.subnet
= network_info
.cidr_block
796 if network_info
.tags
:
797 for tag
in network_info
.tags
:
798 if tag
['Key'] == 'Name':
799 link
.name
= tag
['Value']
800 for port
in port_list
:
801 c_point
= link
.connection_points
.add()
802 RwcalAWSPlugin
._fill
_connection
_point
_info
(c_point
, port
)
807 def _fill_vdu_info(vm_info
, port_list
):
808 """Create a GI object for VDUInfoParams
810 Converts VM information dictionary object returned by AWS
811 driver into Protobuf Gi Object
814 vm_info - EC2 instance information from AWS
815 port_list - A list of network interface information from AWS
817 Protobuf Gi object for VDUInfoParams
819 vdu
= RwcalYang
.VDUInfoParams()
820 vdu
.vdu_id
= vm_info
.id
821 mgmt_port
= [port
for port
in port_list
if port
.attachment
and port
.attachment
['DeviceIndex'] == 0]
822 assert(len(mgmt_port
) == 1)
823 vdu
.management_ip
= mgmt_port
[0].private_ip_address
824 if mgmt_port
[0].association
and 'PublicIp' in mgmt_port
[0].association
:
825 vdu
.public_ip
= mgmt_port
[0].association
['PublicIp']
826 #For now set managemnet ip also to public ip
827 #vdu.management_ip = vdu.public_ip
829 for tag
in vm_info
.tags
:
830 if tag
['Key'] == 'Name':
831 vdu
.name
= tag
['Value']
832 elif tag
['Key'] == 'node_id':
833 vdu
.node_id
= tag
['Value']
834 vdu
.image_id
= vm_info
.image_id
835 vdu
.flavor_id
= vm_info
.instance_type
836 if vm_info
.state
['Name'] == 'running':
839 vdu
.state
= 'inactive'
840 #if vm_info.placement and 'AvailabilityZone' in vm_info.placement:
841 # vdu.availability_zone = vm_info.placement['AvailabilityZone']
842 # Fill the port information
843 cp_port_list
= [port
for port
in port_list
if port
.attachment
and port
.attachment
['DeviceIndex'] != 0]
845 for port
in cp_port_list
:
846 c_point
= vdu
.connection_points
.add()
847 RwcalAWSPlugin
._fill
_connection
_point
_info
(c_point
, port
)
851 @rwstatus(ret_on_failure
=[None])
852 def do_get_virtual_link(self
, account
, link_id
):
853 """Get information about virtual link.
856 account - a cloud account
857 link_id - id for the virtual-link
860 Object of type RwcalYang.VirtualLinkInfoParams
862 drv
= self
._get
_driver
(account
)
863 network
= drv
.get_subnet(SubnetId
=link_id
)
864 port_list
= drv
.get_network_interface_list(SubnetId
=link_id
)
865 virtual_link
= RwcalAWSPlugin
._fill
_virtual
_link
_info
(network
, port_list
)
868 @rwstatus(ret_on_failure
=[[]])
869 def do_get_virtual_link_list(self
, account
):
870 """Get information about all the virtual links
873 account - a cloud account
876 A list of objects of type RwcalYang.VirtualLinkInfoParams
878 vnf_resources
= RwcalYang
.VNFResources()
879 drv
= self
._get
_driver
(account
)
880 networks
= drv
.get_subnet_list()
881 for network
in networks
:
882 port_list
= drv
.get_network_interface_list(SubnetId
=network
.id)
883 virtual_link
= RwcalAWSPlugin
._fill
_virtual
_link
_info
(network
, port_list
)
884 vnf_resources
.virtual_link_info_list
.append(virtual_link
)
887 def _create_connection_point(self
, account
, c_point
):
889 Create a connection point
891 account - a cloud account
892 c_point - connection_points
894 drv
= self
._get
_driver
(account
)
895 port
= drv
.create_network_interface(SubnetId
=c_point
.virtual_link_id
)
897 port
.create_tags(Tags
=[{'Key': 'Name','Value':c_point
.name
}])
898 if c_point
.associate_public_ip
:
899 drv
.associate_public_ip_to_network_interface(NetworkInterfaceId
= port
.id)
902 def prepare_vdu_on_boot(self
, account
, server_id
,vdu_init_params
,vdu_port_list
= None):
903 cmd
= PREPARE_VM_CMD
.format(key
= account
.aws
.key
,
904 secret
= account
.aws
.secret
,
905 region
= account
.aws
.region
,
906 server_id
= server_id
)
907 if vdu_init_params
.has_field('name'):
908 cmd
+= (" --vdu_name "+ vdu_init_params
.name
)
909 if vdu_init_params
.has_field('node_id'):
910 cmd
+= (" --vdu_node_id "+ vdu_init_params
.node_id
)
911 if vdu_port_list
is not None:
912 for port_id
in vdu_port_list
:
913 cmd
+= (" --vdu_port_list "+ port_id
)
915 exec_path
= 'python3 ' + os
.path
.dirname(aws_drv
.__file
__)
916 exec_cmd
= exec_path
+'/'+cmd
917 self
.log
.info("Running command: %s" %(exec_cmd))
918 subprocess
.call(exec_cmd
, shell
=True)
920 @rwcalstatus(ret_on_failure
=[""])
921 def do_create_vdu(self
, account
, vdu_init
):
922 """Create a new virtual deployment unit
925 account - a cloud account
926 vdu_init - information about VDU to create (RwcalYang.VDUInitParams)
931 drv
= self
._get
_driver
(account
)
932 ### First create required number of ports aka connection points
937 kwargs
['ImageId'] = vdu_init
.image_id
938 if vdu_init
.has_field('flavor_id'):
939 #Get instance type from flavor id which is of form c3.xlarge-1
940 inst_type
= vdu_init
.flavor_id
.split('-')[0]
942 inst_type
= drv
.map_flavor_to_instance_type(ram
= vdu_init
.vm_flavor
.memory_mb
,
943 vcpus
= vdu_init
.vm_flavor
.vcpu_count
,
944 disk
= vdu_init
.vm_flavor
.storage_gb
)
946 kwargs
['InstanceType'] = inst_type
947 if vdu_init
.vdu_init
and vdu_init
.vdu_init
.userdata
:
948 kwargs
['UserData'] = vdu_init
.vdu_init
.userdata
950 #If we need to allocate public IP address create network interface and associate elastic
952 if vdu_init
.allocate_public_address
:
953 port_id
= drv
.create_network_interface(SubnetId
=drv
.default_subnet_id
)
954 drv
.associate_public_ip_to_network_interface(NetworkInterfaceId
= port_id
.id)
955 network_interface
= {'NetworkInterfaceId':port_id
.id,'DeviceIndex':0}
956 kwargs
['NetworkInterfaces'] = [network_interface
]
958 #AWS Driver will use default subnet id to create first network interface
959 # if network interface is not specified and will also have associate public ip
960 # if enabled for the subnet
961 vm_inst
= drv
.create_instance(**kwargs
)
963 # Wait for instance to get to running state before attaching network interface
965 #vm_inst[0].wait_until_running()
968 #vm_inst[0].create_tags(Tags=[{'Key': 'Name','Value':vdu_init.name}])
969 #if vdu_init.node_id is not None:
970 #vm_inst[0].create_tags(Tags=[{'Key':'node_id','Value':vdu_init.node_id}])
972 # Create the connection points
974 for index
,c_point
in enumerate(vdu_init
.connection_points
):
975 port_id
= self
._create
_connection
_point
(account
, c_point
)
976 port_list
.append(port_id
.id)
977 #drv.attach_network_interface(NetworkInterfaceId = port_id.id,InstanceId = vm_inst[0].id,DeviceIndex=index+1)
979 # We wait for instance to get to running state and update name,node_id and attach network intfs
980 self
.prepare_vdu_on_boot(account
, vm_inst
[0].id, vdu_init
, port_list
)
985 def do_modify_vdu(self
, account
, vdu_modify
):
986 """Modify Properties of existing virtual deployment unit
989 account - a cloud account
990 vdu_modify - Information about VDU Modification (RwcalYang.VDUModifyParams)
992 ### First create required number of ports aka connection points
993 drv
= self
._get
_driver
(account
)
996 vm_inst
= drv
.get_instance(vdu_modify
.vdu_id
)
998 if vm_inst
.state
['Name'] != 'running':
999 self
.log
.error("RWCAL-AWS: VM with id %s is not in running state during modify VDU",vdu_modify
.vdu_id
)
1000 raise exceptions
.RWErrorFailure("RWCAL-AWS: VM with id %s is not in running state during modify VDU",vdu_modify
.vdu_id
)
1002 port_list
= drv
.get_network_interface_list(InstanceId
= vdu_modify
.vdu_id
)
1003 used_device_indexs
= [port
.attachment
['DeviceIndex'] for port
in port_list
if port
.attachment
]
1006 for c_point
in vdu_modify
.connection_points_add
:
1007 #Get unused device index
1008 while device_index
in used_device_indexs
:
1009 device_index
= device_index
+1
1010 port_id
= self
._create
_connection
_point
(account
, c_point
)
1011 drv
.attach_network_interface(NetworkInterfaceId
= port_id
.id,InstanceId
= vdu_modify
.vdu_id
,DeviceIndex
=device_index
)
1013 ### Detach the requested connection_points
1014 for c_point
in vdu_modify
.connection_points_remove
:
1015 port
= drv
.get_network_interface(NetworkInterfaceId
=c_point
.connection_point_id
)
1016 #Check if elastic IP is associated with interface and release it
1017 if port
and port
.association
and 'AssociationId' in port
.association
:
1018 drv
.disassociate_public_ip_from_network_interface(NetworkInterfaceId
=port
.id)
1019 if port
and port
.attachment
and port
.attachment
['DeviceIndex'] != 0:
1020 drv
.detach_network_interface(AttachmentId
= port
.attachment
['AttachmentId'],Force
=True) #force detach as otherwise delete fails
1022 self
.log
.error("RWCAL-AWS: Cannot modify connection port at index 0")
1024 # Delete the connection points. Interfaces take time to get detached from instance and so
1025 # we check status before doing delete network interface
1026 for c_point
in vdu_modify
.connection_points_remove
:
1027 port
= drv
.get_network_interface(NetworkInterfaceId
=c_point
.connection_point_id
)
1029 if port
and port
.attachment
and port
.attachment
['DeviceIndex'] == 0:
1030 self
.log
.error("RWCAL-AWS: Cannot modify connection port at index 0")
1032 while port
.status
== 'in-use' and retries
< 10:
1034 port
= drv
.get_network_interface(NetworkInterfaceId
=c_point
.connection_point_id
)
1035 drv
.delete_network_interface(port
.id)
1037 def cleanup_vdu_on_term(self
, account
, server_id
,vdu_port_list
= None):
1038 cmd
= DELETE_VM_CMD
.format(key
= account
.aws
.key
,
1039 secret
= account
.aws
.secret
,
1040 region
= account
.aws
.region
,
1041 server_id
= server_id
)
1042 if vdu_port_list
is not None:
1043 for port_id
in vdu_port_list
:
1044 cmd
+= (" --vdu_port_list "+ port_id
)
1046 exec_path
= 'python3 ' + os
.path
.dirname(aws_drv
.__file
__)
1047 exec_cmd
= exec_path
+'/'+cmd
1048 self
.log
.info("Running command: %s" %(exec_cmd))
1049 subprocess
.call(exec_cmd
, shell
=True)
1052 def do_delete_vdu(self
, account
, vdu_id
):
1053 """Delete a virtual deployment unit
1056 account - a cloud account
1057 vdu_id - id for the vdu to be deleted
1062 drv
= self
._get
_driver
(account
)
1063 ### Get list of port on VM and delete them.
1064 #vm_inst = drv.get_instance(vdu_id)
1066 port_list
= drv
.get_network_interface_list(InstanceId
= vdu_id
)
1067 delete_port_list
= [port
.id for port
in port_list
if port
.attachment
and port
.attachment
['DeleteOnTermination'] is False]
1068 drv
.terminate_instance(vdu_id
)
1070 self
.cleanup_vdu_on_term(account
,vdu_id
,delete_port_list
)
1073 @rwstatus(ret_on_failure
=[None])
1074 def do_get_vdu(self
, account
, vdu_id
):
1075 """Get information about a virtual deployment unit.
1078 account - a cloud account
1079 vdu_id - id for the vdu
1082 Object of type RwcalYang.VDUInfoParams
1084 drv
= self
._get
_driver
(account
)
1086 ### Get list of ports excluding the one for management network
1087 vm
= drv
.get_instance(vdu_id
)
1088 port_list
= drv
.get_network_interface_list(InstanceId
= vdu_id
)
1089 return RwcalAWSPlugin
._fill
_vdu
_info
(vm
,port_list
)
1092 @rwstatus(ret_on_failure
=[[]])
1093 def do_get_vdu_list(self
, account
):
1094 """Get information about all the virtual deployment units
1097 account - a cloud account
1100 A list of objects of type RwcalYang.VDUInfoParams
1102 vnf_resources
= RwcalYang
.VNFResources()
1103 drv
= self
._get
_driver
(account
)
1104 vms
= drv
.list_instances()
1106 ### Get list of ports excluding one for management network
1107 port_list
= [p
for p
in drv
.get_network_interface_list(InstanceId
= vm
.id)]
1108 vdu
= RwcalAWSPlugin
._fill
_vdu
_info
(vm
,
1110 vnf_resources
.vdu_info_list
.append(vdu
)
1111 return vnf_resources