--- /dev/null
+
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import time
+import os
+import subprocess
+import logging
+import rift.rwcal.aws as aws_drv
+import rw_status
+import rift.cal.rwcal_status as rwcal_status
+import rwlogger
+import rift.rwcal.aws.exceptions as exceptions
+from gi import require_version
+require_version('RwCal', '1.0')
+
+from gi.repository import (
+ GObject,
+ RwCal,
+ RwTypes,
+ RwcalYang)
+
+
+PREPARE_VM_CMD = "prepare_vm.py --aws_key {key} --aws_secret {secret} --aws_region {region} --server_id {server_id}"
+DELETE_VM_CMD = "delete_vm.py --aws_key {key} --aws_secret {secret} --aws_region {region} --server_id {server_id}"
+
+rwstatus_exception_map = {IndexError: RwTypes.RwStatus.NOTFOUND,
+ KeyError: RwTypes.RwStatus.NOTFOUND,
+ NotImplementedError: RwTypes.RwStatus.NOT_IMPLEMENTED,
+ AttributeError: RwTypes.RwStatus.FAILURE,
+ exceptions.RWErrorNotFound: RwTypes.RwStatus.NOTFOUND,
+ exceptions.RWErrorDuplicate: RwTypes.RwStatus.DUPLICATE,
+ exceptions.RWErrorExists: RwTypes.RwStatus.EXISTS,
+ exceptions.RWErrorNotConnected: RwTypes.RwStatus.NOTCONNECTED,
+ }
+
+rwstatus = rw_status.rwstatus_from_exc_map(rwstatus_exception_map)
+rwcalstatus = rwcal_status.rwcalstatus_from_exc_map(rwstatus_exception_map)
+
+class RwcalAWSPlugin(GObject.Object, RwCal.Cloud):
+ """This class implements the CAL VALA methods for AWS."""
+
+ flavor_id = 1;
+ instance_num = 1
+ def __init__(self):
+ GObject.Object.__init__(self)
+ self._driver_class = aws_drv.AWSDriver
+ self._flavor_list = []
+ self.log = logging.getLogger('rwcal.aws.%s' % RwcalAWSPlugin.instance_num)
+ self.log.setLevel(logging.DEBUG)
+
+ RwcalAWSPlugin.instance_num += 1
+
+ def _get_driver(self, account):
+ return self._driver_class(key = account.aws.key,
+ secret = account.aws.secret,
+ region = account.aws.region,
+ ssh_key = account.aws.ssh_key,
+ vpcid = account.aws.vpcid,
+ availability_zone = account.aws.availability_zone,
+ default_subnet_id = account.aws.default_subnet_id)
+
+ @rwstatus
+ def do_init(self, rwlog_ctx):
+ self.log.addHandler(
+ rwlogger.RwLogger(
+ category="rw-cal-log",
+ subcategory="aws",
+ log_hdl=rwlog_ctx,
+ )
+ )
+
+ @rwstatus(ret_on_failure=[None])
+ def do_validate_cloud_creds(self, account):
+ """
+ Validates the cloud account credentials for the specified account.
+ Performs an access to the resources using underlying API. If creds
+ are not valid, returns an error code & reason string
+ Arguments:
+ account - a cloud account to validate
+
+ Returns:
+ Validation Code and Details String
+ """
+ status = RwcalYang.CloudConnectionStatus(
+ status="success",
+ details="AWS Cloud Account validation not implemented yet"
+ )
+
+ return status
+
+ @rwstatus(ret_on_failure=[""])
+ def do_get_management_network(self, account):
+ """
+ Returns the management network associated with the specified account.
+ Arguments:
+ account - a cloud account
+
+ Returns:
+ The management network
+ """
+ raise NotImplementedError
+
+ @rwstatus(ret_on_failure=[""])
+ def do_create_tenant(self, account, name):
+ """Create a new tenant.
+
+ Arguments:
+ account - a cloud account
+ name - name of the tenant
+
+ Returns:
+ The tenant id
+ """
+ raise NotImplementedError
+
+ @rwstatus
+ def do_delete_tenant(self, account, tenant_id):
+ """delete a tenant.
+
+ Arguments:
+ account - a cloud account
+ tenant_id - id of the tenant
+ """
+ raise NotImplementedError
+
+ @rwstatus(ret_on_failure=[[]])
+ def do_get_tenant_list(self, account):
+ """List tenants.
+
+ Arguments:
+ account - a cloud account
+
+ Returns:
+ List of tenants
+ """
+ raise NotImplementedError
+
+ @rwstatus(ret_on_failure=[""])
+ def do_create_role(self, account, name):
+ """Create a new user.
+
+ Arguments:
+ account - a cloud account
+ name - name of the user
+
+ Returns:
+ The user id
+ """
+ raise NotImplementedError
+
+ @rwstatus
+ def do_delete_role(self, account, role_id):
+ """Delete a user.
+
+ Arguments:
+ account - a cloud account
+ role_id - id of the user
+ """
+ raise NotImplementedError
+
+ @rwstatus(ret_on_failure=[[]])
+ def do_get_role_list(self, account):
+ """List roles.
+
+ Arguments:
+ account - a cloud account
+
+ Returns:
+ List of roles
+ """
+ raise NotImplementedError
+
+ @rwstatus(ret_on_failure=[""])
+ def do_create_image(self, account, image):
+ """Create an image
+
+ Arguments:
+ account - a cloud account
+ image - a description of the image to create
+
+ Returns:
+ The image id
+ """
+ raise NotImplementedError
+
+ @rwstatus
+ def do_delete_image(self, account, image_id):
+ """Delete a vm image.
+
+ Arguments:
+ account - a cloud account
+ image_id - id of the image to delete
+ """
+ raise NotImplementedError
+
+ @staticmethod
+ def _fill_image_info(img_info):
+ """Create a GI object from image info dictionary
+
+ Converts image information dictionary object returned by AWS
+ driver into Protobuf Gi Object
+
+ Arguments:
+ account - a cloud account
+ img_info - image information dictionary object from AWS
+
+ Returns:
+ The ImageInfoItem
+ """
+ img = RwcalYang.ImageInfoItem()
+ img.name = img_info.name
+ img.id = img_info.id
+
+ #tag_fields = ['checksum']
+ # Look for any properties
+ if img_info.tags:
+ for tag in img_info.tags:
+ if tag['Key'] == 'checksum':
+ setattr(img, tag['Key'], tag['Value'])
+ img.disk_format = 'ami'
+ if img_info.state == 'available':
+ img.state = 'active'
+ else:
+ img.state = 'inactive'
+ return img
+
+ @rwstatus(ret_on_failure=[[]])
+ def do_get_image_list(self, account):
+ """Return a list of the names of all available images.
+
+ Arguments:
+ account - a cloud account
+
+ Returns:
+ The the list of images in VimResources object
+ """
+ response = RwcalYang.VimResources()
+ images = self._get_driver(account).list_images()
+ for img in images:
+ response.imageinfo_list.append(RwcalAWSPlugin._fill_image_info(img))
+ return response
+
+ @rwstatus(ret_on_failure=[None])
+ def do_get_image(self, account, image_id):
+ """Return a image information.
+
+ Arguments:
+ account - a cloud account
+ image_id - an id of the image
+
+ Returns:
+ ImageInfoItem object containing image information.
+ """
+ image = self._get_driver(account).get_image(image_id)
+ return RwcalAWSPlugin._fill_image_info(image)
+
+ @rwstatus(ret_on_failure=[""])
+ def do_create_vm(self, account, vminfo):
+ """Create a new virtual machine.
+
+ Arguments:
+ account - a cloud account
+ vminfo - information that defines the type of VM to create
+
+ Returns:
+ The image id
+ """
+ raise NotImplementedError
+
+ @rwstatus
+ def do_start_vm(self, account, vm_id):
+ """Start an existing virtual machine.
+
+ Arguments:
+ account - a cloud account
+ vm_id - an id of the VM
+ """
+ raise NotImplementedError
+
+ @rwstatus
+ def do_stop_vm(self, account, vm_id):
+ """Stop a running virtual machine.
+
+ Arguments:
+ account - a cloud account
+ vm_id - an id of the VM
+ """
+ raise NotImplementedError
+
+ @rwstatus
+ def do_delete_vm(self, account, vm_id):
+ """Delete a virtual machine.
+
+ Arguments:
+ account - a cloud account
+ vm_id - an id of the VM
+ """
+ raise NotImplementedError
+
+ @rwstatus
+ def do_reboot_vm(self, account, vm_id):
+ """Reboot a virtual machine.
+
+ Arguments:
+ account - a cloud account
+ vm_id - an id of the VM
+ """
+ raise NotImplementedError
+
+ @staticmethod
+ def _fill_vm_info(vm_info):
+ """Create a GI object from vm info dictionary
+
+ Converts VM information dictionary object returned by openstack
+ driver into Protobuf Gi Object
+
+ Arguments:
+ vm_info - VM information from AWS
+
+ Returns:
+ Protobuf Gi object for VM
+ """
+ vm = RwcalYang.VMInfoItem()
+ vm.vm_id = vm_info.id
+ vm.image_id = vm_info.image_id
+ vm.flavor_id = vm_info.instance_type
+ if vm_info.state['Name'] == 'running':
+ vm.state = 'active'
+ else:
+ vm.state = 'inactive'
+ for network_intf in vm_info.network_interfaces:
+ if 'Attachment' in network_intf and network_intf['Attachment']['DeviceIndex'] == 0:
+ if 'Association' in network_intf and 'PublicIp' in network_intf['Association']:
+ vm.public_ip = network_intf['Association']['PublicIp']
+ vm.management_ip = network_intf['PrivateIpAddress']
+ else:
+ addr = vm.private_ip_list.add()
+ addr.ip_address = network_intf['PrivateIpAddress']
+ if 'Association' in network_intf and 'PublicIp' in network_intf['Association']:
+ addr = vm.public_ip_list.add()
+ addr.ip_address = network_intf['Association']['PublicIp']
+
+ if vm_info.placement and 'AvailabilityZone' in vm_info.placement:
+ vm.availability_zone = vm_info.placement['AvailabilityZone']
+ if vm_info.tags:
+ for tag in vm_info.tags:
+ if tag['Key'] == 'Name':
+ vm.vm_name = tag['Value']
+ elif tag['Key'] in vm.user_tags.fields:
+ setattr(vm.user_tags,tag['Key'],tag['Value'])
+ return vm
+
+ @rwstatus(ret_on_failure=[[]])
+ def do_get_vm_list(self, account):
+ """Return a list of the VMs as vala boxed objects
+
+ Arguments:
+ account - a cloud account
+
+ Returns:
+ List containing VM information
+ """
+ response = RwcalYang.VimResources()
+ vms = self._get_driver(account).list_instances()
+ for vm in vms:
+ response.vminfo_list.append(RwcalAWSPlugin._fill_vm_info(vm))
+ return response
+
+ @rwstatus(ret_on_failure=[None])
+ def do_get_vm(self, account, id):
+ """Return vm information.
+
+ Arguments:
+ account - a cloud account
+ id - an id for the VM
+
+ Returns:
+ VM information
+ """
+ vm = self._get_driver(account).get_instance(id)
+ return RwcalAWSPlugin._fill_vm_info(vm)
+
+ @rwstatus(ret_on_failure=[""])
+ def do_create_flavor(self, account, flavor):
+ """Create new flavor.
+ AWS has fixed set of AWS types and so we map flavor to existing instance type
+ and create local flavor for the same.
+
+ Arguments:
+ account - a cloud account
+ flavor - flavor of the VM
+
+ Returns:
+ flavor id (with EC2 instance type included in id)
+ """
+ drv = self._get_driver(account)
+ inst_type = drv.map_flavor_to_instance_type(ram = flavor.vm_flavor.memory_mb,
+ vcpus = flavor.vm_flavor.vcpu_count,
+ disk = flavor.vm_flavor.storage_gb)
+
+ new_flavor = RwcalYang.FlavorInfoItem()
+ new_flavor.name = flavor.name
+ new_flavor.vm_flavor.memory_mb = flavor.vm_flavor.memory_mb
+ new_flavor.vm_flavor.vcpu_count = flavor.vm_flavor.vcpu_count
+ new_flavor.vm_flavor.storage_gb = flavor.vm_flavor.storage_gb
+ new_flavor.id = inst_type + '-' + str(RwcalAWSPlugin.flavor_id)
+ RwcalAWSPlugin.flavor_id = RwcalAWSPlugin.flavor_id+1
+ self._flavor_list.append(new_flavor)
+ return new_flavor.id
+
+ @rwstatus
+ def do_delete_flavor(self, account, flavor_id):
+ """Delete flavor.
+
+ Arguments:
+ account - a cloud account
+ flavor_id - id flavor of the VM
+ """
+
+ flavor = [flav for flav in self._flavor_list if flav.id == flavor_id]
+ self._flavor_list.delete(flavor[0])
+
+ @staticmethod
+ def _fill_flavor_info(flavor_info):
+ """Create a GI object from flavor info dictionary
+
+ Converts Flavor information dictionary object returned by openstack
+ driver into Protobuf Gi Object
+
+ Arguments:
+ flavor_info: Flavor information from openstack
+
+ Returns:
+ Object of class FlavorInfoItem
+ """
+ flavor = RwcalYang.FlavorInfoItem()
+ flavor.name = flavor_info.name
+ flavor.id = flavor_info.id
+ flavor.vm_flavor.memory_mb = flavor_info.vm_flavor.memory_mb
+ flavor.vm_flavor.vcpu_count = flavor_info.vm_flavor.vcpu_count
+ flavor.vm_flavor.storage_gb = flavor_info.vm_flavor.storage_gb
+ return flavor
+
+ @rwstatus(ret_on_failure=[[]])
+ def do_get_flavor_list(self, account):
+ """Return flavor information.
+
+ Arguments:
+ account - a cloud account
+
+ Returns:
+ List of flavors
+ """
+ response = RwcalYang.VimResources()
+ for flv in self._flavor_list:
+ response.flavorinfo_list.append(RwcalAWSPlugin._fill_flavor_info(flv))
+ return response
+
+
+ @rwstatus(ret_on_failure=[None])
+ def do_get_flavor(self, account, id):
+ """Return flavor information.
+
+ Arguments:
+ account - a cloud account
+ id - an id for the flavor
+
+ Returns:
+ Flavor info item
+ """
+ flavor = [flav for flav in self._flavor_list if flav.id == id]
+ return (RwcalAWSPlugin._fill_flavor_info(flavor[0]))
+
+ def _fill_network_info(self, network_info, account):
+ """Create a GI object from network info dictionary
+
+ Converts Network information dictionary object returned by AWS
+ driver into Protobuf Gi Object
+
+ Arguments:
+ network_info - Network information from AWS
+ account - a cloud account
+
+ Returns:
+ Network info item
+ """
+ network = RwcalYang.NetworkInfoItem()
+ network.network_id = network_info.subnet_id
+ network.subnet = network_info.cidr_block
+ if network_info.tags:
+ for tag in network_info.tags:
+ if tag['Key'] == 'Name':
+ network.network_name = tag['Value']
+ return network
+
+ @rwstatus(ret_on_failure=[[]])
+ def do_get_network_list(self, account):
+ """Return a list of networks
+
+ Arguments:
+ account - a cloud account
+
+ Returns:
+ List of networks
+ """
+ response = RwcalYang.VimResources()
+ networks = self._get_driver(account).get_subnet_list()
+ for network in networks:
+ response.networkinfo_list.append(self._fill_network_info(network, account))
+ return response
+
+ @rwstatus(ret_on_failure=[None])
+ def do_get_network(self, account, id):
+ """Return a network
+
+ Arguments:
+ account - a cloud account
+ id - an id for the network
+
+ Returns:
+ Network info item
+ """
+ network = self._get_driver(account).get_subnet(id)
+ return self._fill_network_info(network, account)
+
+ @rwstatus(ret_on_failure=[""])
+ def do_create_network(self, account, network):
+ """Create a new network
+
+ Arguments:
+ account - a cloud account
+ network - Network object
+
+ Returns:
+ Network id
+ """
+ raise NotImplementedError
+
+ @rwstatus
+ def do_delete_network(self, account, network_id):
+ """Delete a network
+
+ Arguments:
+ account - a cloud account
+ network_id - an id for the network
+ """
+ raise NotImplementedError
+
+ @staticmethod
+ def _fill_port_info(port_info):
+ """Create a GI object from port info dictionary
+
+ Converts Port information dictionary object returned by AWS
+ driver into Protobuf Gi Object
+
+ Arguments:
+ port_info - Port/Network interface information from AWS
+
+ Returns:
+ Port info item
+ """
+ port = RwcalYang.PortInfoItem()
+
+ port.port_id = port_info.id
+ port.network_id = port_info.subnet_id
+ if port_info.attachment and 'InstanceId' in port_info.attachment:
+ port.vm_id = port_info.attachment['InstanceId']
+ port.ip_address = port_info.private_ip_address
+ if port_info.status == 'in-use':
+ port.port_state = 'active'
+ elif port_info.status == 'available':
+ port.port_state = 'inactive'
+ else:
+ port.port_state = 'unknown'
+ if port_info.tag_set:
+ for tag in port_info.tag_set:
+ if tag['Key'] == 'Name':
+ port.port_name = tag['Value']
+ return port
+
+
+ @rwstatus(ret_on_failure=[None])
+ def do_get_port(self, account, port_id):
+ """Return a port
+
+ Arguments:
+ account - a cloud account
+ port_id - an id for the port
+
+ Returns:
+ Port info item
+ """
+ port = self._get_driver(account).get_network_interface(port_id)
+ return RwcalAWSPlugin._fill_port_info(port)
+
+ @rwstatus(ret_on_failure=[[]])
+ def do_get_port_list(self, account):
+ """Return a list of ports
+
+ Arguments:
+ account - a cloud account
+
+ Returns:
+ Port info list
+ """
+ response = RwcalYang.VimResources()
+ ports = self._get_driver(account).get_network_interface_list()
+ for port in ports:
+ response.portinfo_list.append(RwcalAWSPlugin._fill_port_info(port))
+ return response
+
+ @rwstatus(ret_on_failure=[""])
+ def do_create_port(self, account, port):
+ """Create a new port
+
+ Arguments:
+ account - a cloud account
+ port - port object
+
+ Returns:
+ Port id
+ """
+ raise NotImplementedError
+
+ @rwstatus
+ def do_delete_port(self, account, port_id):
+ """Delete a port
+
+ Arguments:
+ account - a cloud account
+ port_id - an id for port
+ """
+ raise NotImplementedError
+
+ @rwstatus(ret_on_failure=[""])
+ def do_add_host(self, account, host):
+ """Add a new host
+
+ Arguments:
+ account - a cloud account
+ host - a host object
+
+ Returns:
+ An id for the host
+ """
+ raise NotImplementedError
+
+ @rwstatus
+ def do_remove_host(self, account, host_id):
+ """Remove a host
+
+ Arguments:
+ account - a cloud account
+ host_id - an id for the host
+ """
+ raise NotImplementedError
+
+ @rwstatus(ret_on_failure=[None])
+ def do_get_host(self, account, host_id):
+ """Return a host
+
+ Arguments:
+ account - a cloud account
+ host_id - an id for host
+
+ Returns:
+ Host info item
+ """
+ raise NotImplementedError
+
+ @rwstatus(ret_on_failure=[[]])
+ def do_get_host_list(self, account):
+ """Return a list of hosts
+
+ Arguments:
+ account - a cloud account
+
+ Returns:
+ List of hosts
+ """
+ raise NotImplementedError
+
+ @rwcalstatus(ret_on_failure=[""])
+ def do_create_virtual_link(self, account, link_params):
+ """Create a new virtual link
+
+ Arguments:
+ account - a cloud account
+ link_params - information that defines the type of VDU to create
+
+ Returns:
+ The vdu_id
+ """
+ drv = self._get_driver(account)
+ kwargs = {}
+ kwargs['CidrBlock'] = link_params.subnet
+
+ subnet = drv.create_subnet(**kwargs)
+ if link_params.name:
+ subnet.create_tags(Tags=[{'Key': 'Name','Value':link_params.name}])
+ if link_params.associate_public_ip:
+ drv.modify_subnet(SubnetId=subnet.id,MapPublicIpOnLaunch=link_params.associate_public_ip)
+ return subnet.id
+
+ @rwstatus
+ def do_delete_virtual_link(self, account, link_id):
+ """Delete a virtual link
+
+ Arguments:
+ account - a cloud account
+ link_id - id for the virtual-link to be deleted
+
+ Returns:
+ None
+ """
+ drv = self._get_driver(account)
+ port_list = drv.get_network_interface_list(SubnetId=link_id)
+ for port in port_list:
+ if port and port.association and 'AssociationId' in port.association:
+ drv.disassociate_public_ip_from_network_interface(NetworkInterfaceId=port.id)
+ if port and port.attachment and 'AttachmentId' in port.attachment:
+ drv.detach_network_interface(AttachmentId = port.attachment['AttachmentId'],Force=True) #force detach as otherwise delete fails
+ #detach instance takes time; so poll to check port is not in-use
+ port = drv.get_network_interface(NetworkInterfaceId=port.id)
+ retries = 0
+ while port.status == 'in-use' and retries < 10:
+ time.sleep(5)
+ port = drv.get_network_interface(NetworkInterfaceId=port.id)
+ drv.delete_network_interface(NetworkInterfaceId=port.id)
+ drv.delete_subnet(link_id)
+
+ @staticmethod
+ def _fill_connection_point_info(c_point, port_info):
+ """Create a GI object for RwcalYang.VDUInfoParams_ConnectionPoints()
+
+ Converts EC2.NetworkInterface object returned by AWS driver into
+ Protobuf Gi Object
+
+ Arguments:
+ port_info - Network Interface information from AWS
+ Returns:
+ Protobuf Gi object for RwcalYang.VDUInfoParams_ConnectionPoints
+ """
+ c_point.virtual_link_id = port_info.subnet_id
+ c_point.connection_point_id = port_info.id
+ if port_info.attachment:
+ c_point.vdu_id = port_info.attachment['InstanceId']
+ c_point.ip_address = port_info.private_ip_address
+ if port_info.association and 'PublicIp' in port_info.association:
+ c_point.public_ip = port_info.association['PublicIp']
+ if port_info.tag_set:
+ for tag in port_info.tag_set:
+ if tag['Key'] == 'Name':
+ c_point.name = tag['Value']
+ if port_info.status == 'in-use':
+ c_point.state = 'active'
+ elif port_info.status == 'available':
+ c_point.state = 'inactive'
+ else:
+ c_point.state = 'unknown'
+
+ @staticmethod
+ def _fill_virtual_link_info(network_info, port_list):
+ """Create a GI object for VirtualLinkInfoParams
+
+ Converts Subnet and NetworkInterface object
+ returned by AWS driver into Protobuf Gi Object
+
+ Arguments:
+ network_info - Subnet information from AWS
+ port_list - A list of network interface information from openstack
+ Returns:
+ Protobuf Gi object for VirtualLinkInfoParams
+ """
+ link = RwcalYang.VirtualLinkInfoParams()
+ if network_info.state == 'available':
+ link.state = 'active'
+ else:
+ link.state = 'inactive'
+ link.virtual_link_id = network_info.subnet_id
+ link.subnet = network_info.cidr_block
+ if network_info.tags:
+ for tag in network_info.tags:
+ if tag['Key'] == 'Name':
+ link.name = tag['Value']
+ for port in port_list:
+ c_point = link.connection_points.add()
+ RwcalAWSPlugin._fill_connection_point_info(c_point, port)
+
+ return link
+
+ @staticmethod
+ def _fill_vdu_info(vm_info, port_list):
+ """Create a GI object for VDUInfoParams
+
+ Converts VM information dictionary object returned by AWS
+ driver into Protobuf Gi Object
+
+ Arguments:
+ vm_info - EC2 instance information from AWS
+ port_list - A list of network interface information from AWS
+ Returns:
+ Protobuf Gi object for VDUInfoParams
+ """
+ vdu = RwcalYang.VDUInfoParams()
+ vdu.vdu_id = vm_info.id
+ mgmt_port = [port for port in port_list if port.attachment and port.attachment['DeviceIndex'] == 0]
+ assert(len(mgmt_port) == 1)
+ vdu.management_ip = mgmt_port[0].private_ip_address
+ if mgmt_port[0].association and 'PublicIp' in mgmt_port[0].association:
+ vdu.public_ip = mgmt_port[0].association['PublicIp']
+ #For now set managemnet ip also to public ip
+ #vdu.management_ip = vdu.public_ip
+ if vm_info.tags:
+ for tag in vm_info.tags:
+ if tag['Key'] == 'Name':
+ vdu.name = tag['Value']
+ elif tag['Key'] == 'node_id':
+ vdu.node_id = tag['Value']
+ vdu.image_id = vm_info.image_id
+ vdu.flavor_id = vm_info.instance_type
+ if vm_info.state['Name'] == 'running':
+ vdu.state = 'active'
+ else:
+ vdu.state = 'inactive'
+ #if vm_info.placement and 'AvailabilityZone' in vm_info.placement:
+ # vdu.availability_zone = vm_info.placement['AvailabilityZone']
+ # Fill the port information
+ cp_port_list = [port for port in port_list if port.attachment and port.attachment['DeviceIndex'] != 0]
+
+ for port in cp_port_list:
+ c_point = vdu.connection_points.add()
+ RwcalAWSPlugin._fill_connection_point_info(c_point, port)
+ return vdu
+
+
+ @rwstatus(ret_on_failure=[None])
+ def do_get_virtual_link(self, account, link_id):
+ """Get information about virtual link.
+
+ Arguments:
+ account - a cloud account
+ link_id - id for the virtual-link
+
+ Returns:
+ Object of type RwcalYang.VirtualLinkInfoParams
+ """
+ drv = self._get_driver(account)
+ network = drv.get_subnet(SubnetId=link_id)
+ port_list = drv.get_network_interface_list(SubnetId=link_id)
+ virtual_link = RwcalAWSPlugin._fill_virtual_link_info(network, port_list)
+ return virtual_link
+
+ @rwstatus(ret_on_failure=[[]])
+ def do_get_virtual_link_list(self, account):
+ """Get information about all the virtual links
+
+ Arguments:
+ account - a cloud account
+
+ Returns:
+ A list of objects of type RwcalYang.VirtualLinkInfoParams
+ """
+ vnf_resources = RwcalYang.VNFResources()
+ drv = self._get_driver(account)
+ networks = drv.get_subnet_list()
+ for network in networks:
+ port_list = drv.get_network_interface_list(SubnetId=network.id)
+ virtual_link = RwcalAWSPlugin._fill_virtual_link_info(network, port_list)
+ vnf_resources.virtual_link_info_list.append(virtual_link)
+ return vnf_resources
+
+ def _create_connection_point(self, account, c_point):
+ """
+ Create a connection point
+ Arguments:
+ account - a cloud account
+ c_point - connection_points
+ """
+ drv = self._get_driver(account)
+ port = drv.create_network_interface(SubnetId=c_point.virtual_link_id)
+ if c_point.name:
+ port.create_tags(Tags=[{'Key': 'Name','Value':c_point.name}])
+ if c_point.associate_public_ip:
+ drv.associate_public_ip_to_network_interface(NetworkInterfaceId = port.id)
+ return port
+
+ def prepare_vdu_on_boot(self, account, server_id,vdu_init_params,vdu_port_list = None):
+ cmd = PREPARE_VM_CMD.format(key = account.aws.key,
+ secret = account.aws.secret,
+ region = account.aws.region,
+ server_id = server_id)
+ if vdu_init_params.has_field('name'):
+ cmd += (" --vdu_name "+ vdu_init_params.name)
+ if vdu_init_params.has_field('node_id'):
+ cmd += (" --vdu_node_id "+ vdu_init_params.node_id)
+ if vdu_port_list is not None:
+ for port_id in vdu_port_list:
+ cmd += (" --vdu_port_list "+ port_id)
+
+ exec_path = 'python3 ' + os.path.dirname(aws_drv.__file__)
+ exec_cmd = exec_path+'/'+cmd
+ self.log.info("Running command: %s" %(exec_cmd))
+ subprocess.call(exec_cmd, shell=True)
+
+ @rwcalstatus(ret_on_failure=[""])
+ def do_create_vdu(self, account, vdu_init):
+ """Create a new virtual deployment unit
+
+ Arguments:
+ account - a cloud account
+ vdu_init - information about VDU to create (RwcalYang.VDUInitParams)
+
+ Returns:
+ The vdu_id
+ """
+ drv = self._get_driver(account)
+ ### First create required number of ports aka connection points
+ port_list = []
+
+ ### Now Create VM
+ kwargs = {}
+ kwargs['ImageId'] = vdu_init.image_id
+ if vdu_init.has_field('flavor_id'):
+ #Get instance type from flavor id which is of form c3.xlarge-1
+ inst_type = vdu_init.flavor_id.split('-')[0]
+ else:
+ inst_type = drv.map_flavor_to_instance_type(ram = vdu_init.vm_flavor.memory_mb,
+ vcpus = vdu_init.vm_flavor.vcpu_count,
+ disk = vdu_init.vm_flavor.storage_gb)
+
+ kwargs['InstanceType'] = inst_type
+ if vdu_init.vdu_init and vdu_init.vdu_init.userdata:
+ kwargs['UserData'] = vdu_init.vdu_init.userdata
+
+ #If we need to allocate public IP address create network interface and associate elastic
+ #ip to interface
+ if vdu_init.allocate_public_address:
+ port_id = drv.create_network_interface(SubnetId=drv.default_subnet_id)
+ drv.associate_public_ip_to_network_interface(NetworkInterfaceId = port_id.id)
+ network_interface = {'NetworkInterfaceId':port_id.id,'DeviceIndex':0}
+ kwargs['NetworkInterfaces'] = [network_interface]
+
+ #AWS Driver will use default subnet id to create first network interface
+ # if network interface is not specified and will also have associate public ip
+ # if enabled for the subnet
+ vm_inst = drv.create_instance(**kwargs)
+
+ # Wait for instance to get to running state before attaching network interface
+ # to instance
+ #vm_inst[0].wait_until_running()
+
+ #if vdu_init.name:
+ #vm_inst[0].create_tags(Tags=[{'Key': 'Name','Value':vdu_init.name}])
+ #if vdu_init.node_id is not None:
+ #vm_inst[0].create_tags(Tags=[{'Key':'node_id','Value':vdu_init.node_id}])
+
+ # Create the connection points
+ port_list = []
+ for index,c_point in enumerate(vdu_init.connection_points):
+ port_id = self._create_connection_point(account, c_point)
+ port_list.append(port_id.id)
+ #drv.attach_network_interface(NetworkInterfaceId = port_id.id,InstanceId = vm_inst[0].id,DeviceIndex=index+1)
+
+ # We wait for instance to get to running state and update name,node_id and attach network intfs
+ self.prepare_vdu_on_boot(account, vm_inst[0].id, vdu_init, port_list)
+
+ return vm_inst[0].id
+
+ @rwstatus
+ def do_modify_vdu(self, account, vdu_modify):
+ """Modify Properties of existing virtual deployment unit
+
+ Arguments:
+ account - a cloud account
+ vdu_modify - Information about VDU Modification (RwcalYang.VDUModifyParams)
+ """
+ ### First create required number of ports aka connection points
+ drv = self._get_driver(account)
+ port_list = []
+
+ vm_inst = drv.get_instance(vdu_modify.vdu_id)
+
+ if vm_inst.state['Name'] != 'running':
+ self.log.error("RWCAL-AWS: VM with id %s is not in running state during modify VDU",vdu_modify.vdu_id)
+ raise exceptions.RWErrorFailure("RWCAL-AWS: VM with id %s is not in running state during modify VDU",vdu_modify.vdu_id)
+
+ port_list = drv.get_network_interface_list(InstanceId = vdu_modify.vdu_id)
+ used_device_indexs = [port.attachment['DeviceIndex'] for port in port_list if port.attachment]
+
+ device_index = 1
+ for c_point in vdu_modify.connection_points_add:
+ #Get unused device index
+ while device_index in used_device_indexs:
+ device_index = device_index+1
+ port_id = self._create_connection_point(account, c_point)
+ drv.attach_network_interface(NetworkInterfaceId = port_id.id,InstanceId = vdu_modify.vdu_id,DeviceIndex =device_index)
+
+ ### Detach the requested connection_points
+ for c_point in vdu_modify.connection_points_remove:
+ port = drv.get_network_interface(NetworkInterfaceId=c_point.connection_point_id)
+ #Check if elastic IP is associated with interface and release it
+ if port and port.association and 'AssociationId' in port.association:
+ drv.disassociate_public_ip_from_network_interface(NetworkInterfaceId=port.id)
+ if port and port.attachment and port.attachment['DeviceIndex'] != 0:
+ drv.detach_network_interface(AttachmentId = port.attachment['AttachmentId'],Force=True) #force detach as otherwise delete fails
+ else:
+ self.log.error("RWCAL-AWS: Cannot modify connection port at index 0")
+
+ # Delete the connection points. Interfaces take time to get detached from instance and so
+ # we check status before doing delete network interface
+ for c_point in vdu_modify.connection_points_remove:
+ port = drv.get_network_interface(NetworkInterfaceId=c_point.connection_point_id)
+ retries = 0
+ if port and port.attachment and port.attachment['DeviceIndex'] == 0:
+ self.log.error("RWCAL-AWS: Cannot modify connection port at index 0")
+ continue
+ while port.status == 'in-use' and retries < 10:
+ time.sleep(5)
+ port = drv.get_network_interface(NetworkInterfaceId=c_point.connection_point_id)
+ drv.delete_network_interface(port.id)
+
+ def cleanup_vdu_on_term(self, account, server_id,vdu_port_list = None):
+ cmd = DELETE_VM_CMD.format(key = account.aws.key,
+ secret = account.aws.secret,
+ region = account.aws.region,
+ server_id = server_id)
+ if vdu_port_list is not None:
+ for port_id in vdu_port_list:
+ cmd += (" --vdu_port_list "+ port_id)
+
+ exec_path = 'python3 ' + os.path.dirname(aws_drv.__file__)
+ exec_cmd = exec_path+'/'+cmd
+ self.log.info("Running command: %s" %(exec_cmd))
+ subprocess.call(exec_cmd, shell=True)
+
+ @rwstatus
+ def do_delete_vdu(self, account, vdu_id):
+ """Delete a virtual deployment unit
+
+ Arguments:
+ account - a cloud account
+ vdu_id - id for the vdu to be deleted
+
+ Returns:
+ None
+ """
+ drv = self._get_driver(account)
+ ### Get list of port on VM and delete them.
+ #vm_inst = drv.get_instance(vdu_id)
+
+ port_list = drv.get_network_interface_list(InstanceId = vdu_id)
+ delete_port_list = [port.id for port in port_list if port.attachment and port.attachment['DeleteOnTermination'] is False]
+ drv.terminate_instance(vdu_id)
+
+ self.cleanup_vdu_on_term(account,vdu_id,delete_port_list)
+
+
+ @rwstatus(ret_on_failure=[None])
+ def do_get_vdu(self, account, vdu_id):
+ """Get information about a virtual deployment unit.
+
+ Arguments:
+ account - a cloud account
+ vdu_id - id for the vdu
+
+ Returns:
+ Object of type RwcalYang.VDUInfoParams
+ """
+ drv = self._get_driver(account)
+
+ ### Get list of ports excluding the one for management network
+ vm = drv.get_instance(vdu_id)
+ port_list = drv.get_network_interface_list(InstanceId = vdu_id)
+ return RwcalAWSPlugin._fill_vdu_info(vm,port_list)
+
+
+ @rwstatus(ret_on_failure=[[]])
+ def do_get_vdu_list(self, account):
+ """Get information about all the virtual deployment units
+
+ Arguments:
+ account - a cloud account
+
+ Returns:
+ A list of objects of type RwcalYang.VDUInfoParams
+ """
+ vnf_resources = RwcalYang.VNFResources()
+ drv = self._get_driver(account)
+ vms = drv.list_instances()
+ for vm in vms:
+ ### Get list of ports excluding one for management network
+ port_list = [p for p in drv.get_network_interface_list(InstanceId = vm.id)]
+ vdu = RwcalAWSPlugin._fill_vdu_info(vm,
+ port_list)
+ vnf_resources.vdu_info_list.append(vdu)
+ return vnf_resources