4 ## Copyright (c) 2010-2019 Intel Corporation
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 __future__ import print_function
29 from logging.handlers import RotatingFileHandler
30 from logging import handlers
34 stack = "rapid" #Default string for stack. This is not an OpenStack Heat stack, just a group of VMs
35 vms = "rapid.vms" #Default string for vms file
36 key = "prox" # default name for key
37 image = "rapidVM" # default name for the image
38 image_file = "rapidVM.qcow2"
39 dataplane_network = "dataplane-network" # default name for the dataplane network
40 subnet = "dpdk-subnet" #subnet for dataplane
41 subnet_cidr="10.10.10.0/24" # cidr for dataplane
42 internal_network="admin_internal_net"
43 floating_network="admin_floating_net"
44 loglevel="DEBUG" # sets log level for writing to file
47 print("usage: createrapid [--version] [-v]")
48 print(" [--stack STACK_NAME]")
49 print(" [--vms VMS_FILE]")
50 print(" [--key KEY_NAME]")
51 print(" [--image IMAGE_NAME]")
52 print(" [--image_file IMAGE_FILE]")
53 print(" [--dataplane_network DP_NETWORK]")
54 print(" [--subnet DP_SUBNET]")
55 print(" [--subnet_cidr SUBNET_CIDR]")
56 print(" [--internal_network ADMIN_NETWORK]")
57 print(" [--floating_network FLOATING_NETWORK]")
58 print(" [--log DEBUG|INFO|WARNING|ERROR|CRITICAL]")
59 print(" [-h] [--help]")
61 print("Command-line interface to createrapid")
63 print("optional arguments:")
64 print(" -v, --version Show program's version number and exit")
65 print(" --stack STACK_NAME Specify a name for the stack. Default is %s."%stack)
66 print(" --vms VMS_FILE Specify the vms file to be used. Default is %s."%vms)
67 print(" --key KEY_NAME Specify the key to be used. Default is %s."%key)
68 print(" --image IMAGE_NAME Specify the image to be used. Default is %s."%image)
69 print(" --image_file IMAGE_FILE Specify the image qcow2 file to be used. Default is %s."%image_file)
70 print(" --dataplane_network NETWORK Specify the network name to be used for the dataplane. Default is %s."%dataplane_network)
71 print(" --subnet DP_SUBNET Specify the subnet name to be used for the dataplane. Default is %s."%subnet)
72 print(" --subnet_cidr SUBNET_CIDR Specify the subnet CIDR to be used for the dataplane. Default is %s."%subnet_cidr)
73 print(" --internal_network NETWORK Specify the network name to be used for the control plane. Default is %s."%internal_network)
74 print(" --floating_network NETWORK Specify the external floating ip network name. Default is %s. NO if no floating ip used."%floating_network)
75 print(" --log Specify logging level for log file output, screen output level is hard coded")
76 print(" -h, --help Show help message and exit.")
80 opts, args = getopt.getopt(sys.argv[1:], "vh", ["version","help", "vms=","stack=","key=","image=","image_file=","dataplane_network=","subnet=","subnet_cidr=","internal_network=","floating_network=","log="])
81 except getopt.GetoptError as err:
82 print("===========================================")
84 print("===========================================")
91 if opt in ["-h", "--help"]:
94 if opt in ["-v", "--version"]:
95 print("Rapid Automated Performance Indication for Dataplane "+version)
97 if opt in ["--stack"]:
99 print ("Using '"+stack+"' as name for the stack")
100 elif opt in ["--vms"]:
102 print ("Using Virtual Machines Description: "+vms)
103 elif opt in ["--key"]:
105 print ("Using key: "+key)
106 elif opt in ["--image"]:
108 print ("Using image: "+image)
109 elif opt in ["--image_file"]:
111 print ("Using qcow2 file: "+image_file)
112 elif opt in ["--dataplane_network"]:
113 dataplane_network = arg
114 print ("Using dataplane network: "+ dataplane_network)
115 elif opt in ["--subnet"]:
117 print ("Using dataplane subnet: "+ subnet)
118 elif opt in ["--subnet_cidr"]:
120 print ("Using dataplane subnet: "+ subnet_cidr)
121 elif opt in ["--internal_network"]:
122 internal_network = arg
123 print ("Using control plane network: "+ internal_network)
124 elif opt in ["--floating_network"]:
125 floating_network = arg
126 print ("Using floating ip network: "+ floating_network)
127 elif opt in ["--log"]:
129 print ("Log level: "+ loglevel)
133 screen_formatter = logging.Formatter("%(message)s")
134 file_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
136 # get a top-level logger,
138 # BUT PREVENT IT from propagating messages to the root logger
140 log = logging.getLogger()
141 numeric_level = getattr(logging, loglevel.upper(), None)
142 if not isinstance(numeric_level, int):
143 raise ValueError('Invalid log level: %s' % loglevel)
144 log.setLevel(numeric_level)
147 # create a console handler
148 # and set its log level to the command-line option
150 console_handler = logging.StreamHandler(sys.stdout)
151 console_handler.setLevel(logging.INFO)
152 console_handler.setFormatter(screen_formatter)
154 # create a file handler
155 # and set its log level to DEBUG
157 log_file = 'CREATE' +stack +'.log'
158 file_handler = logging.handlers.RotatingFileHandler(log_file, backupCount=10)
159 #file_handler = log.handlers.TimedRotatingFileHandler(log_file, 'D', 1, 5)
160 file_handler.setLevel(numeric_level)
161 file_handler.setFormatter(file_formatter)
163 # add handlers to the logger
165 log.addHandler(file_handler)
166 log.addHandler(console_handler)
168 # Check if log exists and should therefore be rolled
169 needRoll = os.path.isfile(log_file)
172 # This is a stale log, so roll it
175 log.debug('\n---------\nLog closed on %s.\n---------\n' % time.asctime())
176 # Roll over on application start
177 log.handlers[0].doRollover()
180 log.debug('\n---------\nLog started on %s.\n---------\n' % time.asctime())
182 log.debug("createrapid.py version: "+version)
183 # Checking if the control network already exists, if not, stop the script
184 log.debug("Checking control plane network: " + internal_network)
185 cmd = 'openstack network list -f value -c Name'
187 Networks = subprocess.check_output(cmd , shell=True).decode().strip()
188 if internal_network in Networks:
189 log.info("Control plane network (" + internal_network+") already active")
191 log.exception("Control plane network " + internal_network + " not existing")
192 raise Exception("Control plane network " + internal_network + " not existing")
194 # Checking if the floating ip network should be used. If yes, check if it exists and stop the script if it doesn't
195 if floating_network !='NO':
196 log.debug("Checking floating ip network: " + floating_network)
197 if floating_network in Networks:
198 log.info("Floating ip network (" + floating_network + ") already active")
200 log.exception("Floating ip network " + floating_network + " not existing")
201 raise Exception("Floating ip network " + floating_network + " not existing")
203 # Checking if the dataplane network already exists, if not create it
204 log.debug("Checking dataplane network: " + dataplane_network)
205 if dataplane_network in Networks:
206 # If the dataplane already exists, we are assuming that this network is already created before with the proper configuration, hence we do not check if the subnet is created etc...
207 log.info("Dataplane network (" + dataplane_network + ") already active")
208 subnet = "n/a: was already existing"
209 subnet_cidr = "n/a, was already existing"
211 log.info('Creating dataplane network ...')
212 cmd = 'openstack network create '+dataplane_network+' -f value -c status'
214 NetworkExist = subprocess.check_output(cmd , shell=True).decode().strip()
215 if 'ACTIVE' in NetworkExist:
216 log.info("Dataplane network created")
217 # Checking if the dataplane subnet already exists, if not create it
218 log.debug("Checking subnet: "+subnet)
219 cmd = 'openstack subnet list -f value -c Name'
221 Subnets = subprocess.check_output(cmd , shell=True).decode().strip()
222 if subnet in Subnets:
223 log.info("Subnet (" +subnet+ ") already exists")
224 subnet = "n/a, was already existing"
225 subnet_cidr = "n/a, was already existing"
227 log.info('Creating subnet ...')
228 cmd = 'openstack subnet create --network ' + dataplane_network + ' --subnet-range ' + subnet_cidr +' --gateway none ' + subnet+' -f value -c name'
230 Subnets = subprocess.check_output(cmd , shell=True).decode().strip()
231 if subnet in Subnets:
232 log.info("Subnet created")
234 log.exception("Failed to create subnet: " + subnet)
235 raise Exception("Failed to create subnet: " + subnet)
237 log.exception("Failed to create dataplane network: " + dataplane_network)
238 raise Exception("Failed to create dataplane network: " + dataplane_network)
240 # Checking if the image already exists, if not create it
241 log.debug("Checking image: " + image)
242 cmd = 'openstack image list -f value -c Name'
244 Images = subprocess.check_output(cmd , shell=True).decode().strip()
246 log.info("Image (" + image + ") already available")
247 image_file="Don't know, was already existing"
249 log.info('Creating image ...')
250 cmd = 'openstack image create -f value -c status --disk-format qcow2 --container-format bare --public --file ./'+image_file+ ' ' +image
252 ImageExist = subprocess.check_output(cmd , shell=True).decode().strip()
253 if 'active' in ImageExist:
254 log.info('Image created and active')
255 # cmd = 'openstack image set --property hw_vif_multiqueue_enabled="true" ' +image
256 # subprocess.check_call(cmd , shell=True)
258 log.exception("Failed to create image")
259 raise Exception("Failed to create image")
261 # Checking if the key already exists, if not create it
262 log.debug("Checking key: "+key)
263 cmd = 'openstack keypair list -f value -c Name'
265 KeyExist = subprocess.check_output(cmd , shell=True).decode().strip()
267 log.info("Key ("+key+") already installed")
269 log.info('Creating key ...')
270 cmd = 'openstack keypair create '+ key + '>' +key+'.pem'
272 subprocess.check_call(cmd , shell=True)
273 cmd = 'chmod 600 ' +key+'.pem'
274 subprocess.check_call(cmd , shell=True)
275 cmd = 'openstack keypair list -f value -c Name'
277 KeyExist = subprocess.check_output(cmd , shell=True).decode().strip()
279 log.info("Key created")
281 log.exception("Failed to create key: " + key)
282 raise Exception("Failed to create key: " + key)
286 config = ConfigParser.RawConfigParser()
287 vmconfig = ConfigParser.RawConfigParser()
288 vmname = os.path.dirname(os.path.realpath(__file__))+'/' + vms
289 #vmconfig.read_file(open(vmname))
290 vmconfig.readfp(open(vmname))
291 total_number_of_VMs = vmconfig.get('DEFAULT', 'total_number_of_vms')
292 cmd = 'openstack server list -f value -c Name'
294 Servers = subprocess.check_output(cmd , shell=True).decode().strip()
295 cmd = 'openstack flavor list -f value -c Name'
297 Flavors = subprocess.check_output(cmd , shell=True).decode().strip()
298 for vm in range(1, int(total_number_of_VMs)+1):
299 flavor_info = vmconfig.get('VM%d'%vm, 'flavor_info')
300 flavor_meta_data = vmconfig.get('VM%d'%vm, 'flavor_meta_data')
301 boot_info = vmconfig.get('VM%d'%vm, 'boot_info')
302 SRIOV_port = vmconfig.get('VM%d'%vm, 'SRIOV_port')
303 SRIOV_mgmt_port = vmconfig.get('VM%d'%vm, 'SRIOV_mgmt_port')
304 ServerName.append('%s-VM%d'%(stack,vm))
305 flavor_name = '%s-VM%d-flavor'%(stack,vm)
306 log.debug("Checking server: " + ServerName[-1])
307 if ServerName[-1] in Servers:
308 log.info("Server (" + ServerName[-1] + ") already active")
309 ServerToBeCreated.append("no")
311 ServerToBeCreated.append("yes")
312 # Checking if the flavor already exists, if not create it
313 log.debug("Checking flavor: " + flavor_name)
314 if flavor_name in Flavors:
315 log.info("Flavor (" + flavor_name+") already installed")
317 log.info('Creating flavor ...')
318 cmd = 'openstack flavor create %s %s -f value -c name'%(flavor_name,flavor_info)
320 NewFlavor = subprocess.check_output(cmd , shell=True).decode().strip()
321 if flavor_name in NewFlavor:
322 cmd = 'openstack flavor set %s %s'%(flavor_name, flavor_meta_data)
324 subprocess.check_call(cmd , shell=True)
325 log.info("Flavor created")
327 log.exception("Failed to create flavor: " + flavor_name)
328 raise Exception("Failed to create flavor: " + flavor_name)
329 if SRIOV_mgmt_port == 'NO':
330 nic_info = '--nic net-id=%s'%(internal_network)
332 nic_info = '--nic port-id=%s'%(SRIOV_mgmt_port)
333 if SRIOV_port == 'NO':
334 nic_info = nic_info + ' --nic net-id=%s'%(dataplane_network)
336 for port in SRIOV_port.split(','):
337 nic_info = nic_info + ' --nic port-id=%s'%(port)
338 if vm==int(total_number_of_VMs):
339 # For the last server, we want to wait for the server creation to complete, so the next operations will succeeed (e.g. IP allocation)
340 # Note that this waiting is not bullet proof. Imagine, we loop through all the VMs, and the last VM was already running, while the previous
341 # VMs still needed to be created. Or the previous server creations take much longer than the last one.
342 # In that case, we might be too fast when we query for the IP & MAC addresses.
346 log.info("Creating server...")
347 cmd = 'openstack server create --flavor %s --key-name %s --image %s %s %s %s %s'%(flavor_name,key,image,nic_info,boot_info,wait,ServerName[-1])
349 output = subprocess.check_output(cmd , shell=True).decode().strip()
350 if floating_network != 'NO':
351 for vm in range(0, int(total_number_of_VMs)):
352 if ServerToBeCreated[vm] =="yes":
353 log.info('Creating & Associating floating IP for ('+ServerName[vm]+')...')
354 cmd = 'openstack server show %s -c addresses -f value |grep -Eo "%s=[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*" | cut -d"=" -f2'%(ServerName[vm],internal_network)
356 vmportIP = subprocess.check_output(cmd , shell=True).decode().strip()
357 cmd = 'openstack port list -c ID -c "Fixed IP Addresses" | grep %s | cut -d" " -f 2 ' %(vmportIP)
359 vmportID = subprocess.check_output(cmd , shell=True).decode().strip()
360 cmd = 'openstack floating ip create --port %s %s'%(vmportID,floating_network)
362 output = subprocess.check_output(cmd , shell=True).decode().strip()
364 config.add_section('rapid')
365 config.set('rapid', 'loglevel', loglevel)
366 config.set('rapid', 'version', version)
367 config.set('rapid', 'total_number_of_machines', total_number_of_VMs)
368 for vm in range(1, int(total_number_of_VMs)+1):
369 cmd = 'openstack server show %s'%(ServerName[vm-1])
371 output = subprocess.check_output(cmd , shell=True).decode().strip()
372 searchString = '.*%s=([0-9]*\.[0-9]*\.[0-9]*\.[0-9]*)' %(dataplane_network)
373 matchObj = re.search(searchString, output, re.DOTALL)
374 vmDPIP = matchObj.group(1)
375 searchString = '.*%s=([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+),*\s*([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)*' %(internal_network)
376 matchObj = re.search(searchString, output, re.DOTALL)
377 vmAdminIP = matchObj.group(2)
378 if vmAdminIP == None:
379 vmAdminIP = matchObj.group(1)
380 cmd = 'openstack port list |egrep "\\b%s\\b" | tr -s " " | cut -d"|" -f 4'%(vmDPIP)
382 vmDPmac = subprocess.check_output(cmd , shell=True).decode().strip()
383 config.add_section('M%d'%vm)
384 config.set('M%d'%vm, 'name', ServerName[vm-1])
385 config.set('M%d'%vm, 'admin_ip', vmAdminIP)
386 config.set('M%d'%vm, 'dp_ip', vmDPIP)
387 config.set('M%d'%vm, 'dp_mac', vmDPmac)
388 log.info('%s: (admin IP: %s), (dataplane IP: %s), (dataplane MAC: %s)' % (ServerName[vm-1],vmAdminIP,vmDPIP,vmDPmac))
390 config.add_section('ssh')
391 config.set('ssh', 'key', key+'.pem')
392 config.set('ssh', 'user', 'centos')
393 config.add_section('Varia')
394 config.set('Varia', 'VIM', 'OpenStack')
395 config.set('Varia', 'stack', stack)
396 config.set('Varia', 'VMs', vms)
397 config.set('Varia', 'image', image)
398 config.set('Varia', 'image_file', image_file)
399 config.set('Varia', 'dataplane_network', dataplane_network)
400 config.set('Varia', 'subnet', subnet)
401 config.set('Varia', 'subnet_cidr', subnet_cidr)
402 config.set('Varia', 'internal_network', internal_network)
403 config.set('Varia', 'floating_network', floating_network)
404 # Writing the environment file
405 with open(stack+'.env', 'wb') as envfile:
406 config.write(envfile)