update from RIFT as of 696b75d2fe9fb046261b08c616f1bcf6c0b54a9b second try
[osm/SO.git] / rwcal / plugins / vala / rwcal_aws / rwcal_aws.py
1
2 #
3 # Copyright 2016 RIFT.IO Inc
4 #
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
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
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.
16 #
17
18 import time
19 import os
20 import subprocess
21 import logging
22 import rift.rwcal.aws as aws_drv
23 import rw_status
24 import rift.cal.rwcal_status as rwcal_status
25 import rwlogger
26 import rift.rwcal.aws.exceptions as exceptions
27 from gi import require_version
28 require_version('RwCal', '1.0')
29
30 from gi.repository import (
31 GObject,
32 RwCal,
33 RwTypes,
34 RwcalYang)
35
36
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}"
39
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,
48 }
49
50 rwstatus = rw_status.rwstatus_from_exc_map(rwstatus_exception_map)
51 rwcalstatus = rwcal_status.rwcalstatus_from_exc_map(rwstatus_exception_map)
52
53 class RwcalAWSPlugin(GObject.Object, RwCal.Cloud):
54 """This class implements the CAL VALA methods for AWS."""
55
56 flavor_id = 1;
57 instance_num = 1
58 def __init__(self):
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)
64
65 RwcalAWSPlugin.instance_num += 1
66
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)
75
76 @rwstatus
77 def do_init(self, rwlog_ctx):
78 self.log.addHandler(
79 rwlogger.RwLogger(
80 category="rw-cal-log",
81 subcategory="aws",
82 log_hdl=rwlog_ctx,
83 )
84 )
85
86 @rwstatus(ret_on_failure=[None])
87 def do_validate_cloud_creds(self, account):
88 """
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
92 Arguments:
93 account - a cloud account to validate
94
95 Returns:
96 Validation Code and Details String
97 """
98 status = RwcalYang.YangData_Rwcal_ConnectionStatus(
99 status="success",
100 details="AWS Cloud Account validation not implemented yet"
101 )
102
103 return status
104
105 @rwstatus(ret_on_failure=[""])
106 def do_get_management_network(self, account):
107 """
108 Returns the management network associated with the specified account.
109 Arguments:
110 account - a cloud account
111
112 Returns:
113 The management network
114 """
115 raise NotImplementedError
116
117 @rwstatus(ret_on_failure=[""])
118 def do_create_tenant(self, account, name):
119 """Create a new tenant.
120
121 Arguments:
122 account - a cloud account
123 name - name of the tenant
124
125 Returns:
126 The tenant id
127 """
128 raise NotImplementedError
129
130 @rwstatus
131 def do_delete_tenant(self, account, tenant_id):
132 """delete a tenant.
133
134 Arguments:
135 account - a cloud account
136 tenant_id - id of the tenant
137 """
138 raise NotImplementedError
139
140 @rwstatus(ret_on_failure=[[]])
141 def do_get_tenant_list(self, account):
142 """List tenants.
143
144 Arguments:
145 account - a cloud account
146
147 Returns:
148 List of tenants
149 """
150 raise NotImplementedError
151
152 @rwstatus(ret_on_failure=[""])
153 def do_create_role(self, account, name):
154 """Create a new user.
155
156 Arguments:
157 account - a cloud account
158 name - name of the user
159
160 Returns:
161 The user id
162 """
163 raise NotImplementedError
164
165 @rwstatus
166 def do_delete_role(self, account, role_id):
167 """Delete a user.
168
169 Arguments:
170 account - a cloud account
171 role_id - id of the user
172 """
173 raise NotImplementedError
174
175 @rwstatus(ret_on_failure=[[]])
176 def do_get_role_list(self, account):
177 """List roles.
178
179 Arguments:
180 account - a cloud account
181
182 Returns:
183 List of roles
184 """
185 raise NotImplementedError
186
187 @rwstatus(ret_on_failure=[""])
188 def do_create_image(self, account, image):
189 """Create an image
190
191 Arguments:
192 account - a cloud account
193 image - a description of the image to create
194
195 Returns:
196 The image id
197 """
198 raise NotImplementedError
199
200 @rwstatus
201 def do_delete_image(self, account, image_id):
202 """Delete a vm image.
203
204 Arguments:
205 account - a cloud account
206 image_id - id of the image to delete
207 """
208 raise NotImplementedError
209
210 @staticmethod
211 def _fill_image_info(img_info):
212 """Create a GI object from image info dictionary
213
214 Converts image information dictionary object returned by AWS
215 driver into Protobuf Gi Object
216
217 Arguments:
218 account - a cloud account
219 img_info - image information dictionary object from AWS
220
221 Returns:
222 The ImageInfoItem
223 """
224 img = RwcalYang.YangData_RwProject_Project_VimResources_ImageinfoList()
225 img.name = img_info.name
226 img.id = img_info.id
227
228 #tag_fields = ['checksum']
229 # Look for any properties
230 if img_info.tags:
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':
236 img.state = 'active'
237 else:
238 img.state = 'inactive'
239 return img
240
241 @rwstatus(ret_on_failure=[[]])
242 def do_get_image_list(self, account):
243 """Return a list of the names of all available images.
244
245 Arguments:
246 account - a cloud account
247
248 Returns:
249 The the list of images in VimResources object
250 """
251 response = RwcalYang.YangData_RwProject_Project_VimResources()
252 images = self._get_driver(account).list_images()
253 for img in images:
254 response.imageinfo_list.append(RwcalAWSPlugin._fill_image_info(img))
255 return response
256
257 @rwstatus(ret_on_failure=[None])
258 def do_get_image(self, account, image_id):
259 """Return a image information.
260
261 Arguments:
262 account - a cloud account
263 image_id - an id of the image
264
265 Returns:
266 ImageInfoItem object containing image information.
267 """
268 image = self._get_driver(account).get_image(image_id)
269 return RwcalAWSPlugin._fill_image_info(image)
270
271 @rwstatus(ret_on_failure=[""])
272 def do_create_vm(self, account, vminfo):
273 """Create a new virtual machine.
274
275 Arguments:
276 account - a cloud account
277 vminfo - information that defines the type of VM to create
278
279 Returns:
280 The image id
281 """
282 raise NotImplementedError
283
284 @rwstatus
285 def do_start_vm(self, account, vm_id):
286 """Start an existing virtual machine.
287
288 Arguments:
289 account - a cloud account
290 vm_id - an id of the VM
291 """
292 raise NotImplementedError
293
294 @rwstatus
295 def do_stop_vm(self, account, vm_id):
296 """Stop a running virtual machine.
297
298 Arguments:
299 account - a cloud account
300 vm_id - an id of the VM
301 """
302 raise NotImplementedError
303
304 @rwstatus
305 def do_delete_vm(self, account, vm_id):
306 """Delete a virtual machine.
307
308 Arguments:
309 account - a cloud account
310 vm_id - an id of the VM
311 """
312 raise NotImplementedError
313
314 @rwstatus
315 def do_reboot_vm(self, account, vm_id):
316 """Reboot a virtual machine.
317
318 Arguments:
319 account - a cloud account
320 vm_id - an id of the VM
321 """
322 raise NotImplementedError
323
324 @staticmethod
325 def _fill_vm_info(vm_info):
326 """Create a GI object from vm info dictionary
327
328 Converts VM information dictionary object returned by openstack
329 driver into Protobuf Gi Object
330
331 Arguments:
332 vm_info - VM information from AWS
333
334 Returns:
335 Protobuf Gi object for VM
336 """
337 vm = RwcalYang.YangData_RwProject_Project_VimResources_VminfoList()
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':
342 vm.state = 'active'
343 else:
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']
350 else:
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']
356
357 if vm_info.placement and 'AvailabilityZone' in vm_info.placement:
358 vm.availability_zone = vm_info.placement['AvailabilityZone']
359 if vm_info.tags:
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'])
365 return vm
366
367 @rwstatus(ret_on_failure=[[]])
368 def do_get_vm_list(self, account):
369 """Return a list of the VMs as vala boxed objects
370
371 Arguments:
372 account - a cloud account
373
374 Returns:
375 List containing VM information
376 """
377 response = RwcalYang.YangData_RwProject_Project_VimResources()
378 vms = self._get_driver(account).list_instances()
379 for vm in vms:
380 response.vminfo_list.append(RwcalAWSPlugin._fill_vm_info(vm))
381 return response
382
383 @rwstatus(ret_on_failure=[None])
384 def do_get_vm(self, account, id):
385 """Return vm information.
386
387 Arguments:
388 account - a cloud account
389 id - an id for the VM
390
391 Returns:
392 VM information
393 """
394 vm = self._get_driver(account).get_instance(id)
395 return RwcalAWSPlugin._fill_vm_info(vm)
396
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.
402
403 Arguments:
404 account - a cloud account
405 flavor - flavor of the VM
406
407 Returns:
408 flavor id (with EC2 instance type included in id)
409 """
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)
414
415 new_flavor = RwcalYang.YangData_RwProject_Project_VimResources_FlavorinfoList()
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)
423 return new_flavor.id
424
425 @rwstatus
426 def do_delete_flavor(self, account, flavor_id):
427 """Delete flavor.
428
429 Arguments:
430 account - a cloud account
431 flavor_id - id flavor of the VM
432 """
433
434 flavor = [flav for flav in self._flavor_list if flav.id == flavor_id]
435 self._flavor_list.delete(flavor[0])
436
437 @staticmethod
438 def _fill_flavor_info(flavor_info):
439 """Create a GI object from flavor info dictionary
440
441 Converts Flavor information dictionary object returned by openstack
442 driver into Protobuf Gi Object
443
444 Arguments:
445 flavor_info: Flavor information from openstack
446
447 Returns:
448 Object of class FlavorInfoItem
449 """
450 flavor = RwcalYang.YangData_RwProject_Project_VimResources_FlavorinfoList()
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
456 return flavor
457
458 @rwstatus(ret_on_failure=[[]])
459 def do_get_flavor_list(self, account):
460 """Return flavor information.
461
462 Arguments:
463 account - a cloud account
464
465 Returns:
466 List of flavors
467 """
468 response = RwcalYang.YangData_RwProject_Project_VimResources()
469 for flv in self._flavor_list:
470 response.flavorinfo_list.append(RwcalAWSPlugin._fill_flavor_info(flv))
471 return response
472
473
474 @rwstatus(ret_on_failure=[None])
475 def do_get_flavor(self, account, id):
476 """Return flavor information.
477
478 Arguments:
479 account - a cloud account
480 id - an id for the flavor
481
482 Returns:
483 Flavor info item
484 """
485 flavor = [flav for flav in self._flavor_list if flav.id == id]
486 return (RwcalAWSPlugin._fill_flavor_info(flavor[0]))
487
488 def _fill_network_info(self, network_info, account):
489 """Create a GI object from network info dictionary
490
491 Converts Network information dictionary object returned by AWS
492 driver into Protobuf Gi Object
493
494 Arguments:
495 network_info - Network information from AWS
496 account - a cloud account
497
498 Returns:
499 Network info item
500 """
501 network = RwcalYang.YangData_RwProject_Project_VimResources_NetworkinfoList()
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']
508 return network
509
510 @rwstatus(ret_on_failure=[[]])
511 def do_get_network_list(self, account):
512 """Return a list of networks
513
514 Arguments:
515 account - a cloud account
516
517 Returns:
518 List of networks
519 """
520 response = RwcalYang.YangData_RwProject_Project_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))
524 return response
525
526 @rwstatus(ret_on_failure=[None])
527 def do_get_network(self, account, id):
528 """Return a network
529
530 Arguments:
531 account - a cloud account
532 id - an id for the network
533
534 Returns:
535 Network info item
536 """
537 network = self._get_driver(account).get_subnet(id)
538 return self._fill_network_info(network, account)
539
540 @rwstatus(ret_on_failure=[""])
541 def do_create_network(self, account, network):
542 """Create a new network
543
544 Arguments:
545 account - a cloud account
546 network - Network object
547
548 Returns:
549 Network id
550 """
551 raise NotImplementedError
552
553 @rwstatus
554 def do_delete_network(self, account, network_id):
555 """Delete a network
556
557 Arguments:
558 account - a cloud account
559 network_id - an id for the network
560 """
561 raise NotImplementedError
562
563 @staticmethod
564 def _fill_port_info(port_info):
565 """Create a GI object from port info dictionary
566
567 Converts Port information dictionary object returned by AWS
568 driver into Protobuf Gi Object
569
570 Arguments:
571 port_info - Port/Network interface information from AWS
572
573 Returns:
574 Port info item
575 """
576 port = RwcalYang.YangData_RwProject_Project_VimResources_PortinfoList()
577
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'
587 else:
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']
593 return port
594
595
596 @rwstatus(ret_on_failure=[None])
597 def do_get_port(self, account, port_id):
598 """Return a port
599
600 Arguments:
601 account - a cloud account
602 port_id - an id for the port
603
604 Returns:
605 Port info item
606 """
607 port = self._get_driver(account).get_network_interface(port_id)
608 return RwcalAWSPlugin._fill_port_info(port)
609
610 @rwstatus(ret_on_failure=[[]])
611 def do_get_port_list(self, account):
612 """Return a list of ports
613
614 Arguments:
615 account - a cloud account
616
617 Returns:
618 Port info list
619 """
620 response = RwcalYang.YangData_RwProject_Project_VimResources()
621 ports = self._get_driver(account).get_network_interface_list()
622 for port in ports:
623 response.portinfo_list.append(RwcalAWSPlugin._fill_port_info(port))
624 return response
625
626 @rwstatus(ret_on_failure=[""])
627 def do_create_port(self, account, port):
628 """Create a new port
629
630 Arguments:
631 account - a cloud account
632 port - port object
633
634 Returns:
635 Port id
636 """
637 raise NotImplementedError
638
639 @rwstatus
640 def do_delete_port(self, account, port_id):
641 """Delete a port
642
643 Arguments:
644 account - a cloud account
645 port_id - an id for port
646 """
647 raise NotImplementedError
648
649 @rwstatus(ret_on_failure=[""])
650 def do_add_host(self, account, host):
651 """Add a new host
652
653 Arguments:
654 account - a cloud account
655 host - a host object
656
657 Returns:
658 An id for the host
659 """
660 raise NotImplementedError
661
662 @rwstatus
663 def do_remove_host(self, account, host_id):
664 """Remove a host
665
666 Arguments:
667 account - a cloud account
668 host_id - an id for the host
669 """
670 raise NotImplementedError
671
672 @rwstatus(ret_on_failure=[None])
673 def do_get_host(self, account, host_id):
674 """Return a host
675
676 Arguments:
677 account - a cloud account
678 host_id - an id for host
679
680 Returns:
681 Host info item
682 """
683 raise NotImplementedError
684
685 @rwstatus(ret_on_failure=[[]])
686 def do_get_host_list(self, account):
687 """Return a list of hosts
688
689 Arguments:
690 account - a cloud account
691
692 Returns:
693 List of hosts
694 """
695 raise NotImplementedError
696
697 @rwcalstatus(ret_on_failure=[""])
698 def do_create_virtual_link(self, account, link_params):
699 """Create a new virtual link
700
701 Arguments:
702 account - a cloud account
703 link_params - information that defines the type of VDU to create
704
705 Returns:
706 The vdu_id
707 """
708 drv = self._get_driver(account)
709 kwargs = {}
710 kwargs['CidrBlock'] = link_params.subnet
711
712 subnet = drv.create_subnet(**kwargs)
713 if link_params.name:
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)
717 return subnet.id
718
719 @rwstatus
720 def do_delete_virtual_link(self, account, link_id):
721 """Delete a virtual link
722
723 Arguments:
724 account - a cloud account
725 link_id - id for the virtual-link to be deleted
726
727 Returns:
728 None
729 """
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)
739 retries = 0
740 while port.status == 'in-use' and retries < 10:
741 time.sleep(5)
742 port = drv.get_network_interface(NetworkInterfaceId=port.id)
743 drv.delete_network_interface(NetworkInterfaceId=port.id)
744 drv.delete_subnet(link_id)
745
746 @staticmethod
747 def _fill_connection_point_info(c_point, port_info):
748 """Create a GI object for RwcalYang.YangData_RwProject_Project_VnfResources_VduInfoList_ConnectionPoints()
749
750 Converts EC2.NetworkInterface object returned by AWS driver into
751 Protobuf Gi Object
752
753 Arguments:
754 port_info - Network Interface information from AWS
755 Returns:
756 Protobuf Gi object for RwcalYang.YangData_RwProject_Project_VnfResources_VduInfoList_ConnectionPoints
757 """
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 port_info.association.public_ip:
764 c_point.public_ip = port_info.association.public_ip
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'
773 else:
774 c_point.state = 'unknown'
775
776 @staticmethod
777 def _fill_virtual_link_info(network_info, port_list):
778 """Create a GI object for VirtualLinkInfoParams
779
780 Converts Subnet and NetworkInterface object
781 returned by AWS driver into Protobuf Gi Object
782
783 Arguments:
784 network_info - Subnet information from AWS
785 port_list - A list of network interface information from openstack
786 Returns:
787 Protobuf Gi object for VirtualLinkInfoParams
788 """
789 link = RwcalYang.YangData_RwProject_Project_VnfResources_VirtualLinkInfoList()
790 if network_info.state == 'available':
791 link.state = 'active'
792 else:
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)
803
804 return link
805
806 @staticmethod
807 def _fill_vdu_info(vm_info, port_list):
808 """Create a GI object for VDUInfoParams
809
810 Converts VM information dictionary object returned by AWS
811 driver into Protobuf Gi Object
812
813 Arguments:
814 vm_info - EC2 instance information from AWS
815 port_list - A list of network interface information from AWS
816 Returns:
817 Protobuf Gi object for VDUInfoParams
818 """
819 vdu = RwcalYang.YangData_RwProject_Project_VnfResources_VduInfoList()
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 mgmt_port[0].association.public_ip:
825 vdu.public_ip = mgmt_port[0].association.public_ip
826 #For now set managemnet ip also to public ip
827 #vdu.management_ip = vdu.public_ip
828 if vm_info.tags:
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':
837 vdu.state = 'active'
838 else:
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
844 # cp_port_list = [port for port in port_list if port.attachment and port.attachment['DeviceIndex'] != 0]
845 # The above conversion of the port list was leaving out the management networks attached to the vdu.
846
847 for port in port_list:
848 c_point = vdu.connection_points.add()
849 RwcalAWSPlugin._fill_connection_point_info(c_point, port)
850
851 return vdu
852
853
854 @rwstatus(ret_on_failure=[None])
855 def do_get_virtual_link(self, account, link_id):
856 """Get information about virtual link.
857
858 Arguments:
859 account - a cloud account
860 link_id - id for the virtual-link
861
862 Returns:
863 Object of type RwcalYang.YangData_RwProject_Project_VnfResources_VirtualLinkInfoList
864 """
865 drv = self._get_driver(account)
866 network = drv.get_subnet(SubnetId=link_id)
867 port_list = drv.get_network_interface_list(SubnetId=link_id)
868 virtual_link = RwcalAWSPlugin._fill_virtual_link_info(network, port_list)
869 return virtual_link
870
871 @rwstatus(ret_on_failure=[None])
872 def do_get_virtual_link_by_name(self, account, link_name):
873 raise NotImplementedError()
874
875 @rwstatus(ret_on_failure=[[]])
876 def do_get_virtual_link_list(self, account):
877 """Get information about all the virtual links
878
879 Arguments:
880 account - a cloud account
881
882 Returns:
883 A list of objects of type RwcalYang.YangData_RwProject_Project_VnfResources_VirtualLinkInfoList
884 """
885 vnf_resources = RwcalYang.YangData_RwProject_Project_VnfResources()
886 drv = self._get_driver(account)
887 networks = drv.get_subnet_list()
888 for network in networks:
889 port_list = drv.get_network_interface_list(SubnetId=network.id)
890 virtual_link = RwcalAWSPlugin._fill_virtual_link_info(network, port_list)
891 vnf_resources.virtual_link_info_list.append(virtual_link)
892 return vnf_resources
893
894 def _create_connection_point(self, account, c_point):
895 """
896 Create a connection point
897 Arguments:
898 account - a cloud account
899 c_point - connection_points
900 """
901 drv = self._get_driver(account)
902 port = drv.create_network_interface(SubnetId=c_point.virtual_link_id)
903 if c_point.name:
904 port.create_tags(Tags=[{'Key': 'Name','Value':c_point.name}])
905 if c_point.associate_public_ip:
906 drv.associate_public_ip_to_network_interface(NetworkInterfaceId = port.id)
907 return port
908
909 def prepare_vdu_on_boot(self, account, server_id,vdu_init_params,vdu_port_list = None):
910 cmd = PREPARE_VM_CMD.format(key = account.aws.key,
911 secret = account.aws.secret,
912 region = account.aws.region,
913 server_id = server_id)
914 if vdu_init_params.has_field('name'):
915 cmd += (" --vdu_name "+ vdu_init_params.name)
916 if vdu_init_params.has_field('node_id'):
917 cmd += (" --vdu_node_id "+ vdu_init_params.node_id)
918 if vdu_port_list is not None:
919 for port_id in vdu_port_list:
920 cmd += (" --vdu_port_list "+ port_id)
921
922 exec_path = 'python3 ' + os.path.dirname(aws_drv.__file__)
923 exec_cmd = exec_path+'/'+cmd
924 self.log.info("Running command: %s" %(exec_cmd))
925 subprocess.call(exec_cmd, shell=True)
926
927 @rwcalstatus(ret_on_failure=[""])
928 def do_create_vdu(self, account, vdu_init):
929 """Create a new virtual deployment unit
930
931 Arguments:
932 account - a cloud account
933 vdu_init - information about VDU to create (RwcalYang.YangData_RwProject_Project_VduInitParams)
934
935 Returns:
936 The vdu_id
937 """
938 drv = self._get_driver(account)
939 ### First create required number of ports aka connection points
940 port_list = []
941
942 ### Now Create VM
943 kwargs = {}
944 kwargs['ImageId'] = vdu_init.image_id
945 if vdu_init.has_field('flavor_id'):
946 #Get instance type from flavor id which is of form c3.xlarge-1
947 inst_type = vdu_init.flavor_id.split('-')[0]
948 else:
949 inst_type = drv.map_flavor_to_instance_type(ram = vdu_init.vm_flavor.memory_mb,
950 vcpus = vdu_init.vm_flavor.vcpu_count,
951 disk = vdu_init.vm_flavor.storage_gb)
952
953 kwargs['InstanceType'] = inst_type
954 if vdu_init.vdu_init and vdu_init.vdu_init.userdata:
955 kwargs['UserData'] = vdu_init.vdu_init.userdata
956
957 #If we need to allocate public IP address create network interface and associate elastic
958 #ip to interface
959 if vdu_init.allocate_public_address:
960 port_id = drv.create_network_interface(SubnetId=drv.default_subnet_id)
961 drv.associate_public_ip_to_network_interface(NetworkInterfaceId = port_id.id)
962 network_interface = {'NetworkInterfaceId':port_id.id,'DeviceIndex':0}
963 kwargs['NetworkInterfaces'] = [network_interface]
964
965 #AWS Driver will use default subnet id to create first network interface
966 # if network interface is not specified and will also have associate public ip
967 # if enabled for the subnet
968 vm_inst = drv.create_instance(**kwargs)
969
970 # Wait for instance to get to running state before attaching network interface
971 # to instance
972 #vm_inst[0].wait_until_running()
973
974 #if vdu_init.name:
975 #vm_inst[0].create_tags(Tags=[{'Key': 'Name','Value':vdu_init.name}])
976 #if vdu_init.node_id is not None:
977 #vm_inst[0].create_tags(Tags=[{'Key':'node_id','Value':vdu_init.node_id}])
978
979 # Create the connection points
980 port_list = []
981 for index,c_point in enumerate(vdu_init.connection_points):
982 port_id = self._create_connection_point(account, c_point)
983 port_list.append(port_id.id)
984 #drv.attach_network_interface(NetworkInterfaceId = port_id.id,InstanceId = vm_inst[0].id,DeviceIndex=index+1)
985
986 # We wait for instance to get to running state and update name,node_id and attach network intfs
987 self.prepare_vdu_on_boot(account, vm_inst[0].id, vdu_init, port_list)
988
989 return vm_inst[0].id
990
991 @rwstatus
992 def do_modify_vdu(self, account, vdu_modify):
993 """Modify Properties of existing virtual deployment unit
994
995 Arguments:
996 account - a cloud account
997 vdu_modify - Information about VDU Modification (RwcalYang.YangData_RwProject_Project_VduModifyParams)
998 """
999 ### First create required number of ports aka connection points
1000 drv = self._get_driver(account)
1001 port_list = []
1002
1003 vm_inst = drv.get_instance(vdu_modify.vdu_id)
1004
1005 if vm_inst.state['Name'] != 'running':
1006 self.log.error("RWCAL-AWS: VM with id %s is not in running state during modify VDU",vdu_modify.vdu_id)
1007 raise exceptions.RWErrorFailure("RWCAL-AWS: VM with id %s is not in running state during modify VDU",vdu_modify.vdu_id)
1008
1009 port_list = drv.get_network_interface_list(InstanceId = vdu_modify.vdu_id)
1010 used_device_indexs = [port.attachment['DeviceIndex'] for port in port_list if port.attachment]
1011
1012 device_index = 1
1013 for c_point in vdu_modify.connection_points_add:
1014 #Get unused device index
1015 while device_index in used_device_indexs:
1016 device_index = device_index+1
1017 port_id = self._create_connection_point(account, c_point)
1018 drv.attach_network_interface(NetworkInterfaceId = port_id.id,InstanceId = vdu_modify.vdu_id,DeviceIndex =device_index)
1019
1020 ### Detach the requested connection_points
1021 for c_point in vdu_modify.connection_points_remove:
1022 port = drv.get_network_interface(NetworkInterfaceId=c_point.connection_point_id)
1023 #Check if elastic IP is associated with interface and release it
1024 if port and port.association is not None:
1025 drv.disassociate_public_ip_from_network_interface(NetworkInterfaceId=port.id)
1026 if port and port.attachment and port.attachment['DeviceIndex'] != 0:
1027 drv.detach_network_interface(AttachmentId = port.attachment['AttachmentId'],Force=True) #force detach as otherwise delete fails
1028 else:
1029 self.log.error("RWCAL-AWS: Cannot modify connection port at index 0")
1030
1031 # Delete the connection points. Interfaces take time to get detached from instance and so
1032 # we check status before doing delete network interface
1033 for c_point in vdu_modify.connection_points_remove:
1034 port = drv.get_network_interface(NetworkInterfaceId=c_point.connection_point_id)
1035 retries = 0
1036 if port and port.attachment and port.attachment['DeviceIndex'] == 0:
1037 self.log.error("RWCAL-AWS: Cannot modify connection port at index 0")
1038 continue
1039 while port.status == 'in-use' and retries < 10:
1040 time.sleep(5)
1041 port = drv.get_network_interface(NetworkInterfaceId=c_point.connection_point_id)
1042 drv.delete_network_interface(port.id)
1043
1044 def cleanup_vdu_on_term(self, account, server_id,vdu_port_list = None):
1045 cmd = DELETE_VM_CMD.format(key = account.aws.key,
1046 secret = account.aws.secret,
1047 region = account.aws.region,
1048 server_id = server_id)
1049 if vdu_port_list is not None:
1050 for port_id in vdu_port_list:
1051 cmd += (" --vdu_port_list "+ port_id)
1052
1053 exec_path = 'python3 ' + os.path.dirname(aws_drv.__file__)
1054 exec_cmd = exec_path+'/'+cmd
1055 self.log.info("Running command: %s" %(exec_cmd))
1056 subprocess.call(exec_cmd, shell=True)
1057
1058 @rwstatus
1059 def do_delete_vdu(self, account, vdu_id):
1060 """Delete a virtual deployment unit
1061
1062 Arguments:
1063 account - a cloud account
1064 vdu_id - id for the vdu to be deleted
1065
1066 Returns:
1067 None
1068 """
1069 drv = self._get_driver(account)
1070 ### Get list of port on VM and delete them.
1071 #vm_inst = drv.get_instance(vdu_id)
1072
1073 port_list = drv.get_network_interface_list(InstanceId = vdu_id)
1074 delete_port_list = [port.id for port in port_list if port.attachment and port.attachment['DeleteOnTermination'] is False]
1075 drv.terminate_instance(vdu_id)
1076
1077 self.cleanup_vdu_on_term(account,vdu_id,delete_port_list)
1078
1079
1080 @rwcalstatus(ret_on_failure=[None])
1081 def do_get_vdu(self, account, vdu_id, mgmt_network):
1082 """Get information about a virtual deployment unit.
1083
1084 Arguments:
1085 account - a cloud account
1086 vdu_id - id for the vdu,
1087 mgmt_network - Added due to need for mgmt network.
1088 # TO DO: Investigate the need for aws.
1089
1090 Returns:
1091 Object of type RwcalYang.YangData_RwProject_Project_VnfResources_VduInfoList
1092 """
1093 drv = self._get_driver(account)
1094
1095 ### Get list of ports excluding the one for management network
1096 vm = drv.get_instance(vdu_id)
1097 port_list = drv.get_network_interface_list(InstanceId = vdu_id)
1098 return RwcalAWSPlugin._fill_vdu_info(vm,port_list)
1099
1100
1101 @rwcalstatus(ret_on_failure=[None])
1102 def do_get_vdu_list(self, account):
1103 """Get information about all the virtual deployment units
1104
1105 Arguments:
1106 account - a cloud account
1107
1108 Returns:
1109 A list of objects of type RwcalYang.YangData_RwProject_Project_VnfResources_VduInfoList
1110 """
1111 vnf_resources = RwcalYang.YangData_RwProject_Project_VnfResources()
1112 drv = self._get_driver(account)
1113 vms = drv.list_instances()
1114 for vm in vms:
1115 ### Get list of ports excluding one for management network
1116 port_list = [p for p in drv.get_network_interface_list(InstanceId = vm.id)]
1117 vdu = RwcalAWSPlugin._fill_vdu_info(vm,
1118 port_list)
1119 vnf_resources.vdu_info_list.append(vdu)
1120 return vnf_resources