4 # Copyright 2016 RIFT.IO Inc
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
10 # http://www.apache.org/licenses/LICENSE-2.0
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.
19 from gi
import require_version
20 require_version('RwCal', '1.0')
22 from gi
.repository
import RwcalYang
23 from gi
.repository
.RwTypes
import RwStatus
32 from os
.path
import basename
34 FLAVOR_NAME
= 'm1.medium'
35 DEFAULT_IMAGE
='/net/sharedfiles/home1/common/vm/rift-root-latest.qcow2'
37 persistent_resources
= {
38 'vms' : ['mission_control','launchpad',],
39 'networks' : ['public', 'private', 'multisite'],
40 'flavors' : ['m1.tiny', 'm1.small', 'm1.medium', 'm1.large', 'm1.xlarge'],
41 'images' : ['rwimage','rift-root-latest.qcow2','rift-root-latest-trafgen.qcow2', 'rift-root-latest-trafgen-f.qcow2']
45 # Important information about openstack installation. This needs to be manually verified
49 'password' : 'mypasswd',
50 'project_name' : 'demo',
51 'mgmt_network' : 'private',
52 'physical_network' : 'physnet1',
53 'network_type' : 'VLAN',
54 'segmentation_id' : 42, ### What else?
55 'subnets' : ["11.0.0.0/24", "12.0.0.0/24", "13.0.0.0/24", "14.0.0.0/24"],
60 logging
.basicConfig(level
=logging
.INFO
)
62 USERDATA_FILENAME
= os
.path
.join(os
.environ
['RIFT_INSTALL'],
63 'etc/userdata-template')
66 RIFT_BASE_USERDATA
= '''
70 - /usr/rift/scripts/cloud/enable_lab
71 - /usr/rift/etc/fix_this_vm
75 fd
= open(USERDATA_FILENAME
, 'r')
76 except Exception as e
:
77 #logger.error("Received exception during opening of userdata (%s) file. Exception: %s" %(USERDATA_FILENAME, str(e)))
80 LP_USERDATA_FILE
= fd
.read()
81 # Run the enable lab script when the openstack vm comes up
82 LP_USERDATA_FILE
+= "runcmd:\n"
83 LP_USERDATA_FILE
+= " - /usr/rift/scripts/cloud/enable_lab\n"
84 LP_USERDATA_FILE
+= " - /usr/rift/etc/fix_this_vm\n"
90 Loads rw.cal plugin via libpeas
92 plugin
= rw_peas
.PeasPlugin('rwcal_openstack', 'RwCal-1.0')
93 engine
, info
, extension
= plugin()
94 cal
= plugin
.get_interface("Cloud")
95 # Get the RwLogger context
96 rwloggerctx
= rwlogger
.RwLog
.Ctx
.new("Cal-Log")
98 rc
= cal
.init(rwloggerctx
)
99 assert rc
== RwStatus
.SUCCESS
101 logger
.error("ERROR:Cal plugin instantiation failed. Aborting tests")
103 logger
.info("Openstack Cal plugin successfully instantiated")
106 def get_cal_account(auth_url
):
110 account
= RwcalYang
.CloudAccount()
111 account
.account_type
= "openstack"
112 account
.openstack
.key
= openstack_info
['username']
113 account
.openstack
.secret
= openstack_info
['password']
114 account
.openstack
.auth_url
= auth_url
115 account
.openstack
.tenant
= openstack_info
['project_name']
116 account
.openstack
.mgmt_network
= openstack_info
['mgmt_network']
120 logger
= logging
.getLogger('rift.cal.openstackresources')
122 class OpenstackResources(object):
124 A stupid class to manage bunch of openstack resources
126 def __init__(self
, controller
):
127 self
._cal
= get_cal_plugin()
128 self
._acct
= get_cal_account('http://'+controller
+':5000/v3/')
130 self
._image
_id
= None
131 self
._flavor
_id
= None
133 def _destroy_vms(self
):
137 logger
.info("Initiating VM cleanup")
138 rc
, rsp
= self
._cal
.get_vdu_list(self
._acct
)
139 vdu_list
= [vm
for vm
in rsp
.vdu_info_list
if vm
.name
not in persistent_resources
['vms']]
140 logger
.info("Deleting VMs : %s" %([x
.name
for x
in vdu_list
]))
143 self
._cal
.delete_vdu(self
._acct
, vdu
.vdu_id
)
145 logger
.info("VM cleanup complete")
147 def _destroy_networks(self
):
151 logger
.info("Initiating Network cleanup")
152 rc
, rsp
= self
._cal
.get_virtual_link_list(self
._acct
)
153 vlink_list
= [vlink
for vlink
in rsp
.virtual_link_info_list
if vlink
.name
not in persistent_resources
['networks']]
155 logger
.info("Deleting Networks : %s" %([x
.name
for x
in vlink_list
]))
156 for vlink
in vlink_list
:
157 self
._cal
.delete_virtual_link(self
._acct
, vlink
.virtual_link_id
)
158 logger
.info("Network cleanup complete")
160 def _destroy_flavors(self
):
164 logger
.info("Initiating flavor cleanup")
165 rc
, rsp
= self
._cal
.get_flavor_list(self
._acct
)
166 flavor_list
= [flavor
for flavor
in rsp
.flavorinfo_list
if flavor
.name
not in persistent_resources
['flavors']]
168 logger
.info("Deleting flavors : %s" %([x
.name
for x
in flavor_list
]))
170 for flavor
in flavor_list
:
171 self
._cal
.delete_flavor(self
._acct
, flavor
.id)
173 logger
.info("Flavor cleanup complete")
175 def _destroy_images(self
):
176 logger
.info("Initiating image cleanup")
177 rc
, rsp
= self
._cal
.get_image_list(self
._acct
)
178 image_list
= [image
for image
in rsp
.imageinfo_list
if image
.name
not in persistent_resources
['images']]
180 logger
.info("Deleting images : %s" %([x
.name
for x
in image_list
]))
182 for image
in image_list
:
183 self
._cal
.delete_image(self
._acct
, image
.id)
185 logger
.info("Image cleanup complete")
187 def destroy_resource(self
):
191 logger
.info("Cleaning up openstack resources")
193 self
._destroy
_networks
()
194 self
._destroy
_flavors
()
195 self
._destroy
_images
()
196 logger
.info("Cleaning up openstack resources.......[Done]")
198 def create_mission_control(self
):
199 vm_id
= self
.create_vm('mission_control',
200 userdata
= RIFT_BASE_USERDATA
)
204 def create_launchpad_vm(self
, salt_master
=None):
205 node_id
= str(uuid
.uuid4())
206 if salt_master
is not None:
207 userdata
= LP_USERDATA_FILE
.format(master_ip
= salt_master
,
210 userdata
= RIFT_BASE_USERDATA
212 vm_id
= self
.create_vm('launchpad',
215 # vm_id = self.create_vm('launchpad2',
216 # userdata = userdata,
220 def create_vm(self
, name
, userdata
, node_id
= None):
222 Creates a VM. The VM name is derived from username
225 vm
= RwcalYang
.VDUInitParams()
227 vm
.flavor_id
= self
._flavor
_id
228 vm
.image_id
= self
._image
_id
229 if node_id
is not None:
231 vm
.vdu_init
.userdata
= userdata
232 vm
.allocate_public_address
= True
233 logger
.info("Starting a VM with parameter: %s" %(vm))
235 rc
, vm_id
= self
._cal
.create_vdu(self
._acct
, vm
)
236 assert rc
== RwStatus
.SUCCESS
237 logger
.info('Created vm: %s with id: %s', name
, vm_id
)
240 def create_network(self
, name
):
241 logger
.info("Creating network with name: %s" %name
)
242 network
= RwcalYang
.NetworkInfoItem()
243 network
.network_name
= name
244 network
.subnet
= openstack_info
['subnets'][openstack_info
['subnet_index']]
246 if openstack_info
['subnet_index'] == len(openstack_info
['subnets']):
247 openstack_info
['subnet_index'] = 0
249 openstack_info
['subnet_index'] += 1
251 if openstack_info
['physical_network']:
252 network
.provider_network
.physical_network
= openstack_info
['physical_network']
253 if openstack_info
['network_type']:
254 network
.provider_network
.overlay_type
= openstack_info
['network_type']
255 if openstack_info
['segmentation_id']:
256 network
.provider_network
.segmentation_id
= openstack_info
['segmentation_id']
257 openstack_info
['segmentation_id'] += 1
259 rc
, net_id
= self
._cal
.create_network(self
._acct
, network
)
260 assert rc
== RwStatus
.SUCCESS
262 logger
.info("Successfully created network with id: %s" %net_id
)
267 def create_image(self
, location
):
268 img
= RwcalYang
.ImageInfoItem()
269 img
.name
= basename(location
)
270 img
.location
= location
271 img
.disk_format
= "qcow2"
272 img
.container_format
= "bare"
274 logger
.info("Uploading image : %s" %img
.name
)
275 rc
, img_id
= self
._cal
.create_image(self
._acct
, img
)
276 assert rc
== RwStatus
.SUCCESS
282 rc
, rs
= self
._cal
.get_image(self
._acct
, img_id
)
283 assert rc
== RwStatus
.SUCCESS
284 logger
.info("Image (image_id: %s) reached status : %s" %(img_id
, rs
.state
))
285 if rs
.state
== 'active':
289 time
.sleep(2) # Sleep for a second
292 logger
.error("Failed to upload openstack image: %s", img
)
295 self
._image
_id
= img_id
296 logger
.info("Uploading image.......[Done]")
298 def create_flavor(self
):
300 Create Flavor suitable for rift_ping_pong VNF
302 flavor
= RwcalYang
.FlavorInfoItem()
303 flavor
.name
= FLAVOR_NAME
304 flavor
.vm_flavor
.memory_mb
= 16384 # 16GB
305 flavor
.vm_flavor
.vcpu_count
= 4
306 flavor
.vm_flavor
.storage_gb
= 20 # 20 GB
308 logger
.info("Creating new flavor. Flavor Info: %s" %str
(flavor
.vm_flavor
))
310 rc
, flavor_id
= self
._cal
.create_flavor(self
._acct
, flavor
)
311 assert rc
== RwStatus
.SUCCESS
312 logger
.info("Creating new flavor.......[Done]")
315 def find_image(self
, name
):
316 logger
.info("Searching for uploaded image: %s" %name
)
317 rc
, rsp
= self
._cal
.get_image_list(self
._acct
)
318 image_list
= [image
for image
in rsp
.imageinfo_list
if image
.name
== name
]
321 logger
.error("Image %s not found" %name
)
324 self
._image
_id
= image_list
[0].id
325 logger
.info("Searching for uploaded image.......[Done]")
326 return self
._image
_id
328 def find_flavor(self
, name
=FLAVOR_NAME
):
329 logger
.info("Searching for required flavor: %s" %name
)
330 rc
, rsp
= self
._cal
.get_flavor_list(self
._acct
)
331 flavor_list
= [flavor
for flavor
in rsp
.flavorinfo_list
if flavor
.name
== name
]
334 logger
.error("Flavor %s not found" %name
)
335 self
._flavor
_id
= self
.create_flavor()
337 self
._flavor
_id
= flavor_list
[0].id
339 logger
.info("Searching for required flavor.......[Done]")
340 return self
._flavor
_id
349 parser
= argparse
.ArgumentParser(description
='Script to manage openstack resources')
351 parser
.add_argument('--controller',
355 help='IP Address of openstack controller. This is mandatory parameter')
357 parser
.add_argument('--cleanup',
362 help = 'Perform resource cleanup for openstack installation. \n Possible options are {all, flavors, vms, networks, images}')
364 parser
.add_argument('--persist-vms',
366 dest
= 'persist_vms',
367 help = 'VM instance name to persist')
369 parser
.add_argument('--salt-master',
371 dest
= 'salt_master',
373 help='IP Address of salt controller. Required, if VMs are being created.')
375 parser
.add_argument('--upload-image',
377 dest
= 'upload_image',
378 help='Openstack image location to upload and use when creating vms.x')
380 parser
.add_argument('--use-image',
383 help='Image name to be used for VM creation')
385 parser
.add_argument('--use-flavor',
388 help='Flavor name to be used for VM creation')
390 parser
.add_argument('--mission-control',
391 action
= 'store_true',
392 dest
= 'mission_control',
393 help='Create Mission Control VM')
396 parser
.add_argument('--launchpad',
397 action
= 'store_true',
399 help='Create LaunchPad VM')
401 parser
.add_argument('--use-project',
403 dest
= 'use_project',
404 help='Project name to be used for VM creation')
406 parser
.add_argument('--clean-mclp',
409 help='Remove Mission Control and Launchpad VMs')
411 argument
= parser
.parse_args()
413 if argument
.persist_vms
is not None:
414 global persistent_resources
415 vm_name_list
= argument
.persist_vms
.split(',')
416 for single_vm
in vm_name_list
:
417 persistent_resources
['vms'].append(single_vm
)
418 logger
.info("persist-vms: %s" % persistent_resources
['vms'])
420 if argument
.clean_mclp
:
421 persistent_resources
['vms'] = []
423 if argument
.controller
is None:
424 logger
.error('Need openstack controller IP address')
428 if argument
.use_project
is not None:
429 openstack_info
['project_name'] = argument
.use_project
432 logger
.info("Instantiating cloud-abstraction-layer")
433 drv
= OpenstackResources(argument
.controller
)
434 logger
.info("Instantiating cloud-abstraction-layer.......[Done]")
437 if argument
.cleanup
is not None:
438 for r_type
in argument
.cleanup
:
440 drv
.destroy_resource()
442 if r_type
== 'images':
443 drv
._destroy
_images
()
444 if r_type
== 'flavors':
445 drv
._destroy
_flavors
()
448 if r_type
== 'networks':
449 drv
._destroy
_networks
()
451 if argument
.upload_image
is not None:
452 image_name_list
= argument
.upload_image
.split(',')
453 logger
.info("Will upload %d image(s): %s" % (len(image_name_list
), image_name_list
))
454 for image_name
in image_name_list
:
455 drv
.create_image(image_name
)
456 #print("Uploaded :", image_name)
458 elif argument
.use_image
is not None:
459 img
= drv
.find_image(argument
.use_image
)
461 logger
.error("Image: %s not found" %(argument
.use_image
))
464 if argument
.mission_control
or argument
.launchpad
:
465 img
= drv
.find_image(basename(DEFAULT_IMAGE
))
467 drv
.create_image(DEFAULT_IMAGE
)
469 if argument
.use_flavor
is not None:
470 drv
.find_flavor(argument
.use_flavor
)
474 if argument
.mission_control
== True:
475 drv
.create_mission_control()
477 if argument
.launchpad
== True:
478 drv
.create_launchpad_vm(salt_master
= argument
.salt_master
)
481 if __name__
== '__main__':