blob: 524e3485188b5e0868f50b921a93eec4599cf82c [file] [log] [blame]
#!/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)