2 # -*- coding: utf-8 -*-
3 # PYTHON_ARGCOMPLETE_OK
6 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
7 # This file is part of openvim
10 # Licensed under the Apache License, Version 2.0 (the "License"); you may
11 # not use this file except in compliance with the License. You may obtain
12 # a copy of the License at
14 # http://www.apache.org/licenses/LICENSE-2.0
16 # Unless required by applicable law or agreed to in writing, software
17 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
18 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
19 # License for the specific language governing permissions and limitations
22 # For those usages not covered by the Apache License, Version 2.0 please
23 # contact with: nfvlabs@tid.es
27 This is a client for managing the openvim server.
28 Useful for human CLI management of items at openvim
30 __author__="Alfonso Tierno, Gerardo Garcia"
31 __date__ ="$26-nov-2014 19:09:29$"
32 __version__="0.4.0-r443"
33 version_date="Nov 2015"
36 from argcomplete.completers import FilesCompleter
43 from jsonschema import validate as js_v, exceptions as js_e
45 class ArgumentParserError(Exception): pass
47 class ThrowingArgumentParser(argparse.ArgumentParser):
48 def error(self, message):
49 print "Error: %s" %message
54 print "Type 'openvim -h' for help"
55 raise ArgumentParserError
58 config_items=("HOST", "PORT", "ADMIN_PORT", "TENANT")
59 show_at_verbose1=('status', 'created', 'path', 'last_error','description','hostId','progress',
60 'ram','vcpus','type','shared','admin_state_up', 'enabled', 'ip_name')
62 #template for creating new items
66 "${name} provide a port name",
67 "${net_id} provide the network uuid (null):",
68 "${vlan} provide the vlan if any (null):",
69 "${port} provide the attached switch port (Te0/47)",
70 "${mac} provide the mac of external device if known (null):"
74 "network_id": "${net_id null}",
76 "binding:vlan": "${vlan null-int}",
77 "binding:switch_port": "${port}",
78 "mac_address": "${mac null}"
83 "${name} provide a network name",
84 "${type} provide a type: bridge_data,bridge_man,data,ptp (data)",
85 "${shared} external network: true,false (true)",
86 "${phy} conected to: bridge:name,macvtap:iface,default (null)"
91 "provider:physical": "${phy null}",
92 "shared": "${shared bool}"
98 "${user} host user (user)",
99 "${ip_name} host access IP or name (${name})",
100 "${description} host description (${name})"
106 "ip_name": "${ip_name}",
107 "description": "${description}"
112 "${name} flavor name",
113 "${description} flavor description (${name})",
114 "${processor_ranking} processor ranking (100)",
115 "${memory} memory in GB (2)",
116 "${threads} threads needed (2)"
120 "description":"${description}",
122 "processor_ranking":"${processor_ranking int}",
125 "memory":"${memory int}",
126 "threads":"${threads int}"
134 "${name} tenant name",
135 "${description} tenant description (${name})"
139 "description": "${description}"
144 "${name} image name",
145 "${path} image path (/path/to/shared/compute/host/folder/image.qcow2)",
146 "${description} image description (${name})"
150 "description":"${description}",
156 "${name} provide a name (VM)",
157 "${description} provide a description (${name})",
158 "${image_id} provide image_id uuid",
159 "${flavor_id} provide flavor_id uuid",
160 "${network0} provide a bridge network id; enter='default' (00000000-0000-0000-0000-000000000000)",
161 "${network1} provide a bridge network id; enter='virbrMan' (60f5227e-195f-11e4-836d-52540030594e)"
167 "vpci": "0000:00:0a.0",
172 "vpci": "0000:00:0b.0",
177 "description":"${description}",
178 "imageRef": "${image_id}",
179 "flavorRef": "${flavor_id}"
184 def check_configuration(*exception ):
186 Check that the configuration variables are present.
187 exception can contain variables that are not tested, normally because is not used for the command
190 for item in config_items:
191 if item not in exception and vim_config[item]==None:
192 print "OPENVIM_"+item+" variable not defined. Try '" + name + " config file_cfg' or 'export OPENVIM_"+item+"=something'"
193 exit(-401); #HTTP_Unauthorized
195 def check_valid_uuid(uuid):
197 Determines if the param uuid is a well formed uuid. Otherwise it is consider a name
199 id_schema = {"type" : "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"}
201 js_v(uuid, id_schema)
203 except js_e.ValidationError:
206 def _print_verbose(row_data, element, verbose=0):
208 data = row_data[element]
209 if verbose==1 or verbose==2:
210 data2= dict((k,v) for (k,v) in data.iteritems() if k in show_at_verbose1 and v!=None)
211 if 'image' in data and 'id' in data['image']:
212 data2['image'] = data['image']['id']
213 if 'flavor' in data and 'id' in data['flavor']:
214 data2['flavor'] = data['flavor']['id']
216 #TODO add more intems in a digest mode, extended, numas ...
219 #if "extended" in data
222 #print json.dumps(c2, indent=4)
225 # if data[key]==None: del data[key]
226 # elif key=='id' or key=='name' or key=='links': del data[key]
228 #print json.dumps(data2, indent=4)
229 data2={element: data2}
230 print yaml.safe_dump(data2, indent=4, default_flow_style=False)
234 Send a GET http to VIM
236 headers_req = {'content-type': 'application/json'}
238 vim_response = requests.get(url, headers = headers_req)
239 if vim_response.status_code == 200:
240 #print vim_response.json()
241 #print json.dumps(vim_response.json(), indent=4)
242 content = vim_response.json()
246 #print " Error. VIM response '%s': not possible to GET %s, error %s" % (vim_response.status_code, url, vim_response.text)
247 return -vim_response.status_code, vim_response.text
248 except requests.exceptions.RequestException, e:
249 return -1, " Exception GET at '"+url+"' " + str(e.message)
253 Send a DELETE http to VIM
255 headers_req = {'content-type': 'application/json'}
257 vim_response = requests.delete(url, headers = headers_req)
258 if vim_response.status_code != 200:
259 #print " Error. VIM response '%s': not possible to DELETE %s, error %s" % (vim_response.status_code, url, vim_response.text)
260 return -vim_response.status_code, vim_response.text
261 except requests.exceptions.RequestException, e:
262 return -1, " Exception DELETE at '"+url+"' " + str(e.message)
263 return 1, vim_response.json()
265 def vim_action(url, payload):
267 Send a POST http to VIM
269 headers_req = {'content-type': 'application/json'}
271 vim_response = requests.post(url, data=json.dumps(payload), headers = headers_req)
272 if vim_response.status_code != 200:
273 #print " Error. VIM response '%s': not possible to POST %s, error %s" % (vim_response.status_code, url, vim_response.text)
274 return -vim_response.status_code, vim_response.text
275 except requests.exceptions.RequestException, e:
276 return -1, " Exception POST at '"+url+"' " + str(e.message)
277 return 1, vim_response.json()
279 def vim_edit(url, payload):
280 headers_req = {'content-type': 'application/json'}
282 vim_response = requests.put(url, data=json.dumps(payload), headers = headers_req)
283 if vim_response.status_code != 200:
284 #print " Error. VIM response '%s': not possible to PUT %s, error %s" % (vim_response.status_code, url, vim_response.text)
285 return -vim_response.status_code, vim_response.text
286 except requests.exceptions.RequestException, e:
287 return -1, " Exception PUT at '"+url+"' " + str(e.message)
288 return 1, vim_response.json()
290 def vim_create(url, payload):
291 headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
294 vim_response = requests.post(url, data=json.dumps(payload), headers=headers_req)
295 if vim_response.status_code != 200:
296 #print " Error. VIM response '%s': not possible to POST %s, error %s" % (vim_response.status_code, url, vim_response.text)
297 return -vim_response.status_code, vim_response.text
298 except requests.exceptions.RequestException, e:
299 return -1, " Exception POST at '"+url+"' " + str(e.message)
300 return 1, vim_response.json()
302 def parse_yaml_json(text, file_name=None):
305 if file_name[-5:]=='.yaml' or file_name[-4:]=='.yml':
307 elif file_name[-5:]=='.json':
309 if parser_json==None:
310 parser_json=True if '\t' in text else False
312 if parser_json: #try parse in json format, because yaml does not admit tabs
314 data = json.loads(text)
316 except Exception as e:
317 return -1, "Error json format: " + str(e)
318 #try to parse in yaml format
320 data = yaml.load(text)
322 except yaml.YAMLError as exc:
324 if hasattr(exc, 'problem_mark'):
325 mark = exc.problem_mark
326 error_pos = " at line:%s column:%s" % (mark.line+1, mark.column+1)
327 return -1, " Error yaml format error" + error_pos
329 def change_string(text, var_list):
331 See change_var_recursively help
332 Used for changed any '${var format}' content inside the 'text' string
333 into the corresponding value at 'var_list'[var]
334 'format' is optional, string by default. Contain a - separated formats,ej, int-null
339 ini = text.find("${", end)
340 if ini<0: return text
341 end = text.find("}", ini)
342 if end<0: return text
350 var = var_list.get(var, None)
351 if var==None: return text
353 text = text[:ini] + var + text[end:]
355 if 'null' in type_ and text=="null":
357 if 'int' in type_ : #and text.isnumeric():
359 if 'bool' in type_ : #and text.isnumeric():
360 if text.lower()=="true": return True
361 elif text.lower()=="false": return False
363 print "input boolean paramter must be 'true' or 'false'"
367 def chage_var_recursively(data, var_list):
369 Check recursively the content of 'data', and look for "*${*}*" variables and changes
370 It assumes that this variables are not in the key of dictionary,
371 The overall target is having json/yaml templates with this variables in the text.
372 The user is asked for a value that is replaced wherever appears
374 'data': dictionary, or list. None or empty is considered valid
375 'var_list': dictionary (name:change) pairs
377 None, data is modified
380 if type(data) is dict:
381 for k in data.keys():
382 if type(data[k]) is dict or type(data[k]) is tuple or type(data[k]) is list:
383 chage_var_recursively(data[k], var_list)
384 elif type(data[k]) is str:
385 data[k] = change_string(data[k], var_list)
386 if type(data) is list:
387 for k in range(0,len(data)):
388 if type(data[k]) is dict or type(data[k]) is list:
389 chage_var_recursively(data[k], var_list)
390 elif type(data[k]) is str:
391 data[k] = change_string(data[k], var_list)
393 def change_var(data, default_values={}):
394 ''' Look for a text "${}" key at 'data' that indicates that this json contains
395 variables that must be ask for values to the user and changes in the text of
397 'default_values' contain a dictionary of values that must be used and not be asked
398 Useful for creating templates
399 "${}" entry contain a list with the text:
400 ${var-name} prompt text to show user (Default value to allocate if user type CR)
402 if type(data) is not dict:
403 return -1, "Format error, not a object (dictionary)"
404 if "${}" not in data:
408 for var in data["${}"]:
409 r = var.find("}",) + 1
410 if r<=2 or var[:2] != '${':
411 return -1, "Format error at '${}':" + var
412 #change variables inside description text
414 var = var[:r] + change_string(var[r:], var_list)
415 d_start = var.rfind("(",) + 1
416 d_end = var.rfind(")",)
417 if d_start>0 and d_end>=d_start:
418 default = var[d_start:d_end]
420 if var[2:r-1] in default_values:
421 var_list[ var[:r] ] = default_values[ var[2:r-1] ]
423 v = raw_input(var[r:] + "? ")
428 v = raw_input(" empty string? try again: ")
429 var_list[ var[:r] ] = str(v)
431 chage_var_recursively(data, var_list)
434 def load_file(file_, parse=False):
442 return -1, " Error opening file '" + file_ + "': " + e.args[1]
443 return parse_yaml_json(read_data, file_)
445 def load_file_or_yaml(content):
447 'content' can be or a yaml/json file or a text containing a yaml/json text format
448 This function autodetect, trying to load and parse the filename 'content',
449 if fails trying to parse the 'content' as text
450 Returns the dictionary once parsed, or print an error and finish the program
452 r,payload = load_file(content, parse=True)
454 if r==-1 and "{" in content or ":" in content:
455 #try to parse directly
456 r,payload = parse_yaml_json(content)
466 #print "config-list",args
467 if args.file != None:
469 f = open(args.file, 'r')
473 print " Error opening file '" + args.file + "': " + e.args[1]
476 data = yaml.load(read_data)
478 if "http_host" in data:
479 print " export OPENVIM_HOST="+ data["http_host"]
480 if "http_port" in data:
481 print " export OPENVIM_PORT="+ str(data["http_port"])
482 #vim_config[item] = data["OPENVIM_"+item] #TODO find the way to change envioronment
483 #os.setenv('OPENVIM_'+item, vim_config[item])
484 if "http_admin_port" in data:
485 print " export OPENVIM_ADMIN_PORT="+ str(data["http_admin_port"])
486 if "tenant_id" in data:
487 print " export OPENVIM_TENANT="+ data["tenant_id"]
489 except yaml.YAMLError, exc:
491 if hasattr(exc, 'problem_mark'):
492 mark = exc.problem_mark
493 error_pos = " at position: (%s:%s)" % (mark.line+1, mark.column+1)
494 print " Error yaml/json format error at '"+ args.file +"'"+error_pos
497 print "OPENVIM_HOST: %s" %vim_config["HOST"]
498 print "OPENVIM_PORT: %s" %vim_config["PORT"]
499 print "OPENVIM_ADMIN_PORT: %s" %vim_config["ADMIN_PORT"]
500 print "OPENVIM_TENANT: %s" %vim_config["TENANT"]
503 def element_new(args):
506 if args.element in ('flavors','images','servers'):
507 check_configuration( "ADMIN_PORT" )
508 tenant="/"+vim_config["TENANT"]
510 check_configuration("ADMIN_PORT", "TENANT")
514 if args.name != None:
515 default_values["name"] = args.name
516 if "description" in args and args.description != None:
517 default_values["description"] = args.description
518 if "path" in args and args.path != None:
519 default_values["path"] = args.path
521 payload= template[args.element[:-1] ]
522 payload = yaml.load(str(payload)) #with this trick we make a completely copy of the data, so to not modified the original one
524 payload=load_file_or_yaml(args.file)
525 r,c= change_var(payload, default_values)
527 print "Template error", c
531 if args.element[:-1] not in payload:
532 payload = {args.element[:-1]: payload }
533 item = payload[args.element[:-1]]
535 url = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["PORT"], tenant, args.element)
536 if "ADMIN_PORT" in vim_config and vim_config["ADMIN_PORT"]!=None:
537 url_admin = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["ADMIN_PORT"], tenant, args.element)
541 if args.name != None:
542 item["name"] = args.name
543 if "description" in args and args.description != None:
544 item["description"] = args.description
545 if "path" in args and args.path != None:
546 item["path"] = args.path
549 r,c = vim_create(url, payload)
550 if r==-401 and url_admin!=None and url != url_admin: #Unauthorized, try with admin privileges
552 r,c = vim_create(url, payload)
558 item=c[ args.element[:-1] ]
559 uuid=item.get('id', None)
561 uuid=item.get('uuid', 'uuid-not-found?')
562 name = item.get('name', '')
563 print " " + uuid +" "+ name.ljust(20) + " Created"
564 for e in ('warning', 'error', 'last_error'):
565 if e in item: print e + "\n" + item[e]
566 if args.verbose!=None:
567 _print_verbose(c, args.element[:-1], args.verbose)
570 def element_action(args):
573 if args.element in ('flavors','images','servers'):
574 check_configuration( "ADMIN_PORT")
575 tenant="/"+vim_config["TENANT"]
577 check_configuration("ADMIN_PORT", "TENANT")
579 url = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["PORT"], tenant, args.element)
580 if "ADMIN_PORT" in vim_config and vim_config["ADMIN_PORT"]!=None:
581 url_admin = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["ADMIN_PORT"], tenant, args.element)
586 filter_qs += "?" + args.filter
588 if check_valid_uuid(args.name):
589 if len(filter_qs) > 0: filter_qs += "&" + "id=" + str(args.name)
590 else: filter_qs += "?" + "id=" + str(args.name)
592 if len(filter_qs) > 0: filter_qs += "&" + "name=" + str(args.name)
593 else: filter_qs += "?" + "name=" + str(args.name)
596 r,c = vim_read(url + filter_qs)
597 if r==-401 and url_admin!=None and url != url_admin: #Unauthorized, try with admin privileges
598 r,c = vim_read(url_admin + filter_qs)
602 if args.action=='createImage':
603 payload={ args.action: {"name":args.imageName} }
604 if args.description != None:
605 payload[args.action]["description"]=args.description
606 if args.path != None:
607 payload[args.action]["path"]=args.path
609 payload={ args.action: None}
610 #print json.dumps(c, indent=4)
611 item_list = c[ args.element ]
612 if len(item_list)==0 and args.name != None:
613 print " Not found " + args.element + " " + args.name
614 return 404 #HTTP_Not_Found
616 for item in item_list:
617 uuid=item.get('id', None)
619 uuid=item.get('uuid', None)
623 name = item.get('name', '')
625 r = raw_input(" Action over " + args.element + " " + uuid + " " + name + " (y/N)? ")
626 if len(r)>0 and r[0].lower()=="y":
630 r,c = vim_action(url + "/" + uuid + "/action", payload)
631 if r==-401 and url_admin!=None and url != url_admin: #Unauthorized, try with admin privileges
633 r,c = vim_action(url + "/" + uuid + "/action", payload)
635 print " " + uuid +" "+ name.ljust(20) + " " + c
638 if args.action == "createImage": #response contain an {image: {...} }, not a {server: {...} }.
639 print " " + c["image"]["id"] +" "+ c["image"]["name"].ljust(20)
640 args.element="images"
642 print " " + uuid +" "+ name.ljust(20) + " "+ args.action
643 if "verbose" in args and args.verbose!=None:
644 _print_verbose(c, args.element[:-1], args.verbose)
647 def element_edit(args):
650 if args.element in ('flavors','images','servers'):
651 check_configuration( "ADMIN_PORT")
652 tenant="/"+vim_config["TENANT"]
654 check_configuration("ADMIN_PORT", "TENANT")
657 url = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["PORT"], tenant, args.element)
658 if "ADMIN_PORT" in vim_config and vim_config["ADMIN_PORT"]!=None:
659 url_admin = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["ADMIN_PORT"], tenant, args.element)
664 filter_qs += "?" + args.filter
666 if check_valid_uuid(args.name):
667 if len(filter_qs) > 0: filter_qs += "&" + "id=" + str(args.name)
668 else: filter_qs += "?" + "id=" + str(args.name)
670 if len(filter_qs) > 0: filter_qs += "&" + "name=" + str(args.name)
671 else: filter_qs += "?" + "name=" + str(args.name)
674 r,c = vim_read(url + filter_qs)
675 if r==-401 and url_admin!=None and url != url_admin: #Unauthorized, try with admin privileges
676 r,c = vim_read(url_admin + filter_qs)
681 payload=load_file_or_yaml(args.file)
682 r2,c2= change_var(payload)
684 print "Template error", c2
687 if args.element[:-1] not in payload:
688 payload = {args.element[:-1]: payload }
690 #print json.dumps(c, indent=4)
691 item_list = c[ args.element ]
692 if len(item_list)==0 and args.name != None:
693 print " Not found " + args.element + " " + args.name
694 return 404 #HTTP_Not_Found
696 for item in item_list:
697 uuid=item.get('id', None)
699 uuid=item.get('uuid', None)
703 name = item.get('name', '')
704 if not args.force or (args.name==None and args.filer==None):
705 r = raw_input(" Edit " + args.element + " " + uuid + " " + name + " (y/N)? ")
706 if len(r)>0 and r[0].lower()=="y":
710 r,c = vim_edit(url + "/" + uuid, payload)
711 if r==-401 and url_admin!=None and url != url_admin: #Unauthorized, try with admin privileges
713 r,c = vim_edit(url + "/" + uuid, payload)
715 print " " + uuid +" "+ name.ljust(20) + " " + c
718 print " " + uuid +" "+ name.ljust(20) + " edited"
719 if "verbose" in args and args.verbose!=None:
720 _print_verbose(c, args.element[:-1], args.verbose)
723 def element_action_edit(args):
725 if args.element=='ports':
726 if args.action=='attach':
727 args.file='network_id: ' + args.network_id
728 else: #args.action=='detach'
729 args.file='network_id: null'
730 if args.action=='up':
731 args.file='admin_state_up: true'
732 if args.action=='down':
733 args.file='admin_state_up: false'
734 return element_edit(args)
736 def element_delete(args):
739 if args.element in ('flavors','images','servers'):
740 check_configuration("ADMIN_PORT" )
741 tenant="/"+vim_config["TENANT"]
743 check_configuration("ADMIN_PORT", "TENANT")
746 url = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["PORT"], tenant, args.element)
747 if "ADMIN_PORT" in vim_config and vim_config["ADMIN_PORT"]!=None:
748 url_admin = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["ADMIN_PORT"], tenant, args.element)
753 filter_qs += "?" + args.filter
755 if check_valid_uuid(args.name):
756 if len(filter_qs) > 0: filter_qs += "&" + "id=" + str(args.name)
757 else: filter_qs += "?" + "id=" + str(args.name)
759 if len(filter_qs) > 0: filter_qs += "&" + "name=" + str(args.name)
760 else: filter_qs += "?" + "name=" + str(args.name)
763 r,c = vim_read(url + filter_qs)
764 if r==-401 and url_admin!=None and url != url_admin: #Unauthorized, try with admin privileges
765 r,c = vim_read(url_admin + filter_qs)
770 #print json.dumps(c, indent=4)
771 item_list = c[ args.element ]
772 if len(item_list)==0 and args.name != None:
773 print " Not found " + args.element + " " + args.name
774 return 404 #HTTP_Not_Found
776 for item in item_list:
777 uuid=item.get('id', None)
779 uuid=item.get('uuid', None)
784 name = item.get('name', '')
786 r = raw_input(" Delete " + args.element + " " + uuid + " " + name + " (y/N)? ")
787 if len(r)>0 and r[0].lower()=="y":
791 r,c = vim_delete(url + "/" + uuid)
792 if r==-401 and url_admin!=None and url != url_admin: #Unauthorized, try with admin privileges
794 r,c = vim_delete(url + "/" + uuid)
796 print " " + uuid +" "+ name.ljust(20) + " " + c
799 print " " + uuid +" "+ name.ljust(20) + " deleted"
802 def element_list(args):
803 #print "element_list", args
806 if args.element in ('flavors','images','servers'):
807 check_configuration( "ADMIN_PORT" )
808 tenant="/"+vim_config["TENANT"]
810 check_configuration( "ADMIN_PORT", "TENANT" )
813 # what += "/" + args.name
814 url = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["PORT"], tenant, args.element)
815 if "ADMIN_PORT" in vim_config and vim_config["ADMIN_PORT"]!=None:
816 url_admin = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["ADMIN_PORT"], tenant, args.element)
819 #print " get", what, " >>>>>>>> ",
821 filter_qs += "?" + args.filter
823 if check_valid_uuid(args.name):
824 if len(filter_qs) > 0: filter_qs += "&" + "id=" + str(args.name)
825 else: filter_qs += "?" + "id=" + str(args.name)
827 if len(filter_qs) > 0: filter_qs += "&" + "name=" + str(args.name)
828 else: filter_qs += "?" + "name=" + str(args.name)
831 r,c = vim_read(url + filter_qs)
832 if r==-401 and url_admin!=None and url != url_admin: #Unauthorized, try with admin privileges
833 r,c = vim_read(url_admin + filter_qs)
837 #print json.dumps(c, indent=4)
839 item_list = c[ args.element ]
841 #if args.name!=None and len(item_list)==1:
843 if args.verbose!=None:
844 verbose += args.verbose
845 for item in item_list:
847 if args.element=="servers" or args.element=="networks": extra = " "+item['status']
848 if args.element in ("hosts","networks","ports") and not item['admin_state_up']: extra += " admin_state_up=false"
849 print item['id']+" "+item['name'].ljust(20) + extra
851 r2,c2 = vim_read(url + "/"+ item['id'])
852 if r2==-401 and url_admin!=None and url != url_admin: #Unauthorized, try with admin privileges
854 r2,c2 = vim_read(url + "/"+ item['id'])
856 _print_verbose(c2, args.element[:-1], verbose)
860 def openflow_action(args):
861 if args.action=='port-list':
862 url = "http://%s:%s/openvim/networks/openflow/ports" %(vim_config["HOST"], vim_config["PORT"])
864 elif args.action=='rules-list' or args.action=='reinstall':
865 PORT = vim_config["PORT"]
866 if args.action=='reinstall':
867 if "ADMIN_PORT" not in vim_config:
868 print "OPENVIM_ADMIN_PORT variable not defined"
869 return 404 #HTTP_Not_Found
870 PORT = vim_config["ADMIN_PORT"]
872 url = "http://%s:%s/openvim/networks" %(vim_config["HOST"], PORT)
873 if check_valid_uuid(args.name):
874 url += "?id=" + str(args.name)
876 url += "?name=" + str(args.name)
881 if len (c["networks"]) == 0:
882 print " Network not found"
883 return 404 #HTTP_Not_Found
884 if len (c["networks"]) > 1:
885 print " More than one net with this name found. Use uuid instead of name to concretize"
886 return 404 #HTTP_Not_Found
887 network = c["networks"][0]["id"]
890 url = "http://%s:%s/openvim/networks/%s/openflow" %(vim_config["HOST"], PORT, network)
891 if args.action=='reinstall':
892 r,c = vim_edit(url, None)
895 elif args.action=='clear-all':
896 if "ADMIN_PORT" not in vim_config:
897 print "OPENVIM_ADMIN_PORT variable not defined"
898 return 401 # HTTP_Unauthorized
899 url = "http://%s:%s/openvim/networks/openflow/clear" %(vim_config["HOST"], vim_config["ADMIN_PORT"])
900 r,c = vim_delete(url)
902 return 400 #HTTP_Bad_Request
907 print yaml.safe_dump(c, indent=4, default_flow_style=False)
910 if __name__=="__main__":
912 item_dict={'vm':'servers','host':'hosts','tenant':'tenants','image':'images','flavor':'flavors','net':'networks','port':'ports'}
915 vim_config["HOST"] = os.getenv('OPENVIM_HOST', 'localhost')
916 vim_config["PORT"] = os.getenv('OPENVIM_PORT', '9080')
917 vim_config["ADMIN_PORT"] = os.getenv('OPENVIM_ADMIN_PORT', '9085')
918 vim_config["TENANT"] = os.getenv('OPENVIM_TENANT', None)
921 main_parser = ThrowingArgumentParser(description='User program to interact with OPENVIM-SERVER (openvimd)')
922 #main_parser = argparse.ArgumentParser(description='User program to interact with OPENVIM-SERVER (openvimd)')
923 main_parser.add_argument('--version', action='version', version='%(prog)s ' + __version__ + ' '+version_date)
925 subparsers = main_parser.add_subparsers(help='commands')
927 config_parser = subparsers.add_parser('config', help="prints configuration values")
928 config_parser.add_argument("file", nargs='?', help="configuration file to extract the configuration").completer = FilesCompleter
929 config_parser.set_defaults(func=config)
931 for item in item_dict:
933 element_list_parser = subparsers.add_parser(item+'-list', help="lists information about "+item_dict[item])
934 element_list_parser.add_argument("name", nargs='?', help="name or ID of the " + item)
935 element_list_parser.add_argument("-F","--filter", action="store", help="filter query string")
936 element_list_parser.add_argument('--verbose', '-v', action='count', help="increment the verbosity")
937 element_list_parser.set_defaults(func=element_list, element=item_dict[item])
940 element_new_parser = subparsers.add_parser(item+'-add', help="adds a new compute node")
942 element_new_parser = subparsers.add_parser(item+'-create', help="creates a new "+item_dict[item][:-1])
943 element_new_parser.add_argument("file", nargs='?', help="json/yaml text or file with content").completer = FilesCompleter
944 element_new_parser.add_argument("--name", action="store", help="Use this name")
946 element_new_parser.add_argument("--description", action="store", help="Use this descrition")
948 element_new_parser.add_argument("--path", action="store", help="Use this path")
949 element_new_parser.add_argument('--verbose', '-v', action='count', help="increment the verbosity")
950 element_new_parser.set_defaults(func=element_new, element=item_dict[item])
953 element_del_parser = subparsers.add_parser(item+'-remove', help="removes a compute node")
955 element_del_parser = subparsers.add_parser(item+'-delete', help="deletes one or several "+item_dict[item])
956 element_del_parser.add_argument("name", nargs='?', help="name or ID of the "+item+", if missing means all")
957 element_del_parser.add_argument("-F","--filter", action="store", help="filter query string")
958 element_del_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
959 element_del_parser.set_defaults(func=element_delete, element=item_dict[item])
961 element_edit_parser = subparsers.add_parser(item+'-edit', help="edits one or several "+item_dict[item])
962 element_edit_parser.add_argument("name", help="name or ID of the "+item+"")
963 element_edit_parser.add_argument("file", help="json/yaml text or file with the changes").completer = FilesCompleter
964 element_edit_parser.add_argument("-F","--filter", action="store", help="filter query string")
965 element_edit_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
966 element_edit_parser.add_argument('--verbose', '-v', action='count', help="increment the verbosity")
967 element_edit_parser.set_defaults(func=element_edit, element=item_dict[item])
970 for item2 in ('shutdown', 'start', 'rebuild', 'reboot'):
971 vm_action_parser = subparsers.add_parser("vm-"+item2, help="performs this action over the virtual machine")
972 vm_action_parser.add_argument("name", nargs='?', help="name or ID of the server, if missing means all")
973 vm_action_parser.add_argument("-F","--filter", action="store", help="filter query string")
974 vm_action_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
975 vm_action_parser.set_defaults(func=element_action, element="servers", action=item2 )
976 vm_action_image_parser = subparsers.add_parser("vm-createImage", help="creates a snapshot of the virtual machine disk into a new image")
977 vm_action_image_parser.add_argument("name", help="name or ID of the server")
978 vm_action_image_parser.add_argument("imageName", help="image name")
979 vm_action_image_parser.add_argument("--description", action="store", help="Provide a new image description")
980 vm_action_image_parser.add_argument("--path", action="store", help="Provide a new image complete path")
981 vm_action_image_parser.add_argument("-F","--filter", action="store", help="filter query string")
982 vm_action_image_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
983 vm_action_image_parser.add_argument('--verbose', '-v', action='count', help="increment the verbosity")
984 vm_action_image_parser.set_defaults(func=element_action, element="servers", action="createImage" )
985 #ACTION that are implemented with EDITION
987 port_action_attach_parser = subparsers.add_parser("port-attach", help="connects a port to a network")
988 port_action_attach_parser.add_argument("name", help="name or ID of the port")
989 port_action_attach_parser.add_argument("network_id", help="ID of the network")
990 port_action_attach_parser.add_argument("-F","--filter", action="store", help="filter query string")
991 port_action_attach_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
992 port_action_attach_parser.set_defaults(func=element_action_edit, element="ports", action="attach")
994 port_action_detach_parser = subparsers.add_parser("port-detach", help="removes a port from a network")
995 port_action_detach_parser.add_argument("name", help="name or ID of the port")
996 port_action_detach_parser.add_argument("-F","--filter", action="store", help="filter query string")
997 port_action_detach_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
998 port_action_detach_parser.set_defaults(func=element_action_edit, element="ports", action="dettach")
1000 if item=='net' or item=='host':
1001 nethost_action_up_parser = subparsers.add_parser(item+"-up", help="puts admin_state_up of "+item_dict[item][:-1]+" to true")
1002 nethost_action_up_parser.add_argument("name", help="name or ID of the "+item_dict[item][:-1])
1003 nethost_action_up_parser.add_argument("-F","--filter", action="store", help="filter query string")
1004 nethost_action_up_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
1005 nethost_action_up_parser.set_defaults(func=element_action_edit, element=item_dict[item], action="up")
1007 nethost_action_down_parser = subparsers.add_parser(item+"-down", help="puts admin_state_up of "+item_dict[item][:-1]+" to false")
1008 nethost_action_down_parser.add_argument("name", help="name or ID of the "+item_dict[item][:-1])
1009 nethost_action_down_parser.add_argument("-F","--filter", action="store", help="filter query string")
1010 nethost_action_down_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
1011 nethost_action_down_parser.set_defaults(func=element_action_edit, element=item_dict[item], action="down")
1014 openflow_list_action = subparsers.add_parser("openflow-port-list", help="list openflow switch ports name")
1015 openflow_list_action.set_defaults(func=openflow_action, action="port-list")
1017 openflow_list_action = subparsers.add_parser("openflow-clear-all", help="removes all openflow rules")
1018 openflow_list_action.set_defaults(func=openflow_action, action="clear-all")
1020 openflow_list_action = subparsers.add_parser("openflow-net-reinstall", help="reinstall the openflow rules for a network")
1021 openflow_list_action.add_argument("name", nargs='?', help="network name, if missing all networks")
1022 openflow_list_action.set_defaults(func=openflow_action, action="reinstall")
1024 openflow_list_action = subparsers.add_parser("openflow-net-list", help="list installed openflow rules for a network")
1025 openflow_list_action.add_argument("name", nargs='?', help="network name, if missing all networks")
1026 openflow_list_action.set_defaults(func=openflow_action, action="rules-list")
1028 argcomplete.autocomplete(main_parser)
1031 args = main_parser.parse_args()
1032 result = args.func(args)
1035 #for some reason it fails if call exit inside try instance. Need to call exit at the end !?
1036 except (requests.exceptions.ConnectionError):
1037 print "Connection error: not possible to contact OPENVIM-SERVER (openvimd)"
1039 except (KeyboardInterrupt):
1040 print 'Exiting openVIM'
1042 except (SystemExit, ArgumentParserError):