#!/usr/bin/env python # -*- coding: utf-8 -*- # PYTHON_ARGCOMPLETE_OK ## # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. # This file is part of openvim # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # For those usages not covered by the Apache License, Version 2.0 please # contact with: nfvlabs@tid.es ## ''' This is a client for managing the openvim server. Useful for human CLI management of items at openvim ''' __author__="Alfonso Tierno, Gerardo Garcia" __date__ ="$26-nov-2014 19:09:29$" __version__="0.4.0-r443" version_date="Nov 2015" name="openvim" from argcomplete.completers import FilesCompleter import os import argparse import argcomplete import requests import json import yaml from jsonschema import validate as js_v, exceptions as js_e class ArgumentParserError(Exception): pass class ThrowingArgumentParser(argparse.ArgumentParser): def error(self, message): print "Error: %s" %message print self.print_usage() #self.print_help() print print "Type 'openvim -h' for help" raise ArgumentParserError global vim_config config_items=("HOST", "PORT", "ADMIN_PORT", "TENANT") show_at_verbose1=('status', 'created', 'path', 'last_error','description','hostId','progress', 'ram','vcpus','type','shared','admin_state_up', 'enabled', 'ip_name') #template for creating new items template={ "port":{ "${}":[ "${name} provide a port name", "${net_id} provide the network uuid (null):", "${vlan} provide the vlan if any (null):", "${port} provide the attached switch port (Te0/47)", "${mac} provide the mac of external device if known (null):" ], "port":{ "name": "${name}", "network_id": "${net_id null}", "type": "external", "binding:vlan": "${vlan null-int}", "binding:switch_port": "${port}", "mac_address": "${mac null}" }, }, "network":{ "${}":[ "${name} provide a network name", "${type} provide a type: bridge_data,bridge_man,data,ptp (data)", "${shared} external network: true,false (true)", "${phy} conected to: bridge:name,macvtap:iface,default (null)" ], "network":{ "name": "${name}", "type": "${type}", "provider:physical": "${phy null}", "shared": "${shared bool}" } }, "host": { "${}":[ "${name} host name", "${user} host user (user)", "${ip_name} host access IP or name (${name})", "${description} host description (${name})" ], "host":{ "name": "${name}", "user": "${user}", "ip_name": "${ip_name}", "description": "${description}" } }, "flavor":{ "${}":[ "${name} flavor name", "${description} flavor description (${name})", "${processor_ranking} processor ranking (100)", "${memory} memory in GB (2)", "${threads} threads needed (2)" ], "flavor":{ "name":"${name}", "description":"${description}", "extended":{ "processor_ranking":"${processor_ranking int}", "numas":[ { "memory":"${memory int}", "threads":"${threads int}" } ] } } }, "tenant":{ "${}":[ "${name} tenant name", "${description} tenant description (${name})" ], "tenant":{ "name": "${name}", "description": "${description}" } }, "image":{ "${}":[ "${name} image name", "${path} image path (/path/to/shared/compute/host/folder/image.qcow2)", "${description} image description (${name})" ], "image":{ "name":"${name}", "description":"${description}", "path":"${path}" } }, "server":{ "${}":[ "${name} provide a name (VM)", "${description} provide a description (${name})", "${image_id} provide image_id uuid", "${flavor_id} provide flavor_id uuid", "${network0} provide a bridge network id; enter='default' (00000000-0000-0000-0000-000000000000)", "${network1} provide a bridge network id; enter='virbrMan' (60f5227e-195f-11e4-836d-52540030594e)" ], "server":{ "networks":[ { "name":"mgmt0", "vpci": "0000:00:0a.0", "uuid":"${network0}" }, { "name":"ge0", "vpci": "0000:00:0b.0", "uuid":"${network1}" } ], "name":"${name}", "description":"${description}", "imageRef": "${image_id}", "flavorRef": "${flavor_id}" } } } def check_configuration(*exception ): ''' Check that the configuration variables are present. exception can contain variables that are not tested, normally because is not used for the command ''' #print exception for item in config_items: if item not in exception and vim_config[item]==None: print "OPENVIM_"+item+" variable not defined. Try '" + name + " config file_cfg' or 'export OPENVIM_"+item+"=something'" exit(-401); #HTTP_Unauthorized def check_valid_uuid(uuid): ''' Determines if the param uuid is a well formed uuid. Otherwise it is consider a name ''' id_schema = {"type" : "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"} try: js_v(uuid, id_schema) return True except js_e.ValidationError: return False def _print_verbose(row_data, element, verbose=0): #print row_data data = row_data[element] if verbose==1 or verbose==2: data2= dict((k,v) for (k,v) in data.iteritems() if k in show_at_verbose1 and v!=None) if 'image' in data and 'id' in data['image']: data2['image'] = data['image']['id'] if 'flavor' in data and 'id' in data['flavor']: data2['flavor'] = data['flavor']['id'] if verbose==2: #TODO add more intems in a digest mode, extended, numas ... pass #if "numas" in data #if "extended" in data else: data2= data #print json.dumps(c2, indent=4) # keys = data.keys() # for key in keys: # if data[key]==None: del data[key] # elif key=='id' or key=='name' or key=='links': del data[key] #print json.dumps(data2, indent=4) data2={element: data2} print yaml.safe_dump(data2, indent=4, default_flow_style=False) def vim_read(url): ''' Send a GET http to VIM ''' headers_req = {'content-type': 'application/json'} try: vim_response = requests.get(url, headers = headers_req) if vim_response.status_code == 200: #print vim_response.json() #print json.dumps(vim_response.json(), indent=4) content = vim_response.json() return 1, content #print http_content else: #print " Error. VIM response '%s': not possible to GET %s, error %s" % (vim_response.status_code, url, vim_response.text) return -vim_response.status_code, vim_response.text except requests.exceptions.RequestException, e: return -1, " Exception GET at '"+url+"' " + str(e.message) def vim_delete(url): ''' Send a DELETE http to VIM ''' headers_req = {'content-type': 'application/json'} try: vim_response = requests.delete(url, headers = headers_req) if vim_response.status_code != 200: #print " Error. VIM response '%s': not possible to DELETE %s, error %s" % (vim_response.status_code, url, vim_response.text) return -vim_response.status_code, vim_response.text except requests.exceptions.RequestException, e: return -1, " Exception DELETE at '"+url+"' " + str(e.message) return 1, vim_response.json() def vim_action(url, payload): ''' Send a POST http to VIM ''' headers_req = {'content-type': 'application/json'} try: vim_response = requests.post(url, data=json.dumps(payload), headers = headers_req) if vim_response.status_code != 200: #print " Error. VIM response '%s': not possible to POST %s, error %s" % (vim_response.status_code, url, vim_response.text) return -vim_response.status_code, vim_response.text except requests.exceptions.RequestException, e: return -1, " Exception POST at '"+url+"' " + str(e.message) return 1, vim_response.json() def vim_edit(url, payload): headers_req = {'content-type': 'application/json'} try: vim_response = requests.put(url, data=json.dumps(payload), headers = headers_req) if vim_response.status_code != 200: #print " Error. VIM response '%s': not possible to PUT %s, error %s" % (vim_response.status_code, url, vim_response.text) return -vim_response.status_code, vim_response.text except requests.exceptions.RequestException, e: return -1, " Exception PUT at '"+url+"' " + str(e.message) return 1, vim_response.json() def vim_create(url, payload): headers_req = {'Accept': 'application/json', 'content-type': 'application/json'} #print str(payload) try: vim_response = requests.post(url, data=json.dumps(payload), headers=headers_req) if vim_response.status_code != 200: #print " Error. VIM response '%s': not possible to POST %s, error %s" % (vim_response.status_code, url, vim_response.text) return -vim_response.status_code, vim_response.text except requests.exceptions.RequestException, e: return -1, " Exception POST at '"+url+"' " + str(e.message) return 1, vim_response.json() def parse_yaml_json(text, file_name=None): parser_json=None if file_name: if file_name[-5:]=='.yaml' or file_name[-4:]=='.yml': parser_json = False elif file_name[-5:]=='.json': parser_json = True if parser_json==None: parser_json=True if '\t' in text else False if parser_json: #try parse in json format, because yaml does not admit tabs try: data = json.loads(text) return 0, data except Exception as e: return -1, "Error json format: " + str(e) #try to parse in yaml format try: data = yaml.load(text) return 0, data except yaml.YAMLError as exc: error_pos = "" if hasattr(exc, 'problem_mark'): mark = exc.problem_mark error_pos = " at line:%s column:%s" % (mark.line+1, mark.column+1) return -1, " Error yaml format error" + error_pos def change_string(text, var_list): ''' See change_var_recursively help Used for changed any '${var format}' content inside the 'text' string into the corresponding value at 'var_list'[var] 'format' is optional, string by default. Contain a - separated formats,ej, int-null ''' end=0 type_=None while True: ini = text.find("${", end) if ini<0: return text end = text.find("}", ini) if end<0: return text end+=1 var = text[ini:end] if ' ' in var: kk=var.split(" ") var=kk[0]+"}" type_=kk[-1][:-1] var = var_list.get(var, None) if var==None: return text text = text[:ini] + var + text[end:] if type_ != None: if 'null' in type_ and text=="null": return None if 'int' in type_ : #and text.isnumeric(): return int(text) if 'bool' in type_ : #and text.isnumeric(): if text.lower()=="true": return True elif text.lower()=="false": return False else: print "input boolean paramter must be 'true' or 'false'" exit(1) return text def chage_var_recursively(data, var_list): ''' Check recursively the content of 'data', and look for "*${*}*" variables and changes It assumes that this variables are not in the key of dictionary, The overall target is having json/yaml templates with this variables in the text. The user is asked for a value that is replaced wherever appears Attributes: 'data': dictionary, or list. None or empty is considered valid 'var_list': dictionary (name:change) pairs Return: None, data is modified ''' if type(data) is dict: for k in data.keys(): if type(data[k]) is dict or type(data[k]) is tuple or type(data[k]) is list: chage_var_recursively(data[k], var_list) elif type(data[k]) is str: data[k] = change_string(data[k], var_list) if type(data) is list: for k in range(0,len(data)): if type(data[k]) is dict or type(data[k]) is list: chage_var_recursively(data[k], var_list) elif type(data[k]) is str: data[k] = change_string(data[k], var_list) def change_var(data, default_values={}): ''' Look for a text "${}" key at 'data' that indicates that this json contains variables that must be ask for values to the user and changes in the text of the dictionary 'default_values' contain a dictionary of values that must be used and not be asked Useful for creating templates "${}" entry contain a list with the text: ${var-name} prompt text to show user (Default value to allocate if user type CR) ''' if type(data) is not dict: return -1, "Format error, not a object (dictionary)" if "${}" not in data: return 0, data var_list={} for var in data["${}"]: r = var.find("}",) + 1 if r<=2 or var[:2] != '${': return -1, "Format error at '${}':" + var #change variables inside description text if "${" in var[r:]: var = var[:r] + change_string(var[r:], var_list) d_start = var.rfind("(",) + 1 d_end = var.rfind(")",) if d_start>0 and d_end>=d_start: default = var[d_start:d_end] else: default=None if var[2:r-1] in default_values: var_list[ var[:r] ] = default_values[ var[2:r-1] ] continue v = raw_input(var[r:] + "? ") if v=="": if default != None: v = default else: v = raw_input(" empty string? try again: ") var_list[ var[:r] ] = str(v) del data["${}"] chage_var_recursively(data, var_list) return 0, data def load_file(file_, parse=False): try: f = open(file_, 'r') read_data = f.read() f.close() if not parse: return 0, read_data except IOError, e: return -1, " Error opening file '" + file_ + "': " + e.args[1] return parse_yaml_json(read_data, file_) def load_file_or_yaml(content): ''' 'content' can be or a yaml/json file or a text containing a yaml/json text format This function autodetect, trying to load and parse the filename 'content', if fails trying to parse the 'content' as text Returns the dictionary once parsed, or print an error and finish the program ''' r,payload = load_file(content, parse=True) if r<0: if r==-1 and "{" in content or ":" in content: #try to parse directly r,payload = parse_yaml_json(content) if r<0: print payload exit (-1) else: print payload exit (-1) return payload def config(args): #print "config-list",args if args.file != None: try: f = open(args.file, 'r') read_data = f.read() f.close() except IOError, e: print " Error opening file '" + args.file + "': " + e.args[1] return -1 try: data = yaml.load(read_data) #print data if "http_host" in data: print " export OPENVIM_HOST="+ data["http_host"] if "http_port" in data: print " export OPENVIM_PORT="+ str(data["http_port"]) #vim_config[item] = data["OPENVIM_"+item] #TODO find the way to change envioronment #os.setenv('OPENVIM_'+item, vim_config[item]) if "http_admin_port" in data: print " export OPENVIM_ADMIN_PORT="+ str(data["http_admin_port"]) if "tenant_id" in data: print " export OPENVIM_TENANT="+ data["tenant_id"] return 0 except yaml.YAMLError, exc: error_pos = "" if hasattr(exc, 'problem_mark'): mark = exc.problem_mark error_pos = " at position: (%s:%s)" % (mark.line+1, mark.column+1) print " Error yaml/json format error at '"+ args.file +"'"+error_pos return -1 print args.file print "OPENVIM_HOST: %s" %vim_config["HOST"] print "OPENVIM_PORT: %s" %vim_config["PORT"] print "OPENVIM_ADMIN_PORT: %s" %vim_config["ADMIN_PORT"] print "OPENVIM_TENANT: %s" %vim_config["TENANT"] return 0 def element_new(args): #print args tenant="" if args.element in ('flavors','images','servers'): check_configuration( "ADMIN_PORT" ) tenant="/"+vim_config["TENANT"] else: check_configuration("ADMIN_PORT", "TENANT") tenant="" default_values={} if args.name != None: default_values["name"] = args.name if "description" in args and args.description != None: default_values["description"] = args.description if "path" in args and args.path != None: default_values["path"] = args.path if args.file==None: payload= template[args.element[:-1] ] payload = yaml.load(str(payload)) #with this trick we make a completely copy of the data, so to not modified the original one else: payload=load_file_or_yaml(args.file) r,c= change_var(payload, default_values) if r<0: print "Template error", c return -1 payload=c #print payload if args.element[:-1] not in payload: payload = {args.element[:-1]: payload } item = payload[args.element[:-1]] url = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["PORT"], tenant, args.element) if "ADMIN_PORT" in vim_config and vim_config["ADMIN_PORT"]!=None: url_admin = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["ADMIN_PORT"], tenant, args.element) else: url_admin = None if args.name != None: item["name"] = args.name if "description" in args and args.description != None: item["description"] = args.description if "path" in args and args.path != None: item["path"] = args.path r,c = vim_create(url, payload) if r==-401 and url_admin!=None and url != url_admin: #Unauthorized, try with admin privileges url=url_admin r,c = vim_create(url, payload) if r<0: print c return -r else: #print c item=c[ args.element[:-1] ] uuid=item.get('id', None) if uuid is None: uuid=item.get('uuid', 'uuid-not-found?') name = item.get('name', '') print " " + uuid +" "+ name.ljust(20) + " Created" for e in ('warning', 'error', 'last_error'): if e in item: print e + "\n" + item[e] if args.verbose!=None: _print_verbose(c, args.element[:-1], args.verbose) return 0 def element_action(args): filter_qs = "" tenant="" if args.element in ('flavors','images','servers'): check_configuration( "ADMIN_PORT") tenant="/"+vim_config["TENANT"] else: check_configuration("ADMIN_PORT", "TENANT") tenant="" url = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["PORT"], tenant, args.element) if "ADMIN_PORT" in vim_config and vim_config["ADMIN_PORT"]!=None: url_admin = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["ADMIN_PORT"], tenant, args.element) else: url_admin = None if args.filter: filter_qs += "?" + args.filter if args.name!=None: if check_valid_uuid(args.name): if len(filter_qs) > 0: filter_qs += "&" + "id=" + str(args.name) else: filter_qs += "?" + "id=" + str(args.name) else: if len(filter_qs) > 0: filter_qs += "&" + "name=" + str(args.name) else: filter_qs += "?" + "name=" + str(args.name) r,c = vim_read(url + filter_qs) if r==-401 and url_admin!=None and url != url_admin: #Unauthorized, try with admin privileges r,c = vim_read(url_admin + filter_qs) if r<0: print "Error:", c return -r if args.action=='createImage': payload={ args.action: {"name":args.imageName} } if args.description != None: payload[args.action]["description"]=args.description if args.path != None: payload[args.action]["path"]=args.path else: payload={ args.action: None} #print json.dumps(c, indent=4) item_list = c[ args.element ] if len(item_list)==0 and args.name != None: print " Not found " + args.element + " " + args.name return 404 #HTTP_Not_Found result = 0 for item in item_list: uuid=item.get('id', None) if uuid is None: uuid=item.get('uuid', None) if uuid is None: print "Id not found" continue name = item.get('name', '') if not args.force: r = raw_input(" Action over " + args.element + " " + uuid + " " + name + " (y/N)? ") if len(r)>0 and r[0].lower()=="y": pass else: continue r,c = vim_action(url + "/" + uuid + "/action", payload) if r==-401 and url_admin!=None and url != url_admin: #Unauthorized, try with admin privileges url=url_admin r,c = vim_action(url + "/" + uuid + "/action", payload) if r<0: print " " + uuid +" "+ name.ljust(20) + " " + c result = -r else: if args.action == "createImage": #response contain an {image: {...} }, not a {server: {...} }. print " " + c["image"]["id"] +" "+ c["image"]["name"].ljust(20) args.element="images" else: print " " + uuid +" "+ name.ljust(20) + " "+ args.action if "verbose" in args and args.verbose!=None: _print_verbose(c, args.element[:-1], args.verbose) return result def element_edit(args): filter_qs = "" tenant="" if args.element in ('flavors','images','servers'): check_configuration( "ADMIN_PORT") tenant="/"+vim_config["TENANT"] else: check_configuration("ADMIN_PORT", "TENANT") tenant="" url = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["PORT"], tenant, args.element) if "ADMIN_PORT" in vim_config and vim_config["ADMIN_PORT"]!=None: url_admin = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["ADMIN_PORT"], tenant, args.element) else: url_admin = None if args.filter: filter_qs += "?" + args.filter if args.name!=None: if check_valid_uuid(args.name): if len(filter_qs) > 0: filter_qs += "&" + "id=" + str(args.name) else: filter_qs += "?" + "id=" + str(args.name) else: if len(filter_qs) > 0: filter_qs += "&" + "name=" + str(args.name) else: filter_qs += "?" + "name=" + str(args.name) r,c = vim_read(url + filter_qs) if r==-401 and url_admin!=None and url != url_admin: #Unauthorized, try with admin privileges r,c = vim_read(url_admin + filter_qs) if r<0: print "Error:", c return -r payload=load_file_or_yaml(args.file) r2,c2= change_var(payload) if r2<0: print "Template error", c2 return -1 payload=c2 if args.element[:-1] not in payload: payload = {args.element[:-1]: payload } #print json.dumps(c, indent=4) item_list = c[ args.element ] if len(item_list)==0 and args.name != None: print " Not found " + args.element + " " + args.name return 404 #HTTP_Not_Found result = 0 for item in item_list: uuid=item.get('id', None) if uuid is None: uuid=item.get('uuid', None) if uuid is None: print "Id not found" continue name = item.get('name', '') if not args.force or (args.name==None and args.filer==None): r = raw_input(" Edit " + args.element + " " + uuid + " " + name + " (y/N)? ") if len(r)>0 and r[0].lower()=="y": pass else: continue r,c = vim_edit(url + "/" + uuid, payload) if r==-401 and url_admin!=None and url != url_admin: #Unauthorized, try with admin privileges url=url_admin r,c = vim_edit(url + "/" + uuid, payload) if r<0: print " " + uuid +" "+ name.ljust(20) + " " + c result = -r else: print " " + uuid +" "+ name.ljust(20) + " edited" if "verbose" in args and args.verbose!=None: _print_verbose(c, args.element[:-1], args.verbose) return result def element_action_edit(args): #print args if args.element=='ports': if args.action=='attach': args.file='network_id: ' + args.network_id else: #args.action=='detach' args.file='network_id: null' if args.action=='up': args.file='admin_state_up: true' if args.action=='down': args.file='admin_state_up: false' return element_edit(args) def element_delete(args): filter_qs = "" tenant="" if args.element in ('flavors','images','servers'): check_configuration("ADMIN_PORT" ) tenant="/"+vim_config["TENANT"] else: check_configuration("ADMIN_PORT", "TENANT") tenant="" url = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["PORT"], tenant, args.element) if "ADMIN_PORT" in vim_config and vim_config["ADMIN_PORT"]!=None: url_admin = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["ADMIN_PORT"], tenant, args.element) else: url_admin = None if args.filter: filter_qs += "?" + args.filter if args.name!=None: if check_valid_uuid(args.name): if len(filter_qs) > 0: filter_qs += "&" + "id=" + str(args.name) else: filter_qs += "?" + "id=" + str(args.name) else: if len(filter_qs) > 0: filter_qs += "&" + "name=" + str(args.name) else: filter_qs += "?" + "name=" + str(args.name) r,c = vim_read(url + filter_qs) if r==-401 and url_admin!=None and url != url_admin: #Unauthorized, try with admin privileges r,c = vim_read(url_admin + filter_qs) if r<0: print "Error:", c return -r #print json.dumps(c, indent=4) item_list = c[ args.element ] if len(item_list)==0 and args.name != None: print " Not found " + args.element + " " + args.name return 404 #HTTP_Not_Found result = 0 for item in item_list: uuid=item.get('id', None) if uuid is None: uuid=item.get('uuid', None) if uuid is None: print "Id not found" result = -500 continue name = item.get('name', '') if not args.force: r = raw_input(" Delete " + args.element + " " + uuid + " " + name + " (y/N)? ") if len(r)>0 and r[0].lower()=="y": pass else: continue r,c = vim_delete(url + "/" + uuid) if r==-401 and url_admin!=None and url != url_admin: #Unauthorized, try with admin privileges url=url_admin r,c = vim_delete(url + "/" + uuid) if r<0: print " " + uuid +" "+ name.ljust(20) + " " + c result = -r else: print " " + uuid +" "+ name.ljust(20) + " deleted" return result def element_list(args): #print "element_list", args filter_qs = "" tenant="" if args.element in ('flavors','images','servers'): check_configuration( "ADMIN_PORT" ) tenant="/"+vim_config["TENANT"] else: check_configuration( "ADMIN_PORT", "TENANT" ) tenant="" #if args.name: # what += "/" + args.name url = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["PORT"], tenant, args.element) if "ADMIN_PORT" in vim_config and vim_config["ADMIN_PORT"]!=None: url_admin = "http://%s:%s/openvim%s/%s" %(vim_config["HOST"], vim_config["ADMIN_PORT"], tenant, args.element) else: url_admin = None #print " get", what, " >>>>>>>> ", if args.filter: filter_qs += "?" + args.filter if args.name!=None: if check_valid_uuid(args.name): if len(filter_qs) > 0: filter_qs += "&" + "id=" + str(args.name) else: filter_qs += "?" + "id=" + str(args.name) else: if len(filter_qs) > 0: filter_qs += "&" + "name=" + str(args.name) else: filter_qs += "?" + "name=" + str(args.name) r,c = vim_read(url + filter_qs) if r==-401 and url_admin!=None and url != url_admin: #Unauthorized, try with admin privileges r,c = vim_read(url_admin + filter_qs) if r<0: print "Error:", c return -r #print json.dumps(c, indent=4) result = 0 item_list = c[ args.element ] verbose=0 #if args.name!=None and len(item_list)==1: # verbose+=1 if args.verbose!=None: verbose += args.verbose for item in item_list: extra="" if args.element=="servers" or args.element=="networks": extra = " "+item['status'] if args.element in ("hosts","networks","ports") and not item['admin_state_up']: extra += " admin_state_up=false" print item['id']+" "+item['name'].ljust(20) + extra if verbose>0: r2,c2 = vim_read(url + "/"+ item['id']) if r2==-401 and url_admin!=None and url != url_admin: #Unauthorized, try with admin privileges url=url_admin r2,c2 = vim_read(url + "/"+ item['id']) if r2>0: _print_verbose(c2, args.element[:-1], verbose) return result def openflow_action(args): if args.action=='port-list': url = "http://%s:%s/openvim/networks/openflow/ports" %(vim_config["HOST"], vim_config["PORT"]) r,c = vim_read(url) elif args.action == 'port-mapping': url = "http://%s:%s/openvim/openflow/mapping" % (vim_config["HOST"], vim_config["PORT"]) r, c = vim_read(url) elif args.action=='rules-list' or args.action=='reinstall': PORT = vim_config["PORT"] if args.action=='reinstall': if "ADMIN_PORT" not in vim_config: print "OPENVIM_ADMIN_PORT variable not defined" return 404 #HTTP_Not_Found PORT = vim_config["ADMIN_PORT"] if args.name!=None: url = "http://%s:%s/openvim/networks" %(vim_config["HOST"], PORT) if check_valid_uuid(args.name): url += "?id=" + str(args.name) else: url += "?name=" + str(args.name) r,c = vim_read(url) if r<0: print "Error:", c return -r if len (c["networks"]) == 0: print " Network not found" return 404 #HTTP_Not_Found if len (c["networks"]) > 1: print " More than one net with this name found. Use uuid instead of name to concretize" return 404 #HTTP_Not_Found network = c["networks"][0]["id"] else: network="all" url = "http://%s:%s/openvim/networks/%s/openflow" %(vim_config["HOST"], PORT, network) if args.action=='reinstall': r,c = vim_edit(url, None) else: r,c = vim_read(url) elif args.action=='clear-all': if "ADMIN_PORT" not in vim_config: print "OPENVIM_ADMIN_PORT variable not defined" return 401 # HTTP_Unauthorized url = "http://%s:%s/openvim/networks/clear/openflow" %(vim_config["HOST"], vim_config["ADMIN_PORT"]) r,c = vim_delete(url) else: return 400 #HTTP_Bad_Request if r<0: print "Error:", c return -r else: print yaml.safe_dump(c, indent=4, default_flow_style=False) return 0 if __name__=="__main__": item_dict={'vm':'servers','host':'hosts','tenant':'tenants','image':'images','flavor':'flavors','net':'networks','port':'ports'} vim_config={} vim_config["HOST"] = os.getenv('OPENVIM_HOST', 'localhost') vim_config["PORT"] = os.getenv('OPENVIM_PORT', '9080') vim_config["ADMIN_PORT"] = os.getenv('OPENVIM_ADMIN_PORT', '9085') vim_config["TENANT"] = os.getenv('OPENVIM_TENANT', None) main_parser = ThrowingArgumentParser(description='User program to interact with OPENVIM-SERVER (openvimd)') #main_parser = argparse.ArgumentParser(description='User program to interact with OPENVIM-SERVER (openvimd)') main_parser.add_argument('--version', action='version', version='%(prog)s ' + __version__ + ' '+version_date) subparsers = main_parser.add_subparsers(help='commands') config_parser = subparsers.add_parser('config', help="prints configuration values") config_parser.add_argument("file", nargs='?', help="configuration file to extract the configuration").completer = FilesCompleter config_parser.set_defaults(func=config) #HELP for item in item_dict: #LIST element_list_parser = subparsers.add_parser(item+'-list', help="lists information about "+item_dict[item]) element_list_parser.add_argument("name", nargs='?', help="name or ID of the " + item) element_list_parser.add_argument("-F","--filter", action="store", help="filter query string") element_list_parser.add_argument('--verbose', '-v', action='count', help="increment the verbosity") element_list_parser.set_defaults(func=element_list, element=item_dict[item]) #NEW if item=='host': element_new_parser = subparsers.add_parser(item+'-add', help="adds a new compute node") else: element_new_parser = subparsers.add_parser(item+'-create', help="creates a new "+item_dict[item][:-1]) element_new_parser.add_argument("file", nargs='?', help="json/yaml text or file with content").completer = FilesCompleter element_new_parser.add_argument("--name", action="store", help="Use this name") if item!="network": element_new_parser.add_argument("--description", action="store", help="Use this descrition") if item=="image": element_new_parser.add_argument("--path", action="store", help="Use this path") element_new_parser.add_argument('--verbose', '-v', action='count', help="increment the verbosity") element_new_parser.set_defaults(func=element_new, element=item_dict[item]) #DELETE if item=='host': element_del_parser = subparsers.add_parser(item+'-remove', help="removes a compute node") else: element_del_parser = subparsers.add_parser(item+'-delete', help="deletes one or several "+item_dict[item]) element_del_parser.add_argument("name", nargs='?', help="name or ID of the "+item+", if missing means all") element_del_parser.add_argument("-F","--filter", action="store", help="filter query string") element_del_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation") element_del_parser.set_defaults(func=element_delete, element=item_dict[item]) #EDIT element_edit_parser = subparsers.add_parser(item+'-edit', help="edits one or several "+item_dict[item]) element_edit_parser.add_argument("name", help="name or ID of the "+item+"") element_edit_parser.add_argument("file", help="json/yaml text or file with the changes").completer = FilesCompleter element_edit_parser.add_argument("-F","--filter", action="store", help="filter query string") element_edit_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation") element_edit_parser.add_argument('--verbose', '-v', action='count', help="increment the verbosity") element_edit_parser.set_defaults(func=element_edit, element=item_dict[item]) #ACTION if item=='vm': for item2 in ('shutdown', 'start', 'rebuild', 'reboot'): vm_action_parser = subparsers.add_parser("vm-"+item2, help="performs this action over the virtual machine") vm_action_parser.add_argument("name", nargs='?', help="name or ID of the server, if missing means all") vm_action_parser.add_argument("-F","--filter", action="store", help="filter query string") vm_action_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation") vm_action_parser.set_defaults(func=element_action, element="servers", action=item2 ) vm_action_image_parser = subparsers.add_parser("vm-createImage", help="creates a snapshot of the virtual machine disk into a new image") vm_action_image_parser.add_argument("name", help="name or ID of the server") vm_action_image_parser.add_argument("imageName", help="image name") vm_action_image_parser.add_argument("--description", action="store", help="Provide a new image description") vm_action_image_parser.add_argument("--path", action="store", help="Provide a new image complete path") vm_action_image_parser.add_argument("-F","--filter", action="store", help="filter query string") vm_action_image_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation") vm_action_image_parser.add_argument('--verbose', '-v', action='count', help="increment the verbosity") vm_action_image_parser.set_defaults(func=element_action, element="servers", action="createImage" ) #ACTION that are implemented with EDITION if item=='port': port_action_attach_parser = subparsers.add_parser("port-attach", help="connects a port to a network") port_action_attach_parser.add_argument("name", help="name or ID of the port") port_action_attach_parser.add_argument("network_id", help="ID of the network") port_action_attach_parser.add_argument("-F","--filter", action="store", help="filter query string") port_action_attach_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation") port_action_attach_parser.set_defaults(func=element_action_edit, element="ports", action="attach") port_action_detach_parser = subparsers.add_parser("port-detach", help="removes a port from a network") port_action_detach_parser.add_argument("name", help="name or ID of the port") port_action_detach_parser.add_argument("-F","--filter", action="store", help="filter query string") port_action_detach_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation") port_action_detach_parser.set_defaults(func=element_action_edit, element="ports", action="dettach") if item=='net' or item=='host': nethost_action_up_parser = subparsers.add_parser(item+"-up", help="puts admin_state_up of "+item_dict[item][:-1]+" to true") nethost_action_up_parser.add_argument("name", help="name or ID of the "+item_dict[item][:-1]) nethost_action_up_parser.add_argument("-F","--filter", action="store", help="filter query string") nethost_action_up_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation") nethost_action_up_parser.set_defaults(func=element_action_edit, element=item_dict[item], action="up") nethost_action_down_parser = subparsers.add_parser(item+"-down", help="puts admin_state_up of "+item_dict[item][:-1]+" to false") nethost_action_down_parser.add_argument("name", help="name or ID of the "+item_dict[item][:-1]) nethost_action_down_parser.add_argument("-F","--filter", action="store", help="filter query string") nethost_action_down_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation") nethost_action_down_parser.set_defaults(func=element_action_edit, element=item_dict[item], action="down") #openflow rules openflow_list_action = subparsers.add_parser("openflow-port-list", help="list openflow switch ports name") openflow_list_action.set_defaults(func=openflow_action, action="port-list") openflow_list_action = subparsers.add_parser("openflow-port-mapping-list", help="list computes port mapping") openflow_list_action.set_defaults(func=openflow_action, action="port-mapping") openflow_list_action = subparsers.add_parser("openflow-clear-all", help="removes all openflow rules") openflow_list_action.set_defaults(func=openflow_action, action="clear-all") openflow_list_action = subparsers.add_parser("openflow-net-reinstall", help="reinstall the openflow rules for a network") openflow_list_action.add_argument("name", nargs='?', help="network name, if missing all networks") openflow_list_action.set_defaults(func=openflow_action, action="reinstall") openflow_list_action = subparsers.add_parser("openflow-net-list", help="list installed openflow rules for a network") openflow_list_action.add_argument("name", nargs='?', help="network name, if missing all networks") openflow_list_action.set_defaults(func=openflow_action, action="rules-list") argcomplete.autocomplete(main_parser) try: args = main_parser.parse_args() result = args.func(args) if result == None: result = 0 #for some reason it fails if call exit inside try instance. Need to call exit at the end !? except (requests.exceptions.ConnectionError): print "Connection error: not possible to contact OPENVIM-SERVER (openvimd)" result = -2 except (KeyboardInterrupt): print 'Exiting openVIM' result = -3 except (SystemExit, ArgumentParserError): result = -4 #print result exit(result)