update from RIFT as of 696b75d2fe9fb046261b08c616f1bcf6c0b54a9b second try
[osm/SO.git] / rwcal / test / aws_resources.py
1 #!/usr/bin/python3
2
3 #
4 # Copyright 2016 RIFT.IO Inc
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 #
18
19
20 import os
21 import sys
22 import uuid
23 import rw_peas
24 from gi import require_version
25 require_version('RwCal', '1.0')
26
27 from gi.repository import RwcalYang
28 from gi.repository.RwTypes import RwStatus
29 import argparse
30 import logging
31 import rwlogger
32 import boto3
33 import botocore
34
35 persistent_resources = {
36 'vms' : [],
37 'networks' : [],
38 }
39
40 MISSION_CONTROL_NAME = 'mission-control'
41 LAUNCHPAD_NAME = 'launchpad'
42
43 RIFT_IMAGE_AMI = 'ami-7070231a'
44
45 logging.basicConfig(level=logging.ERROR)
46 logger = logging.getLogger('rift.cal.awsresources')
47 logger.setLevel(logging.INFO)
48
49 def get_cal_plugin():
50 """
51 Load AWS cal plugin
52 """
53 plugin = rw_peas.PeasPlugin('rwcal_aws', 'RwCal-1.0')
54 engine, info, extension = plugin()
55 cal = plugin.get_interface("Cloud")
56 rwloggerctx = rwlogger.RwLog.Ctx.new("Cal-Log")
57 try:
58 rc = cal.init(rwloggerctx)
59 assert rc == RwStatus.SUCCESS
60 except Exception as e:
61 logger.error("ERROR:Cal plugin instantiation failed with exception %s",repr(e))
62 else:
63 logger.info("AWS Cal plugin successfully instantiated")
64 return cal
65
66 def get_cal_account(**kwargs):
67 """
68 Returns AWS cal account
69 """
70 account = RwcalYang.YangData_RwProject_Project_CloudAccounts_CloudAccountList()
71 account.account_type = "aws"
72 account.aws.key = kwargs['key']
73 account.aws.secret = kwargs['secret']
74 account.aws.region = kwargs['region']
75 if 'ssh_key' in kwargs and kwargs['ssh_key'] is not None:
76 account.aws.ssh_key = kwargs['ssh_key']
77 account.aws.availability_zone = kwargs['availability_zone']
78 if 'vpcid' in kwargs and kwargs['vpcid'] is not None:
79 account.aws.vpcid = kwargs['vpcid']
80 if 'default_subnet_id' in kwargs and kwargs['default_subnet_id'] is not None:
81 account.aws.default_subnet_id = kwargs['default_subnet_id']
82 return account
83
84 class AWSResources(object):
85 """
86 Class with methods to manage AWS resources
87 """
88 def __init__(self,**kwargs):
89 self._cal = get_cal_plugin()
90 self._acct = get_cal_account(**kwargs)
91
92 def _destroy_vms(self):
93 """
94 Destroy VMs
95 """
96 logger.info("Initiating VM cleanup")
97 rc, rsp = self._cal.get_vdu_list(self._acct)
98 vdu_list = [vm for vm in rsp.vdu_info_list if vm.name not in persistent_resources['vms']]
99 logger.info("Deleting VMs : %s" %([x.name for x in vdu_list]))
100
101 for vdu in vdu_list:
102 self._cal.delete_vdu(self._acct, vdu.vdu_id)
103
104 logger.info("VM cleanup complete")
105
106 def _destroy_networks(self):
107 """
108 Destroy Networks
109 """
110 logger.info("Initiating Network cleanup")
111 driver = self._cal._get_driver(self._acct)
112 subnets = driver.get_subnet_list()
113 subnet_list = [subnet for subnet in subnets if subnet.default_for_az is False]
114
115 logger.info("Deleting Networks : %s" %([x.id for x in subnet_list]))
116 for subnet in subnet_list:
117 self._cal.delete_virtual_link(self._acct, subnet.subnet_id)
118 logger.info("Network cleanup complete")
119
120 def destroy_resource(self):
121 """
122 Destroy resources
123 """
124 logger.info("Cleaning up AWS resources")
125 self._destroy_vms()
126 self._destroy_networks()
127 logger.info("Cleaning up AWS resources.......[Done]")
128
129 def _destroy_mission_control(self):
130 """
131 Destroy Mission Control VM
132 """
133 logger.info("Initiating MC VM cleanup")
134 rc, rsp = self._cal.get_vdu_list(self._acct)
135 vdu_list = [vm for vm in rsp.vdu_info_list if vm.name == MISSION_CONTROL_NAME]
136 logger.info("Deleting VMs : %s" %([x.name for x in vdu_list]))
137
138 for vdu in vdu_list:
139 self._cal.delete_vdu(self._acct, vdu.vdu_id)
140 logger.info("MC VM cleanup complete")
141
142 def _destroy_launchpad(self):
143 """
144 Destroy Launchpad VM
145 """
146 logger.info("Initiating LP VM cleanup")
147 rc, rsp = self._cal.get_vdu_list(self._acct)
148 vdu_list = [vm for vm in rsp.vdu_info_list if vm.name == LAUNCHPAD_NAME]
149 logger.info("Deleting VMs : %s" %([x.name for x in vdu_list]))
150
151 for vdu in vdu_list:
152 self._cal.delete_vdu(self._acct, vdu.vdu_id)
153 logger.info("LP VM cleanup complete")
154
155
156 def create_mission_control(self):
157 """
158 Create Mission Control VM in AWS
159 """
160 logger.info("Creating mission control VM")
161 vdu = RwcalYang.YangData_RwProject_Project_VduInitParams()
162 vdu.name = MISSION_CONTROL_NAME
163 vdu.image_id = RIFT_IMAGE_AMI
164 vdu.flavor_id = 'c3.large'
165 vdu.allocate_public_address = True
166 vdu.vdu_init.userdata = "#cloud-config\n\nruncmd:\n - echo Sleeping for 5 seconds and attempting to start salt-master\n - sleep 5\n - /bin/systemctl restart salt-master.service\n"
167
168 rc,rs=self._cal.create_vdu(self._acct,vdu)
169 assert rc == RwStatus.SUCCESS
170 self._mc_id = rs
171
172 driver = self._cal._get_driver(self._acct)
173 inst=driver.get_instance(self._mc_id)
174 inst.wait_until_running()
175
176 rc,rs =self._cal.get_vdu(self._acct,self._mc_id, "")
177 assert rc == RwStatus.SUCCESS
178 self._mc_public_ip = rs.public_ip
179 self._mc_private_ip = rs.management_ip
180
181 logger.info("Started Mission Control VM with id %s and IP Address %s\n",self._mc_id, self._mc_public_ip)
182
183 def create_launchpad_vm(self, salt_master = None):
184 """
185 Create Launchpad VM in AWS
186 Arguments
187 salt_master (String): String with Salt master IP typically MC VM private IP
188 """
189 logger.info("Creating launchpad VM")
190 USERDATA_FILENAME = os.path.join(os.environ['RIFT_INSTALL'],
191 'etc/userdata-template')
192
193 try:
194 fd = open(USERDATA_FILENAME, 'r')
195 except Exception as e:
196 sys.exit(-1)
197 else:
198 LP_USERDATA_FILE = fd.read()
199 # Run the enable lab script when the openstack vm comes up
200 LP_USERDATA_FILE += "runcmd:\n"
201 LP_USERDATA_FILE += " - echo Sleeping for 5 seconds and attempting to start elastic-network-interface\n"
202 LP_USERDATA_FILE += " - sleep 5\n"
203 LP_USERDATA_FILE += " - /bin/systemctl restart elastic-network-interfaces.service\n"
204
205 if salt_master is None:
206 salt_master=self._mc_private_ip
207 node_id = str(uuid.uuid4())
208
209 vdu = RwcalYang.YangData_RwProject_Project_VduInitParams()
210 vdu.name = LAUNCHPAD_NAME
211 vdu.image_id = RIFT_IMAGE_AMI
212 vdu.flavor_id = 'c3.xlarge'
213 vdu.allocate_public_address = True
214 vdu.vdu_init.userdata = LP_USERDATA_FILE.format(master_ip = salt_master,
215 lxcname = node_id)
216 vdu.node_id = node_id
217
218 rc,rs=self._cal.create_vdu(self._acct,vdu)
219 assert rc == RwStatus.SUCCESS
220 self._lp_id = rs
221
222 driver = self._cal._get_driver(self._acct)
223 inst=driver.get_instance(self._lp_id)
224 inst.wait_until_running()
225
226 rc,rs =self._cal.get_vdu(self._acct,self._lp_id, "")
227 assert rc == RwStatus.SUCCESS
228
229 self._lp_public_ip = rs.public_ip
230 self._lp_private_ip = rs.management_ip
231 logger.info("Started Launchpad VM with id %s and IP Address %s\n",self._lp_id, self._lp_public_ip)
232
233 def upload_ssh_key_to_ec2(self):
234 """
235 Upload SSH key to EC2 region
236 """
237 driver = self._cal._get_driver(self._acct)
238 key_name = os.getlogin() + '-' + 'sshkey'
239 key_path = '%s/.ssh/id_rsa.pub' % (os.environ['HOME'])
240 if os.path.isfile(key_path):
241 logger.info("Uploading ssh public key file in path %s with keypair name %s", key_path,key_name)
242 with open(key_path) as fp:
243 driver.upload_ssh_key(key_name,fp.read())
244 else:
245 logger.error("Valid Public key file %s not found", key_path)
246
247
248 def main():
249 """
250 Main routine
251 """
252 parser = argparse.ArgumentParser(description='Script to manage AWS resources')
253
254 parser.add_argument('--aws-key',
255 action = 'store',
256 dest = 'aws_key',
257 type = str,
258 help='AWS key')
259
260 parser.add_argument('--aws-secret',
261 action = 'store',
262 dest = 'aws_secret',
263 type = str,
264 help='AWS secret')
265
266 parser.add_argument('--aws-region',
267 action = 'store',
268 dest = 'aws_region',
269 type = str,
270 help='AWS region')
271
272 parser.add_argument('--aws-az',
273 action = 'store',
274 dest = 'aws_az',
275 type = str,
276 help='AWS Availability zone')
277
278 parser.add_argument('--aws-sshkey',
279 action = 'store',
280 dest = 'aws_sshkey',
281 type = str,
282 help='AWS SSH Key to login to instance')
283
284 parser.add_argument('--aws-vpcid',
285 action = 'store',
286 dest = 'aws_vpcid',
287 type = str,
288 help='AWS VPC ID to use to indicate non default VPC')
289
290 parser.add_argument('--aws-default-subnet',
291 action = 'store',
292 dest = 'aws_default_subnet',
293 type = str,
294 help='AWS Default subnet id in VPC to be used for mgmt network')
295
296 parser.add_argument('--mission-control',
297 action = 'store_true',
298 dest = 'mission_control',
299 help='Create Mission Control VM')
300
301 parser.add_argument('--launchpad',
302 action = 'store_true',
303 dest = 'launchpad',
304 help='Create LaunchPad VM')
305
306 parser.add_argument('--salt-master',
307 action = 'store',
308 dest = 'salt_master',
309 type = str,
310 help='IP Address of salt controller. Required, if only launchpad VM is being created.')
311
312 parser.add_argument('--cleanup',
313 action = 'store',
314 dest = 'cleanup',
315 nargs = '+',
316 type = str,
317 help = 'Perform resource cleanup for AWS installation. \n Possible options are {all, mc, lp, vms, networks }')
318
319 parser.add_argument('--upload-ssh-key',
320 action = 'store_true',
321 dest = 'upload_ssh_key',
322 help = 'Upload users SSH public key ~/.ssh/id_rsa.pub')
323
324 argument = parser.parse_args()
325
326 if (argument.aws_key is None or argument.aws_secret is None or argument.aws_region is None or
327 argument.aws_az is None):
328 logger.error("Missing mandatory params. AWS Key, Secret, Region, AZ and SSH key are mandatory params")
329 sys.exit(-1)
330
331 if (argument.cleanup is None and argument.mission_control is None and argument.launchpad is None
332 and argument.upload_ssh_key is None):
333 logger.error('Insufficient parameters')
334 sys.exit(-1)
335
336 ### Start processing
337 logger.info("Instantiating cloud-abstraction-layer")
338 drv = AWSResources(key=argument.aws_key, secret=argument.aws_secret, region=argument.aws_region, availability_zone = argument.aws_az,
339 ssh_key = argument.aws_sshkey, vpcid = argument.aws_vpcid, default_subnet_id = argument.aws_default_subnet)
340 logger.info("Instantiating cloud-abstraction-layer.......[Done]")
341
342 if argument.upload_ssh_key:
343 drv.upload_ssh_key_to_ec2()
344
345 if argument.cleanup is not None:
346 for r_type in argument.cleanup:
347 if r_type == 'all':
348 drv.destroy_resource()
349 break
350 if r_type == 'vms':
351 drv._destroy_vms()
352 if r_type == 'networks':
353 drv._destroy_networks()
354 if r_type == 'mc':
355 drv._destroy_mission_control()
356 if r_type == 'lp':
357 drv._destroy_launchpad()
358
359 if argument.mission_control == True:
360 drv.create_mission_control()
361
362 if argument.launchpad == True:
363 if argument.salt_master is None and argument.mission_control is False:
364 logger.error('Salt Master IP address not provided to start Launchpad.')
365 sys.exit(-2)
366
367 drv.create_launchpad_vm(argument.salt_master)
368
369 if __name__ == '__main__':
370 main()