--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+
+##
+# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
+# This file is part of openmano
+# 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 tester for openvim.
+It is almost DEPRECATED by the openvim client
+
+The reason for keeping is because it is used for some scripts
+and it contain the -r option (delete recursive)
+that it is very useful for deleting content of database.
+Another difference from openvim is that it is more verbose
+and so more suitable for the developers
+'''
+
+__author__="Alfonso Tierno"
+__date__ ="$5-oct-2014 11:09:29$"
+
+import requests
+import json
+import yaml
+import sys
+import getopt
+from jsonschema import validate as js_v, exceptions as js_e
+
+version="0.0.2"
+global global_config
+
+
+def get_elements(url):
+ headers_req = {'content-type': 'application/json'}
+ try:
+ vim_response = requests.get(url, headers = headers_req)
+ #print vim_response
+ #print vim_response.status_code
+ 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:
+ text = " Error. VIM response '%s': not possible to GET %s" % (vim_response.status_code, url)
+ text += "\n " + vim_response.text
+ #print text
+ return -vim_response.status_code,text
+ except requests.exceptions.RequestException, e:
+ return -1, " Exception "+ str(e.message)
+
+def delete_elements(url):
+ headers_req = {'content-type': 'application/json'}
+
+ try:
+ vim_response = requests.delete(url, headers = headers_req)
+ #print vim_response
+ #print vim_response.status_code
+ if vim_response.status_code == 200:
+ pass
+ #print vim_response.json()
+ #print json.dumps(vim_response.json(), indent=4)
+ else:
+ #print vim_response.text
+ text = " Error. VIM response '%s': not possible to DELETE %s" % (vim_response.status_code, url)
+ text += "\n " + vim_response.text
+ #print text
+ return -vim_response.status_code,text
+ except requests.exceptions.RequestException, e:
+ return -1, " Exception "+ str(e.message)
+ return 1, None
+
+
+def new_elements(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)
+ #print vim_response
+ #print vim_response.status_code
+ if vim_response.status_code == 200:
+ #print vim_response.json()
+ #print json.dumps(vim_response.json(), indent=4)
+ return 1, vim_response.text
+ else:
+ #print vim_response.text
+ text = "Error. VIM response '%s': not possible to ADD %s" % (vim_response.status_code, url)
+ text += "\n" + vim_response.text
+ #print text
+ return -vim_response.status_code,text
+ except requests.exceptions.RequestException, e:
+ return -1, " Exception "+ str(e.message)
+
+
+def get_details(url, what, c):
+ item_list = []
+ return_dict = {what+'s': []}
+
+ item = c.get(what,None)
+ if item is None: item = c.get(what+'s',None)
+ if item is None:
+ error_text= " Internal error, not found '" + what +"[s]' in content"
+ print 'get_details()', error_text, c
+ return -1, error_text
+ if type(item) is list:
+ item_list = item
+ else:
+ item_list.append(item)
+ if len(item_list)==0:
+ print what, "not found"
+ return 1
+ for item in item_list:
+ uuid = item.get('id',None)
+ if uuid is None: uuid = item.get('uuid',None)
+ if uuid is None:
+ error_text= " Internal error, not found 'id/uuid' in item"
+ print 'get_details()', error_text, item
+ return -1, error_text
+ #print " get", what, uuid, " >>>>>>>> ",
+ r,c = get_elements(url + "/" + uuid)
+ if r<0:
+ # print "fail"
+ print " get", what, uuid, "fail", c
+ return -1, c
+ #else:
+ # print 'ok'
+ return_dict[what+'s'].append(c[what])
+ return 1, return_dict
+
+
+def action_details(url, what, c, force, payload):
+ item_list = []
+ return_dict = {what+'s': []}
+ headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
+ fail=0
+ ok=0
+
+ #Allows for payload both keypairs inside a 'server','port' ... or directly. In later case, put keypairs inside what
+
+ item = c.get(what,None)
+ if item is None: item = c.get(what+'s',None)
+ if item is None:
+ error_text= " Internal error, not found '" + what +"[s]' in content"
+ print 'get_details()', error_text, c
+ return -1, error_text
+ if type(item) is list:
+ item_list = item
+ else:
+ item_list.append(item)
+ if len(item_list)==0:
+ print what, "not found"
+ return 1
+ for item in item_list:
+ name = item.get('name',None)
+ uuid = item.get('id',None)
+ if uuid is None: uuid = item.get('uuid',None)
+ if uuid is None:
+ error_text= " Internal error, not found 'id/uuid' in item"
+ print 'get_details()', error_text, item
+ return -1, error_text
+ if not force:
+ r = raw_input("Action on " + what + " " + uuid + " " + name + " (y/N)? ")
+ if len(r)>0 and r[0].lower()=="y":
+ print " put", what, uuid, " >>>>>>>> ",
+ else:
+ continue
+
+ #print str(payload)
+ try:
+ vim_response = requests.post(url + "/" + uuid + "/action", data=json.dumps(payload), headers=headers_req)
+ if vim_response.status_code == 200:
+ print 'ok'
+ ok += 1
+ return_dict[what+'s'].append(vim_response.json())
+ return_dict[what+'s'][-1]['uuid'] = uuid
+ return_dict[what+'s'][-1]['name'] = name
+ else:
+ fail += 1
+ print "fail"
+ #print vim_response.text
+ #text = "Error. VIM response '%s': not possible to PUT %s" % (vim_response.status_code, url)
+ #text += "\n" + vim_response.text
+ #print text
+ error_dict = vim_response.json()
+ error_dict['error']['uuid']=uuid
+ error_dict['error']['name']=name
+ return_dict[what+'s'].append(error_dict)
+ except requests.exceptions.RequestException, e:
+ return -1, " Exception "+ str(e.message)
+ if ok>0 and fail>0: return 0, return_dict
+ elif fail==0 : return 1, return_dict
+ else: return -1, return_dict
+
+
+
+def edit_details(url, what, c, force, payload):
+ item_list = []
+ return_dict = {what+'s': []}
+ headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
+ fail=0
+ ok=0
+
+ #Allows for payload both keypairs inside a 'server','port' ... or directly. In later case, put keypairs inside what
+ if what not in payload:
+ payload = {what:payload}
+
+ item = c.get(what,None)
+ if item is None: item = c.get(what+'s',None)
+ if item is None:
+ error_text= " Internal error, not found '" + what +"[s]' in content"
+ print 'get_details()', error_text, c
+ return -1, error_text
+ if type(item) is list:
+ item_list = item
+ else:
+ item_list.append(item)
+ if len(item_list)==0:
+ print what, "not found"
+ return 1
+ for item in item_list:
+ name = item.get('name',None)
+ uuid = item.get('id',None)
+ if uuid is None: uuid = item.get('uuid',None)
+ if uuid is None:
+ error_text= " Internal error, not found 'id/uuid' in item"
+ print 'get_details()', error_text, item
+ return -1, error_text
+ if not force:
+ r = raw_input("Edit " + what + " " + uuid + " " + name + " (y/N)? ")
+ if len(r)>0 and r[0].lower()=="y":
+ print " put", what, uuid, " >>>>>>>> ",
+ else:
+ continue
+
+ #print str(payload)
+ try:
+ vim_response = requests.put(url + "/" + uuid, data=json.dumps(payload), headers=headers_req)
+ if vim_response.status_code == 200:
+ print 'ok'
+ ok += 1
+ return_dict[what+'s'].append( vim_response.json()[what] )
+ else:
+ fail += 1
+ print "fail"
+ #print vim_response.text
+ #text = "Error. VIM response '%s': not possible to PUT %s" % (vim_response.status_code, url)
+ #text += "\n" + vim_response.text
+ #print text
+ error_dict = vim_response.json()
+ error_dict['error']['uuid']=uuid
+ error_dict['error']['name']=name
+ return_dict[what+'s'].append(error_dict)
+ except requests.exceptions.RequestException, e:
+ return -1, " Exception "+ str(e.message)
+ if ok>0 and fail>0: return 0, return_dict
+ elif fail==0 : return 1, return_dict
+ else: return -1, return_dict
+
+def get_del_recursive(url, what, url_suffix, force=False, recursive=False):
+ #print
+ #print " get", what, a, " >>>>>>>> ",
+ r,c = get_elements(url + what + 's' + url_suffix)
+ if r<0:
+ print c, "when getting", what, url_suffix
+ return -1
+ # print "ok"
+
+ list_todelete = c.get(what, None)
+ if list_todelete is None: list_todelete = c.get(what+'s', None)
+ if list_todelete is None:
+ print " Internal error, not found '" + what +"[s]' in", c
+ return -3, " Internal error, not found a valid dictionary"
+ if type(list_todelete) == dict:
+ list_todelete = (list_todelete, )
+
+ if len(list_todelete)==0:
+ print what, url_suffix, "not found"
+ return 1
+ for c in list_todelete:
+ uuid=c.get('id', None)
+ if uuid is None:
+ uuid=c.get('uuid', None)
+ if uuid is None:
+ print "Id not found"
+ continue
+ name = c.get("name","")
+ if recursive:
+ if what=='tenant' :
+ get_del_recursive(url + uuid + "/", 'server', "", force, recursive)
+ get_del_recursive(url + uuid + "/", 'flavor', "", force, recursive)
+ get_del_recursive(url + uuid + "/", 'image', "", force, recursive)
+ get_del_recursive(url, 'network', "?tenant_id="+uuid, force, recursive)
+ elif what=='flavors' :
+ #get_del_recursive(url, 'servers', "?flavorRef="+uuid, force, recursive)
+ pass
+ elif what=='image' :
+ get_del_recursive(url, 'server', "?imageRef="+uuid, force, recursive)
+ elif what=='hosts' :
+ get_del_recursive(url, 'server', "?hostId="+uuid, force, recursive)
+
+ if not force:
+ r = raw_input("Delete " + what + " " + uuid + " " + name + " (y/N)? ")
+ if len(r)>0 and r[0].lower()=="y":
+ pass
+ else:
+ continue
+ r,c = delete_elements(url + what + "s/" + uuid)
+ if r<0:
+ #print "Error deleting", vimURI, -r
+ print c
+ else:
+ print what, uuid, name, "deleted"
+ return 1
+
+def check_valid_uuid(uuid):
+ 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 change_string(text, var_list):
+ 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)
+ return text
+
+def chage_var_recursively(data, var_list):
+ '''Check recursively the conent of data, and look for "*${*}*" variables and changes
+ It assumes that this variables are not in the key of dictionary,
+ Attributes:
+ 'data': dictionary, or list. None or empty is consideted 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):
+ 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
+ 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 parse_yaml_json(text):
+ try:
+ data = yaml.load(text)
+ return 0, data
+ 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)
+ return -1, " Error yaml/json format error at " + error_pos
+
+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]
+
+ try:
+ data = yaml.load(read_data)
+ return change_var(data)
+ 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)
+ return -2, " Error yaml/json format error at '"+ file_ +"'"+error_pos
+
+def load_configuration(configuration_file):
+ default_tokens ={'http_port':8080, 'http_host':'localhost', 'test_mode':False, 'of_controller_nets_with_same_vlan':True}
+
+ r, config = load_file(configuration_file, parse=True)
+ if r < 0:
+ return False, config
+
+ #Check default values tokens
+ for k,v in default_tokens.items():
+ if k not in config: config[k]=v
+
+ return (True, config)
+
+items_list = ('server','host','tenant','image','flavor','network','port')
+action_list = ('list','get','new','del','edit','action')
+
+
+def usage(complete=False):
+ global items_list
+ global action_list
+ print "Usage: ", sys.argv[0], "[options]", " [" + ",".join(action_list) +"] ", "<item> [<other>] "
+ print " Perform an test action over openvim"
+ print " "+",".join(action_list)+": List (by default), GET detais, Creates, Deletes, Edit"
+ print " <item>: can be one of " + ",".join(items_list)
+ print " <other>: list of uuid|name for 'get|del'; list of json/yaml files for 'new' or 'edit'"
+ if not complete:
+ print " Type -h or --help for a complete list of options"
+ return
+ print " Options:"
+ print " -v|--version: prints current version"
+ print " -c|--config [configuration_file]: loads the configuration file (default: openvimd.cfg)"
+ print " -h|--help: shows this help"
+ print " -u|--url [URL]: url to use instead of the one loaded from configuration file"
+ print " -t|--tenant [tenant uuid]: tenant to be used for some comands. IF mising it will use the default obtained in configuration file"
+ print " -F|--filter [A=B[&C=D...]: URL query string used for 'get' or 'del' commands"
+ print " -f|--force : Do not ask for confirmation when deleting. Also remove dependent objects."
+ print " -r|--recursive : Delete also dependency elements, (from tenants: images, flavors,server; from hosts: instances; ..."
+ print " Examples:"
+ print " ",sys.argv[0]," tenant #list tenants "
+ print " ",sys.argv[0]," -F'device_owner=external' get port #get details of all external ports"
+ print " ",sys.argv[0]," del server ses pan #delete server names 'ses' and 'pan'. Do not ask for confirmation"
+ print " ",sys.argv[0]," -r -f del host #delete all host and all the dependencies "
+ print " ",sys.argv[0]," new host ./Host/nfv100.json #add a host which information is in this file"
+ print " ",sys.argv[0]," edit network f348faf8-59ef-11e4-b4c7-52540030594e '{\"network\":{\"admin_state_up\":false}}'"
+ print " #change the admin status of this network"
+ return
+
+
+if __name__=="__main__":
+ global vimURI
+ global vimURI_admin
+
+ global what
+ global query_string
+#init variables
+ action="list"
+ what=None
+ url=None
+ query_string = ""
+ force = False
+ recursive = False
+ tenant = None
+ additional = []
+ #look for parent dir
+ config_file = '../openvimd.cfg'
+ pos = sys.argv[0].rfind("/")
+ if pos<0:
+ base_dir="./"
+ else:
+ base_dir = sys.argv[0] [:pos+1]
+ if pos>=0:
+ config_file = base_dir + config_file
+
+#get params
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "hvrfc:u:t:F:",
+ ["config", "help", "version", "force", "filter","tenant","url","recursive"])
+ except getopt.GetoptError, err:
+ print " Error:", err # will print something like "option -a not recognized"
+ usage()
+ sys.exit(-2)
+
+ for o, a in opts:
+ if o in ("-v", "--version"):
+ print "test_openvim version", version, "Oct 2014"
+ print "(c) Copyright Telefonica"
+ sys.exit(0)
+ elif o in ("-h", "--help"):
+ usage(True)
+ sys.exit(0)
+ elif o in ("-c", "--config"): config_file = a
+ elif o in ("-f", "--force"): force = True
+ elif o in ("-r", "--recursive"): recursive = True
+ elif o in ("-F", "--filter"): query_string = "?"+a
+ elif o in ("-u", "--url"): url = a
+ elif o in ("-t", "--tenant"): tenant = a
+ else:
+ assert False, "Unhandled option"
+
+ for a in args:
+ if len(a) == 0:
+ print " Warning!!! Found an empty parameter?"
+ elif a[0]=="-":
+ print " Error!!! Put options parameter at the beginning"
+ sys.exit(-2)
+ elif what is not None:
+ additional.append(a)
+ elif a in items_list:
+ what=a
+ elif a[:-1] in items_list and a[-1]=='s':
+ what=a[:-1]
+ elif a in action_list:
+ action=a
+ else:
+ print " Missing <item>", ",".join(items_list)
+ sys.exit(-2)
+ if what is None:
+ usage()
+ sys.exit(-1)
+ #Load configuration file
+ r, config_dic = load_configuration(config_file)
+ #print config_dic
+ if not r:
+ print config_dic
+ config_dic={}
+ #exit(-1)
+
+ #override parameters obtained by command line
+ try:
+ if url is not None:
+ vimURI = vimURI_admin = url
+ else:
+ vimURI = "http://" + config_dic['http_host'] +":"+ str(config_dic['http_port']) + "/openvim/"
+ if 'http_admin_port' in config_dic:
+ vimURI_admin = "http://" + config_dic['http_host'] +":"+ str(config_dic['http_admin_port']) + "/openvim/"
+ except: #key error
+ print " Error: can not get URL; neither option --u,-url, nor reading configuration file"
+ exit(-1)
+ if tenant is None:
+ tenant = config_dic.get('tenant_id', None)
+
+#check enough parameters
+ URI=vimURI
+ if (what in ('host','port') and action in ('del','new')) or (what=='host' and action=='edit' ):
+ if vimURI_admin is None:
+ print " Error: Can not get admin URL; neither option -t,--tenant, nor reading configuration file"
+ exit(-1)
+ else:
+ URI=vimURI_admin
+ if URI[-1] != "/": URI+="/"
+ if what in ('server','image','flavor'):
+ if tenant is None:
+ print " Error: Can not get tenant; neither option -t,--tenant, nor reading configuration file"
+ exit(-1)
+ URI += tenant + "/"
+
+
+ exit_code=0
+ try:
+#load file for new/edit
+ payload_list=[]
+ if action=='new' or action=='edit' or action=='action':
+ if len(additional)==0:
+ if action=='new' :
+ additional.append(base_dir+what+"s/new_"+what+".yaml")
+ #print " New what? Missing additional parameters to complete action"
+ else:
+ print " What must be edited? Missing additional parameters to complete action"
+ exit(-1)
+ if action=='edit'or action=='action':
+ #obtain only last element
+ additional_temp = additional[:-1]
+ additional = additional[-1:]
+
+ for a in additional:
+ r,payload = load_file(a, parse=True)
+ if r<0:
+ if r==-1 and "{" in a or ":" in a:
+ #try to parse directly
+ r,payload = parse_yaml_json(a)
+ if r<0:
+ print payload
+ exit (-1)
+ else:
+ print payload
+ exit (-1)
+ payload_list.append(payload)
+ if action=='edit'or action=='action':
+ additional = additional_temp
+
+
+#perform actions NEW
+ if action=='new':
+ for payload in payload_list:
+ print "\n new", what, a, " >>>>>>>> ",
+ r,c = new_elements(URI+what+'s', payload)
+ if r>0:
+ print "ok"
+ else:
+ print "fail"
+ exit_code = -1
+ print c
+ #try to decode
+ exit(exit_code)
+
+ #perform actions GET LIST EDIT DEL
+ if len(additional)==0:
+ additional=[""]
+ for a in additional:
+ filter_qs = query_string
+ if a != "" :
+ if check_valid_uuid(a):
+ if len(filter_qs) > 0: filter_qs += "&" + "id=" + str(a)
+ else: filter_qs += "?" + "id=" + str(a)
+ else:
+ if len(filter_qs) > 0: filter_qs += "&" + "name=" + str(a)
+ else: filter_qs += "?" + "name=" + str(a)
+
+ if action=='list' or action=='get' or action=='edit'or action=='action':
+ url = URI + what+'s'
+ print url + filter_qs
+ #print " get", what, a, " >>>>>>>> ",
+ r,c = get_elements(url + filter_qs)
+ if r<0:
+ #print "fail"
+ exit_code = -1
+ print c
+ else:
+ #print "ok"
+ if action=='list':
+ print json.dumps(c, indent=4)
+ continue
+
+ if action=='get':
+ r1,c1 = get_details(url, what, c)
+ elif action=='action':
+ r1,c1 = action_details(url, what, c, force, payload_list[0])
+ else: # action=='edit':
+ r1,c1 = edit_details(url, what, c, force, payload_list[0])
+ if r1<0:
+ exit_code = -1
+ else:
+ if r>0: print "ok"
+ else: print "ok with some fails"
+ print json.dumps(c1, indent=4)
+
+ elif action=='del':
+ r = get_del_recursive(URI, what, filter_qs, force, recursive)
+ if r<0:
+ exit_code = -1
+ exit(exit_code)
+
+ except KeyboardInterrupt:
+ print " Canceled"
+
+