--- /dev/null
+#include MANIFEST.in
+#include requirements.txt
+include README.rst
+include openmano
+include openmanod.py
+include openmanod.cfg
+include osm-ro.service
+recursive-include osm_ro *
+
all: pypackage debpackage
prepare:
- mkdir -p build
- cp *.py build/
- #cd build; mv openmanod.py openmanod
- cp openmano build/
- cp openmanod.cfg build/
+ mkdir -p build/
+ cp MANIFEST.in build/
cp requirements.txt build/
cp README.rst build/
- cp openmano.service build/
- cp -r vnfs build/
- cp -r scenarios build/
- cp -r instance-scenarios build/
- cp -r scripts build/
- cd build/scripts; mv service-openmano.sh service-openmano; mv openmano-report.sh openmano-report
- cp -r database_utils build/
+ cp setup.py build/
+ cp -r osm_ro build/
+ cp openmano build/
+ cp openmanod.py build/
+ cp openmanod.cfg build/
+ cp osm-ro.service build/
+ cp -r vnfs build/osm_ro
+ cp -r scenarios build/osm_ro
+ cp -r instance-scenarios build/osm_ro
+ cp -r scripts build/osm_ro
+ cp -r database_utils build/osm_ro
connectors: prepare
# python-novaclient is required for that
- rm -f build/openmanolinkervimconn.py
- cd build; for i in `ls vimconn_*.py |sed "s/\.py//"` ; do echo "import $$i" >> openmanolinkervimconn.py; done
- python build/openmanolinkervimconn.py
- rm -f build/openmanolinkervimconn.py
+ rm -f build/osm_ro/openmanolinkervimconn.py
+ cd build/osm_ro; for i in `ls vimconn_*.py |sed "s/\.py//"` ; do echo "import $$i" >> openmanolinkervimconn.py; done
+ python build/osm_ro/openmanolinkervimconn.py
+ rm -f build/osm_ro/openmanolinkervimconn.py
-build: prepare
- python -m py_compile build/*.py
+build: connectors prepare
+ python -m py_compile build/osm_ro/*.py
-pypackage: build
+pypackage: prepare
cd build; ./setup.py sdist
- #cp build/dist/* /root/artifacts/...
+ cd build; ./setup.py bdist_wheel
-debpackage: build
- echo "Nothing to be done yet"
+debpackage: prepare
+ echo "Nothing to be done"
+ #cd build; ./setup.py --command-packages=stdeb.command bdist_deb
#fpm -s python -t deb build/setup.py
snappackage:
echo "Nothing to be done yet"
+sync:
+ #cp build/dist/* /root/artifacts/...
+
test:
./test/basictest.sh --force --insert-bashrc --install-openvim --init-openvim
+++ /dev/null
-# -*- 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
-##
-
-'''
-Implement like a proxy for TCP/IP in a separated thread.
-It creates two sockets to bypass the TCP/IP packets among the fix console
-server specified at class construction (console_host, console_port)
-and a client that connect against the (host, port) specified also at construction
-
- --------------------- -------------------------------
- | OPENMANO | | VIM |
-client 1 ----> | ConsoleProxyThread | ------> | Console server |
-client 2 ----> | (host, port) | ------> |(console_host, console_server)|
- ... -------------------- ------------------------------
-'''
-__author__="Alfonso Tierno"
-__date__ ="$19-nov-2015 09:07:15$"
-
-import socket
-import select
-import threading
-import logging
-
-
-class ConsoleProxyException(Exception):
- '''raise when an exception has found'''
-class ConsoleProxyExceptionPortUsed(ConsoleProxyException):
- '''raise when the port is used'''
-
-class ConsoleProxyThread(threading.Thread):
- buffer_size = 4096
- check_finish = 1 #frequency to check if requested to end in seconds
-
- def __init__(self, host, port, console_host, console_port, log_level=None):
- try:
- threading.Thread.__init__(self)
- self.console_host = console_host
- self.console_port = console_port
- self.host = host
- self.port = port
- self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- self.server.bind((host, port))
- self.server.listen(200)
- #TODO timeout in a lock section can be used to autoterminate the thread
- #when inactivity and timeout<time : set timeout=0 and terminate
- #from outside, close class when timeout==0; set timeout=time+120 when adding a new console on this thread
- #set self.timeout = time.time() + 120 at init
- self.name = "ConsoleProxy " + console_host + ":" + str(console_port)
- self.input_list = [self.server]
- self.channel = {}
- self.terminate = False #put at True from outside to force termination
- self.logger = logging.getLogger('openmano.console')
- if log_level:
- self.logger.setLevel( getattr(logging, log_level) )
-
- except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e:
- if e is socket.error and e.errno==98:
- raise ConsoleProxyExceptionPortUsed("socket.error " + str(e))
- raise ConsoleProxyException(type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) )
-
- def run(self):
- while 1:
- try:
- inputready, _, _ = select.select(self.input_list, [], [], self.check_finish)
- except select.error as e:
- self.logger.error("Exception on select %s: %s", type(e).__name__, str(e) )
- self.on_terminate()
-
- if self.terminate:
- self.on_terminate()
- self.logger.debug("Terminate because commanded")
- break
-
- for sock in inputready:
- if sock == self.server:
- self.on_accept()
- else:
- self.on_recv(sock)
-
- def on_terminate(self):
- while self.input_list:
- if self.input_list[0] is self.server:
- self.server.close()
- del self.input_list[0]
- else:
- self.on_close(self.input_list[0], "Terminating thread")
-
- def on_accept(self):
- #accept
- try:
- clientsock, clientaddr = self.server.accept()
- except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e:
- self.logger.error("Exception on_accept %s: %s", type(e).__name__, str(e) )
- return False
- #print self.name, ": Accept new client ", clientaddr
-
- #connect
- try:
- forward = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- forward.connect((self.console_host, self.console_port))
- name = "%s:%d => (%s:%d => %s:%d) => %s:%d" %\
- (clientsock.getpeername() + clientsock.getsockname() + forward.getsockname() + forward.getpeername() )
- self.logger.warn("new connection " + name)
-
- self.input_list.append(clientsock)
- self.input_list.append(forward)
- info = { "name": name,
- "clientsock" : clientsock,
- "serversock" : forward
- }
- self.channel[clientsock] = info
- self.channel[forward] = info
- return True
- except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e:
- self.logger.error("Exception on_connect to server %s:%d; %s: %s Close client side %s",
- self.console_host, self.console_port, type(e).__name__, str(e), str(clientaddr) )
- clientsock.close()
- return False
-
- def on_close(self, sock, cause):
- if sock not in self.channel:
- return #can happen if there is data ready to received at both sides and the channel has been deleted. QUITE IMPROBABLE but just in case
- info = self.channel[sock]
- #debug info
- sockname = "client" if sock is info["clientsock"] else "server"
- self.logger.warn("del connection %s %s at %s side", info["name"], str(cause), str(sockname) )
- #close sockets
- try:
- # close the connection with client
- info["clientsock"].close() # equivalent to do self.s.close()
- except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e:
- self.logger.error("Exception on_close client socket %s: %s", type(e).__name__, str(e) )
- try:
- # close the connection with remote server
- info["serversock"].close()
- except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e:
- self.logger.error("Exception on_close server socket %s: %s", type(e).__name__, str(e) )
-
- #remove objects from input_list
- self.input_list.remove(info["clientsock"])
- self.input_list.remove(info["serversock"])
- # delete both objects from channel dict
- del self.channel[info["clientsock"]]
- del self.channel[info["serversock"]]
-
- def on_recv(self, sock):
- if sock not in self.channel:
- return #can happen if there is data ready to received at both sides and the channel has been deleted. QUITE IMPROBABLE but just in case
- info = self.channel[sock]
- peersock = info["serversock"] if sock is info["clientsock"] else info["clientsock"]
- try:
- data = sock.recv(self.buffer_size)
- if len(data) == 0:
- self.on_close(sock, "peer closed")
- else:
- #print self.data
- sock = peersock
- peersock.send(data)
- except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e:
- #print self.name, ": Exception %s: %s" % (type(e).__name__, str(e) )
- self.on_close(sock, "Exception %s: %s" % (type(e).__name__, str(e) ))
-
-
-
- #def start_timeout(self):
- # self.timeout = time.time() + 120
-
+++ /dev/null
-# -*- 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
-##
-
-'''
-Base class for openmano database manipulation
-'''
-__author__="Alfonso Tierno"
-__date__ ="$4-Apr-2016 10:05:01$"
-
-import MySQLdb as mdb
-import uuid as myUuid
-import utils as af
-import json
-#import yaml
-import time
-import logging
-import datetime
-from jsonschema import validate as js_v, exceptions as js_e
-
-HTTP_Bad_Request = 400
-HTTP_Unauthorized = 401
-HTTP_Not_Found = 404
-HTTP_Method_Not_Allowed = 405
-HTTP_Request_Timeout = 408
-HTTP_Conflict = 409
-HTTP_Service_Unavailable = 503
-HTTP_Internal_Server_Error = 500
-
-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}$"}
- id_schema2 = {"type" : "string", "pattern": "^[a-fA-F0-9]{32}$"}
- try:
- js_v(uuid, id_schema)
- return True
- except js_e.ValidationError:
- try:
- js_v(uuid, id_schema2)
- return True
- except js_e.ValidationError:
- return False
- return False
-
-def _convert_datetime2str(var):
- '''Converts a datetime variable to a string with the format '%Y-%m-%dT%H:%i:%s'
- It enters recursively in the dict var finding this kind of variables
- '''
- if type(var) is dict:
- for k,v in var.items():
- if type(v) is datetime.datetime:
- var[k]= v.strftime('%Y-%m-%dT%H:%M:%S')
- elif type(v) is dict or type(v) is list or type(v) is tuple:
- _convert_datetime2str(v)
- if len(var) == 0: return True
- elif type(var) is list or type(var) is tuple:
- for v in var:
- _convert_datetime2str(v)
-
-def _convert_bandwidth(data, reverse=False, logger=None):
- '''Check the field bandwidth recursivelly and when found, it removes units and convert to number
- It assumes that bandwidth is well formed
- Attributes:
- 'data': dictionary bottle.FormsDict variable to be checked. None or empty is consideted valid
- 'reverse': by default convert form str to int (Mbps), if True it convert from number to units
- Return:
- None
- '''
- 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:
- _convert_bandwidth(data[k], reverse, logger)
- if "bandwidth" in data:
- try:
- value=str(data["bandwidth"])
- if not reverse:
- pos = value.find("bps")
- if pos>0:
- if value[pos-1]=="G": data["bandwidth"] = int(data["bandwidth"][:pos-1]) * 1000
- elif value[pos-1]=="k": data["bandwidth"]= int(data["bandwidth"][:pos-1]) / 1000
- else: data["bandwidth"]= int(data["bandwidth"][:pos-1])
- else:
- value = int(data["bandwidth"])
- if value % 1000 == 0: data["bandwidth"]=str(value/1000) + " Gbps"
- else: data["bandwidth"]=str(value) + " Mbps"
- except:
- if logger:
- logger.error("convert_bandwidth exception for type '%s' data '%s'", type(data["bandwidth"]), data["bandwidth"])
- return
- if type(data) is tuple or type(data) is list:
- for k in data:
- if type(k) is dict or type(k) is tuple or type(k) is list:
- _convert_bandwidth(k, reverse, logger)
-
-def _convert_str2boolean(data, items):
- '''Check recursively the content of data, and if there is an key contained in items, convert value from string to boolean
- Done recursively
- Attributes:
- 'data': dictionary variable to be checked. None or empty is considered valid
- 'items': tuple of keys to convert
- Return:
- None
- '''
- 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:
- _convert_str2boolean(data[k], items)
- if k in items:
- if type(data[k]) is str:
- if data[k]=="false" or data[k]=="False" or data[k]=="0": data[k]=False
- elif data[k]=="true" or data[k]=="True" or data[k]=="1": data[k]=True
- elif type(data[k]) is int:
- if data[k]==0: data[k]=False
- elif data[k]==1: data[k]=True
- if type(data) is tuple or type(data) is list:
- for k in data:
- if type(k) is dict or type(k) is tuple or type(k) is list:
- _convert_str2boolean(k, items)
-
-class db_base_Exception(Exception):
- '''Common Exception for all database exceptions'''
-
- def __init__(self, message, http_code=HTTP_Bad_Request):
- Exception.__init__(self, message)
- self.http_code = http_code
-
-class db_base():
- tables_with_created_field=()
-
- def __init__(self, host=None, user=None, passwd=None, database=None, log_name='db', log_level=None):
- self.host = host
- self.user = user
- self.passwd = passwd
- self.database = database
- self.con = None
- self.log_level=log_level
- self.logger = logging.getLogger(log_name)
- if self.log_level:
- self.logger.setLevel( getattr(logging, log_level) )
-
- def connect(self, host=None, user=None, passwd=None, database=None):
- '''Connect to specific data base.
- The first time a valid host, user, passwd and database must be provided,
- Following calls can skip this parameters
- '''
- try:
- if host: self.host = host
- if user: self.user = user
- if passwd: self.passwd = passwd
- if database: self.database = database
-
- self.con = mdb.connect(self.host, self.user, self.passwd, self.database)
- self.logger.debug("DB: connected to '%s' at '%s@%s'", self.database, self.user, self.host)
- except mdb.Error as e:
- raise db_base_Exception("Cannot connect to DataBase '{}' at '{}@{}' Error {}: {}".format(
- self.database, self.user, self.host, e.args[0], e.args[1]),
- http_code = HTTP_Unauthorized )
-
- def get_db_version(self):
- ''' Obtain the database schema version.
- Return: (negative, text) if error or version 0.0 where schema_version table is missing
- (version_int, version_text) if ok
- '''
- cmd = "SELECT version_int,version FROM schema_version"
- tries = 2
- while tries:
- try:
- with self.con:
- self.cur = self.con.cursor()
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- rows = self.cur.fetchall()
- highest_version_int=0
- highest_version=""
- for row in rows: #look for the latest version
- if row[0]>highest_version_int:
- highest_version_int, highest_version = row[0:2]
- return highest_version_int, highest_version
- except (mdb.Error, AttributeError) as e:
- #self.logger.error("get_db_version DB Exception %d: %s. Command %s",e.args[0], e.args[1], cmd)
- self._format_error(e, tries)
- tries -= 1
-
- def disconnect(self):
- '''disconnect from specific data base'''
- try:
- self.con.close()
- self.con = None
- except mdb.Error as e:
- self.logger.error("while disconnecting from DB: Error %d: %s",e.args[0], e.args[1])
- return
- except AttributeError as e: #self.con not defined
- if e[0][-5:] == "'con'":
- self.logger.warn("while disconnecting from DB: Error %d: %s",e.args[0], e.args[1])
- return
- else:
- raise
-
- def _format_error(self, e, tries=1, command=None, extra=None):
- '''Creates a text error base on the produced exception
- Params:
- e: mdb exception
- retry: in case of timeout, if reconnecting to database and retry, or raise and exception
- cmd: database command that produce the exception
- command: if the intention is update or delete
- extra: extra information to add to some commands
- Return
- HTTP error in negative, formatted error text
- '''
- if isinstance(e,AttributeError ):
- raise db_base_Exception("DB Exception " + str(e), HTTP_Internal_Server_Error)
- if e.args[0]==2006 or e.args[0]==2013 : #MySQL server has gone away (((or))) Exception 2013: Lost connection to MySQL server during query
- if tries>1:
- self.logger.warn("DB Exception '%s'. Retry", str(e))
- #reconnect
- self.connect()
- return
- else:
- raise db_base_Exception("Database connection timeout Try Again", HTTP_Request_Timeout)
-
- fk=e.args[1].find("foreign key constraint fails")
- if fk>=0:
- if command=="update":
- raise db_base_Exception("tenant_id '{}' not found.".format(extra), HTTP_Not_Found)
- elif command=="delete":
- raise db_base_Exception("Resource is not free. There are {} that prevent deleting it.".format(extra), HTTP_Conflict)
- de = e.args[1].find("Duplicate entry")
- fk = e.args[1].find("for key")
- uk = e.args[1].find("Unknown column")
- wc = e.args[1].find("in 'where clause'")
- fl = e.args[1].find("in 'field list'")
- #print de, fk, uk, wc,fl
- if de>=0:
- if fk>=0: #error 1062
- raise db_base_Exception("Value {} already in use for {}".format(e.args[1][de+15:fk], e.args[1][fk+7:]), HTTP_Conflict)
- if uk>=0:
- if wc>=0:
- raise db_base_Exception("Field {} can not be used for filtering".format(e.args[1][uk+14:wc]), HTTP_Bad_Request)
- if fl>=0:
- raise db_base_Exception("Field {} does not exist".format(e.args[1][uk+14:wc]), HTTP_Bad_Request)
- raise db_base_Exception("Database internal Error {}: {}".format(e.args[0], e.args[1]), HTTP_Internal_Server_Error)
-
- def __str2db_format(self, data):
- '''Convert string data to database format.
- If data is None it returns the 'Null' text,
- otherwise it returns the text surrounded by quotes ensuring internal quotes are escaped.
- '''
- if data==None:
- return 'Null'
- else:
- return json.dumps(str(data))
-
- def __tuple2db_format_set(self, data):
- '''Compose the needed text for a SQL SET, parameter 'data' is a pair tuple (A,B),
- and it returns the text 'A="B"', where A is a field of a table and B is the value
- If B is None it returns the 'A=Null' text, without surrounding Null by quotes
- If B is not None it returns the text "A='B'" or 'A="B"' where B is surrounded by quotes,
- and it ensures internal quotes of B are escaped.
- '''
- if data[1]==None:
- return str(data[0]) + "=Null"
- else:
- return str(data[0]) + '=' + json.dumps(str(data[1]))
-
- def __tuple2db_format_where(self, data):
- '''Compose the needed text for a SQL WHERE, parameter 'data' is a pair tuple (A,B),
- and it returns the text 'A="B"', where A is a field of a table and B is the value
- If B is None it returns the 'A is Null' text, without surrounding Null by quotes
- If B is not None it returns the text "A='B'" or 'A="B"' where B is surrounded by quotes,
- and it ensures internal quotes of B are escaped.
- '''
- if data[1]==None:
- return str(data[0]) + " is Null"
-
-# if type(data[1]) is tuple: #this can only happen in a WHERE_OR clause
-# text =[]
-# for d in data[1]:
-# if d==None:
-# text.append(str(data[0]) + " is Null")
-# continue
-# out=str(d)
-# if "'" not in out:
-# text.append( str(data[0]) + "='" + out + "'" )
-# elif '"' not in out:
-# text.append( str(data[0]) + '="' + out + '"' )
-# else:
-# text.append( str(data[0]) + '=' + json.dumps(out) )
-# return " OR ".join(text)
-
- out=str(data[1])
- return str(data[0]) + '=' + json.dumps(out)
-
- def __tuple2db_format_where_not(self, data):
- '''Compose the needed text for a SQL WHERE(not). parameter 'data' is a pair tuple (A,B),
- and it returns the text 'A<>"B"', where A is a field of a table and B is the value
- If B is None it returns the 'A is not Null' text, without surrounding Null by quotes
- If B is not None it returns the text "A<>'B'" or 'A<>"B"' where B is surrounded by quotes,
- and it ensures internal quotes of B are escaped.
- '''
- if data[1]==None:
- return str(data[0]) + " is not Null"
- out=str(data[1])
- return str(data[0]) + '<>' + json.dumps(out)
-
- def __remove_quotes(self, data):
- '''remove single quotes ' of any string content of data dictionary'''
- for k,v in data.items():
- if type(v) == str:
- if "'" in v:
- data[k] = data[k].replace("'","_")
-
- def _update_rows(self, table, UPDATE, WHERE, modified_time=0):
- ''' Update one or several rows into a table.
- Atributes
- UPDATE: dictionary with the key: value to change
- table: table where to update
- WHERE: dictionary of elements to update
- Return: the number of updated rows, exception if error
- '''
- #gettting uuid
- values = ",".join(map(self.__tuple2db_format_set, UPDATE.iteritems() ))
- if modified_time:
- values += ",modified_at={:f}".format(modified_time)
- cmd= "UPDATE " + table +" SET " + values +\
- " WHERE " + " and ".join(map(self.__tuple2db_format_where, WHERE.iteritems() ))
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- return self.cur.rowcount
-
- def _new_row_internal(self, table, INSERT, add_uuid=False, root_uuid=None, created_time=0):
- ''' Add one row into a table. It DOES NOT begin or end the transaction, so self.con.cursor must be created
- Attribute
- INSERT: dictionary with the key:value to insert
- table: table where to insert
- add_uuid: if True, it will create an uuid key entry at INSERT if not provided
- created_time: time to add to the created_time column
- It checks presence of uuid and add one automatically otherwise
- Return: uuid
- '''
-
- if add_uuid:
- #create uuid if not provided
- if 'uuid' not in INSERT:
- uuid = INSERT['uuid'] = str(myUuid.uuid1()) # create_uuid
- else:
- uuid = str(INSERT['uuid'])
- else:
- uuid=None
- if add_uuid:
- #defining root_uuid if not provided
- if root_uuid is None:
- root_uuid = uuid
- if created_time:
- created_at = created_time
- else:
- created_at=time.time()
- #inserting new uuid
- cmd = "INSERT INTO uuids (uuid, root_uuid, used_at, created_at) VALUES ('{:s}','{:s}','{:s}', {:f})".format(uuid, root_uuid, table, created_at)
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- #insertion
- cmd= "INSERT INTO " + table +" SET " + \
- ",".join(map(self.__tuple2db_format_set, INSERT.iteritems() ))
- if created_time:
- cmd += ",created_at=%f" % created_time
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- self.cur.rowcount
- return uuid
-
- def _get_rows(self,table,uuid):
- cmd = "SELECT * FROM {} WHERE uuid='{}'".format(str(table), str(uuid))
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- rows = self.cur.fetchall()
- return rows
-
- def new_row(self, table, INSERT, add_uuid=False, created_time=0):
- ''' Add one row into a table.
- Attribute
- INSERT: dictionary with the key: value to insert
- table: table where to insert
- tenant_id: only useful for logs. If provided, logs will use this tenant_id
- add_uuid: if True, it will create an uuid key entry at INSERT if not provided
- It checks presence of uuid and add one automatically otherwise
- Return: (result, uuid) where result can be 0 if error, or 1 if ok
- '''
- if table in self.tables_with_created_field and created_time==0:
- created_time=time.time()
- tries = 2
- while tries:
- try:
- with self.con:
- self.cur = self.con.cursor()
- return self._new_row_internal(table, INSERT, add_uuid, None, created_time)
-
- except (mdb.Error, AttributeError) as e:
- self._format_error(e, tries)
- tries -= 1
-
- def update_rows(self, table, UPDATE, WHERE, modified_time=0):
- ''' Update one or several rows into a table.
- Atributes
- UPDATE: dictionary with the key: value to change
- table: table where to update
- WHERE: dictionary of elements to update
- Return: (result, descriptive text) where result indicates the number of updated files
- '''
- if table in self.tables_with_created_field and modified_time==0:
- modified_time=time.time()
- tries = 2
- while tries:
- try:
- with self.con:
- self.cur = self.con.cursor()
- return self._update_rows(table, UPDATE, WHERE)
-
- except (mdb.Error, AttributeError) as e:
- self._format_error(e, tries)
- tries -= 1
-
- def delete_row_by_id(self, table, uuid):
- tries = 2
- while tries:
- try:
- with self.con:
- #delete host
- self.cur = self.con.cursor()
- cmd = "DELETE FROM {} WHERE uuid = '{}'".format(table, uuid)
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- deleted = self.cur.rowcount
- if deleted:
- #delete uuid
- self.cur = self.con.cursor()
- cmd = "DELETE FROM uuids WHERE root_uuid = '{}'".format(uuid)
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- return deleted
- except (mdb.Error, AttributeError) as e:
- self._format_error(e, tries, "delete", "dependencies")
- tries -= 1
-
- def delete_row(self, **sql_dict):
- ''' Deletes rows from a table.
- Attribute sql_dir: dictionary with the following key: value
- 'FROM': string of table name (Mandatory)
- 'WHERE': dict of key:values, translated to key=value AND ... (Optional)
- 'WHERE_NOT': dict of key:values, translated to key<>value AND ... (Optional)
- if value is None, it is translated to key is not null
- 'LIMIT': limit of number of rows (Optional)
- Return: the number of deleted or exception if error
- '''
- #print sql_dict
- from_ = "FROM " + str(sql_dict['FROM'])
- #print 'from_', from_
- if 'WHERE' in sql_dict and len(sql_dict['WHERE']) > 0:
- w=sql_dict['WHERE']
- where_ = "WHERE " + " AND ".join(map(self.__tuple2db_format_where, w.iteritems()))
- else: where_ = ""
- if 'WHERE_NOT' in sql_dict and len(sql_dict['WHERE_NOT']) > 0:
- w=sql_dict['WHERE_NOT']
- where_2 = " AND ".join(map(self.__tuple2db_format_where_not, w.iteritems()))
- if len(where_)==0: where_ = "WHERE " + where_2
- else: where_ = where_ + " AND " + where_2
- #print 'where_', where_
- limit_ = "LIMIT " + str(sql_dict['LIMIT']) if 'LIMIT' in sql_dict else ""
- #print 'limit_', limit_
- cmd = " ".join( ("DELETE", from_, where_, limit_) )
- tries = 2
- while tries:
- try:
- with self.con:
- self.cur = self.con.cursor()
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- deleted = self.cur.rowcount
- return deleted
- except (mdb.Error, AttributeError) as e:
- self._format_error(e, tries)
- tries -= 1
-
- def get_rows_by_id(self, table, uuid):
- '''get row from a table based on uuid'''
- tries = 2
- while tries:
- try:
- with self.con:
- self.cur = self.con.cursor(mdb.cursors.DictCursor)
- cmd="SELECT * FROM {} where uuid='{}'".format(str(table), str(uuid))
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- rows = self.cur.fetchall()
- return rows
- except (mdb.Error, AttributeError) as e:
- self._format_error(e, tries)
- tries -= 1
-
- def get_rows(self, **sql_dict):
- ''' Obtain rows from a table.
- Attribute sql_dir: dictionary with the following key: value
- 'SELECT': list or tuple of fields to retrieve) (by default all)
- 'FROM': string of table name (Mandatory)
- 'WHERE': dict of key:values, translated to key=value (key is null) AND ... (Optional)
- 'WHERE_NOT': dict of key:values, translated to key<>value (key is not null) AND ... (Optional)
- 'WHERE_OR': dict of key:values, translated to key=value OR ... (Optional)
- 'WHERE_AND_OR: str 'AND' or 'OR'(by default) mark the priority to 'WHERE AND (WHERE_OR)' or (WHERE) OR WHERE_OR' (Optional)
- 'LIMIT': limit of number of rows (Optional)
- 'ORDER_BY': list or tuple of fields to order
- Return: a list with dictionaries at each row
- '''
- #print sql_dict
- select_= "SELECT " + ("*" if 'SELECT' not in sql_dict else ",".join(map(str,sql_dict['SELECT'])) )
- #print 'select_', select_
- from_ = "FROM " + str(sql_dict['FROM'])
- #print 'from_', from_
- where_and = ""
- where_or = ""
- w=sql_dict.get('WHERE')
- if w:
- where_and = " AND ".join(map(self.__tuple2db_format_where, w.iteritems() ))
- w=sql_dict.get('WHERE_NOT')
- if w:
- if where_and: where_and += " AND "
- where_and += " AND ".join(map(self.__tuple2db_format_where_not, w.iteritems() ) )
- w=sql_dict.get('WHERE_OR')
- if w:
- where_or = " OR ".join(map(self.__tuple2db_format_where, w.iteritems() ))
- if where_and and where_or:
- if sql_dict.get("WHERE_AND_OR") == "AND":
- where_ = "WHERE " + where_and + " AND (" + where_or + ")"
- else:
- where_ = "WHERE (" + where_and + ") OR " + where_or
- elif where_and and not where_or:
- where_ = "WHERE " + where_and
- elif not where_and and where_or:
- where_ = "WHERE " + where_or
- else:
- where_ = ""
- #print 'where_', where_
- limit_ = "LIMIT " + str(sql_dict['LIMIT']) if 'LIMIT' in sql_dict else ""
- order_ = "ORDER BY " + ",".join(map(str,sql_dict['SELECT'])) if 'ORDER_BY' in sql_dict else ""
-
- #print 'limit_', limit_
- cmd = " ".join( (select_, from_, where_, limit_, order_) )
- tries = 2
- while tries:
- try:
- with self.con:
- self.cur = self.con.cursor(mdb.cursors.DictCursor)
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- rows = self.cur.fetchall()
- return rows
- except (mdb.Error, AttributeError) as e:
- self._format_error(e, tries)
- tries -= 1
-
- def get_table_by_uuid_name(self, table, uuid_name, error_item_text=None, allow_serveral=False, WHERE_OR={}, WHERE_AND_OR="OR"):
- ''' Obtain One row from a table based on name or uuid.
- Attribute:
- table: string of table name
- uuid_name: name or uuid. If not uuid format is found, it is considered a name
- allow_severeral: if False return ERROR if more than one row are founded
- error_item_text: in case of error it identifies the 'item' name for a proper output text
- 'WHERE_OR': dict of key:values, translated to key=value OR ... (Optional)
- 'WHERE_AND_OR: str 'AND' or 'OR'(by default) mark the priority to 'WHERE AND (WHERE_OR)' or (WHERE) OR WHERE_OR' (Optional
- Return: if allow_several==False, a dictionary with this row, or error if no item is found or more than one is found
- if allow_several==True, a list of dictionaries with the row or rows, error if no item is found
- '''
-
- if error_item_text==None:
- error_item_text = table
- what = 'uuid' if af.check_valid_uuid(uuid_name) else 'name'
- cmd = " SELECT * FROM {} WHERE {}='{}'".format(table, what, uuid_name)
- if WHERE_OR:
- where_or = " OR ".join(map(self.__tuple2db_format_where, WHERE_OR.iteritems() ))
- if WHERE_AND_OR == "AND":
- cmd += " AND (" + where_or + ")"
- else:
- cmd += " OR " + where_or
-
-
- tries = 2
- while tries:
- try:
- with self.con:
- self.cur = self.con.cursor(mdb.cursors.DictCursor)
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- number = self.cur.rowcount
- if number==0:
- return -HTTP_Not_Found, "No %s found with %s '%s'" %(error_item_text, what, uuid_name)
- elif number>1 and not allow_serveral:
- return -HTTP_Bad_Request, "More than one %s found with %s '%s'" %(error_item_text, what, uuid_name)
- if allow_serveral:
- rows = self.cur.fetchall()
- else:
- rows = self.cur.fetchone()
- return rows
- except (mdb.Error, AttributeError) as e:
- self._format_error(e, tries)
- tries -= 1
-
- def get_uuid(self, uuid):
- '''check in the database if this uuid is already present'''
- for retry_ in range(0,2):
- try:
- with self.con:
- self.cur = self.con.cursor(mdb.cursors.DictCursor)
- self.cur.execute("SELECT * FROM uuids where uuid='" + str(uuid) + "'")
- rows = self.cur.fetchall()
- return self.cur.rowcount, rows
- except (mdb.Error, AttributeError) as e:
- print "nfvo_db.get_uuid DB Exception %d: %s" % (e.args[0], e.args[1])
- r,c = self._format_error(e)
- if r!=-HTTP_Request_Timeout or retry_==1: return r,c
-
- def get_uuid_from_name(self, table, name):
- '''Searchs in table the name and returns the uuid
- '''
- tries = 2
- while tries:
- try:
- with self.con:
- self.cur = self.con.cursor(mdb.cursors.DictCursor)
- where_text = "name='" + name +"'"
- self.cur.execute("SELECT * FROM " + table + " WHERE "+ where_text)
- rows = self.cur.fetchall()
- if self.cur.rowcount==0:
- return 0, "Name %s not found in table %s" %(name, table)
- elif self.cur.rowcount>1:
- return self.cur.rowcount, "More than one VNF with name %s found in table %s" %(name, table)
- return self.cur.rowcount, rows[0]["uuid"]
- except (mdb.Error, AttributeError) as e:
- self._format_error(e, tries)
- tries -= 1
-
+++ /dev/null
-# -*- 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
-##
-
-'''
-HTTP server implementing the openmano API. It will answer to POST, PUT, GET methods in the appropriate URLs
-and will use the nfvo.py module to run the appropriate method.
-Every YAML/JSON file is checked against a schema in openmano_schemas.py module.
-'''
-__author__="Alfonso Tierno, Gerardo Garcia"
-__date__ ="$17-sep-2014 09:07:15$"
-
-import bottle
-import yaml
-import json
-import threading
-import time
-import logging
-
-from jsonschema import validate as js_v, exceptions as js_e
-from openmano_schemas import vnfd_schema_v01, vnfd_schema_v02, \
- nsd_schema_v01, nsd_schema_v02, nsd_schema_v03, scenario_edit_schema, \
- scenario_action_schema, instance_scenario_action_schema, instance_scenario_create_schema_v01, \
- tenant_schema, tenant_edit_schema,\
- datacenter_schema, datacenter_edit_schema, datacenter_action_schema, datacenter_associate_schema,\
- object_schema, netmap_new_schema, netmap_edit_schema
-import nfvo
-import utils
-from db_base import db_base_Exception
-from functools import wraps
-
-global mydb
-global url_base
-global logger
-url_base="/openmano"
-logger = None
-
-HTTP_Bad_Request = 400
-HTTP_Unauthorized = 401
-HTTP_Not_Found = 404
-HTTP_Forbidden = 403
-HTTP_Method_Not_Allowed = 405
-HTTP_Not_Acceptable = 406
-HTTP_Service_Unavailable = 503
-HTTP_Internal_Server_Error= 500
-
-def delete_nulls(var):
- if type(var) is dict:
- for k in var.keys():
- if var[k] is None: del var[k]
- elif type(var[k]) is dict or type(var[k]) is list or type(var[k]) is tuple:
- if delete_nulls(var[k]): del var[k]
- if len(var) == 0: return True
- elif type(var) is list or type(var) is tuple:
- for k in var:
- if type(k) is dict: delete_nulls(k)
- if len(var) == 0: return True
- return False
-
-def convert_datetime2str(var):
- '''Converts a datetime variable to a string with the format '%Y-%m-%dT%H:%i:%s'
- It enters recursively in the dict var finding this kind of variables
- '''
- if type(var) is dict:
- for k,v in var.items():
- if type(v) is float and k in ("created_at", "modified_at"):
- var[k] = time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime(v) )
- elif type(v) is dict or type(v) is list or type(v) is tuple:
- convert_datetime2str(v)
- if len(var) == 0: return True
- elif type(var) is list or type(var) is tuple:
- for v in var:
- convert_datetime2str(v)
-
-def log_to_logger(fn):
- '''
- Wrap a Bottle request so that a log line is emitted after it's handled.
- (This decorator can be extended to take the desired logger as a param.)
- '''
- @wraps(fn)
- def _log_to_logger(*args, **kwargs):
- actual_response = fn(*args, **kwargs)
- # modify this to log exactly what you need:
- logger.info('FROM %s %s %s %s' % (bottle.request.remote_addr,
- bottle.request.method,
- bottle.request.url,
- bottle.response.status))
- return actual_response
- return _log_to_logger
-
-class httpserver(threading.Thread):
- def __init__(self, db, admin=False, host='localhost', port=9090):
- #global url_base
- global mydb
- global logger
- #initialization
- if not logger:
- logger = logging.getLogger('openmano.http')
- threading.Thread.__init__(self)
- self.host = host
- self.port = port #Port where the listen service must be started
- if admin==True:
- self.name = "http_admin"
- else:
- self.name = "http"
- #self.url_preffix = 'http://' + host + ':' + str(port) + url_base
- mydb = db
- #self.first_usable_connection_index = 10
- #self.next_connection_index = self.first_usable_connection_index #The next connection index to be used
- #Ensure that when the main program exits the thread will also exit
- self.daemon = True
- self.setDaemon(True)
-
- def run(self):
- bottle.install(log_to_logger)
- bottle.run(host=self.host, port=self.port, debug=False, quiet=True)
-
-def run_bottle(db, host_='localhost', port_=9090):
- '''used for launching in main thread, so that it can be debugged'''
- global mydb
- mydb = db
- bottle.run(host=host_, port=port_, debug=True) #quiet=True
-
-
-@bottle.route(url_base + '/', method='GET')
-def http_get():
- #print
- return 'works' #TODO: to be completed
-
-#
-# Util functions
-#
-
-def change_keys_http2db(data, http_db, reverse=False):
- '''Change keys of dictionary data acording to the key_dict values
- This allow change from http interface names to database names.
- When reverse is True, the change is otherwise
- Attributes:
- data: can be a dictionary or a list
- http_db: is a dictionary with hhtp names as keys and database names as value
- reverse: by default change is done from http api to database. If True change is done otherwise
- Return: None, but data is modified'''
- if type(data) is tuple or type(data) is list:
- for d in data:
- change_keys_http2db(d, http_db, reverse)
- elif type(data) is dict or type(data) is bottle.FormsDict:
- if reverse:
- for k,v in http_db.items():
- if v in data: data[k]=data.pop(v)
- else:
- for k,v in http_db.items():
- if k in data: data[v]=data.pop(k)
-
-def format_out(data):
- '''return string of dictionary data according to requested json, yaml, xml. By default json'''
- logger.debug("OUT: " + yaml.safe_dump(data, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True) )
- if 'application/yaml' in bottle.request.headers.get('Accept'):
- bottle.response.content_type='application/yaml'
- return yaml.safe_dump(data, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True) #, canonical=True, default_style='"'
- else: #by default json
- bottle.response.content_type='application/json'
- #return data #json no style
- return json.dumps(data, indent=4) + "\n"
-
-def format_in(default_schema, version_fields=None, version_dict_schema=None):
- ''' Parse the content of HTTP request against a json_schema
- Parameters
- default_schema: The schema to be parsed by default if no version field is found in the client data
- version_fields: If provided it contains a tuple or list with the fields to iterate across the client data to obtain the version
- version_dict_schema: It contains a dictionary with the version as key, and json schema to apply as value
- It can contain a None as key, and this is apply if the client data version does not match any key
- Return:
- user_data, used_schema: if the data is successfully decoded and matches the schema
- launch a bottle abort if fails
- '''
- #print "HEADERS :" + str(bottle.request.headers.items())
- try:
- error_text = "Invalid header format "
- format_type = bottle.request.headers.get('Content-Type', 'application/json')
- if 'application/json' in format_type:
- error_text = "Invalid json format "
- #Use the json decoder instead of bottle decoder because it informs about the location of error formats with a ValueError exception
- client_data = json.load(bottle.request.body)
- #client_data = bottle.request.json()
- elif 'application/yaml' in format_type:
- error_text = "Invalid yaml format "
- client_data = yaml.load(bottle.request.body)
- elif 'application/xml' in format_type:
- bottle.abort(501, "Content-Type: application/xml not supported yet.")
- else:
- logger.warning('Content-Type ' + str(format_type) + ' not supported.')
- bottle.abort(HTTP_Not_Acceptable, 'Content-Type ' + str(format_type) + ' not supported.')
- return
- #if client_data == None:
- # bottle.abort(HTTP_Bad_Request, "Content error, empty")
- # return
-
- logger.debug('IN: %s', yaml.safe_dump(client_data, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True) )
- #look for the client provider version
- error_text = "Invalid content "
- client_version = None
- used_schema = None
- if version_fields != None:
- client_version = client_data
- for field in version_fields:
- if field in client_version:
- client_version = client_version[field]
- else:
- client_version=None
- break
- if client_version==None:
- used_schema=default_schema
- elif version_dict_schema!=None:
- if client_version in version_dict_schema:
- used_schema = version_dict_schema[client_version]
- elif None in version_dict_schema:
- used_schema = version_dict_schema[None]
- if used_schema==None:
- bottle.abort(HTTP_Bad_Request, "Invalid schema version or missing version field")
-
- js_v(client_data, used_schema)
- return client_data, used_schema
- except (ValueError, yaml.YAMLError) as exc:
- error_text += str(exc)
- logger.error(error_text)
- bottle.abort(HTTP_Bad_Request, error_text)
- except js_e.ValidationError as exc:
- logger.error("validate_in error, jsonschema exception at '%s' '%s' ", str(exc.path), str(exc.message))
- error_pos = ""
- if len(exc.path)>0: error_pos=" at " + ":".join(map(json.dumps, exc.path))
- bottle.abort(HTTP_Bad_Request, error_text + exc.message + error_pos)
- #except:
- # bottle.abort(HTTP_Bad_Request, "Content error: Failed to parse Content-Type", error_pos)
- # raise
-
-def filter_query_string(qs, http2db, allowed):
- '''Process query string (qs) checking that contains only valid tokens for avoiding SQL injection
- Attributes:
- 'qs': bottle.FormsDict variable to be processed. None or empty is considered valid
- 'http2db': dictionary with change from http API naming (dictionary key) to database naming(dictionary value)
- 'allowed': list of allowed string tokens (API http naming). All the keys of 'qs' must be one of 'allowed'
- Return: A tuple with the (select,where,limit) to be use in a database query. All of then transformed to the database naming
- select: list of items to retrieve, filtered by query string 'field=token'. If no 'field' is present, allowed list is returned
- where: dictionary with key, value, taken from the query string token=value. Empty if nothing is provided
- limit: limit dictated by user with the query string 'limit'. 100 by default
- abort if not permited, using bottel.abort
- '''
- where={}
- limit=100
- select=[]
- #if type(qs) is not bottle.FormsDict:
- # bottle.abort(HTTP_Internal_Server_Error, '!!!!!!!!!!!!!!invalid query string not a dictionary')
- # #bottle.abort(HTTP_Internal_Server_Error, "call programmer")
- for k in qs:
- if k=='field':
- select += qs.getall(k)
- for v in select:
- if v not in allowed:
- bottle.abort(HTTP_Bad_Request, "Invalid query string at 'field="+v+"'")
- elif k=='limit':
- try:
- limit=int(qs[k])
- except:
- bottle.abort(HTTP_Bad_Request, "Invalid query string at 'limit="+qs[k]+"'")
- else:
- if k not in allowed:
- bottle.abort(HTTP_Bad_Request, "Invalid query string at '"+k+"="+qs[k]+"'")
- if qs[k]!="null": where[k]=qs[k]
- else: where[k]=None
- if len(select)==0: select += allowed
- #change from http api to database naming
- for i in range(0,len(select)):
- k=select[i]
- if http2db and k in http2db:
- select[i] = http2db[k]
- if http2db:
- change_keys_http2db(where, http2db)
- #print "filter_query_string", select,where,limit
-
- return select,where,limit
-
-@bottle.hook('after_request')
-def enable_cors():
- '''Don't know yet if really needed. Keep it just in case'''
- bottle.response.headers['Access-Control-Allow-Origin'] = '*'
-
-#
-# VNFs
-#
-
-@bottle.route(url_base + '/tenants', method='GET')
-def http_get_tenants():
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- select_,where_,limit_ = filter_query_string(bottle.request.query, None,
- ('uuid','name','description','created_at') )
- try:
- tenants = mydb.get_rows(FROM='nfvo_tenants', SELECT=select_,WHERE=where_,LIMIT=limit_)
- #change_keys_http2db(content, http2db_tenant, reverse=True)
- convert_datetime2str(tenants)
- data={'tenants' : tenants}
- return format_out(data)
- except db_base_Exception as e:
- logger.error("http_get_tenants error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/tenants/<tenant_id>', method='GET')
-def http_get_tenant_id(tenant_id):
- '''get tenant details, can use both uuid or name'''
- #obtain data
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- try:
- tenant = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id, "tenant")
- #change_keys_http2db(content, http2db_tenant, reverse=True)
- convert_datetime2str(tenant)
- data={'tenant' : tenant}
- return format_out(data)
- except db_base_Exception as e:
- logger.error("http_get_tenant_id error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/tenants', method='POST')
-def http_post_tenants():
- '''insert a tenant into the catalogue. '''
- #parse input data
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- http_content,_ = format_in( tenant_schema )
- r = utils.remove_extra_items(http_content, tenant_schema)
- if r:
- logger.debug("Remove received extra items %s", str(r))
- try:
- data = nfvo.new_tenant(mydb, http_content['tenant'])
- return http_get_tenant_id(data)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_post_tenants error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/tenants/<tenant_id>', method='PUT')
-def http_edit_tenant_id(tenant_id):
- '''edit tenant details, can use both uuid or name'''
- #parse input data
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- http_content,_ = format_in( tenant_edit_schema )
- r = utils.remove_extra_items(http_content, tenant_edit_schema)
- if r:
- logger.debug("Remove received extra items %s", str(r))
-
- #obtain data, check that only one exist
- try:
- tenant = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id)
- #edit data
- tenant_id = tenant['uuid']
- where={'uuid': tenant['uuid']}
- mydb.update_rows('nfvo_tenants', http_content['tenant'], where)
- return http_get_tenant_id(tenant_id)
- except db_base_Exception as e:
- logger.error("http_edit_tenant_id error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/tenants/<tenant_id>', method='DELETE')
-def http_delete_tenant_id(tenant_id):
- '''delete a tenant from database, can use both uuid or name'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- try:
- data = nfvo.delete_tenant(mydb, tenant_id)
- return format_out({"result":"tenant " + data + " deleted"})
- except db_base_Exception as e:
- logger.error("http_delete_tenant_id error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/datacenters', method='GET')
-def http_get_datacenters(tenant_id):
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- try:
- if tenant_id != 'any':
- #check valid tenant_id
- nfvo.check_tenant(mydb, tenant_id)
- select_,where_,limit_ = filter_query_string(bottle.request.query, None,
- ('uuid','name','vim_url','type','created_at') )
- if tenant_id != 'any':
- where_['nfvo_tenant_id'] = tenant_id
- if 'created_at' in select_:
- select_[ select_.index('created_at') ] = 'd.created_at as created_at'
- if 'created_at' in where_:
- where_['d.created_at'] = where_.pop('created_at')
- datacenters = mydb.get_rows(FROM='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id',
- SELECT=select_,WHERE=where_,LIMIT=limit_)
- else:
- datacenters = mydb.get_rows(FROM='datacenters',
- SELECT=select_,WHERE=where_,LIMIT=limit_)
- #change_keys_http2db(content, http2db_tenant, reverse=True)
- convert_datetime2str(datacenters)
- data={'datacenters' : datacenters}
- return format_out(data)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_get_datacenters error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='GET')
-def http_get_datacenter_id(tenant_id, datacenter_id):
- '''get datacenter details, can use both uuid or name'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- try:
- if tenant_id != 'any':
- #check valid tenant_id
- nfvo.check_tenant(mydb, tenant_id)
- #obtain data
- what = 'uuid' if utils.check_valid_uuid(datacenter_id) else 'name'
- where_={}
- where_[what] = datacenter_id
- select_=['uuid', 'name','vim_url', 'vim_url_admin', 'type', 'd.config as config', 'description', 'd.created_at as created_at']
- if tenant_id != 'any':
- select_.append("datacenter_tenant_id")
- where_['td.nfvo_tenant_id']= tenant_id
- from_='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id'
- else:
- from_='datacenters as d'
- datacenters = mydb.get_rows(
- SELECT=select_,
- FROM=from_,
- WHERE=where_)
-
- if len(datacenters)==0:
- bottle.abort( HTTP_Not_Found, "No datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
- elif len(datacenters)>1:
- bottle.abort( HTTP_Bad_Request, "More than one datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
- datacenter = datacenters[0]
- if tenant_id != 'any':
- #get vim tenant info
- vim_tenants = mydb.get_rows(
- SELECT=("vim_tenant_name", "vim_tenant_id", "user", "passwd", "config"),
- FROM="datacenter_tenants",
- WHERE={"uuid": datacenters[0]["datacenter_tenant_id"]},
- ORDER_BY=("created", ) )
- del datacenter["datacenter_tenant_id"]
- datacenter["vim_tenants"] = vim_tenants
- for vim_tenant in vim_tenants:
- if vim_tenant["passwd"]:
- vim_tenant["passwd"] = "******"
- if vim_tenant['config'] != None:
- try:
- config_dict = yaml.load(vim_tenant['config'])
- vim_tenant['config'] = config_dict
- except Exception as e:
- logger.error("Exception '%s' while trying to load config information", str(e))
-
- if datacenter['config'] != None:
- try:
- config_dict = yaml.load(datacenter['config'])
- datacenter['config'] = config_dict
- except Exception as e:
- logger.error("Exception '%s' while trying to load config information", str(e))
- #change_keys_http2db(content, http2db_datacenter, reverse=True)
- convert_datetime2str(datacenter)
- data={'datacenter' : datacenter}
- return format_out(data)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_get_datacenter_id error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/datacenters', method='POST')
-def http_post_datacenters():
- '''insert a tenant into the catalogue. '''
- #parse input data
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- http_content,_ = format_in( datacenter_schema )
- r = utils.remove_extra_items(http_content, datacenter_schema)
- if r:
- logger.debug("Remove received extra items %s", str(r))
- try:
- data = nfvo.new_datacenter(mydb, http_content['datacenter'])
- return http_get_datacenter_id('any', data)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_post_datacenters error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/datacenters/<datacenter_id_name>', method='PUT')
-def http_edit_datacenter_id(datacenter_id_name):
- '''edit datacenter details, can use both uuid or name'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- #parse input data
- http_content,_ = format_in( datacenter_edit_schema )
- r = utils.remove_extra_items(http_content, datacenter_edit_schema)
- if r:
- logger.debug("Remove received extra items %s", str(r))
-
- try:
- datacenter_id = nfvo.edit_datacenter(mydb, datacenter_id_name, http_content['datacenter'])
- return http_get_datacenter_id('any', datacenter_id)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_edit_datacenter_id error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/networks', method='GET') #deprecated
-@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='GET')
-@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='GET')
-def http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
- '''get datacenter networks, can use both uuid or name'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- #obtain data
- try:
- datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
- where_= {"datacenter_id":datacenter_dict['uuid']}
- if netmap_id:
- if utils.check_valid_uuid(netmap_id):
- where_["uuid"] = netmap_id
- else:
- where_["name"] = netmap_id
- netmaps =mydb.get_rows(FROM='datacenter_nets',
- SELECT=('name','vim_net_id as vim_id', 'uuid', 'type','multipoint','shared','description', 'created_at'),
- WHERE=where_ )
- convert_datetime2str(netmaps)
- utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
- if netmap_id and len(netmaps)==1:
- data={'netmap' : netmaps[0]}
- elif netmap_id and len(netmaps)==0:
- bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), where_.iteritems())) )
- return
- else:
- data={'netmaps' : netmaps}
- return format_out(data)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_getnetwork_datacenter_id error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='DELETE')
-@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='DELETE')
-def http_delnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
- '''get datacenter networks, can use both uuid or name'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- #obtain data
- try:
- datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
- where_= {"datacenter_id":datacenter_dict['uuid']}
- if netmap_id:
- if utils.check_valid_uuid(netmap_id):
- where_["uuid"] = netmap_id
- else:
- where_["name"] = netmap_id
- #change_keys_http2db(content, http2db_tenant, reverse=True)
- deleted = mydb.delete_row(FROM='datacenter_nets', WHERE= where_)
- if deleted == 0 and netmap_id :
- bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), where_.iteritems())) )
- if netmap_id:
- return format_out({"result": "netmap %s deleted" % netmap_id})
- else:
- return format_out({"result": "%d netmap deleted" % deleted})
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_delnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/upload', method='POST')
-def http_uploadnetmap_datacenter_id(tenant_id, datacenter_id):
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- try:
- netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, None)
- convert_datetime2str(netmaps)
- utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
- data={'netmaps' : netmaps}
- return format_out(data)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_uploadnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='POST')
-def http_postnetmap_datacenter_id(tenant_id, datacenter_id):
- '''creates a new netmap'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- #parse input data
- http_content,_ = format_in( netmap_new_schema )
- r = utils.remove_extra_items(http_content, netmap_new_schema)
- if r:
- logger.debug("Remove received extra items %s", str(r))
- try:
- #obtain data, check that only one exist
- netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, http_content)
- convert_datetime2str(netmaps)
- utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
- data={'netmaps' : netmaps}
- return format_out(data)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_postnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='PUT')
-def http_putnettmap_datacenter_id(tenant_id, datacenter_id, netmap_id):
- '''edit a netmap'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- #parse input data
- http_content,_ = format_in( netmap_edit_schema )
- r = utils.remove_extra_items(http_content, netmap_edit_schema)
- if r:
- logger.debug("Remove received extra items %s", str(r))
-
- #obtain data, check that only one exist
- try:
- nfvo.datacenter_edit_netmap(mydb, tenant_id, datacenter_id, netmap_id, http_content)
- return http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_putnettmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/action', method='POST')
-def http_action_datacenter_id(tenant_id, datacenter_id):
- '''perform an action over datacenter, can use both uuid or name'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- #parse input data
- http_content,_ = format_in( datacenter_action_schema )
- r = utils.remove_extra_items(http_content, datacenter_action_schema)
- if r:
- logger.debug("Remove received extra items %s", str(r))
- try:
- #obtain data, check that only one exist
- result = nfvo.datacenter_action(mydb, tenant_id, datacenter_id, http_content)
- if 'net-update' in http_content:
- return http_getnetmap_datacenter_id(datacenter_id)
- else:
- return format_out(result)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_action_datacenter_id error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/datacenters/<datacenter_id>', method='DELETE')
-def http_delete_datacenter_id( datacenter_id):
- '''delete a tenant from database, can use both uuid or name'''
-
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- try:
- data = nfvo.delete_datacenter(mydb, datacenter_id)
- return format_out({"result":"datacenter '" + data + "' deleted"})
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_delete_datacenter_id error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='POST')
-def http_associate_datacenters(tenant_id, datacenter_id):
- '''associate an existing datacenter to a this tenant. '''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- #parse input data
- http_content,_ = format_in( datacenter_associate_schema )
- r = utils.remove_extra_items(http_content, datacenter_associate_schema)
- if r:
- logger.debug("Remove received extra items %s", str(r))
- try:
- id_ = nfvo.associate_datacenter_to_tenant(mydb, tenant_id, datacenter_id,
- http_content['datacenter'].get('vim_tenant'),
- http_content['datacenter'].get('vim_tenant_name'),
- http_content['datacenter'].get('vim_username'),
- http_content['datacenter'].get('vim_password'),
- http_content['datacenter'].get('config')
- )
- return http_get_datacenter_id(tenant_id, id_)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_associate_datacenters error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='DELETE')
-def http_deassociate_datacenters(tenant_id, datacenter_id):
- '''deassociate an existing datacenter to a this tenant. '''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- try:
- data = nfvo.deassociate_datacenter_to_tenant(mydb, tenant_id, datacenter_id)
- return format_out({"result": data})
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_deassociate_datacenters error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='GET')
-@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='GET')
-def http_get_vim_items(tenant_id, datacenter_id, item, name=None):
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- try:
- data = nfvo.vim_action_get(mydb, tenant_id, datacenter_id, item, name)
- return format_out(data)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_get_vim_items error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='DELETE')
-def http_del_vim_items(tenant_id, datacenter_id, item, name):
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- try:
- data = nfvo.vim_action_delete(mydb, tenant_id, datacenter_id, item, name)
- return format_out({"result":data})
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_del_vim_items error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='POST')
-def http_post_vim_items(tenant_id, datacenter_id, item):
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- http_content,_ = format_in( object_schema )
- try:
- data = nfvo.vim_action_create(mydb, tenant_id, datacenter_id, item, http_content)
- return format_out(data)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_post_vim_items error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/vnfs', method='GET')
-def http_get_vnfs(tenant_id):
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- try:
- if tenant_id != 'any':
- #check valid tenant_id
- nfvo.check_tenant(mydb, tenant_id)
- select_,where_,limit_ = filter_query_string(bottle.request.query, None,
- ('uuid','name','description','public', "tenant_id", "created_at") )
- where_or = {}
- if tenant_id != "any":
- where_or["tenant_id"] = tenant_id
- where_or["public"] = True
- vnfs = mydb.get_rows(FROM='vnfs', SELECT=select_,WHERE=where_,WHERE_OR=where_or, WHERE_AND_OR="AND",LIMIT=limit_)
- #change_keys_http2db(content, http2db_vnf, reverse=True)
- utils.convert_str2boolean(vnfs, ('public',))
- convert_datetime2str(vnfs)
- data={'vnfs' : vnfs}
- return format_out(data)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_get_vnfs error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='GET')
-def http_get_vnf_id(tenant_id,vnf_id):
- '''get vnf details, can use both uuid or name'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- try:
- vnf = nfvo.get_vnf_id(mydb,tenant_id,vnf_id)
- utils.convert_str2boolean(vnf, ('public',))
- convert_datetime2str(vnf)
- return format_out(vnf)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_get_vnf_id error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/vnfs', method='POST')
-def http_post_vnfs(tenant_id):
- '''insert a vnf into the catalogue. Creates the flavor and images in the VIM, and creates the VNF and its internal structure in the OPENMANO DB'''
- #print "Parsing the YAML file of the VNF"
- #parse input data
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- http_content, used_schema = format_in( vnfd_schema_v01, ("schema_version",), {"0.2": vnfd_schema_v02})
- r = utils.remove_extra_items(http_content, used_schema)
- if r:
- logger.debug("Remove received extra items %s", str(r))
- try:
- if used_schema == vnfd_schema_v01:
- vnf_id = nfvo.new_vnf(mydb,tenant_id,http_content)
- elif used_schema == vnfd_schema_v02:
- vnf_id = nfvo.new_vnf_v02(mydb,tenant_id,http_content)
- else:
- logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
- bottle.abort(HTTP_Bad_Request, "Invalid schema version")
- return http_get_vnf_id(tenant_id, vnf_id)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_post_vnfs error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='DELETE')
-def http_delete_vnf_id(tenant_id,vnf_id):
- '''delete a vnf from database, and images and flavors in VIM when appropriate, can use both uuid or name'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- #check valid tenant_id and deletes the vnf, including images,
- try:
- data = nfvo.delete_vnf(mydb,tenant_id,vnf_id)
- #print json.dumps(data, indent=4)
- return format_out({"result":"VNF " + data + " deleted"})
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_delete_vnf_id error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-#@bottle.route(url_base + '/<tenant_id>/hosts/topology', method='GET')
-#@bottle.route(url_base + '/<tenant_id>/physicalview/Madrid-Alcantara', method='GET')
-@bottle.route(url_base + '/<tenant_id>/physicalview/<datacenter>', method='GET')
-def http_get_hosts(tenant_id, datacenter):
- '''get the tidvim host hopology from the vim.'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- #print "http_get_hosts received by tenant " + tenant_id + ' datacenter ' + datacenter
- try:
- if datacenter == 'treeview':
- data = nfvo.get_hosts(mydb, tenant_id)
- else:
- #openmano-gui is using a hardcoded value for the datacenter
- result, data = nfvo.get_hosts_info(mydb, tenant_id) #, datacenter)
-
- if result < 0:
- #print "http_get_hosts error %d %s" % (-result, data)
- bottle.abort(-result, data)
- else:
- convert_datetime2str(data)
- #print json.dumps(data, indent=4)
- return format_out(data)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_get_hosts error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<path:path>', method='OPTIONS')
-def http_options_deploy(path):
- '''For some reason GUI web ask for OPTIONS that must be responded'''
- #TODO: check correct path, and correct headers request
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- bottle.response.set_header('Access-Control-Allow-Methods','POST, GET, PUT, DELETE, OPTIONS')
- bottle.response.set_header('Accept','application/yaml,application/json')
- bottle.response.set_header('Content-Type','application/yaml,application/json')
- bottle.response.set_header('Access-Control-Allow-Headers','content-type')
- bottle.response.set_header('Access-Control-Allow-Origin','*')
- return
-
-@bottle.route(url_base + '/<tenant_id>/topology/deploy', method='POST')
-def http_post_deploy(tenant_id):
- '''post topology deploy.'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
-
- http_content, used_schema = format_in( nsd_schema_v01, ("schema_version",), {2: nsd_schema_v02})
- #r = utils.remove_extra_items(http_content, used_schema)
- #if r is not None: print "http_post_deploy: Warning: remove extra items ", r
- #print "http_post_deploy input: ", http_content
-
- try:
- scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
- instance = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['name'], http_content['name'])
- #print json.dumps(data, indent=4)
- return format_out(instance)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_post_deploy error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/topology/verify', method='POST')
-def http_post_verify(tenant_id):
- #TODO:
-# '''post topology verify'''
-# print "http_post_verify by tenant " + tenant_id + ' datacenter ' + datacenter
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- return
-
-#
-# SCENARIOS
-#
-
-@bottle.route(url_base + '/<tenant_id>/scenarios', method='POST')
-def http_post_scenarios(tenant_id):
- '''add a scenario into the catalogue. Creates the scenario and its internal structure in the OPENMANO DB'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- http_content, used_schema = format_in( nsd_schema_v01, ("schema_version",), {2: nsd_schema_v02, "0.3": nsd_schema_v03})
- #r = utils.remove_extra_items(http_content, used_schema)
- #if r is not None: print "http_post_scenarios: Warning: remove extra items ", r
- #print "http_post_scenarios input: ", http_content
- try:
- if used_schema == nsd_schema_v01:
- scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
- elif used_schema == nsd_schema_v02:
- scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content, "0.2")
- elif used_schema == nsd_schema_v03:
- scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content, "0.3")
- else:
- logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
- bottle.abort(HTTP_Bad_Request, "Invalid schema version")
- #print json.dumps(data, indent=4)
- #return format_out(data)
- return http_get_scenario_id(tenant_id, scenario_id)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_post_scenarios error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>/action', method='POST')
-def http_post_scenario_action(tenant_id, scenario_id):
- '''take an action over a scenario'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- # parse input data
- http_content, _ = format_in(scenario_action_schema)
- r = utils.remove_extra_items(http_content, scenario_action_schema)
- if r:
- logger.debug("Remove received extra items %s", str(r))
- try:
- # check valid tenant_id
- nfvo.check_tenant(mydb, tenant_id)
- if "start" in http_content:
- data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['start']['instance_name'], \
- http_content['start'].get('description',http_content['start']['instance_name']),
- http_content['start'].get('datacenter') )
- return format_out(data)
- elif "deploy" in http_content: #Equivalent to start
- data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['deploy']['instance_name'],
- http_content['deploy'].get('description',http_content['deploy']['instance_name']),
- http_content['deploy'].get('datacenter') )
- return format_out(data)
- elif "reserve" in http_content: #Reserve resources
- data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['reserve']['instance_name'],
- http_content['reserve'].get('description',http_content['reserve']['instance_name']),
- http_content['reserve'].get('datacenter'), startvms=False )
- return format_out(data)
- elif "verify" in http_content: #Equivalent to start and then delete
- data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['verify']['instance_name'],
- http_content['verify'].get('description',http_content['verify']['instance_name']),
- http_content['verify'].get('datacenter'), startvms=False )
- instance_id = data['uuid']
- nfvo.delete_instance(mydb, tenant_id,instance_id)
- return format_out({"result":"Verify OK"})
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_post_scenario_action error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/scenarios', method='GET')
-def http_get_scenarios(tenant_id):
- '''get scenarios list'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- try:
- #check valid tenant_id
- if tenant_id != "any":
- nfvo.check_tenant(mydb, tenant_id)
- #obtain data
- s,w,l=filter_query_string(bottle.request.query, None, ('uuid', 'name', 'description', 'tenant_id', 'created_at', 'public'))
- where_or={}
- if tenant_id != "any":
- where_or["tenant_id"] = tenant_id
- where_or["public"] = True
- scenarios = mydb.get_rows(SELECT=s, WHERE=w, WHERE_OR=where_or, WHERE_AND_OR="AND", LIMIT=l, FROM='scenarios')
- convert_datetime2str(scenarios)
- utils.convert_str2boolean(scenarios, ('public',) )
- data={'scenarios':scenarios}
- #print json.dumps(scenarios, indent=4)
- return format_out(data)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='GET')
-def http_get_scenario_id(tenant_id, scenario_id):
- '''get scenario details, can use both uuid or name'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- try:
- #check valid tenant_id
- if tenant_id != "any":
- nfvo.check_tenant(mydb, tenant_id)
- #obtain data
- scenario = mydb.get_scenario(scenario_id, tenant_id)
- convert_datetime2str(scenario)
- data={'scenario' : scenario}
- return format_out(data)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='DELETE')
-def http_delete_scenario_id(tenant_id, scenario_id):
- '''delete a scenario from database, can use both uuid or name'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- try:
- #check valid tenant_id
- if tenant_id != "any":
- nfvo.check_tenant(mydb, tenant_id)
- #obtain data
- data = mydb.delete_scenario(scenario_id, tenant_id)
- #print json.dumps(data, indent=4)
- return format_out({"result":"scenario " + data + " deleted"})
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_delete_scenario_id error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='PUT')
-def http_put_scenario_id(tenant_id, scenario_id):
- '''edit an existing scenario id'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- http_content,_ = format_in( scenario_edit_schema )
- #r = utils.remove_extra_items(http_content, scenario_edit_schema)
- #if r is not None: print "http_put_scenario_id: Warning: remove extra items ", r
- #print "http_put_scenario_id input: ", http_content
- try:
- nfvo.edit_scenario(mydb, tenant_id, scenario_id, http_content)
- #print json.dumps(data, indent=4)
- #return format_out(data)
- return http_get_scenario_id(tenant_id, scenario_id)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_put_scenario_id error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-@bottle.route(url_base + '/<tenant_id>/instances', method='POST')
-def http_post_instances(tenant_id):
- '''create an instance-scenario'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- # parse input data
- http_content, used_schema = format_in(instance_scenario_create_schema_v01)
- r = utils.remove_extra_items(http_content, used_schema)
- if r is not None:
- logger.warning("http_post_instances: Warning: remove extra items %s", str(r))
- try:
- #check valid tenant_id
- if tenant_id != "any":
- nfvo.check_tenant(mydb, tenant_id)
- data = nfvo.create_instance(mydb, tenant_id, http_content["instance"])
- return format_out(data)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_post_instances error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-#
-# INSTANCES
-#
-@bottle.route(url_base + '/<tenant_id>/instances', method='GET')
-def http_get_instances(tenant_id):
- '''get instance list'''
- try:
- #check valid tenant_id
- if tenant_id != "any":
- nfvo.check_tenant(mydb, tenant_id)
- #obtain data
- s,w,l=filter_query_string(bottle.request.query, None, ('uuid', 'name', 'scenario_id', 'tenant_id', 'description', 'created_at'))
- if tenant_id != "any":
- w['tenant_id'] = tenant_id
- instances = mydb.get_rows(SELECT=s, WHERE=w, LIMIT=l, FROM='instance_scenarios')
- convert_datetime2str(instances)
- utils.convert_str2boolean(instances, ('public',) )
- data={'instances':instances}
- return format_out(data)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_get_instances error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='GET')
-def http_get_instance_id(tenant_id, instance_id):
- '''get instances details, can use both uuid or name'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- try:
- #check valid tenant_id
- if tenant_id != "any":
- nfvo.check_tenant(mydb, tenant_id)
- if tenant_id == "any":
- tenant_id = None
- #obtain data (first time is only to check that the instance exists)
- instance_dict = mydb.get_instance_scenario(instance_id, tenant_id, verbose=True)
- try:
- nfvo.refresh_instance(mydb, tenant_id, instance_dict)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.warn("nfvo.refresh_instance couldn't refresh the status of the instance: %s" % str(e))
- #obtain data with results upated
- instance = mydb.get_instance_scenario(instance_id, tenant_id)
- convert_datetime2str(instance)
- #print json.dumps(instance, indent=4)
- return format_out(instance)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_get_instance_id error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='DELETE')
-def http_delete_instance_id(tenant_id, instance_id):
- '''delete instance from VIM and from database, can use both uuid or name'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- try:
- #check valid tenant_id
- if tenant_id != "any":
- nfvo.check_tenant(mydb, tenant_id)
- if tenant_id == "any":
- tenant_id = None
- #obtain data
- message = nfvo.delete_instance(mydb, tenant_id,instance_id)
- return format_out({"result":message})
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_delete_instance_id error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action', method='POST')
-def http_post_instance_scenario_action(tenant_id, instance_id):
- '''take an action over a scenario instance'''
- logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
- # parse input data
- http_content, _ = format_in(instance_scenario_action_schema)
- r = utils.remove_extra_items(http_content, instance_scenario_action_schema)
- if r:
- logger.debug("Remove received extra items %s", str(r))
- try:
- #check valid tenant_id
- if tenant_id != "any":
- nfvo.check_tenant(mydb, tenant_id)
-
- #print "http_post_instance_scenario_action input: ", http_content
- #obtain data
- instance = mydb.get_instance_scenario(instance_id, tenant_id)
- instance_id = instance["uuid"]
-
- data = nfvo.instance_action(mydb, tenant_id, instance_id, http_content)
- return format_out(data)
- except (nfvo.NfvoException, db_base_Exception) as e:
- logger.error("http_post_instance_scenario_action error {}: {}".format(e.http_code, str(e)))
- bottle.abort(e.http_code, str(e))
- except Exception as e:
- logger.error("Unexpected exception: ", exc_info=True)
- bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
-
-@bottle.error(400)
-@bottle.error(401)
-@bottle.error(404)
-@bottle.error(403)
-@bottle.error(405)
-@bottle.error(406)
-@bottle.error(409)
-@bottle.error(503)
-@bottle.error(500)
-def error400(error):
- e={"error":{"code":error.status_code, "type":error.status, "description":error.body}}
- bottle.response.headers['Access-Control-Allow-Origin'] = '*'
- return format_out(e)
-
+++ /dev/null
-# -*- 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
-##
-
-'''
-NFVO engine, implementing all the methods for the creation, deletion and management of vnfs, scenarios and instances
-'''
-__author__="Alfonso Tierno, Gerardo Garcia, Pablo Montes"
-__date__ ="$16-sep-2014 22:05:01$"
-
-import imp
-#import json
-import yaml
-import utils
-import vim_thread
-from db_base import HTTP_Unauthorized, HTTP_Bad_Request, HTTP_Internal_Server_Error, HTTP_Not_Found,\
- HTTP_Conflict, HTTP_Method_Not_Allowed
-import console_proxy_thread as cli
-import vimconn
-import logging
-import collections
-from db_base import db_base_Exception
-import nfvo_db
-from threading import Lock
-from time import time
-
-global global_config
-global vimconn_imported
-global logger
-global default_volume_size
-default_volume_size = '5' #size in GB
-
-
-vimconn_imported = {} # dictionary with VIM type as key, loaded module as value
-vim_threads = {"running":{}, "deleting": {}, "names": []} # threads running for attached-VIMs
-vim_persistent_info = {}
-logger = logging.getLogger('openmano.nfvo')
-task_lock = Lock()
-task_dict = {}
-last_task_id = 0.0
-db=None
-db_lock=Lock()
-
-class NfvoException(Exception):
- def __init__(self, message, http_code):
- self.http_code = http_code
- Exception.__init__(self, message)
-
-
-def get_task_id():
- global last_task_id
- task_id = time()
- if task_id <= last_task_id:
- task_id = last_task_id + 0.000001
- last_task_id = task_id
- return "TASK.{:.6f}".format(task_id)
-
-
-def new_task(name, params, store=True, depends=None):
- task_id = get_task_id()
- task = {"status": "enqueued", "id": task_id, "name": name, "params": params}
- if depends:
- task["depends"] = depends
- if store:
- task_dict[task_id] = task
- return task
-
-
-def is_task_id(id):
- return True if id[:5] == "TASK." else False
-
-
-def get_non_used_vim_name(datacenter_name, datacenter_id, tenant_name, tenant_id):
- name = datacenter_name[:16]
- if name not in vim_threads["names"]:
- vim_threads["names"].append(name)
- return name
- name = datacenter_name[:16] + "." + tenant_name[:16]
- if name not in vim_threads["names"]:
- vim_threads["names"].append(name)
- return name
- name = datacenter_id + "-" + tenant_id
- vim_threads["names"].append(name)
- return name
-
-
-def start_service(mydb):
- global db, global_config
- db = nfvo_db.nfvo_db()
- db.connect(global_config['db_host'], global_config['db_user'], global_config['db_passwd'], global_config['db_name'])
- from_= 'tenants_datacenters as td join datacenters as d on td.datacenter_id=d.uuid join datacenter_tenants as dt on td.datacenter_tenant_id=dt.uuid'
- select_ = ('type','d.config as config','d.uuid as datacenter_id', 'vim_url', 'vim_url_admin', 'd.name as datacenter_name',
- 'dt.uuid as datacenter_tenant_id','dt.vim_tenant_name as vim_tenant_name','dt.vim_tenant_id as vim_tenant_id',
- 'user','passwd', 'dt.config as dt_config', 'nfvo_tenant_id')
- try:
- vims = mydb.get_rows(FROM=from_, SELECT=select_)
- for vim in vims:
- extra={'datacenter_tenant_id': vim.get('datacenter_tenant_id')}
- if vim["config"]:
- extra.update(yaml.load(vim["config"]))
- if vim.get('dt_config'):
- extra.update(yaml.load(vim["dt_config"]))
- if vim["type"] not in vimconn_imported:
- module_info=None
- try:
- module = "vimconn_" + vim["type"]
- module_info = imp.find_module(module)
- vim_conn = imp.load_module(vim["type"], *module_info)
- vimconn_imported[vim["type"]] = vim_conn
- except (IOError, ImportError) as e:
- if module_info and module_info[0]:
- file.close(module_info[0])
- raise NfvoException("Unknown vim type '{}'. Can not open file '{}.py'; {}: {}".format(
- vim["type"], module, type(e).__name__, str(e)), HTTP_Bad_Request)
-
- thread_id = vim["datacenter_id"] + "." + vim['nfvo_tenant_id']
- vim_persistent_info[thread_id] = {}
- try:
- #if not tenant:
- # return -HTTP_Bad_Request, "You must provide a valid tenant name or uuid for VIM %s" % ( vim["type"])
- myvim = vimconn_imported[ vim["type"] ].vimconnector(
- uuid=vim['datacenter_id'], name=vim['datacenter_name'],
- tenant_id=vim['vim_tenant_id'], tenant_name=vim['vim_tenant_name'],
- url=vim['vim_url'], url_admin=vim['vim_url_admin'],
- user=vim['user'], passwd=vim['passwd'],
- config=extra, persistent_info=vim_persistent_info[thread_id]
- )
- except Exception as e:
- raise NfvoException("Error at VIM {}; {}: {}".format(vim["type"], type(e).__name__, str(e)), HTTP_Internal_Server_Error)
- thread_name = get_non_used_vim_name(vim['datacenter_name'], vim['vim_tenant_id'], vim['vim_tenant_name'], vim['vim_tenant_id'])
- new_thread = vim_thread.vim_thread(myvim, task_lock, thread_name, vim['datacenter_name'],
- vim.get('datacenter_tenant_id'), db=db, db_lock=db_lock)
- new_thread.start()
- vim_threads["running"][thread_id] = new_thread
- except db_base_Exception as e:
- raise NfvoException(str(e) + " at nfvo.get_vim", e.http_code)
-
-
-def stop_service():
- for thread_id,thread in vim_threads["running"].items():
- thread.insert_task(new_task("exit", None, store=False))
- vim_threads["deleting"][thread_id] = thread
- vim_threads["running"] = {}
-
-
-def get_flavorlist(mydb, vnf_id, nfvo_tenant=None):
- '''Obtain flavorList
- return result, content:
- <0, error_text upon error
- nb_records, flavor_list on success
- '''
- WHERE_dict={}
- WHERE_dict['vnf_id'] = vnf_id
- if nfvo_tenant is not None:
- WHERE_dict['nfvo_tenant_id'] = nfvo_tenant
-
- #result, content = mydb.get_table(FROM='vms join vnfs on vms.vnf_id = vnfs.uuid',SELECT=('uuid'),WHERE=WHERE_dict )
- #result, content = mydb.get_table(FROM='vms',SELECT=('vim_flavor_id',),WHERE=WHERE_dict )
- flavors = mydb.get_rows(FROM='vms join flavors on vms.flavor_id=flavors.uuid',SELECT=('flavor_id',),WHERE=WHERE_dict )
- #print "get_flavor_list result:", result
- #print "get_flavor_list content:", content
- flavorList=[]
- for flavor in flavors:
- flavorList.append(flavor['flavor_id'])
- return flavorList
-
-
-def get_imagelist(mydb, vnf_id, nfvo_tenant=None):
- '''Obtain imageList
- return result, content:
- <0, error_text upon error
- nb_records, flavor_list on success
- '''
- WHERE_dict={}
- WHERE_dict['vnf_id'] = vnf_id
- if nfvo_tenant is not None:
- WHERE_dict['nfvo_tenant_id'] = nfvo_tenant
-
- #result, content = mydb.get_table(FROM='vms join vnfs on vms-vnf_id = vnfs.uuid',SELECT=('uuid'),WHERE=WHERE_dict )
- images = mydb.get_rows(FROM='vms join images on vms.image_id=images.uuid',SELECT=('image_id',),WHERE=WHERE_dict )
- imageList=[]
- for image in images:
- imageList.append(image['image_id'])
- return imageList
-
-
-def get_vim(mydb, nfvo_tenant=None, datacenter_id=None, datacenter_name=None, datacenter_tenant_id=None,
- vim_tenant=None, vim_tenant_name=None, vim_user=None, vim_passwd=None):
- '''Obtain a dictionary of VIM (datacenter) classes with some of the input parameters
- return dictionary with {datacenter_id: vim_class, ... }. vim_class contain:
- 'nfvo_tenant_id','datacenter_id','vim_tenant_id','vim_url','vim_url_admin','datacenter_name','type','user','passwd'
- raise exception upon error
- '''
- WHERE_dict={}
- if nfvo_tenant is not None: WHERE_dict['nfvo_tenant_id'] = nfvo_tenant
- if datacenter_id is not None: WHERE_dict['d.uuid'] = datacenter_id
- if datacenter_tenant_id is not None: WHERE_dict['datacenter_tenant_id'] = datacenter_tenant_id
- if datacenter_name is not None: WHERE_dict['d.name'] = datacenter_name
- if vim_tenant is not None: WHERE_dict['dt.vim_tenant_id'] = vim_tenant
- if vim_tenant_name is not None: WHERE_dict['vim_tenant_name'] = vim_tenant_name
- if nfvo_tenant or vim_tenant or vim_tenant_name or datacenter_tenant_id:
- from_= 'tenants_datacenters as td join datacenters as d on td.datacenter_id=d.uuid join datacenter_tenants as dt on td.datacenter_tenant_id=dt.uuid'
- select_ = ('type','d.config as config','d.uuid as datacenter_id', 'vim_url', 'vim_url_admin', 'd.name as datacenter_name',
- 'dt.uuid as datacenter_tenant_id','dt.vim_tenant_name as vim_tenant_name','dt.vim_tenant_id as vim_tenant_id',
- 'user','passwd', 'dt.config as dt_config')
- else:
- from_ = 'datacenters as d'
- select_ = ('type','config','d.uuid as datacenter_id', 'vim_url', 'vim_url_admin', 'd.name as datacenter_name')
- try:
- vims = mydb.get_rows(FROM=from_, SELECT=select_, WHERE=WHERE_dict )
- vim_dict={}
- for vim in vims:
- extra={'datacenter_tenant_id': vim.get('datacenter_tenant_id')}
- if vim["config"]:
- extra.update(yaml.load(vim["config"]))
- if vim.get('dt_config'):
- extra.update(yaml.load(vim["dt_config"]))
- if vim["type"] not in vimconn_imported:
- module_info=None
- try:
- module = "vimconn_" + vim["type"]
- module_info = imp.find_module(module)
- vim_conn = imp.load_module(vim["type"], *module_info)
- vimconn_imported[vim["type"]] = vim_conn
- except (IOError, ImportError) as e:
- if module_info and module_info[0]:
- file.close(module_info[0])
- raise NfvoException("Unknown vim type '{}'. Can not open file '{}.py'; {}: {}".format(
- vim["type"], module, type(e).__name__, str(e)), HTTP_Bad_Request)
-
- try:
- if 'nfvo_tenant_id' in vim:
- thread_id = vim["datacenter_id"] + "." + vim['nfvo_tenant_id']
- if thread_id not in vim_persistent_info:
- vim_persistent_info[thread_id] = {}
- persistent_info = vim_persistent_info[thread_id]
- else:
- persistent_info = {}
- #if not tenant:
- # return -HTTP_Bad_Request, "You must provide a valid tenant name or uuid for VIM %s" % ( vim["type"])
- vim_dict[ vim['datacenter_id'] ] = vimconn_imported[ vim["type"] ].vimconnector(
- uuid=vim['datacenter_id'], name=vim['datacenter_name'],
- tenant_id=vim.get('vim_tenant_id',vim_tenant),
- tenant_name=vim.get('vim_tenant_name',vim_tenant_name),
- url=vim['vim_url'], url_admin=vim['vim_url_admin'],
- user=vim.get('user',vim_user), passwd=vim.get('passwd',vim_passwd),
- config=extra, persistent_info=persistent_info
- )
- except Exception as e:
- raise NfvoException("Error at VIM {}; {}: {}".format(vim["type"], type(e).__name__, str(e)), HTTP_Internal_Server_Error)
- return vim_dict
- except db_base_Exception as e:
- raise NfvoException(str(e) + " at nfvo.get_vim", e.http_code)
-
-
-def rollback(mydb, vims, rollback_list):
- undeleted_items=[]
- #delete things by reverse order
- for i in range(len(rollback_list)-1, -1, -1):
- item = rollback_list[i]
- if item["where"]=="vim":
- if item["vim_id"] not in vims:
- continue
- vim=vims[ item["vim_id"] ]
- try:
- if item["what"]=="image":
- vim.delete_image(item["uuid"])
- mydb.delete_row(FROM="datacenters_images", WHERE={"datacenter_id": vim["id"], "vim_id":item["uuid"]})
- elif item["what"]=="flavor":
- vim.delete_flavor(item["uuid"])
- mydb.delete_row(FROM="datacenters_flavors", WHERE={"datacenter_id": vim["id"], "vim_id":item["uuid"]})
- elif item["what"]=="network":
- vim.delete_network(item["uuid"])
- elif item["what"]=="vm":
- vim.delete_vminstance(item["uuid"])
- except vimconn.vimconnException as e:
- logger.error("Error in rollback. Not possible to delete VIM %s '%s'. Message: %s", item['what'], item["uuid"], str(e))
- undeleted_items.append("{} {} from VIM {}".format(item['what'], item["uuid"], vim["name"]))
- except db_base_Exception as e:
- logger.error("Error in rollback. Not possible to delete %s '%s' from DB.datacenters Message: %s", item['what'], item["uuid"], str(e))
-
- else: # where==mano
- try:
- if item["what"]=="image":
- mydb.delete_row(FROM="images", WHERE={"uuid": item["uuid"]})
- elif item["what"]=="flavor":
- mydb.delete_row(FROM="flavors", WHERE={"uuid": item["uuid"]})
- except db_base_Exception as e:
- logger.error("Error in rollback. Not possible to delete %s '%s' from DB. Message: %s", item['what'], item["uuid"], str(e))
- undeleted_items.append("{} '{}'".format(item['what'], item["uuid"]))
- if len(undeleted_items)==0:
- return True," Rollback successful."
- else:
- return False," Rollback fails to delete: " + str(undeleted_items)
-
-
-def check_vnf_descriptor(vnf_descriptor, vnf_descriptor_version=1):
- global global_config
- #create a dictionary with vnfc-name: vnfc:interface-list key:values pairs
- vnfc_interfaces={}
- for vnfc in vnf_descriptor["vnf"]["VNFC"]:
- name_dict = {}
- #dataplane interfaces
- for numa in vnfc.get("numas",() ):
- for interface in numa.get("interfaces",()):
- if interface["name"] in name_dict:
- raise NfvoException(
- "Error at vnf:VNFC[name:'{}']:numas:interfaces:name, interface name '{}' already used in this VNFC".format(
- vnfc["name"], interface["name"]),
- HTTP_Bad_Request)
- name_dict[ interface["name"] ] = "underlay"
- #bridge interfaces
- for interface in vnfc.get("bridge-ifaces",() ):
- if interface["name"] in name_dict:
- raise NfvoException(
- "Error at vnf:VNFC[name:'{}']:bridge-ifaces:name, interface name '{}' already used in this VNFC".format(
- vnfc["name"], interface["name"]),
- HTTP_Bad_Request)
- name_dict[ interface["name"] ] = "overlay"
- vnfc_interfaces[ vnfc["name"] ] = name_dict
- # check bood-data info
- if "boot-data" in vnfc:
- # check that user-data is incompatible with users and config-files
- if (vnfc["boot-data"].get("users") or vnfc["boot-data"].get("config-files")) and vnfc["boot-data"].get("user-data"):
- raise NfvoException(
- "Error at vnf:VNFC:boot-data, fields 'users' and 'config-files' are not compatible with 'user-data'",
- HTTP_Bad_Request)
-
- #check if the info in external_connections matches with the one in the vnfcs
- name_list=[]
- for external_connection in vnf_descriptor["vnf"].get("external-connections",() ):
- if external_connection["name"] in name_list:
- raise NfvoException(
- "Error at vnf:external-connections:name, value '{}' already used as an external-connection".format(
- external_connection["name"]),
- HTTP_Bad_Request)
- name_list.append(external_connection["name"])
- if external_connection["VNFC"] not in vnfc_interfaces:
- raise NfvoException(
- "Error at vnf:external-connections[name:'{}']:VNFC, value '{}' does not match any VNFC".format(
- external_connection["name"], external_connection["VNFC"]),
- HTTP_Bad_Request)
-
- if external_connection["local_iface_name"] not in vnfc_interfaces[ external_connection["VNFC"] ]:
- raise NfvoException(
- "Error at vnf:external-connections[name:'{}']:local_iface_name, value '{}' does not match any interface of this VNFC".format(
- external_connection["name"],
- external_connection["local_iface_name"]),
- HTTP_Bad_Request )
-
- #check if the info in internal_connections matches with the one in the vnfcs
- name_list=[]
- for internal_connection in vnf_descriptor["vnf"].get("internal-connections",() ):
- if internal_connection["name"] in name_list:
- raise NfvoException(
- "Error at vnf:internal-connections:name, value '%s' already used as an internal-connection".format(
- internal_connection["name"]),
- HTTP_Bad_Request)
- name_list.append(internal_connection["name"])
- #We should check that internal-connections of type "ptp" have only 2 elements
-
- if len(internal_connection["elements"])>2 and (internal_connection.get("type") == "ptp" or internal_connection.get("type") == "e-line"):
- raise NfvoException(
- "Error at 'vnf:internal-connections[name:'{}']:elements', size must be 2 for a '{}' type. Consider change it to '{}' type".format(
- internal_connection["name"],
- 'ptp' if vnf_descriptor_version==1 else 'e-line',
- 'data' if vnf_descriptor_version==1 else "e-lan"),
- HTTP_Bad_Request)
- for port in internal_connection["elements"]:
- vnf = port["VNFC"]
- iface = port["local_iface_name"]
- if vnf not in vnfc_interfaces:
- raise NfvoException(
- "Error at vnf:internal-connections[name:'{}']:elements[]:VNFC, value '{}' does not match any VNFC".format(
- internal_connection["name"], vnf),
- HTTP_Bad_Request)
- if iface not in vnfc_interfaces[ vnf ]:
- raise NfvoException(
- "Error at vnf:internal-connections[name:'{}']:elements[]:local_iface_name, value '{}' does not match any interface of this VNFC".format(
- internal_connection["name"], iface),
- HTTP_Bad_Request)
- return -HTTP_Bad_Request,
- if vnf_descriptor_version==1 and "type" not in internal_connection:
- if vnfc_interfaces[vnf][iface] == "overlay":
- internal_connection["type"] = "bridge"
- else:
- internal_connection["type"] = "data"
- if vnf_descriptor_version==2 and "implementation" not in internal_connection:
- if vnfc_interfaces[vnf][iface] == "overlay":
- internal_connection["implementation"] = "overlay"
- else:
- internal_connection["implementation"] = "underlay"
- if (internal_connection.get("type") == "data" or internal_connection.get("type") == "ptp" or \
- internal_connection.get("implementation") == "underlay") and vnfc_interfaces[vnf][iface] == "overlay":
- raise NfvoException(
- "Error at vnf:internal-connections[name:'{}']:elements[]:{}, interface of type {} connected to an {} network".format(
- internal_connection["name"],
- iface, 'bridge' if vnf_descriptor_version==1 else 'overlay',
- 'data' if vnf_descriptor_version==1 else 'underlay'),
- HTTP_Bad_Request)
- if (internal_connection.get("type") == "bridge" or internal_connection.get("implementation") == "overlay") and \
- vnfc_interfaces[vnf][iface] == "underlay":
- raise NfvoException(
- "Error at vnf:internal-connections[name:'{}']:elements[]:{}, interface of type {} connected to an {} network".format(
- internal_connection["name"], iface,
- 'data' if vnf_descriptor_version==1 else 'underlay',
- 'bridge' if vnf_descriptor_version==1 else 'overlay'),
- HTTP_Bad_Request)
-
-
-def create_or_use_image(mydb, vims, image_dict, rollback_list, only_create_at_vim=False, return_on_error = None):
- #look if image exist
- if only_create_at_vim:
- image_mano_id = image_dict['uuid']
- if return_on_error == None:
- return_on_error = True
- else:
- if image_dict['location']:
- images = mydb.get_rows(FROM="images", WHERE={'location':image_dict['location'], 'metadata':image_dict['metadata']})
- else:
- images = mydb.get_rows(FROM="images", WHERE={'universal_name':image_dict['universal_name'], 'checksum':image_dict['checksum']})
- if len(images)>=1:
- image_mano_id = images[0]['uuid']
- else:
- #create image in MANO DB
- temp_image_dict={'name':image_dict['name'], 'description':image_dict.get('description',None),
- 'location':image_dict['location'], 'metadata':image_dict.get('metadata',None),
- 'universal_name':image_dict['universal_name'] , 'checksum':image_dict['checksum']
- }
- #temp_image_dict['location'] = image_dict.get('new_location') if image_dict['location'] is None
- image_mano_id = mydb.new_row('images', temp_image_dict, add_uuid=True)
- rollback_list.append({"where":"mano", "what":"image","uuid":image_mano_id})
- #create image at every vim
- for vim_id,vim in vims.iteritems():
- image_created="false"
- #look at database
- image_db = mydb.get_rows(FROM="datacenters_images", WHERE={'datacenter_id':vim_id, 'image_id':image_mano_id})
- #look at VIM if this image exist
- try:
- if image_dict['location'] is not None:
- image_vim_id = vim.get_image_id_from_path(image_dict['location'])
- else:
- filter_dict = {}
- filter_dict['name'] = image_dict['universal_name']
- if image_dict.get('checksum') != None:
- filter_dict['checksum'] = image_dict['checksum']
- #logger.debug('>>>>>>>> Filter dict: %s', str(filter_dict))
- vim_images = vim.get_image_list(filter_dict)
- #logger.debug('>>>>>>>> VIM images: %s', str(vim_images))
- if len(vim_images) > 1:
- raise vimconn.vimconnException("More than one candidate VIM image found for filter: {}".format(str(filter_dict)), HTTP_Conflict)
- elif len(vim_images) == 0:
- raise vimconn.vimconnNotFoundException("Image not found at VIM with filter: '{}'".format(str(filter_dict)))
- else:
- #logger.debug('>>>>>>>> VIM image 0: %s', str(vim_images[0]))
- image_vim_id = vim_images[0]['id']
-
- except vimconn.vimconnNotFoundException as e:
- #Create the image in VIM only if image_dict['location'] or image_dict['new_location'] is not None
- try:
- #image_dict['location']=image_dict.get('new_location') if image_dict['location'] is None
- if image_dict['location']:
- image_vim_id = vim.new_image(image_dict)
- rollback_list.append({"where":"vim", "vim_id": vim_id, "what":"image","uuid":image_vim_id})
- image_created="true"
- else:
- #If we reach this point, then the image has image name, and optionally checksum, and could not be found
- raise vimconn.vimconnException(str(e))
- except vimconn.vimconnException as e:
- if return_on_error:
- logger.error("Error creating image at VIM '%s': %s", vim["name"], str(e))
- raise
- image_vim_id = None
- logger.warn("Error creating image at VIM '%s': %s", vim["name"], str(e))
- continue
- except vimconn.vimconnException as e:
- if return_on_error:
- logger.error("Error contacting VIM to know if the image exists at VIM: %s", str(e))
- raise
- logger.warn("Error contacting VIM to know if the image exists at VIM: %s", str(e))
- image_vim_id = None
- continue
- #if we reach here, the image has been created or existed
- if len(image_db)==0:
- #add new vim_id at datacenters_images
- mydb.new_row('datacenters_images', {'datacenter_id':vim_id, 'image_id':image_mano_id, 'vim_id': image_vim_id, 'created':image_created})
- elif image_db[0]["vim_id"]!=image_vim_id:
- #modify existing vim_id at datacenters_images
- mydb.update_rows('datacenters_images', UPDATE={'vim_id':image_vim_id}, WHERE={'datacenter_id':vim_id, 'image_id':image_mano_id})
-
- return image_vim_id if only_create_at_vim else image_mano_id
-
-
-def create_or_use_flavor(mydb, vims, flavor_dict, rollback_list, only_create_at_vim=False, return_on_error = None):
- temp_flavor_dict= {'disk':flavor_dict.get('disk',1),
- 'ram':flavor_dict.get('ram'),
- 'vcpus':flavor_dict.get('vcpus'),
- }
- if 'extended' in flavor_dict and flavor_dict['extended']==None:
- del flavor_dict['extended']
- if 'extended' in flavor_dict:
- temp_flavor_dict['extended']=yaml.safe_dump(flavor_dict['extended'],default_flow_style=True,width=256)
-
- #look if flavor exist
- if only_create_at_vim:
- flavor_mano_id = flavor_dict['uuid']
- if return_on_error == None:
- return_on_error = True
- else:
- flavors = mydb.get_rows(FROM="flavors", WHERE=temp_flavor_dict)
- if len(flavors)>=1:
- flavor_mano_id = flavors[0]['uuid']
- else:
- #create flavor
- #create one by one the images of aditional disks
- dev_image_list=[] #list of images
- if 'extended' in flavor_dict and flavor_dict['extended']!=None:
- dev_nb=0
- for device in flavor_dict['extended'].get('devices',[]):
- if "image" not in device and "image name" not in device:
- continue
- image_dict={}
- image_dict['name']=device.get('image name',flavor_dict['name']+str(dev_nb)+"-img")
- image_dict['universal_name']=device.get('image name')
- image_dict['description']=flavor_dict['name']+str(dev_nb)+"-img"
- image_dict['location']=device.get('image')
- #image_dict['new_location']=vnfc.get('image location')
- image_dict['checksum']=device.get('image checksum')
- image_metadata_dict = device.get('image metadata', None)
- image_metadata_str = None
- if image_metadata_dict != None:
- image_metadata_str = yaml.safe_dump(image_metadata_dict,default_flow_style=True,width=256)
- image_dict['metadata']=image_metadata_str
- image_id = create_or_use_image(mydb, vims, image_dict, rollback_list)
- #print "Additional disk image id for VNFC %s: %s" % (flavor_dict['name']+str(dev_nb)+"-img", image_id)
- dev_image_list.append(image_id)
- dev_nb += 1
- temp_flavor_dict['name'] = flavor_dict['name']
- temp_flavor_dict['description'] = flavor_dict.get('description',None)
- content = mydb.new_row('flavors', temp_flavor_dict, add_uuid=True)
- flavor_mano_id= content
- rollback_list.append({"where":"mano", "what":"flavor","uuid":flavor_mano_id})
- #create flavor at every vim
- if 'uuid' in flavor_dict:
- del flavor_dict['uuid']
- flavor_vim_id=None
- for vim_id,vim in vims.items():
- flavor_created="false"
- #look at database
- flavor_db = mydb.get_rows(FROM="datacenters_flavors", WHERE={'datacenter_id':vim_id, 'flavor_id':flavor_mano_id})
- #look at VIM if this flavor exist SKIPPED
- #res_vim, flavor_vim_id = vim.get_flavor_id_from_path(flavor_dict['location'])
- #if res_vim < 0:
- # print "Error contacting VIM to know if the flavor %s existed previously." %flavor_vim_id
- # continue
- #elif res_vim==0:
-
- #Create the flavor in VIM
- #Translate images at devices from MANO id to VIM id
- disk_list = []
- if 'extended' in flavor_dict and flavor_dict['extended']!=None and "devices" in flavor_dict['extended']:
- #make a copy of original devices
- devices_original=[]
-
- for device in flavor_dict["extended"].get("devices",[]):
- dev={}
- dev.update(device)
- devices_original.append(dev)
- if 'image' in device:
- del device['image']
- if 'image metadata' in device:
- del device['image metadata']
- dev_nb=0
- for index in range(0,len(devices_original)) :
- device=devices_original[index]
- if "image" not in device and "image name" not in device:
- if 'size' in device:
- disk_list.append({'size': device.get('size', default_volume_size)})
- continue
- image_dict={}
- image_dict['name']=device.get('image name',flavor_dict['name']+str(dev_nb)+"-img")
- image_dict['universal_name']=device.get('image name')
- image_dict['description']=flavor_dict['name']+str(dev_nb)+"-img"
- image_dict['location']=device.get('image')
- #image_dict['new_location']=device.get('image location')
- image_dict['checksum']=device.get('image checksum')
- image_metadata_dict = device.get('image metadata', None)
- image_metadata_str = None
- if image_metadata_dict != None:
- image_metadata_str = yaml.safe_dump(image_metadata_dict,default_flow_style=True,width=256)
- image_dict['metadata']=image_metadata_str
- image_mano_id=create_or_use_image(mydb, vims, image_dict, rollback_list, only_create_at_vim=False, return_on_error=return_on_error )
- image_dict["uuid"]=image_mano_id
- image_vim_id=create_or_use_image(mydb, vims, image_dict, rollback_list, only_create_at_vim=True, return_on_error=return_on_error)
-
- #save disk information (image must be based on and size
- disk_list.append({'image_id': image_vim_id, 'size': device.get('size', default_volume_size)})
-
- flavor_dict["extended"]["devices"][index]['imageRef']=image_vim_id
- dev_nb += 1
- if len(flavor_db)>0:
- #check that this vim_id exist in VIM, if not create
- flavor_vim_id=flavor_db[0]["vim_id"]
- try:
- vim.get_flavor(flavor_vim_id)
- continue #flavor exist
- except vimconn.vimconnException:
- pass
- #create flavor at vim
- logger.debug("nfvo.create_or_use_flavor() adding flavor to VIM %s", vim["name"])
- try:
- flavor_vim_id = None
- flavor_vim_id=vim.get_flavor_id_from_data(flavor_dict)
- flavor_create="false"
- except vimconn.vimconnException as e:
- pass
- try:
- if not flavor_vim_id:
- flavor_vim_id = vim.new_flavor(flavor_dict)
- rollback_list.append({"where":"vim", "vim_id": vim_id, "what":"flavor","uuid":flavor_vim_id})
- flavor_created="true"
- except vimconn.vimconnException as e:
- if return_on_error:
- logger.error("Error creating flavor at VIM %s: %s.", vim["name"], str(e))
- raise
- logger.warn("Error creating flavor at VIM %s: %s.", vim["name"], str(e))
- flavor_vim_id = None
- continue
- #if reach here the flavor has been create or exist
- if len(flavor_db)==0:
- #add new vim_id at datacenters_flavors
- extended_devices_yaml = None
- if len(disk_list) > 0:
- extended_devices = dict()
- extended_devices['disks'] = disk_list
- extended_devices_yaml = yaml.safe_dump(extended_devices,default_flow_style=True,width=256)
- mydb.new_row('datacenters_flavors',
- {'datacenter_id':vim_id, 'flavor_id':flavor_mano_id, 'vim_id': flavor_vim_id,
- 'created':flavor_created,'extended': extended_devices_yaml})
- elif flavor_db[0]["vim_id"]!=flavor_vim_id:
- #modify existing vim_id at datacenters_flavors
- mydb.update_rows('datacenters_flavors', UPDATE={'vim_id':flavor_vim_id}, WHERE={'datacenter_id':vim_id, 'flavor_id':flavor_mano_id})
-
- return flavor_vim_id if only_create_at_vim else flavor_mano_id
-
-
-def new_vnf(mydb, tenant_id, vnf_descriptor):
- global global_config
-
- # Step 1. Check the VNF descriptor
- check_vnf_descriptor(vnf_descriptor, vnf_descriptor_version=1)
- # Step 2. Check tenant exist
- vims = {}
- if tenant_id != "any":
- check_tenant(mydb, tenant_id)
- if "tenant_id" in vnf_descriptor["vnf"]:
- if vnf_descriptor["vnf"]["tenant_id"] != tenant_id:
- raise NfvoException("VNF can not have a different tenant owner '{}', must be '{}'".format(vnf_descriptor["vnf"]["tenant_id"], tenant_id),
- HTTP_Unauthorized)
- else:
- vnf_descriptor['vnf']['tenant_id'] = tenant_id
- # Step 3. Get the URL of the VIM from the nfvo_tenant and the datacenter
- if global_config["auto_push_VNF_to_VIMs"]:
- vims = get_vim(mydb, tenant_id)
-
- # Step 4. Review the descriptor and add missing fields
- #print vnf_descriptor
- #logger.debug("Refactoring VNF descriptor with fields: description, public (default: true)")
- vnf_name = vnf_descriptor['vnf']['name']
- vnf_descriptor['vnf']['description'] = vnf_descriptor['vnf'].get("description", vnf_name)
- if "physical" in vnf_descriptor['vnf']:
- del vnf_descriptor['vnf']['physical']
- #print vnf_descriptor
-
- # Step 6. For each VNFC in the descriptor, flavors and images are created in the VIM
- logger.debug('BEGIN creation of VNF "%s"' % vnf_name)
- logger.debug("VNF %s: consisting of %d VNFC(s)" % (vnf_name,len(vnf_descriptor['vnf']['VNFC'])))
-
- #For each VNFC, we add it to the VNFCDict and we create a flavor.
- VNFCDict = {} # Dictionary, key: VNFC name, value: dict with the relevant information to create the VNF and VMs in the MANO database
- rollback_list = [] # It will contain the new images created in mano. It is used for rollback
- try:
- logger.debug("Creating additional disk images and new flavors in the VIM for each VNFC")
- for vnfc in vnf_descriptor['vnf']['VNFC']:
- VNFCitem={}
- VNFCitem["name"] = vnfc['name']
- VNFCitem["description"] = vnfc.get("description", 'VM %s of the VNF %s' %(vnfc['name'],vnf_name))
-
- #print "Flavor name: %s. Description: %s" % (VNFCitem["name"]+"-flv", VNFCitem["description"])
-
- myflavorDict = {}
- myflavorDict["name"] = vnfc['name']+"-flv" #Maybe we could rename the flavor by using the field "image name" if exists
- myflavorDict["description"] = VNFCitem["description"]
- myflavorDict["ram"] = vnfc.get("ram", 0)
- myflavorDict["vcpus"] = vnfc.get("vcpus", 0)
- myflavorDict["disk"] = vnfc.get("disk", 1)
- myflavorDict["extended"] = {}
-
- devices = vnfc.get("devices")
- if devices != None:
- myflavorDict["extended"]["devices"] = devices
-
- # TODO:
- # Mapping from processor models to rankings should be available somehow in the NFVO. They could be taken from VIM or directly from a new database table
- # Another option is that the processor in the VNF descriptor specifies directly the ranking of the host
-
- # Previous code has been commented
- #if vnfc['processor']['model'] == "Intel(R) Xeon(R) CPU E5-4620 0 @ 2.20GHz" :
- # myflavorDict["flavor"]['extended']['processor_ranking'] = 200
- #elif vnfc['processor']['model'] == "Intel(R) Xeon(R) CPU E5-2697 v2 @ 2.70GHz" :
- # myflavorDict["flavor"]['extended']['processor_ranking'] = 300
- #else:
- # result2, message = rollback(myvim, myvimURL, myvim_tenant, flavorList, imageList)
- # if result2:
- # print "Error creating flavor: unknown processor model. Rollback successful."
- # return -HTTP_Bad_Request, "Error creating flavor: unknown processor model. Rollback successful."
- # else:
- # return -HTTP_Bad_Request, "Error creating flavor: unknown processor model. Rollback fail: you need to access VIM and delete the following %s" % message
- myflavorDict['extended']['processor_ranking'] = 100 #Hardcoded value, while we decide when the mapping is done
-
- if 'numas' in vnfc and len(vnfc['numas'])>0:
- myflavorDict['extended']['numas'] = vnfc['numas']
-
- #print myflavorDict
-
- # Step 6.2 New flavors are created in the VIM
- flavor_id = create_or_use_flavor(mydb, vims, myflavorDict, rollback_list)
-
- #print "Flavor id for VNFC %s: %s" % (vnfc['name'],flavor_id)
- VNFCitem["flavor_id"] = flavor_id
- VNFCDict[vnfc['name']] = VNFCitem
-
- logger.debug("Creating new images in the VIM for each VNFC")
- # Step 6.3 New images are created in the VIM
- #For each VNFC, we must create the appropriate image.
- #This "for" loop might be integrated with the previous one
- #In case this integration is made, the VNFCDict might become a VNFClist.
- for vnfc in vnf_descriptor['vnf']['VNFC']:
- #print "Image name: %s. Description: %s" % (vnfc['name']+"-img", VNFCDict[vnfc['name']]['description'])
- image_dict={}
- image_dict['name']=vnfc.get('image name',vnf_name+"-"+vnfc['name']+"-img")
- image_dict['universal_name']=vnfc.get('image name')
- image_dict['description']=vnfc.get('image name', VNFCDict[vnfc['name']]['description'])
- image_dict['location']=vnfc.get('VNFC image')
- #image_dict['new_location']=vnfc.get('image location')
- image_dict['checksum']=vnfc.get('image checksum')
- image_metadata_dict = vnfc.get('image metadata', None)
- image_metadata_str = None
- if image_metadata_dict is not None:
- image_metadata_str = yaml.safe_dump(image_metadata_dict,default_flow_style=True,width=256)
- image_dict['metadata']=image_metadata_str
- #print "create_or_use_image", mydb, vims, image_dict, rollback_list
- image_id = create_or_use_image(mydb, vims, image_dict, rollback_list)
- #print "Image id for VNFC %s: %s" % (vnfc['name'],image_id)
- VNFCDict[vnfc['name']]["image_id"] = image_id
- VNFCDict[vnfc['name']]["image_path"] = vnfc.get('VNFC image')
- if vnfc.get("boot-data"):
- VNFCDict[vnfc['name']]["boot_data"] = yaml.safe_dump(vnfc["boot-data"], default_flow_style=True, width=256)
-
-
- # Step 7. Storing the VNF descriptor in the repository
- if "descriptor" not in vnf_descriptor["vnf"]:
- vnf_descriptor["vnf"]["descriptor"] = yaml.safe_dump(vnf_descriptor, indent=4, explicit_start=True, default_flow_style=False)
-
- # Step 8. Adding the VNF to the NFVO DB
- vnf_id = mydb.new_vnf_as_a_whole(tenant_id,vnf_name,vnf_descriptor,VNFCDict)
- return vnf_id
- except (db_base_Exception, vimconn.vimconnException, KeyError) as e:
- _, message = rollback(mydb, vims, rollback_list)
- if isinstance(e, db_base_Exception):
- error_text = "Exception at database"
- elif isinstance(e, KeyError):
- error_text = "KeyError exception "
- e.http_code = HTTP_Internal_Server_Error
- else:
- error_text = "Exception at VIM"
- error_text += " {} {}. {}".format(type(e).__name__, str(e), message)
- #logger.error("start_scenario %s", error_text)
- raise NfvoException(error_text, e.http_code)
-
-
-def new_vnf_v02(mydb, tenant_id, vnf_descriptor):
- global global_config
-
- # Step 1. Check the VNF descriptor
- check_vnf_descriptor(vnf_descriptor, vnf_descriptor_version=2)
- # Step 2. Check tenant exist
- vims = {}
- if tenant_id != "any":
- check_tenant(mydb, tenant_id)
- if "tenant_id" in vnf_descriptor["vnf"]:
- if vnf_descriptor["vnf"]["tenant_id"] != tenant_id:
- raise NfvoException("VNF can not have a different tenant owner '{}', must be '{}'".format(vnf_descriptor["vnf"]["tenant_id"], tenant_id),
- HTTP_Unauthorized)
- else:
- vnf_descriptor['vnf']['tenant_id'] = tenant_id
- # Step 3. Get the URL of the VIM from the nfvo_tenant and the datacenter
- if global_config["auto_push_VNF_to_VIMs"]:
- vims = get_vim(mydb, tenant_id)
-
- # Step 4. Review the descriptor and add missing fields
- #print vnf_descriptor
- #logger.debug("Refactoring VNF descriptor with fields: description, public (default: true)")
- vnf_name = vnf_descriptor['vnf']['name']
- vnf_descriptor['vnf']['description'] = vnf_descriptor['vnf'].get("description", vnf_name)
- if "physical" in vnf_descriptor['vnf']:
- del vnf_descriptor['vnf']['physical']
- #print vnf_descriptor
-
- # Step 6. For each VNFC in the descriptor, flavors and images are created in the VIM
- logger.debug('BEGIN creation of VNF "%s"' % vnf_name)
- logger.debug("VNF %s: consisting of %d VNFC(s)" % (vnf_name,len(vnf_descriptor['vnf']['VNFC'])))
-
- #For each VNFC, we add it to the VNFCDict and we create a flavor.
- VNFCDict = {} # Dictionary, key: VNFC name, value: dict with the relevant information to create the VNF and VMs in the MANO database
- rollback_list = [] # It will contain the new images created in mano. It is used for rollback
- try:
- logger.debug("Creating additional disk images and new flavors in the VIM for each VNFC")
- for vnfc in vnf_descriptor['vnf']['VNFC']:
- VNFCitem={}
- VNFCitem["name"] = vnfc['name']
- VNFCitem["description"] = vnfc.get("description", 'VM %s of the VNF %s' %(vnfc['name'],vnf_name))
-
- #print "Flavor name: %s. Description: %s" % (VNFCitem["name"]+"-flv", VNFCitem["description"])
-
- myflavorDict = {}
- myflavorDict["name"] = vnfc['name']+"-flv" #Maybe we could rename the flavor by using the field "image name" if exists
- myflavorDict["description"] = VNFCitem["description"]
- myflavorDict["ram"] = vnfc.get("ram", 0)
- myflavorDict["vcpus"] = vnfc.get("vcpus", 0)
- myflavorDict["disk"] = vnfc.get("disk", 1)
- myflavorDict["extended"] = {}
-
- devices = vnfc.get("devices")
- if devices != None:
- myflavorDict["extended"]["devices"] = devices
-
- # TODO:
- # Mapping from processor models to rankings should be available somehow in the NFVO. They could be taken from VIM or directly from a new database table
- # Another option is that the processor in the VNF descriptor specifies directly the ranking of the host
-
- # Previous code has been commented
- #if vnfc['processor']['model'] == "Intel(R) Xeon(R) CPU E5-4620 0 @ 2.20GHz" :
- # myflavorDict["flavor"]['extended']['processor_ranking'] = 200
- #elif vnfc['processor']['model'] == "Intel(R) Xeon(R) CPU E5-2697 v2 @ 2.70GHz" :
- # myflavorDict["flavor"]['extended']['processor_ranking'] = 300
- #else:
- # result2, message = rollback(myvim, myvimURL, myvim_tenant, flavorList, imageList)
- # if result2:
- # print "Error creating flavor: unknown processor model. Rollback successful."
- # return -HTTP_Bad_Request, "Error creating flavor: unknown processor model. Rollback successful."
- # else:
- # return -HTTP_Bad_Request, "Error creating flavor: unknown processor model. Rollback fail: you need to access VIM and delete the following %s" % message
- myflavorDict['extended']['processor_ranking'] = 100 #Hardcoded value, while we decide when the mapping is done
-
- if 'numas' in vnfc and len(vnfc['numas'])>0:
- myflavorDict['extended']['numas'] = vnfc['numas']
-
- #print myflavorDict
-
- # Step 6.2 New flavors are created in the VIM
- flavor_id = create_or_use_flavor(mydb, vims, myflavorDict, rollback_list)
-
- #print "Flavor id for VNFC %s: %s" % (vnfc['name'],flavor_id)
- VNFCitem["flavor_id"] = flavor_id
- VNFCDict[vnfc['name']] = VNFCitem
-
- logger.debug("Creating new images in the VIM for each VNFC")
- # Step 6.3 New images are created in the VIM
- #For each VNFC, we must create the appropriate image.
- #This "for" loop might be integrated with the previous one
- #In case this integration is made, the VNFCDict might become a VNFClist.
- for vnfc in vnf_descriptor['vnf']['VNFC']:
- #print "Image name: %s. Description: %s" % (vnfc['name']+"-img", VNFCDict[vnfc['name']]['description'])
- image_dict={}
- image_dict['name']=vnfc.get('image name',vnf_name+"-"+vnfc['name']+"-img")
- image_dict['universal_name']=vnfc.get('image name')
- image_dict['description']=vnfc.get('image name', VNFCDict[vnfc['name']]['description'])
- image_dict['location']=vnfc.get('VNFC image')
- #image_dict['new_location']=vnfc.get('image location')
- image_dict['checksum']=vnfc.get('image checksum')
- image_metadata_dict = vnfc.get('image metadata', None)
- image_metadata_str = None
- if image_metadata_dict is not None:
- image_metadata_str = yaml.safe_dump(image_metadata_dict,default_flow_style=True,width=256)
- image_dict['metadata']=image_metadata_str
- #print "create_or_use_image", mydb, vims, image_dict, rollback_list
- image_id = create_or_use_image(mydb, vims, image_dict, rollback_list)
- #print "Image id for VNFC %s: %s" % (vnfc['name'],image_id)
- VNFCDict[vnfc['name']]["image_id"] = image_id
- VNFCDict[vnfc['name']]["image_path"] = vnfc.get('VNFC image')
- if vnfc.get("boot-data"):
- VNFCDict[vnfc['name']]["boot_data"] = yaml.safe_dump(vnfc["boot-data"], default_flow_style=True, width=256)
-
- # Step 7. Storing the VNF descriptor in the repository
- if "descriptor" not in vnf_descriptor["vnf"]:
- vnf_descriptor["vnf"]["descriptor"] = yaml.safe_dump(vnf_descriptor, indent=4, explicit_start=True, default_flow_style=False)
-
- # Step 8. Adding the VNF to the NFVO DB
- vnf_id = mydb.new_vnf_as_a_whole2(tenant_id,vnf_name,vnf_descriptor,VNFCDict)
- return vnf_id
- except (db_base_Exception, vimconn.vimconnException, KeyError) as e:
- _, message = rollback(mydb, vims, rollback_list)
- if isinstance(e, db_base_Exception):
- error_text = "Exception at database"
- elif isinstance(e, KeyError):
- error_text = "KeyError exception "
- e.http_code = HTTP_Internal_Server_Error
- else:
- error_text = "Exception at VIM"
- error_text += " {} {}. {}".format(type(e).__name__, str(e), message)
- #logger.error("start_scenario %s", error_text)
- raise NfvoException(error_text, e.http_code)
-
-
-def get_vnf_id(mydb, tenant_id, vnf_id):
- #check valid tenant_id
- check_tenant(mydb, tenant_id)
- #obtain data
- where_or = {}
- if tenant_id != "any":
- where_or["tenant_id"] = tenant_id
- where_or["public"] = True
- vnf = mydb.get_table_by_uuid_name('vnfs', vnf_id, "VNF", WHERE_OR=where_or, WHERE_AND_OR="AND")
-
- vnf_id=vnf["uuid"]
- filter_keys = ('uuid','name','description','public', "tenant_id", "created_at")
- filtered_content = dict( (k,v) for k,v in vnf.iteritems() if k in filter_keys )
- #change_keys_http2db(filtered_content, http2db_vnf, reverse=True)
- data={'vnf' : filtered_content}
- #GET VM
- content = mydb.get_rows(FROM='vnfs join vms on vnfs.uuid=vms.vnf_id',
- SELECT=('vms.uuid as uuid','vms.name as name', 'vms.description as description', 'boot_data'),
- WHERE={'vnfs.uuid': vnf_id} )
- if len(content)==0:
- raise NfvoException("vnf '{}' not found".format(vnf_id), HTTP_Not_Found)
- # change boot_data into boot-data
- for vm in content:
- if vm.get("boot_data"):
- vm["boot-data"] = yaml.safe_load(vm["boot_data"])
- del vm["boot_data"]
-
- data['vnf']['VNFC'] = content
- #TODO: GET all the information from a VNFC and include it in the output.
-
- #GET NET
- content = mydb.get_rows(FROM='vnfs join nets on vnfs.uuid=nets.vnf_id',
- SELECT=('nets.uuid as uuid','nets.name as name','nets.description as description', 'nets.type as type', 'nets.multipoint as multipoint'),
- WHERE={'vnfs.uuid': vnf_id} )
- data['vnf']['nets'] = content
-
- #GET ip-profile for each net
- for net in data['vnf']['nets']:
- ipprofiles = mydb.get_rows(FROM='ip_profiles',
- SELECT=('ip_version','subnet_address','gateway_address','dns_address','dhcp_enabled','dhcp_start_address','dhcp_count'),
- WHERE={'net_id': net["uuid"]} )
- if len(ipprofiles)==1:
- net["ip_profile"] = ipprofiles[0]
- elif len(ipprofiles)>1:
- raise NfvoException("More than one ip-profile found with this criteria: net_id='{}'".format(net['uuid']), HTTP_Bad_Request)
-
-
- #TODO: For each net, GET its elements and relevant info per element (VNFC, iface, ip_address) and include them in the output.
-
- #GET External Interfaces
- content = mydb.get_rows(FROM='vnfs join vms on vnfs.uuid=vms.vnf_id join interfaces on vms.uuid=interfaces.vm_id',\
- SELECT=('interfaces.uuid as uuid','interfaces.external_name as external_name', 'vms.name as vm_name', 'interfaces.vm_id as vm_id', \
- 'interfaces.internal_name as internal_name', 'interfaces.type as type', 'interfaces.vpci as vpci','interfaces.bw as bw'),\
- WHERE={'vnfs.uuid': vnf_id},
- WHERE_NOT={'interfaces.external_name': None} )
- #print content
- data['vnf']['external-connections'] = content
-
- return data
-
-
-def delete_vnf(mydb,tenant_id,vnf_id,datacenter=None,vim_tenant=None):
- # Check tenant exist
- if tenant_id != "any":
- check_tenant(mydb, tenant_id)
- # Get the URL of the VIM from the nfvo_tenant and the datacenter
- vims = get_vim(mydb, tenant_id)
- else:
- vims={}
-
- # Checking if it is a valid uuid and, if not, getting the uuid assuming that the name was provided"
- where_or = {}
- if tenant_id != "any":
- where_or["tenant_id"] = tenant_id
- where_or["public"] = True
- vnf = mydb.get_table_by_uuid_name('vnfs', vnf_id, "VNF", WHERE_OR=where_or, WHERE_AND_OR="AND")
- vnf_id = vnf["uuid"]
-
- # "Getting the list of flavors and tenants of the VNF"
- flavorList = get_flavorlist(mydb, vnf_id)
- if len(flavorList)==0:
- logger.warn("delete_vnf error. No flavors found for the VNF id '%s'", vnf_id)
-
- imageList = get_imagelist(mydb, vnf_id)
- if len(imageList)==0:
- logger.warn( "delete_vnf error. No images found for the VNF id '%s'", vnf_id)
-
- deleted = mydb.delete_row_by_id('vnfs', vnf_id)
- if deleted == 0:
- raise NfvoException("vnf '{}' not found".format(vnf_id), HTTP_Not_Found)
-
- undeletedItems = []
- for flavor in flavorList:
- #check if flavor is used by other vnf
- try:
- c = mydb.get_rows(FROM='vms', WHERE={'flavor_id':flavor} )
- if len(c) > 0:
- logger.debug("Flavor '%s' not deleted because it is being used by another VNF", flavor)
- continue
- #flavor not used, must be deleted
- #delelte at VIM
- c = mydb.get_rows(FROM='datacenters_flavors', WHERE={'flavor_id':flavor})
- for flavor_vim in c:
- if flavor_vim["datacenter_id"] not in vims:
- continue
- if flavor_vim['created']=='false': #skip this flavor because not created by openmano
- continue
- myvim=vims[ flavor_vim["datacenter_id"] ]
- try:
- myvim.delete_flavor(flavor_vim["vim_id"])
- except vimconn.vimconnNotFoundException as e:
- logger.warn("VIM flavor %s not exist at datacenter %s", flavor_vim["vim_id"], flavor_vim["datacenter_id"] )
- except vimconn.vimconnException as e:
- logger.error("Not possible to delete VIM flavor %s from datacenter %s: %s %s",
- flavor_vim["vim_id"], flavor_vim["datacenter_id"], type(e).__name__, str(e))
- undeletedItems.append("flavor {} from VIM {}".format(flavor_vim["vim_id"], flavor_vim["datacenter_id"] ))
- #delete flavor from Database, using table flavors and with cascade foreign key also at datacenters_flavors
- mydb.delete_row_by_id('flavors', flavor)
- except db_base_Exception as e:
- logger.error("delete_vnf_error. Not possible to get flavor details and delete '%s'. %s", flavor, str(e))
- undeletedItems.append("flavor %s" % flavor)
-
-
- for image in imageList:
- try:
- #check if image is used by other vnf
- c = mydb.get_rows(FROM='vms', WHERE={'image_id':image} )
- if len(c) > 0:
- logger.debug("Image '%s' not deleted because it is being used by another VNF", image)
- continue
- #image not used, must be deleted
- #delelte at VIM
- c = mydb.get_rows(FROM='datacenters_images', WHERE={'image_id':image})
- for image_vim in c:
- if image_vim["datacenter_id"] not in vims:
- continue
- if image_vim['created']=='false': #skip this image because not created by openmano
- continue
- myvim=vims[ image_vim["datacenter_id"] ]
- try:
- myvim.delete_image(image_vim["vim_id"])
- except vimconn.vimconnNotFoundException as e:
- logger.warn("VIM image %s not exist at datacenter %s", image_vim["vim_id"], image_vim["datacenter_id"] )
- except vimconn.vimconnException as e:
- logger.error("Not possible to delete VIM image %s from datacenter %s: %s %s",
- image_vim["vim_id"], image_vim["datacenter_id"], type(e).__name__, str(e))
- undeletedItems.append("image {} from VIM {}".format(image_vim["vim_id"], image_vim["datacenter_id"] ))
- #delete image from Database, using table images and with cascade foreign key also at datacenters_images
- mydb.delete_row_by_id('images', image)
- except db_base_Exception as e:
- logger.error("delete_vnf_error. Not possible to get image details and delete '%s'. %s", image, str(e))
- undeletedItems.append("image %s" % image)
-
- return vnf_id + " " + vnf["name"]
- #if undeletedItems:
- # return "delete_vnf. Undeleted: %s" %(undeletedItems)
-
-
-def get_hosts_info(mydb, nfvo_tenant_id, datacenter_name=None):
- result, vims = get_vim(mydb, nfvo_tenant_id, None, datacenter_name)
- if result < 0:
- return result, vims
- elif result == 0:
- return -HTTP_Not_Found, "datacenter '%s' not found" % datacenter_name
- myvim = vims.values()[0]
- result,servers = myvim.get_hosts_info()
- if result < 0:
- return result, servers
- topology = {'name':myvim['name'] , 'servers': servers}
- return result, topology
-
-
-def get_hosts(mydb, nfvo_tenant_id):
- vims = get_vim(mydb, nfvo_tenant_id)
- if len(vims) == 0:
- raise NfvoException("No datacenter found for tenant '{}'".format(str(nfvo_tenant_id)), HTTP_Not_Found)
- elif len(vims)>1:
- #print "nfvo.datacenter_action() error. Several datacenters found"
- raise NfvoException("More than one datacenters found, try to identify with uuid", HTTP_Conflict)
- myvim = vims.values()[0]
- try:
- hosts = myvim.get_hosts()
- logger.debug('VIM hosts response: '+ yaml.safe_dump(hosts, indent=4, default_flow_style=False))
-
- datacenter = {'Datacenters': [ {'name':myvim['name'],'servers':[]} ] }
- for host in hosts:
- server={'name':host['name'], 'vms':[]}
- for vm in host['instances']:
- #get internal name and model
- try:
- c = mydb.get_rows(SELECT=('name',), FROM='instance_vms as iv join vms on iv.vm_id=vms.uuid',\
- WHERE={'vim_vm_id':vm['id']} )
- if len(c) == 0:
- logger.warn("nfvo.get_hosts virtual machine at VIM '{}' not found at tidnfvo".format(vm['id']))
- continue
- server['vms'].append( {'name':vm['name'] , 'model':c[0]['name']} )
-
- except db_base_Exception as e:
- logger.warn("nfvo.get_hosts virtual machine at VIM '{}' error {}".format(vm['id'], str(e)))
- datacenter['Datacenters'][0]['servers'].append(server)
- #return -400, "en construccion"
-
- #print 'datacenters '+ json.dumps(datacenter, indent=4)
- return datacenter
- except vimconn.vimconnException as e:
- raise NfvoException("Not possible to get_host_list from VIM: {}".format(str(e)), e.http_code)
-
-
-def new_scenario(mydb, tenant_id, topo):
-
-# result, vims = get_vim(mydb, tenant_id)
-# if result < 0:
-# return result, vims
-#1: parse input
- if tenant_id != "any":
- check_tenant(mydb, tenant_id)
- if "tenant_id" in topo:
- if topo["tenant_id"] != tenant_id:
- raise NfvoException("VNF can not have a different tenant owner '{}', must be '{}'".format(topo["tenant_id"], tenant_id),
- HTTP_Unauthorized)
- else:
- tenant_id=None
-
-#1.1: get VNFs and external_networks (other_nets).
- vnfs={}
- other_nets={} #external_networks, bridge_networks and data_networkds
- nodes = topo['topology']['nodes']
- for k in nodes.keys():
- if nodes[k]['type'] == 'VNF':
- vnfs[k] = nodes[k]
- vnfs[k]['ifaces'] = {}
- elif nodes[k]['type'] == 'other_network' or nodes[k]['type'] == 'external_network':
- other_nets[k] = nodes[k]
- other_nets[k]['external']=True
- elif nodes[k]['type'] == 'network':
- other_nets[k] = nodes[k]
- other_nets[k]['external']=False
-
-
-#1.2: Check that VNF are present at database table vnfs. Insert uuid, description and external interfaces
- for name,vnf in vnfs.items():
- where={}
- where_or={"tenant_id": tenant_id, 'public': "true"}
- error_text = ""
- error_pos = "'topology':'nodes':'" + name + "'"
- if 'vnf_id' in vnf:
- error_text += " 'vnf_id' " + vnf['vnf_id']
- where['uuid'] = vnf['vnf_id']
- if 'VNF model' in vnf:
- error_text += " 'VNF model' " + vnf['VNF model']
- where['name'] = vnf['VNF model']
- if len(where) == 0:
- raise NfvoException("Descriptor need a 'vnf_id' or 'VNF model' field at " + error_pos, HTTP_Bad_Request)
-
- vnf_db = mydb.get_rows(SELECT=('uuid','name','description'),
- FROM='vnfs',
- WHERE=where,
- WHERE_OR=where_or,
- WHERE_AND_OR="AND")
- if len(vnf_db)==0:
- raise NfvoException("unknown" + error_text + " at " + error_pos, HTTP_Not_Found)
- elif len(vnf_db)>1:
- raise NfvoException("more than one" + error_text + " at " + error_pos + " Concrete with 'vnf_id'", HTTP_Conflict)
- vnf['uuid']=vnf_db[0]['uuid']
- vnf['description']=vnf_db[0]['description']
- #get external interfaces
- ext_ifaces = mydb.get_rows(SELECT=('external_name as name','i.uuid as iface_uuid', 'i.type as type'),
- FROM='vnfs join vms on vnfs.uuid=vms.vnf_id join interfaces as i on vms.uuid=i.vm_id',
- WHERE={'vnfs.uuid':vnf['uuid']}, WHERE_NOT={'external_name':None} )
- for ext_iface in ext_ifaces:
- vnf['ifaces'][ ext_iface['name'] ] = {'uuid':ext_iface['iface_uuid'], 'type':ext_iface['type']}
-
-#1.4 get list of connections
- conections = topo['topology']['connections']
- conections_list = []
- conections_list_name = []
- for k in conections.keys():
- if type(conections[k]['nodes'])==dict: #dict with node:iface pairs
- ifaces_list = conections[k]['nodes'].items()
- elif type(conections[k]['nodes'])==list: #list with dictionary
- ifaces_list=[]
- conection_pair_list = map(lambda x: x.items(), conections[k]['nodes'] )
- for k2 in conection_pair_list:
- ifaces_list += k2
-
- con_type = conections[k].get("type", "link")
- if con_type != "link":
- if k in other_nets:
- raise NfvoException("Format error. Reapeted network name at 'topology':'connections':'{}'".format(str(k)), HTTP_Bad_Request)
- other_nets[k] = {'external': False}
- if conections[k].get("graph"):
- other_nets[k]["graph"] = conections[k]["graph"]
- ifaces_list.append( (k, None) )
-
-
- if con_type == "external_network":
- other_nets[k]['external'] = True
- if conections[k].get("model"):
- other_nets[k]["model"] = conections[k]["model"]
- else:
- other_nets[k]["model"] = k
- if con_type == "dataplane_net" or con_type == "bridge_net":
- other_nets[k]["model"] = con_type
-
- conections_list_name.append(k)
- conections_list.append(set(ifaces_list)) #from list to set to operate as a set (this conversion removes elements that are repeated in a list)
- #print set(ifaces_list)
- #check valid VNF and iface names
- for iface in ifaces_list:
- if iface[0] not in vnfs and iface[0] not in other_nets :
- raise NfvoException("format error. Invalid VNF name at 'topology':'connections':'{}':'nodes':'{}'".format(
- str(k), iface[0]), HTTP_Not_Found)
- if iface[0] in vnfs and iface[1] not in vnfs[ iface[0] ]['ifaces']:
- raise NfvoException("format error. Invalid interface name at 'topology':'connections':'{}':'nodes':'{}':'{}'".format(
- str(k), iface[0], iface[1]), HTTP_Not_Found)
-
-#1.5 unify connections from the pair list to a consolidated list
- index=0
- while index < len(conections_list):
- index2 = index+1
- while index2 < len(conections_list):
- if len(conections_list[index] & conections_list[index2])>0: #common interface, join nets
- conections_list[index] |= conections_list[index2]
- del conections_list[index2]
- del conections_list_name[index2]
- else:
- index2 += 1
- conections_list[index] = list(conections_list[index]) # from set to list again
- index += 1
- #for k in conections_list:
- # print k
-
-
-
-#1.6 Delete non external nets
-# for k in other_nets.keys():
-# if other_nets[k]['model']=='bridge' or other_nets[k]['model']=='dataplane_net' or other_nets[k]['model']=='bridge_net':
-# for con in conections_list:
-# delete_indexes=[]
-# for index in range(0,len(con)):
-# if con[index][0] == k: delete_indexes.insert(0,index) #order from higher to lower
-# for index in delete_indexes:
-# del con[index]
-# del other_nets[k]
-#1.7: Check external_ports are present at database table datacenter_nets
- for k,net in other_nets.items():
- error_pos = "'topology':'nodes':'" + k + "'"
- if net['external']==False:
- if 'name' not in net:
- net['name']=k
- if 'model' not in net:
- raise NfvoException("needed a 'model' at " + error_pos, HTTP_Bad_Request)
- if net['model']=='bridge_net':
- net['type']='bridge';
- elif net['model']=='dataplane_net':
- net['type']='data';
- else:
- raise NfvoException("unknown 'model' '"+ net['model'] +"' at " + error_pos, HTTP_Not_Found)
- else: #external
-#IF we do not want to check that external network exist at datacenter
- pass
-#ELSE
-# error_text = ""
-# WHERE_={}
-# if 'net_id' in net:
-# error_text += " 'net_id' " + net['net_id']
-# WHERE_['uuid'] = net['net_id']
-# if 'model' in net:
-# error_text += " 'model' " + net['model']
-# WHERE_['name'] = net['model']
-# if len(WHERE_) == 0:
-# return -HTTP_Bad_Request, "needed a 'net_id' or 'model' at " + error_pos
-# r,net_db = mydb.get_table(SELECT=('uuid','name','description','type','shared'),
-# FROM='datacenter_nets', WHERE=WHERE_ )
-# if r<0:
-# print "nfvo.new_scenario Error getting datacenter_nets",r,net_db
-# elif r==0:
-# print "nfvo.new_scenario Error" +error_text+ " is not present at database"
-# return -HTTP_Bad_Request, "unknown " +error_text+ " at " + error_pos
-# elif r>1:
-# print "nfvo.new_scenario Error more than one external_network for " +error_text+ " is present at database"
-# return -HTTP_Bad_Request, "more than one external_network for " +error_text+ "at "+ error_pos + " Concrete with 'net_id'"
-# other_nets[k].update(net_db[0])
-#ENDIF
- net_list={}
- net_nb=0 #Number of nets
- for con in conections_list:
- #check if this is connected to a external net
- other_net_index=-1
- #print
- #print "con", con
- for index in range(0,len(con)):
- #check if this is connected to a external net
- for net_key in other_nets.keys():
- if con[index][0]==net_key:
- if other_net_index>=0:
- error_text="There is some interface connected both to net '%s' and net '%s'" % (con[other_net_index][0], net_key)
- #print "nfvo.new_scenario " + error_text
- raise NfvoException(error_text, HTTP_Bad_Request)
- else:
- other_net_index = index
- net_target = net_key
- break
- #print "other_net_index", other_net_index
- try:
- if other_net_index>=0:
- del con[other_net_index]
-#IF we do not want to check that external network exist at datacenter
- if other_nets[net_target]['external'] :
- if "name" not in other_nets[net_target]:
- other_nets[net_target]['name'] = other_nets[net_target]['model']
- if other_nets[net_target]["type"] == "external_network":
- if vnfs[ con[0][0] ]['ifaces'][ con[0][1] ]["type"] == "data":
- other_nets[net_target]["type"] = "data"
- else:
- other_nets[net_target]["type"] = "bridge"
-#ELSE
-# if other_nets[net_target]['external'] :
-# type_='data' if len(con)>1 else 'ptp' #an external net is connected to a external port, so it is ptp if only one connection is done to this net
-# if type_=='data' and other_nets[net_target]['type']=="ptp":
-# error_text = "Error connecting %d nodes on a not multipoint net %s" % (len(con), net_target)
-# print "nfvo.new_scenario " + error_text
-# return -HTTP_Bad_Request, error_text
-#ENDIF
- for iface in con:
- vnfs[ iface[0] ]['ifaces'][ iface[1] ]['net_key'] = net_target
- else:
- #create a net
- net_type_bridge=False
- net_type_data=False
- net_target = "__-__net"+str(net_nb)
- net_list[net_target] = {'name': conections_list_name[net_nb], #"net-"+str(net_nb),
- 'description':"net-%s in scenario %s" %(net_nb,topo['name']),
- 'external':False}
- for iface in con:
- vnfs[ iface[0] ]['ifaces'][ iface[1] ]['net_key'] = net_target
- iface_type = vnfs[ iface[0] ]['ifaces'][ iface[1] ]['type']
- if iface_type=='mgmt' or iface_type=='bridge':
- net_type_bridge = True
- else:
- net_type_data = True
- if net_type_bridge and net_type_data:
- error_text = "Error connection interfaces of bridge type with data type. Firs node %s, iface %s" % (iface[0], iface[1])
- #print "nfvo.new_scenario " + error_text
- raise NfvoException(error_text, HTTP_Bad_Request)
- elif net_type_bridge:
- type_='bridge'
- else:
- type_='data' if len(con)>2 else 'ptp'
- net_list[net_target]['type'] = type_
- net_nb+=1
- except Exception:
- error_text = "Error connection node %s : %s does not match any VNF or interface" % (iface[0], iface[1])
- #print "nfvo.new_scenario " + error_text
- #raise e
- raise NfvoException(error_text, HTTP_Bad_Request)
-
-#1.8: Connect to management net all not already connected interfaces of type 'mgmt'
- #1.8.1 obtain management net
- mgmt_net = mydb.get_rows(SELECT=('uuid','name','description','type','shared'),
- FROM='datacenter_nets', WHERE={'name':'mgmt'} )
- #1.8.2 check all interfaces from all vnfs
- if len(mgmt_net)>0:
- add_mgmt_net = False
- for vnf in vnfs.values():
- for iface in vnf['ifaces'].values():
- if iface['type']=='mgmt' and 'net_key' not in iface:
- #iface not connected
- iface['net_key'] = 'mgmt'
- add_mgmt_net = True
- if add_mgmt_net and 'mgmt' not in net_list:
- net_list['mgmt']=mgmt_net[0]
- net_list['mgmt']['external']=True
- net_list['mgmt']['graph']={'visible':False}
-
- net_list.update(other_nets)
- #print
- #print 'net_list', net_list
- #print
- #print 'vnfs', vnfs
- #print
-
-#2: insert scenario. filling tables scenarios,sce_vnfs,sce_interfaces,sce_nets
- c = mydb.new_scenario( { 'vnfs':vnfs, 'nets':net_list,
- 'tenant_id':tenant_id, 'name':topo['name'],
- 'description':topo.get('description',topo['name']),
- 'public': topo.get('public', False)
- })
-
- return c
-
-
-def new_scenario_v02(mydb, tenant_id, scenario_dict, version):
- """ This creates a new scenario for version 0.2 and 0.3"""
- scenario = scenario_dict["scenario"]
- if tenant_id != "any":
- check_tenant(mydb, tenant_id)
- if "tenant_id" in scenario:
- if scenario["tenant_id"] != tenant_id:
- # print "nfvo.new_scenario_v02() tenant '%s' not found" % tenant_id
- raise NfvoException("VNF can not have a different tenant owner '{}', must be '{}'".format(
- scenario["tenant_id"], tenant_id), HTTP_Unauthorized)
- else:
- tenant_id=None
-
- # 1: Check that VNF are present at database table vnfs and update content into scenario dict
- for name,vnf in scenario["vnfs"].iteritems():
- where={}
- where_or={"tenant_id": tenant_id, 'public': "true"}
- error_text = ""
- error_pos = "'scenario':'vnfs':'" + name + "'"
- if 'vnf_id' in vnf:
- error_text += " 'vnf_id' " + vnf['vnf_id']
- where['uuid'] = vnf['vnf_id']
- if 'vnf_name' in vnf:
- error_text += " 'vnf_name' " + vnf['vnf_name']
- where['name'] = vnf['vnf_name']
- if len(where) == 0:
- raise NfvoException("Needed a 'vnf_id' or 'vnf_name' at " + error_pos, HTTP_Bad_Request)
- vnf_db = mydb.get_rows(SELECT=('uuid', 'name', 'description'),
- FROM='vnfs',
- WHERE=where,
- WHERE_OR=where_or,
- WHERE_AND_OR="AND")
- if len(vnf_db) == 0:
- raise NfvoException("Unknown" + error_text + " at " + error_pos, HTTP_Not_Found)
- elif len(vnf_db) > 1:
- raise NfvoException("More than one" + error_text + " at " + error_pos + " Concrete with 'vnf_id'", HTTP_Conflict)
- vnf['uuid'] = vnf_db[0]['uuid']
- vnf['description'] = vnf_db[0]['description']
- vnf['ifaces'] = {}
- # get external interfaces
- ext_ifaces = mydb.get_rows(SELECT=('external_name as name', 'i.uuid as iface_uuid', 'i.type as type'),
- FROM='vnfs join vms on vnfs.uuid=vms.vnf_id join interfaces as i on vms.uuid=i.vm_id',
- WHERE={'vnfs.uuid':vnf['uuid']}, WHERE_NOT={'external_name': None} )
- for ext_iface in ext_ifaces:
- vnf['ifaces'][ ext_iface['name'] ] = {'uuid':ext_iface['iface_uuid'], 'type': ext_iface['type']}
- # TODO? get internal-connections from db.nets and their profiles, and update scenario[vnfs][internal-connections] accordingly
-
- # 2: Insert net_key and ip_address at every vnf interface
- for net_name, net in scenario["networks"].items():
- net_type_bridge = False
- net_type_data = False
- for iface_dict in net["interfaces"]:
- if version == "0.2":
- temp_dict = iface_dict
- ip_address = None
- elif version == "0.3":
- temp_dict = {iface_dict["vnf"] : iface_dict["vnf_interface"]}
- ip_address = iface_dict.get('ip_address', None)
- for vnf, iface in temp_dict.items():
- if vnf not in scenario["vnfs"]:
- error_text = "Error at 'networks':'{}':'interfaces' VNF '{}' not match any VNF at 'vnfs'".format(
- net_name, vnf)
- # logger.debug("nfvo.new_scenario_v02 " + error_text)
- raise NfvoException(error_text, HTTP_Not_Found)
- if iface not in scenario["vnfs"][vnf]['ifaces']:
- error_text = "Error at 'networks':'{}':'interfaces':'{}' interface not match any VNF interface"\
- .format(net_name, iface)
- # logger.debug("nfvo.new_scenario_v02 " + error_text)
- raise NfvoException(error_text, HTTP_Bad_Request)
- if "net_key" in scenario["vnfs"][vnf]['ifaces'][iface]:
- error_text = "Error at 'networks':'{}':'interfaces':'{}' interface already connected at network"\
- "'{}'".format(net_name, iface,scenario["vnfs"][vnf]['ifaces'][iface]['net_key'])
- # logger.debug("nfvo.new_scenario_v02 " + error_text)
- raise NfvoException(error_text, HTTP_Bad_Request)
- scenario["vnfs"][vnf]['ifaces'][ iface ]['net_key'] = net_name
- scenario["vnfs"][vnf]['ifaces'][iface]['ip_address'] = ip_address
- iface_type = scenario["vnfs"][vnf]['ifaces'][iface]['type']
- if iface_type == 'mgmt' or iface_type == 'bridge':
- net_type_bridge = True
- else:
- net_type_data = True
-
- if net_type_bridge and net_type_data:
- error_text = "Error connection interfaces of 'bridge' type and 'data' type at 'networks':'{}':'interfaces'"\
- .format(net_name)
- # logger.debug("nfvo.new_scenario " + error_text)
- raise NfvoException(error_text, HTTP_Bad_Request)
- elif net_type_bridge:
- type_ = 'bridge'
- else:
- type_ = 'data' if len(net["interfaces"]) > 2 else 'ptp'
-
- if net.get("implementation"): # for v0.3
- if type_ == "bridge" and net["implementation"] == "underlay":
- error_text = "Error connecting interfaces of data type to a network declared as 'underlay' at "\
- "'network':'{}'".format(net_name)
- # logger.debug(error_text)
- raise NfvoException(error_text, HTTP_Bad_Request)
- elif type_ != "bridge" and net["implementation"] == "overlay":
- error_text = "Error connecting interfaces of data type to a network declared as 'overlay' at "\
- "'network':'{}'".format(net_name)
- # logger.debug(error_text)
- raise NfvoException(error_text, HTTP_Bad_Request)
- net.pop("implementation")
- if "type" in net and version == "0.3": # for v0.3
- if type_ == "data" and net["type"] == "e-line":
- error_text = "Error connecting more than 2 interfaces of data type to a network declared as type "\
- "'e-line' at 'network':'{}'".format(net_name)
- # logger.debug(error_text)
- raise NfvoException(error_text, HTTP_Bad_Request)
- elif type_ == "ptp" and net["type"] == "e-lan":
- type_ = "data"
-
- net['type'] = type_
- net['name'] = net_name
- net['external'] = net.get('external', False)
-
- # 3: insert at database
- scenario["nets"] = scenario["networks"]
- scenario['tenant_id'] = tenant_id
- scenario_id = mydb.new_scenario(scenario)
- return scenario_id
-
-
-def edit_scenario(mydb, tenant_id, scenario_id, data):
- data["uuid"] = scenario_id
- data["tenant_id"] = tenant_id
- c = mydb.edit_scenario( data )
- return c
-
-
-def start_scenario(mydb, tenant_id, scenario_id, instance_scenario_name, instance_scenario_description, datacenter=None,vim_tenant=None, startvms=True):
- #print "Checking that nfvo_tenant_id exists and getting the VIM URI and the VIM tenant_id"
- datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter, vim_tenant=vim_tenant)
- vims = {datacenter_id: myvim}
- myvim_tenant = myvim['tenant_id']
- datacenter_name = myvim['name']
-
- rollbackList=[]
- try:
- #print "Checking that the scenario_id exists and getting the scenario dictionary"
- scenarioDict = mydb.get_scenario(scenario_id, tenant_id, datacenter_id)
- scenarioDict['datacenter2tenant'] = { datacenter_id: myvim['config']['datacenter_tenant_id'] }
- scenarioDict['datacenter_id'] = datacenter_id
- #print '================scenarioDict======================='
- #print json.dumps(scenarioDict, indent=4)
- #print 'BEGIN launching instance scenario "%s" based on "%s"' % (instance_scenario_name,scenarioDict['name'])
-
- logger.debug("start_scenario Scenario %s: consisting of %d VNF(s)", scenarioDict['name'],len(scenarioDict['vnfs']))
- #print yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False)
-
- auxNetDict = {} #Auxiliar dictionary. First key:'scenario' or sce_vnf uuid. Second Key: uuid of the net/sce_net. Value: vim_net_id
- auxNetDict['scenario'] = {}
-
- logger.debug("start_scenario 1. Creating new nets (sce_nets) in the VIM")
- for sce_net in scenarioDict['nets']:
- #print "Net name: %s. Description: %s" % (sce_net["name"], sce_net["description"])
-
- myNetName = "%s.%s" % (instance_scenario_name, sce_net['name'])
- myNetName = myNetName[0:255] #limit length
- myNetType = sce_net['type']
- myNetDict = {}
- myNetDict["name"] = myNetName
- myNetDict["type"] = myNetType
- myNetDict["tenant_id"] = myvim_tenant
- myNetIPProfile = sce_net.get('ip_profile', None)
- #TODO:
- #We should use the dictionary as input parameter for new_network
- #print myNetDict
- if not sce_net["external"]:
- network_id = myvim.new_network(myNetName, myNetType, myNetIPProfile)
- #print "New VIM network created for scenario %s. Network id: %s" % (scenarioDict['name'],network_id)
- sce_net['vim_id'] = network_id
- auxNetDict['scenario'][sce_net['uuid']] = network_id
- rollbackList.append({'what':'network','where':'vim','vim_id':datacenter_id,'uuid':network_id})
- sce_net["created"] = True
- else:
- if sce_net['vim_id'] == None:
- error_text = "Error, datacenter '%s' does not have external network '%s'." % (datacenter_name, sce_net['name'])
- _, message = rollback(mydb, vims, rollbackList)
- logger.error("nfvo.start_scenario: %s", error_text)
- raise NfvoException(error_text, HTTP_Bad_Request)
- logger.debug("Using existent VIM network for scenario %s. Network id %s", scenarioDict['name'],sce_net['vim_id'])
- auxNetDict['scenario'][sce_net['uuid']] = sce_net['vim_id']
-
- logger.debug("start_scenario 2. Creating new nets (vnf internal nets) in the VIM")
- #For each vnf net, we create it and we add it to instanceNetlist.
- for sce_vnf in scenarioDict['vnfs']:
- for net in sce_vnf['nets']:
- #print "Net name: %s. Description: %s" % (net["name"], net["description"])
-
- myNetName = "%s.%s" % (instance_scenario_name,net['name'])
- myNetName = myNetName[0:255] #limit length
- myNetType = net['type']
- myNetDict = {}
- myNetDict["name"] = myNetName
- myNetDict["type"] = myNetType
- myNetDict["tenant_id"] = myvim_tenant
- myNetIPProfile = net.get('ip_profile', None)
- #print myNetDict
- #TODO:
- #We should use the dictionary as input parameter for new_network
- network_id = myvim.new_network(myNetName, myNetType, myNetIPProfile)
- #print "VIM network id for scenario %s: %s" % (scenarioDict['name'],network_id)
- net['vim_id'] = network_id
- if sce_vnf['uuid'] not in auxNetDict:
- auxNetDict[sce_vnf['uuid']] = {}
- auxNetDict[sce_vnf['uuid']][net['uuid']] = network_id
- rollbackList.append({'what':'network','where':'vim','vim_id':datacenter_id,'uuid':network_id})
- net["created"] = True
-
- #print "auxNetDict:"
- #print yaml.safe_dump(auxNetDict, indent=4, default_flow_style=False)
-
- logger.debug("start_scenario 3. Creating new vm instances in the VIM")
- #myvim.new_vminstance(self,vimURI,tenant_id,name,description,image_id,flavor_id,net_dict)
- i = 0
- for sce_vnf in scenarioDict['vnfs']:
- for vm in sce_vnf['vms']:
- i += 1
- myVMDict = {}
- #myVMDict['name'] = "%s-%s-%s" % (scenarioDict['name'],sce_vnf['name'], vm['name'])
- myVMDict['name'] = "{}.{}.{}".format(instance_scenario_name,sce_vnf['name'],chr(96+i))
- #myVMDict['description'] = vm['description']
- myVMDict['description'] = myVMDict['name'][0:99]
- if not startvms:
- myVMDict['start'] = "no"
- myVMDict['name'] = myVMDict['name'][0:255] #limit name length
- #print "VM name: %s. Description: %s" % (myVMDict['name'], myVMDict['name'])
-
- #create image at vim in case it not exist
- image_dict = mydb.get_table_by_uuid_name("images", vm['image_id'])
- image_id = create_or_use_image(mydb, vims, image_dict, [], True)
- vm['vim_image_id'] = image_id
-
- #create flavor at vim in case it not exist
- flavor_dict = mydb.get_table_by_uuid_name("flavors", vm['flavor_id'])
- if flavor_dict['extended']!=None:
- flavor_dict['extended']= yaml.load(flavor_dict['extended'])
- flavor_id = create_or_use_flavor(mydb, vims, flavor_dict, [], True)
- vm['vim_flavor_id'] = flavor_id
-
-
- myVMDict['imageRef'] = vm['vim_image_id']
- myVMDict['flavorRef'] = vm['vim_flavor_id']
- myVMDict['networks'] = []
- for iface in vm['interfaces']:
- netDict = {}
- if iface['type']=="data":
- netDict['type'] = iface['model']
- elif "model" in iface and iface["model"]!=None:
- netDict['model']=iface['model']
- #TODO in future, remove this because mac_address will not be set, and the type of PV,VF is obtained from iterface table model
- #discover type of interface looking at flavor
- for numa in flavor_dict.get('extended',{}).get('numas',[]):
- for flavor_iface in numa.get('interfaces',[]):
- if flavor_iface.get('name') == iface['internal_name']:
- if flavor_iface['dedicated'] == 'yes':
- netDict['type']="PF" #passthrough
- elif flavor_iface['dedicated'] == 'no':
- netDict['type']="VF" #siov
- elif flavor_iface['dedicated'] == 'yes:sriov':
- netDict['type']="VFnotShared" #sriov but only one sriov on the PF
- netDict["mac_address"] = flavor_iface.get("mac_address")
- break;
- netDict["use"]=iface['type']
- if netDict["use"]=="data" and not netDict.get("type"):
- #print "netDict", netDict
- #print "iface", iface
- e_text = "Cannot determine the interface type PF or VF of VNF '%s' VM '%s' iface '%s'" %(sce_vnf['name'], vm['name'], iface['internal_name'])
- if flavor_dict.get('extended')==None:
- raise NfvoException(e_text + "After database migration some information is not available. \
- Try to delete and create the scenarios and VNFs again", HTTP_Conflict)
- else:
- raise NfvoException(e_text, HTTP_Internal_Server_Error)
- if netDict["use"]=="mgmt" or netDict["use"]=="bridge":
- netDict["type"]="virtual"
- if "vpci" in iface and iface["vpci"] is not None:
- netDict['vpci'] = iface['vpci']
- if "mac" in iface and iface["mac"] is not None:
- netDict['mac_address'] = iface['mac']
- if "port-security" in iface and iface["port-security"] is not None:
- netDict['port_security'] = iface['port-security']
- if "floating-ip" in iface and iface["floating-ip"] is not None:
- netDict['floating_ip'] = iface['floating-ip']
- netDict['name'] = iface['internal_name']
- if iface['net_id'] is None:
- for vnf_iface in sce_vnf["interfaces"]:
- #print iface
- #print vnf_iface
- if vnf_iface['interface_id']==iface['uuid']:
- netDict['net_id'] = auxNetDict['scenario'][ vnf_iface['sce_net_id'] ]
- break
- else:
- netDict['net_id'] = auxNetDict[ sce_vnf['uuid'] ][ iface['net_id'] ]
- #skip bridge ifaces not connected to any net
- #if 'net_id' not in netDict or netDict['net_id']==None:
- # continue
- myVMDict['networks'].append(netDict)
- #print ">>>>>>>>>>>>>>>>>>>>>>>>>>>"
- #print myVMDict['name']
- #print "networks", yaml.safe_dump(myVMDict['networks'], indent=4, default_flow_style=False)
- #print "interfaces", yaml.safe_dump(vm['interfaces'], indent=4, default_flow_style=False)
- #print ">>>>>>>>>>>>>>>>>>>>>>>>>>>"
- vm_id = myvim.new_vminstance(myVMDict['name'],myVMDict['description'],myVMDict.get('start', None),
- myVMDict['imageRef'],myVMDict['flavorRef'],myVMDict['networks'])
- #print "VIM vm instance id (server id) for scenario %s: %s" % (scenarioDict['name'],vm_id)
- vm['vim_id'] = vm_id
- rollbackList.append({'what':'vm','where':'vim','vim_id':datacenter_id,'uuid':vm_id})
- #put interface uuid back to scenario[vnfs][vms[[interfaces]
- for net in myVMDict['networks']:
- if "vim_id" in net:
- for iface in vm['interfaces']:
- if net["name"]==iface["internal_name"]:
- iface["vim_id"]=net["vim_id"]
- break
-
- logger.debug("start scenario Deployment done")
- #print yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False)
- #r,c = mydb.new_instance_scenario_as_a_whole(nfvo_tenant,scenarioDict['name'],scenarioDict)
- instance_id = mydb.new_instance_scenario_as_a_whole(tenant_id,instance_scenario_name, instance_scenario_description, scenarioDict)
- return mydb.get_instance_scenario(instance_id)
-
- except (db_base_Exception, vimconn.vimconnException) as e:
- _, message = rollback(mydb, vims, rollbackList)
- if isinstance(e, db_base_Exception):
- error_text = "Exception at database"
- else:
- error_text = "Exception at VIM"
- error_text += " {} {}. {}".format(type(e).__name__, str(e), message)
- #logger.error("start_scenario %s", error_text)
- raise NfvoException(error_text, e.http_code)
-
-
-def unify_cloud_config(cloud_config_preserve, cloud_config):
- ''' join the cloud config information into cloud_config_preserve.
- In case of conflict cloud_config_preserve preserves
- None is admited
- '''
- if not cloud_config_preserve and not cloud_config:
- return None
-
- new_cloud_config = {"key-pairs":[], "users":[]}
- # key-pairs
- if cloud_config_preserve:
- for key in cloud_config_preserve.get("key-pairs", () ):
- if key not in new_cloud_config["key-pairs"]:
- new_cloud_config["key-pairs"].append(key)
- if cloud_config:
- for key in cloud_config.get("key-pairs", () ):
- if key not in new_cloud_config["key-pairs"]:
- new_cloud_config["key-pairs"].append(key)
- if not new_cloud_config["key-pairs"]:
- del new_cloud_config["key-pairs"]
-
- # users
- if cloud_config:
- new_cloud_config["users"] += cloud_config.get("users", () )
- if cloud_config_preserve:
- new_cloud_config["users"] += cloud_config_preserve.get("users", () )
- index_to_delete = []
- users = new_cloud_config.get("users", [])
- for index0 in range(0,len(users)):
- if index0 in index_to_delete:
- continue
- for index1 in range(index0+1,len(users)):
- if index1 in index_to_delete:
- continue
- if users[index0]["name"] == users[index1]["name"]:
- index_to_delete.append(index1)
- for key in users[index1].get("key-pairs",()):
- if "key-pairs" not in users[index0]:
- users[index0]["key-pairs"] = [key]
- elif key not in users[index0]["key-pairs"]:
- users[index0]["key-pairs"].append(key)
- index_to_delete.sort(reverse=True)
- for index in index_to_delete:
- del users[index]
- if not new_cloud_config["users"]:
- del new_cloud_config["users"]
-
- #boot-data-drive
- if cloud_config and cloud_config.get("boot-data-drive") != None:
- new_cloud_config["boot-data-drive"] = cloud_config["boot-data-drive"]
- if cloud_config_preserve and cloud_config_preserve.get("boot-data-drive") != None:
- new_cloud_config["boot-data-drive"] = cloud_config_preserve["boot-data-drive"]
-
- # user-data
- if cloud_config and cloud_config.get("user-data") != None:
- new_cloud_config["user-data"] = cloud_config["user-data"]
- if cloud_config_preserve and cloud_config_preserve.get("user-data") != None:
- new_cloud_config["user-data"] = cloud_config_preserve["user-data"]
-
- # config files
- new_cloud_config["config-files"] = []
- if cloud_config and cloud_config.get("config-files") != None:
- new_cloud_config["config-files"] += cloud_config["config-files"]
- if cloud_config_preserve:
- for file in cloud_config_preserve.get("config-files", ()):
- for index in range(0, len(new_cloud_config["config-files"])):
- if new_cloud_config["config-files"][index]["dest"] == file["dest"]:
- new_cloud_config["config-files"][index] = file
- break
- else:
- new_cloud_config["config-files"].append(file)
- if not new_cloud_config["config-files"]:
- del new_cloud_config["config-files"]
- return new_cloud_config
-
-
-def get_vim_thread(tenant_id, datacenter_id_name=None, datacenter_tenant_id=None):
- datacenter_id = None
- datacenter_name = None
- thread = None
- if datacenter_id_name:
- if utils.check_valid_uuid(datacenter_id_name):
- datacenter_id = datacenter_id_name
- else:
- datacenter_name = datacenter_id_name
- if datacenter_id:
- thread = vim_threads["running"].get(datacenter_id + "." + tenant_id)
- else:
- for k, v in vim_threads["running"].items():
- datacenter_tenant = k.split(".")
- if datacenter_tenant[0] == datacenter_id and datacenter_tenant[1] == tenant_id:
- if thread:
- raise NfvoException("More than one datacenters found, try to identify with uuid", HTTP_Conflict)
- thread = v
- elif not datacenter_id and datacenter_tenant[1] == tenant_id:
- if thread.datacenter_name == datacenter_name:
- if thread:
- raise NfvoException("More than one datacenters found, try to identify with uuid", HTTP_Conflict)
- thread = v
- if not thread:
- raise NfvoException("datacenter '{}' not found".format(str(datacenter_id_name)), HTTP_Not_Found)
- return thread
-
-
-def get_datacenter_by_name_uuid(mydb, tenant_id, datacenter_id_name=None, **extra_filter):
- datacenter_id = None
- datacenter_name = None
- if datacenter_id_name:
- if utils.check_valid_uuid(datacenter_id_name):
- datacenter_id = datacenter_id_name
- else:
- datacenter_name = datacenter_id_name
- vims = get_vim(mydb, tenant_id, datacenter_id, datacenter_name, **extra_filter)
- if len(vims) == 0:
- raise NfvoException("datacenter '{}' not found".format(str(datacenter_id_name)), HTTP_Not_Found)
- elif len(vims)>1:
- #print "nfvo.datacenter_action() error. Several datacenters found"
- raise NfvoException("More than one datacenters found, try to identify with uuid", HTTP_Conflict)
- return vims.keys()[0], vims.values()[0]
-
-
-def update(d, u):
- '''Takes dict d and updates it with the values in dict u.'''
- '''It merges all depth levels'''
- for k, v in u.iteritems():
- if isinstance(v, collections.Mapping):
- r = update(d.get(k, {}), v)
- d[k] = r
- else:
- d[k] = u[k]
- return d
-
-
-def create_instance(mydb, tenant_id, instance_dict):
- # print "Checking that nfvo_tenant_id exists and getting the VIM URI and the VIM tenant_id"
- # logger.debug("Creating instance...")
- scenario = instance_dict["scenario"]
-
- #find main datacenter
- myvims = {}
- myvim_threads = {}
- datacenter2tenant = {}
- datacenter = instance_dict.get("datacenter")
- default_datacenter_id, vim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
- myvims[default_datacenter_id] = vim
- myvim_threads[default_datacenter_id] = get_vim_thread(tenant_id, default_datacenter_id)
- datacenter2tenant[default_datacenter_id] = vim['config']['datacenter_tenant_id']
- #myvim_tenant = myvim['tenant_id']
-# default_datacenter_name = vim['name']
- rollbackList=[]
-
- #print "Checking that the scenario exists and getting the scenario dictionary"
- scenarioDict = mydb.get_scenario(scenario, tenant_id, default_datacenter_id)
-
- #logger.debug(">>>>>>> Dictionaries before merging")
- #logger.debug(">>>>>>> InstanceDict:\n{}".format(yaml.safe_dump(instance_dict,default_flow_style=False, width=256)))
- #logger.debug(">>>>>>> ScenarioDict:\n{}".format(yaml.safe_dump(scenarioDict,default_flow_style=False, width=256)))
-
- scenarioDict['datacenter_id'] = default_datacenter_id
-
- auxNetDict = {} #Auxiliar dictionary. First key:'scenario' or sce_vnf uuid. Second Key: uuid of the net/sce_net. Value: vim_net_id
- auxNetDict['scenario'] = {}
-
- logger.debug("Creating instance from scenario-dict:\n%s", yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False)) #TODO remove
- instance_name = instance_dict["name"]
- instance_description = instance_dict.get("description")
- instance_tasks={}
- try:
- # 0 check correct parameters
- for net_name, net_instance_desc in instance_dict.get("networks",{}).iteritems():
- found = False
- for scenario_net in scenarioDict['nets']:
- if net_name == scenario_net["name"]:
- found = True
- break
- if not found:
- raise NfvoException("Invalid scenario network name '{}' at instance:networks".format(net_name), HTTP_Bad_Request)
- if "sites" not in net_instance_desc:
- net_instance_desc["sites"] = [ {} ]
- site_without_datacenter_field = False
- for site in net_instance_desc["sites"]:
- if site.get("datacenter"):
- if site["datacenter"] not in myvims:
- #Add this datacenter to myvims
- d, v = get_datacenter_by_name_uuid(mydb, tenant_id, site["datacenter"])
- myvims[d] = v
- myvim_threads[d] = get_vim_thread(tenant_id, site["datacenter"])
- datacenter2tenant[d] = v['config']['datacenter_tenant_id']
- site["datacenter"] = d #change name to id
- else:
- if site_without_datacenter_field:
- raise NfvoException("Found more than one entries without datacenter field at instance:networks:{}:sites".format(net_name), HTTP_Bad_Request)
- site_without_datacenter_field = True
- site["datacenter"] = default_datacenter_id #change name to id
-
- for vnf_name, vnf_instance_desc in instance_dict.get("vnfs",{}).iteritems():
- found=False
- for scenario_vnf in scenarioDict['vnfs']:
- if vnf_name == scenario_vnf['name']:
- found = True
- break
- if not found:
- raise NfvoException("Invalid vnf name '{}' at instance:vnfs".format(vnf_instance_desc), HTTP_Bad_Request)
- if "datacenter" in vnf_instance_desc:
- # Add this datacenter to myvims
- if vnf_instance_desc["datacenter"] not in myvims:
- d, v = get_datacenter_by_name_uuid(mydb, tenant_id, vnf_instance_desc["datacenter"])
- myvims[d] = v
- myvim_threads[d] = get_vim_thread(tenant_id, vnf_instance_desc["datacenter"])
- datacenter2tenant[d] = v['config']['datacenter_tenant_id']
- scenario_vnf["datacenter"] = vnf_instance_desc["datacenter"]
-
- #0.1 parse cloud-config parameters
- cloud_config = unify_cloud_config(instance_dict.get("cloud-config"), scenarioDict.get("cloud-config"))
-
- #0.2 merge instance information into scenario
- #Ideally, the operation should be as simple as: update(scenarioDict,instance_dict)
- #However, this is not possible yet.
- for net_name, net_instance_desc in instance_dict.get("networks",{}).iteritems():
- for scenario_net in scenarioDict['nets']:
- if net_name == scenario_net["name"]:
- if 'ip-profile' in net_instance_desc:
- ipprofile = net_instance_desc['ip-profile']
- ipprofile['subnet_address'] = ipprofile.pop('subnet-address',None)
- ipprofile['ip_version'] = ipprofile.pop('ip-version','IPv4')
- ipprofile['gateway_address'] = ipprofile.pop('gateway-address',None)
- ipprofile['dns_address'] = ipprofile.pop('dns-address',None)
- if 'dhcp' in ipprofile:
- ipprofile['dhcp_start_address'] = ipprofile['dhcp'].get('start-address',None)
- ipprofile['dhcp_enabled'] = ipprofile['dhcp'].get('enabled',True)
- ipprofile['dhcp_count'] = ipprofile['dhcp'].get('count',None)
- del ipprofile['dhcp']
- if 'ip_profile' not in scenario_net:
- scenario_net['ip_profile'] = ipprofile
- else:
- update(scenario_net['ip_profile'],ipprofile)
- for interface in net_instance_desc.get('interfaces', () ):
- if 'ip_address' in interface:
- for vnf in scenarioDict['vnfs']:
- if interface['vnf'] == vnf['name']:
- for vnf_interface in vnf['interfaces']:
- if interface['vnf_interface'] == vnf_interface['external_name']:
- vnf_interface['ip_address']=interface['ip_address']
-
- #logger.debug(">>>>>>>> Merged dictionary")
- logger.debug("Creating instance scenario-dict MERGED:\n%s", yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False))
-
-
- # 1. Creating new nets (sce_nets) in the VIM"
- for sce_net in scenarioDict['nets']:
- sce_net["vim_id_sites"]={}
- descriptor_net = instance_dict.get("networks",{}).get(sce_net["name"],{})
- net_name = descriptor_net.get("vim-network-name")
- auxNetDict['scenario'][sce_net['uuid']] = {}
-
- sites = descriptor_net.get("sites", [ {} ])
- for site in sites:
- if site.get("datacenter"):
- vim = myvims[ site["datacenter"] ]
- datacenter_id = site["datacenter"]
- myvim_thread = myvim_threads[ site["datacenter"] ]
- else:
- vim = myvims[ default_datacenter_id ]
- datacenter_id = default_datacenter_id
- myvim_thread = myvim_threads[default_datacenter_id]
- net_type = sce_net['type']
- lookfor_filter = {'admin_state_up': True, 'status': 'ACTIVE'} #'shared': True
- if sce_net["external"]:
- if not net_name:
- net_name = sce_net["name"]
- if "netmap-use" in site or "netmap-create" in site:
- create_network = False
- lookfor_network = False
- if "netmap-use" in site:
- lookfor_network = True
- if utils.check_valid_uuid(site["netmap-use"]):
- filter_text = "scenario id '%s'" % site["netmap-use"]
- lookfor_filter["id"] = site["netmap-use"]
- else:
- filter_text = "scenario name '%s'" % site["netmap-use"]
- lookfor_filter["name"] = site["netmap-use"]
- if "netmap-create" in site:
- create_network = True
- net_vim_name = net_name
- if site["netmap-create"]:
- net_vim_name = site["netmap-create"]
-
- elif sce_net['vim_id'] != None:
- #there is a netmap at datacenter_nets database #TODO REVISE!!!!
- create_network = False
- lookfor_network = True
- lookfor_filter["id"] = sce_net['vim_id']
- filter_text = "vim_id '%s' datacenter_netmap name '%s'. Try to reload vims with datacenter-net-update" % (sce_net['vim_id'], sce_net["name"])
- #look for network at datacenter and return error
- else:
- #There is not a netmap, look at datacenter for a net with this name and create if not found
- create_network = True
- lookfor_network = True
- lookfor_filter["name"] = sce_net["name"]
- net_vim_name = sce_net["name"]
- filter_text = "scenario name '%s'" % sce_net["name"]
- else:
- if not net_name:
- net_name = "%s.%s" %(instance_name, sce_net["name"])
- net_name = net_name[:255] #limit length
- net_vim_name = net_name
- create_network = True
- lookfor_network = False
-
- if lookfor_network:
- vim_nets = vim.get_network_list(filter_dict=lookfor_filter)
- if len(vim_nets) > 1:
- raise NfvoException("More than one candidate VIM network found for " + filter_text, HTTP_Bad_Request )
- elif len(vim_nets) == 0:
- if not create_network:
- raise NfvoException("No candidate VIM network found for " + filter_text, HTTP_Bad_Request )
- else:
- sce_net["vim_id_sites"][datacenter_id] = vim_nets[0]['id']
- auxNetDict['scenario'][sce_net['uuid']][datacenter_id] = vim_nets[0]['id']
- create_network = False
- if create_network:
- #if network is not external
- task = new_task("new-net", (net_vim_name, net_type, sce_net.get('ip_profile',None)))
- task_id = myvim_thread.insert_task(task)
- instance_tasks[task_id] = task
- #network_id = vim.new_network(net_vim_name, net_type, sce_net.get('ip_profile',None))
- sce_net["vim_id_sites"][datacenter_id] = task_id
- auxNetDict['scenario'][sce_net['uuid']][datacenter_id] = task_id
- rollbackList.append({'what':'network', 'where':'vim', 'vim_id':datacenter_id, 'uuid':task_id})
- sce_net["created"] = True
-
- # 2. Creating new nets (vnf internal nets) in the VIM"
- #For each vnf net, we create it and we add it to instanceNetlist.
- for sce_vnf in scenarioDict['vnfs']:
- for net in sce_vnf['nets']:
- if sce_vnf.get("datacenter"):
- vim = myvims[ sce_vnf["datacenter"] ]
- datacenter_id = sce_vnf["datacenter"]
- myvim_thread = myvim_threads[ sce_vnf["datacenter"]]
- else:
- vim = myvims[ default_datacenter_id ]
- datacenter_id = default_datacenter_id
- myvim_thread = myvim_threads[default_datacenter_id]
- descriptor_net = instance_dict.get("vnfs",{}).get(sce_vnf["name"],{})
- net_name = descriptor_net.get("name")
- if not net_name:
- net_name = "%s.%s" %(instance_name, net["name"])
- net_name = net_name[:255] #limit length
- net_type = net['type']
- task = new_task("new-net", (net_name, net_type, net.get('ip_profile',None)))
- task_id = myvim_thread.insert_task(task)
- instance_tasks[task_id] = task
- # network_id = vim.new_network(net_name, net_type, net.get('ip_profile',None))
- net['vim_id'] = task_id
- if sce_vnf['uuid'] not in auxNetDict:
- auxNetDict[sce_vnf['uuid']] = {}
- auxNetDict[sce_vnf['uuid']][net['uuid']] = task_id
- rollbackList.append({'what':'network','where':'vim','vim_id':datacenter_id,'uuid':task_id})
- net["created"] = True
-
-
- #print "auxNetDict:"
- #print yaml.safe_dump(auxNetDict, indent=4, default_flow_style=False)
-
- # 3. Creating new vm instances in the VIM
- #myvim.new_vminstance(self,vimURI,tenant_id,name,description,image_id,flavor_id,net_dict)
- for sce_vnf in scenarioDict['vnfs']:
- if sce_vnf.get("datacenter"):
- vim = myvims[ sce_vnf["datacenter"] ]
- myvim_thread = myvim_threads[ sce_vnf["datacenter"] ]
- datacenter_id = sce_vnf["datacenter"]
- else:
- vim = myvims[ default_datacenter_id ]
- myvim_thread = myvim_threads[ default_datacenter_id ]
- datacenter_id = default_datacenter_id
- sce_vnf["datacenter_id"] = datacenter_id
- i = 0
- for vm in sce_vnf['vms']:
- i += 1
- myVMDict = {}
- myVMDict['name'] = "{}.{}.{}".format(instance_name,sce_vnf['name'],chr(96+i))
- myVMDict['description'] = myVMDict['name'][0:99]
-# if not startvms:
-# myVMDict['start'] = "no"
- myVMDict['name'] = myVMDict['name'][0:255] #limit name length
- #create image at vim in case it not exist
- image_dict = mydb.get_table_by_uuid_name("images", vm['image_id'])
- image_id = create_or_use_image(mydb, {datacenter_id: vim}, image_dict, [], True)
- vm['vim_image_id'] = image_id
-
- #create flavor at vim in case it not exist
- flavor_dict = mydb.get_table_by_uuid_name("flavors", vm['flavor_id'])
- if flavor_dict['extended']!=None:
- flavor_dict['extended']= yaml.load(flavor_dict['extended'])
- flavor_id = create_or_use_flavor(mydb, {datacenter_id: vim}, flavor_dict, rollbackList, True)
-
- #Obtain information for additional disks
- extended_flavor_dict = mydb.get_rows(FROM='datacenters_flavors', SELECT=('extended',), WHERE={'vim_id': flavor_id})
- if not extended_flavor_dict:
- raise NfvoException("flavor '{}' not found".format(flavor_id), HTTP_Not_Found)
- return
-
- #extended_flavor_dict_yaml = yaml.load(extended_flavor_dict[0])
- myVMDict['disks'] = None
- extended_info = extended_flavor_dict[0]['extended']
- if extended_info != None:
- extended_flavor_dict_yaml = yaml.load(extended_info)
- if 'disks' in extended_flavor_dict_yaml:
- myVMDict['disks'] = extended_flavor_dict_yaml['disks']
-
- vm['vim_flavor_id'] = flavor_id
- myVMDict['imageRef'] = vm['vim_image_id']
- myVMDict['flavorRef'] = vm['vim_flavor_id']
- myVMDict['networks'] = []
- task_depends = {}
- #TODO ALF. connect_mgmt_interfaces. Connect management interfaces if this is true
- for iface in vm['interfaces']:
- netDict = {}
- if iface['type']=="data":
- netDict['type'] = iface['model']
- elif "model" in iface and iface["model"]!=None:
- netDict['model']=iface['model']
- #TODO in future, remove this because mac_address will not be set, and the type of PV,VF is obtained from iterface table model
- #discover type of interface looking at flavor
- for numa in flavor_dict.get('extended',{}).get('numas',[]):
- for flavor_iface in numa.get('interfaces',[]):
- if flavor_iface.get('name') == iface['internal_name']:
- if flavor_iface['dedicated'] == 'yes':
- netDict['type']="PF" #passthrough
- elif flavor_iface['dedicated'] == 'no':
- netDict['type']="VF" #siov
- elif flavor_iface['dedicated'] == 'yes:sriov':
- netDict['type']="VFnotShared" #sriov but only one sriov on the PF
- netDict["mac_address"] = flavor_iface.get("mac_address")
- break;
- netDict["use"]=iface['type']
- if netDict["use"]=="data" and not netDict.get("type"):
- #print "netDict", netDict
- #print "iface", iface
- e_text = "Cannot determine the interface type PF or VF of VNF '%s' VM '%s' iface '%s'" %(sce_vnf['name'], vm['name'], iface['internal_name'])
- if flavor_dict.get('extended')==None:
- raise NfvoException(e_text + "After database migration some information is not available. \
- Try to delete and create the scenarios and VNFs again", HTTP_Conflict)
- else:
- raise NfvoException(e_text, HTTP_Internal_Server_Error)
- if netDict["use"]=="mgmt" or netDict["use"]=="bridge":
- netDict["type"]="virtual"
- if "vpci" in iface and iface["vpci"] is not None:
- netDict['vpci'] = iface['vpci']
- if "mac" in iface and iface["mac"] is not None:
- netDict['mac_address'] = iface['mac']
- if "port-security" in iface and iface["port-security"] is not None:
- netDict['port_security'] = iface['port-security']
- if "floating-ip" in iface and iface["floating-ip"] is not None:
- netDict['floating_ip'] = iface['floating-ip']
- netDict['name'] = iface['internal_name']
- if iface['net_id'] is None:
- for vnf_iface in sce_vnf["interfaces"]:
- #print iface
- #print vnf_iface
- if vnf_iface['interface_id']==iface['uuid']:
- netDict['net_id'] = auxNetDict['scenario'][ vnf_iface['sce_net_id'] ][datacenter_id]
- break
- else:
- netDict['net_id'] = auxNetDict[ sce_vnf['uuid'] ][ iface['net_id'] ]
- if is_task_id(netDict['net_id']):
- task_depends[netDict['net_id']] = instance_tasks[netDict['net_id']]
- #skip bridge ifaces not connected to any net
- #if 'net_id' not in netDict or netDict['net_id']==None:
- # continue
- myVMDict['networks'].append(netDict)
- #print ">>>>>>>>>>>>>>>>>>>>>>>>>>>"
- #print myVMDict['name']
- #print "networks", yaml.safe_dump(myVMDict['networks'], indent=4, default_flow_style=False)
- #print "interfaces", yaml.safe_dump(vm['interfaces'], indent=4, default_flow_style=False)
- #print ">>>>>>>>>>>>>>>>>>>>>>>>>>>"
- if vm.get("boot_data"):
- cloud_config_vm = unify_cloud_config(vm["boot_data"], cloud_config)
- else:
- cloud_config_vm = cloud_config
- task = new_task("new-vm", (myVMDict['name'], myVMDict['description'], myVMDict.get('start', None),
- myVMDict['imageRef'], myVMDict['flavorRef'], myVMDict['networks'],
- cloud_config_vm, myVMDict['disks']), depends=task_depends)
- vm_id = myvim_thread.insert_task(task)
- instance_tasks[vm_id] = task
-
- vm['vim_id'] = vm_id
- rollbackList.append({'what':'vm','where':'vim','vim_id':datacenter_id,'uuid':vm_id})
- #put interface uuid back to scenario[vnfs][vms[[interfaces]
- for net in myVMDict['networks']:
- if "vim_id" in net:
- for iface in vm['interfaces']:
- if net["name"]==iface["internal_name"]:
- iface["vim_id"]=net["vim_id"]
- break
- scenarioDict["datacenter2tenant"] = datacenter2tenant
- logger.debug("create_instance Deployment done scenarioDict: %s",
- yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False) )
- instance_id = mydb.new_instance_scenario_as_a_whole(tenant_id,instance_name, instance_description, scenarioDict)
- # Update database with those ended tasks
- for task in instance_tasks.values():
- if task["status"] == "ok":
- if task["name"] == "new-vm":
- mydb.update_rows("instance_vms", UPDATE={"vim_vm_id": task["result"]},
- WHERE={"vim_vm_id": task["id"]})
- elif task["name"] == "new-net":
- mydb.update_rows("instance_nets", UPDATE={"vim_net_id": task["result"]},
- WHERE={"vim_net_id": task["id"]})
- return mydb.get_instance_scenario(instance_id)
- except (NfvoException, vimconn.vimconnException,db_base_Exception) as e:
- message = rollback(mydb, myvims, rollbackList)
- if isinstance(e, db_base_Exception):
- error_text = "database Exception"
- elif isinstance(e, vimconn.vimconnException):
- error_text = "VIM Exception"
- else:
- error_text = "Exception"
- error_text += " {} {}. {}".format(type(e).__name__, str(e), message)
- #logger.error("create_instance: %s", error_text)
- raise NfvoException(error_text, e.http_code)
-
-
-def delete_instance(mydb, tenant_id, instance_id):
- #print "Checking that the instance_id exists and getting the instance dictionary"
- instanceDict = mydb.get_instance_scenario(instance_id, tenant_id)
- #print yaml.safe_dump(instanceDict, indent=4, default_flow_style=False)
- tenant_id = instanceDict["tenant_id"]
- #print "Checking that nfvo_tenant_id exists and getting the VIM URI and the VIM tenant_id"
-
- #1. Delete from Database
- message = mydb.delete_instance_scenario(instance_id, tenant_id)
-
- #2. delete from VIM
- error_msg = ""
- myvims = {}
- myvim_threads = {}
-
- #2.1 deleting VMs
- #vm_fail_list=[]
- for sce_vnf in instanceDict['vnfs']:
- datacenter_key = (sce_vnf["datacenter_id"], sce_vnf["datacenter_tenant_id"])
- if datacenter_key not in myvims:
- try:
- myvim_thread = get_vim_thread(tenant_id, sce_vnf["datacenter_id"], sce_vnf["datacenter_tenant_id"])
- except NfvoException as e:
- logger.error(str(e))
- myvim_thread = None
- myvim_threads[datacenter_key] = myvim_thread
- vims = get_vim(mydb, tenant_id, datacenter_id=sce_vnf["datacenter_id"],
- datacenter_tenant_id=sce_vnf["datacenter_tenant_id"])
- if len(vims) == 0:
- logger.error("datacenter '{}' with datacenter_tenant_id '{}' not found".format(sce_vnf["datacenter_id"],
- sce_vnf["datacenter_tenant_id"]))
- myvims[datacenter_key] = None
- else:
- myvims[datacenter_key] = vims.values()[0]
- myvim = myvims[datacenter_key]
- myvim_thread = myvim_threads[datacenter_key]
- for vm in sce_vnf['vms']:
- if not myvim:
- error_msg += "\n VM id={} cannot be deleted because datacenter={} not found".format(vm['vim_vm_id'], sce_vnf["datacenter_id"])
- continue
- try:
- task=None
- if is_task_id(vm['vim_vm_id']):
- task_id = vm['vim_vm_id']
- old_task = task_dict.get(task_id)
- if not old_task:
- error_msg += "\n VM was scheduled for create, but task {} is not found".format(task_id)
- continue
- with task_lock:
- if old_task["status"] == "enqueued":
- old_task["status"] = "deleted"
- elif old_task["status"] == "error":
- continue
- elif old_task["status"] == "processing":
- task = new_task("del-vm", task_id, depends={task_id: old_task})
- else: #ok
- task = new_task("del-vm", old_task["result"])
- else:
- task = new_task("del-vm", vm['vim_vm_id'], store=False)
- if task:
- myvim_thread.insert_task(task)
- except vimconn.vimconnNotFoundException as e:
- error_msg+="\n VM VIM_id={} not found at datacenter={}".format(vm['vim_vm_id'], sce_vnf["datacenter_id"])
- logger.warn("VM instance '%s'uuid '%s', VIM id '%s', from VNF_id '%s' not found",
- vm['name'], vm['uuid'], vm['vim_vm_id'], sce_vnf['vnf_id'])
- except vimconn.vimconnException as e:
- error_msg+="\n VM VIM_id={} at datacenter={} Error: {} {}".format(vm['vim_vm_id'], sce_vnf["datacenter_id"], e.http_code, str(e))
- logger.error("Error %d deleting VM instance '%s'uuid '%s', VIM_id '%s', from VNF_id '%s': %s",
- e.http_code, vm['name'], vm['uuid'], vm['vim_vm_id'], sce_vnf['vnf_id'], str(e))
-
- #2.2 deleting NETS
- #net_fail_list=[]
- for net in instanceDict['nets']:
- if not net['created']:
- continue #skip not created nets
- datacenter_key = (net["datacenter_id"], net["datacenter_tenant_id"])
- if datacenter_key not in myvims:
- try:
- myvim_thread = get_vim_thread(tenant_id, sce_vnf["datacenter_id"], sce_vnf["datacenter_tenant_id"])
- except NfvoException as e:
- logger.error(str(e))
- myvim_thread = None
- myvim_threads[datacenter_key] = myvim_thread
- vims = get_vim(mydb, tenant_id, datacenter_id=net["datacenter_id"],
- datacenter_tenant_id=net["datacenter_tenant_id"])
- if len(vims) == 0:
- logger.error("datacenter '{}' with datacenter_tenant_id '{}' not found".format(net["datacenter_id"], net["datacenter_tenant_id"]))
- myvims[datacenter_key] = None
- else:
- myvims[datacenter_key] = vims.values()[0]
- myvim = myvims[datacenter_key]
- myvim_thread = myvim_threads[datacenter_key]
-
- if not myvim:
- error_msg += "\n Net VIM_id={} cannot be deleted because datacenter={} not found".format(net['vim_net_id'], net["datacenter_id"])
- continue
- try:
- task = None
- if is_task_id(net['vim_net_id']):
- task_id = net['vim_net_id']
- old_task = task_dict.get(task_id)
- if not old_task:
- error_msg += "\n NET was scheduled for create, but task {} is not found".format(task_id)
- continue
- with task_lock:
- if old_task["status"] == "enqueued":
- old_task["status"] = "deleted"
- elif old_task["status"] == "error":
- continue
- elif old_task["status"] == "processing":
- task = new_task("del-net", task_id, depends={task_id: old_task})
- else: # ok
- task = new_task("del-net", old_task["result"])
- else:
- task = new_task("del-net", net['vim_net_id'], store=False)
- if task:
- myvim_thread.insert_task(task)
- except vimconn.vimconnNotFoundException as e:
- error_msg += "\n NET VIM_id={} not found at datacenter={}".format(net['vim_net_id'], net["datacenter_id"])
- logger.warn("NET '%s', VIM_id '%s', from VNF_net_id '%s' not found",
- net['uuid'], net['vim_net_id'], str(net['vnf_net_id']))
- except vimconn.vimconnException as e:
- error_msg += "\n NET VIM_id={} at datacenter={} Error: {} {}".format(net['vim_net_id'],
- net["datacenter_id"],
- e.http_code, str(e))
- logger.error("Error %d deleting NET '%s', VIM_id '%s', from VNF_net_id '%s': %s",
- e.http_code, net['uuid'], net['vim_net_id'], str(net['vnf_net_id']), str(e))
- if len(error_msg) > 0:
- return 'instance ' + message + ' deleted but some elements could not be deleted, or already deleted (error: 404) from VIM: ' + error_msg
- else:
- return 'instance ' + message + ' deleted'
-
-
-def refresh_instance(mydb, nfvo_tenant, instanceDict, datacenter=None, vim_tenant=None):
- '''Refreshes a scenario instance. It modifies instanceDict'''
- '''Returns:
- - result: <0 if there is any unexpected error, n>=0 if no errors where n is the number of vms and nets that couldn't be updated in the database
- - error_msg
- '''
- # Assumption: nfvo_tenant and instance_id were checked before entering into this function
- #print "nfvo.refresh_instance begins"
- #print json.dumps(instanceDict, indent=4)
-
- #print "Getting the VIM URL and the VIM tenant_id"
- myvims={}
-
- # 1. Getting VIM vm and net list
- vms_updated = [] #List of VM instance uuids in openmano that were updated
- vms_notupdated=[]
- vm_list = {}
- for sce_vnf in instanceDict['vnfs']:
- datacenter_key = (sce_vnf["datacenter_id"], sce_vnf["datacenter_tenant_id"])
- if datacenter_key not in vm_list:
- vm_list[datacenter_key] = []
- if datacenter_key not in myvims:
- vims = get_vim(mydb, nfvo_tenant, datacenter_id=sce_vnf["datacenter_id"],
- datacenter_tenant_id=sce_vnf["datacenter_tenant_id"])
- if len(vims) == 0:
- logger.error("datacenter '{}' with datacenter_tenant_id '{}' not found".format(sce_vnf["datacenter_id"], sce_vnf["datacenter_tenant_id"]))
- myvims[datacenter_key] = None
- else:
- myvims[datacenter_key] = vims.values()[0]
- for vm in sce_vnf['vms']:
- vm_list[datacenter_key].append(vm['vim_vm_id'])
- vms_notupdated.append(vm["uuid"])
-
- nets_updated = [] #List of VM instance uuids in openmano that were updated
- nets_notupdated=[]
- net_list = {}
- for net in instanceDict['nets']:
- datacenter_key = (net["datacenter_id"], net["datacenter_tenant_id"])
- if datacenter_key not in net_list:
- net_list[datacenter_key] = []
- if datacenter_key not in myvims:
- vims = get_vim(mydb, nfvo_tenant, datacenter_id=net["datacenter_id"],
- datacenter_tenant_id=net["datacenter_tenant_id"])
- if len(vims) == 0:
- logger.error("datacenter '{}' with datacenter_tenant_id '{}' not found".format(net["datacenter_id"], net["datacenter_tenant_id"]))
- myvims[datacenter_key] = None
- else:
- myvims[datacenter_key] = vims.values()[0]
-
- net_list[datacenter_key].append(net['vim_net_id'])
- nets_notupdated.append(net["uuid"])
-
- # 1. Getting the status of all VMs
- vm_dict={}
- for datacenter_key in myvims:
- if not vm_list.get(datacenter_key):
- continue
- failed = True
- failed_message=""
- if not myvims[datacenter_key]:
- failed_message = "datacenter '{}' with datacenter_tenant_id '{}' not found".format(net["datacenter_id"], net["datacenter_tenant_id"])
- else:
- try:
- vm_dict.update(myvims[datacenter_key].refresh_vms_status(vm_list[datacenter_key]) )
- failed = False
- except vimconn.vimconnException as e:
- logger.error("VIM exception %s %s", type(e).__name__, str(e))
- failed_message = str(e)
- if failed:
- for vm in vm_list[datacenter_key]:
- vm_dict[vm] = {'status': "VIM_ERROR", 'error_msg': failed_message}
-
- # 2. Update the status of VMs in the instanceDict, while collects the VMs whose status changed
- for sce_vnf in instanceDict['vnfs']:
- for vm in sce_vnf['vms']:
- vm_id = vm['vim_vm_id']
- interfaces = vm_dict[vm_id].pop('interfaces', [])
- #2.0 look if contain manamgement interface, and if not change status from ACTIVE:NoMgmtIP to ACTIVE
- has_mgmt_iface = False
- for iface in vm["interfaces"]:
- if iface["type"]=="mgmt":
- has_mgmt_iface = True
- if vm_dict[vm_id]['status'] == "ACTIVE:NoMgmtIP" and not has_mgmt_iface:
- vm_dict[vm_id]['status'] = "ACTIVE"
- if vm_dict[vm_id].get('error_msg') and len(vm_dict[vm_id]['error_msg']) >= 1024:
- vm_dict[vm_id]['error_msg'] = vm_dict[vm_id]['error_msg'][:516] + " ... " + vm_dict[vm_id]['error_msg'][-500:]
- if vm['status'] != vm_dict[vm_id]['status'] or vm.get('error_msg')!=vm_dict[vm_id].get('error_msg') or vm.get('vim_info')!=vm_dict[vm_id].get('vim_info'):
- vm['status'] = vm_dict[vm_id]['status']
- vm['error_msg'] = vm_dict[vm_id].get('error_msg')
- vm['vim_info'] = vm_dict[vm_id].get('vim_info')
- # 2.1. Update in openmano DB the VMs whose status changed
- try:
- updates = mydb.update_rows('instance_vms', UPDATE=vm_dict[vm_id], WHERE={'uuid':vm["uuid"]})
- vms_notupdated.remove(vm["uuid"])
- if updates>0:
- vms_updated.append(vm["uuid"])
- except db_base_Exception as e:
- logger.error("nfvo.refresh_instance error database update: %s", str(e))
- # 2.2. Update in openmano DB the interface VMs
- for interface in interfaces:
- #translate from vim_net_id to instance_net_id
- network_id_list=[]
- for net in instanceDict['nets']:
- if net["vim_net_id"] == interface["vim_net_id"]:
- network_id_list.append(net["uuid"])
- if not network_id_list:
- continue
- del interface["vim_net_id"]
- try:
- for network_id in network_id_list:
- mydb.update_rows('instance_interfaces', UPDATE=interface, WHERE={'instance_vm_id':vm["uuid"], "instance_net_id":network_id})
- except db_base_Exception as e:
- logger.error( "nfvo.refresh_instance error with vm=%s, interface_net_id=%s", vm["uuid"], network_id)
-
- # 3. Getting the status of all nets
- net_dict = {}
- for datacenter_key in myvims:
- if not net_list.get(datacenter_key):
- continue
- failed = True
- failed_message = ""
- if not myvims[datacenter_key]:
- failed_message = "datacenter '{}' with datacenter_tenant_id '{}' not found".format(net["datacenter_id"], net["datacenter_tenant_id"])
- else:
- try:
- net_dict.update(myvims[datacenter_key].refresh_nets_status(net_list[datacenter_key]) )
- failed = False
- except vimconn.vimconnException as e:
- logger.error("VIM exception %s %s", type(e).__name__, str(e))
- failed_message = str(e)
- if failed:
- for net in net_list[datacenter_key]:
- net_dict[net] = {'status': "VIM_ERROR", 'error_msg': failed_message}
-
- # 4. Update the status of nets in the instanceDict, while collects the nets whose status changed
- # TODO: update nets inside a vnf
- for net in instanceDict['nets']:
- net_id = net['vim_net_id']
- if net_dict[net_id].get('error_msg') and len(net_dict[net_id]['error_msg']) >= 1024:
- net_dict[net_id]['error_msg'] = net_dict[net_id]['error_msg'][:516] + " ... " + net_dict[vm_id]['error_msg'][-500:]
- if net['status'] != net_dict[net_id]['status'] or net.get('error_msg')!=net_dict[net_id].get('error_msg') or net.get('vim_info')!=net_dict[net_id].get('vim_info'):
- net['status'] = net_dict[net_id]['status']
- net['error_msg'] = net_dict[net_id].get('error_msg')
- net['vim_info'] = net_dict[net_id].get('vim_info')
- # 5.1. Update in openmano DB the nets whose status changed
- try:
- updated = mydb.update_rows('instance_nets', UPDATE=net_dict[net_id], WHERE={'uuid':net["uuid"]})
- nets_notupdated.remove(net["uuid"])
- if updated>0:
- nets_updated.append(net["uuid"])
- except db_base_Exception as e:
- logger.error("nfvo.refresh_instance error database update: %s", str(e))
-
- # Returns appropriate output
- #print "nfvo.refresh_instance finishes"
- logger.debug("VMs updated in the database: %s; nets updated in the database %s; VMs not updated: %s; nets not updated: %s",
- str(vms_updated), str(nets_updated), str(vms_notupdated), str(nets_notupdated))
- instance_id = instanceDict['uuid']
- if len(vms_notupdated)+len(nets_notupdated)>0:
- error_msg = "VMs not updated: " + str(vms_notupdated) + "; nets not updated: " + str(nets_notupdated)
- return len(vms_notupdated)+len(nets_notupdated), 'Scenario instance ' + instance_id + ' refreshed but some elements could not be updated in the database: ' + error_msg
-
- return 0, 'Scenario instance ' + instance_id + ' refreshed.'
-
-
-def instance_action(mydb,nfvo_tenant,instance_id, action_dict):
- #print "Checking that the instance_id exists and getting the instance dictionary"
- instanceDict = mydb.get_instance_scenario(instance_id, nfvo_tenant)
- #print yaml.safe_dump(instanceDict, indent=4, default_flow_style=False)
-
- #print "Checking that nfvo_tenant_id exists and getting the VIM URI and the VIM tenant_id"
- vims = get_vim(mydb, nfvo_tenant, instanceDict['datacenter_id'])
- if len(vims) == 0:
- raise NfvoException("datacenter '{}' not found".format(str(instanceDict['datacenter_id'])), HTTP_Not_Found)
- myvim = vims.values()[0]
-
-
- input_vnfs = action_dict.pop("vnfs", [])
- input_vms = action_dict.pop("vms", [])
- action_over_all = True if len(input_vnfs)==0 and len (input_vms)==0 else False
- vm_result = {}
- vm_error = 0
- vm_ok = 0
- for sce_vnf in instanceDict['vnfs']:
- for vm in sce_vnf['vms']:
- if not action_over_all:
- if sce_vnf['uuid'] not in input_vnfs and sce_vnf['vnf_name'] not in input_vnfs and \
- vm['uuid'] not in input_vms and vm['name'] not in input_vms:
- continue
- try:
- data = myvim.action_vminstance(vm['vim_vm_id'], action_dict)
- if "console" in action_dict:
- if not global_config["http_console_proxy"]:
- vm_result[ vm['uuid'] ] = {"vim_result": 200,
- "description": "{protocol}//{ip}:{port}/{suffix}".format(
- protocol=data["protocol"],
- ip = data["server"],
- port = data["port"],
- suffix = data["suffix"]),
- "name":vm['name']
- }
- vm_ok +=1
- elif data["server"]=="127.0.0.1" or data["server"]=="localhost":
- vm_result[ vm['uuid'] ] = {"vim_result": -HTTP_Unauthorized,
- "description": "this console is only reachable by local interface",
- "name":vm['name']
- }
- vm_error+=1
- else:
- #print "console data", data
- try:
- console_thread = create_or_use_console_proxy_thread(data["server"], data["port"])
- vm_result[ vm['uuid'] ] = {"vim_result": 200,
- "description": "{protocol}//{ip}:{port}/{suffix}".format(
- protocol=data["protocol"],
- ip = global_config["http_console_host"],
- port = console_thread.port,
- suffix = data["suffix"]),
- "name":vm['name']
- }
- vm_ok +=1
- except NfvoException as e:
- vm_result[ vm['uuid'] ] = {"vim_result": e.http_code, "name":vm['name'], "description": str(e)}
- vm_error+=1
-
- else:
- vm_result[ vm['uuid'] ] = {"vim_result": 200, "description": "ok", "name":vm['name']}
- vm_ok +=1
- except vimconn.vimconnException as e:
- vm_result[ vm['uuid'] ] = {"vim_result": e.http_code, "name":vm['name'], "description": str(e)}
- vm_error+=1
-
- if vm_ok==0: #all goes wrong
- return vm_result
- else:
- return vm_result
-
-
-def create_or_use_console_proxy_thread(console_server, console_port):
- #look for a non-used port
- console_thread_key = console_server + ":" + str(console_port)
- if console_thread_key in global_config["console_thread"]:
- #global_config["console_thread"][console_thread_key].start_timeout()
- return global_config["console_thread"][console_thread_key]
-
- for port in global_config["console_port_iterator"]():
- #print "create_or_use_console_proxy_thread() port:", port
- if port in global_config["console_ports"]:
- continue
- try:
- clithread = cli.ConsoleProxyThread(global_config['http_host'], port, console_server, console_port)
- clithread.start()
- global_config["console_thread"][console_thread_key] = clithread
- global_config["console_ports"][port] = console_thread_key
- return clithread
- except cli.ConsoleProxyExceptionPortUsed as e:
- #port used, try with onoher
- continue
- except cli.ConsoleProxyException as e:
- raise NfvoException(str(e), HTTP_Bad_Request)
- raise NfvoException("Not found any free 'http_console_ports'", HTTP_Conflict)
-
-
-def check_tenant(mydb, tenant_id):
- '''check that tenant exists at database'''
- tenant = mydb.get_rows(FROM='nfvo_tenants', SELECT=('uuid',), WHERE={'uuid': tenant_id})
- if not tenant:
- raise NfvoException("tenant '{}' not found".format(tenant_id), HTTP_Not_Found)
- return
-
-
-def new_tenant(mydb, tenant_dict):
- tenant_id = mydb.new_row("nfvo_tenants", tenant_dict, add_uuid=True)
- return tenant_id
-
-
-def delete_tenant(mydb, tenant):
- #get nfvo_tenant info
-
- tenant_dict = mydb.get_table_by_uuid_name('nfvo_tenants', tenant, 'tenant')
- mydb.delete_row_by_id("nfvo_tenants", tenant_dict['uuid'])
- return tenant_dict['uuid'] + " " + tenant_dict["name"]
-
-
-def new_datacenter(mydb, datacenter_descriptor):
- if "config" in datacenter_descriptor:
- datacenter_descriptor["config"]=yaml.safe_dump(datacenter_descriptor["config"],default_flow_style=True,width=256)
- #Check that datacenter-type is correct
- datacenter_type = datacenter_descriptor.get("type", "openvim");
- module_info = None
- try:
- module = "vimconn_" + datacenter_type
- module_info = imp.find_module(module)
- except (IOError, ImportError):
- if module_info and module_info[0]:
- file.close(module_info[0])
- raise NfvoException("Incorrect datacenter type '{}'. Plugin '{}'.py not installed".format(datacenter_type, module), HTTP_Bad_Request)
-
- datacenter_id = mydb.new_row("datacenters", datacenter_descriptor, add_uuid=True)
- return datacenter_id
-
-
-def edit_datacenter(mydb, datacenter_id_name, datacenter_descriptor):
- #obtain data, check that only one exist
- datacenter = mydb.get_table_by_uuid_name('datacenters', datacenter_id_name)
- #edit data
- datacenter_id = datacenter['uuid']
- where={'uuid': datacenter['uuid']}
- if "config" in datacenter_descriptor:
- if datacenter_descriptor['config']!=None:
- try:
- new_config_dict = datacenter_descriptor["config"]
- #delete null fields
- to_delete=[]
- for k in new_config_dict:
- if new_config_dict[k]==None:
- to_delete.append(k)
-
- config_dict = yaml.load(datacenter["config"])
- config_dict.update(new_config_dict)
- #delete null fields
- for k in to_delete:
- del config_dict[k]
- except Exception as e:
- raise NfvoException("Bad format at datacenter:config " + str(e), HTTP_Bad_Request)
- datacenter_descriptor["config"]= yaml.safe_dump(config_dict,default_flow_style=True,width=256) if len(config_dict)>0 else None
- mydb.update_rows('datacenters', datacenter_descriptor, where)
- return datacenter_id
-
-
-def delete_datacenter(mydb, datacenter):
- #get nfvo_tenant info
- datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter, 'datacenter')
- mydb.delete_row_by_id("datacenters", datacenter_dict['uuid'])
- return datacenter_dict['uuid'] + " " + datacenter_dict['name']
-
-
-def associate_datacenter_to_tenant(mydb, nfvo_tenant, datacenter, vim_tenant_id=None, vim_tenant_name=None, vim_username=None, vim_password=None, config=None):
- #get datacenter info
- datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, None, datacenter)
- datacenter_name = myvim["name"]
-
- create_vim_tenant = True if not vim_tenant_id and not vim_tenant_name else False
-
- # get nfvo_tenant info
- tenant_dict = mydb.get_table_by_uuid_name('nfvo_tenants', nfvo_tenant)
- if vim_tenant_name==None:
- vim_tenant_name=tenant_dict['name']
-
- #check that this association does not exist before
- tenants_datacenter_dict={"nfvo_tenant_id":tenant_dict['uuid'], "datacenter_id":datacenter_id }
- tenants_datacenters = mydb.get_rows(FROM='tenants_datacenters', WHERE=tenants_datacenter_dict)
- if len(tenants_datacenters)>0:
- raise NfvoException("datacenter '{}' and tenant'{}' are already attached".format(datacenter_id, tenant_dict['uuid']), HTTP_Conflict)
-
- vim_tenant_id_exist_atdb=False
- if not create_vim_tenant:
- where_={"datacenter_id": datacenter_id}
- if vim_tenant_id!=None:
- where_["vim_tenant_id"] = vim_tenant_id
- if vim_tenant_name!=None:
- where_["vim_tenant_name"] = vim_tenant_name
- #check if vim_tenant_id is already at database
- datacenter_tenants_dict = mydb.get_rows(FROM='datacenter_tenants', WHERE=where_)
- if len(datacenter_tenants_dict)>=1:
- datacenter_tenants_dict = datacenter_tenants_dict[0]
- vim_tenant_id_exist_atdb=True
- #TODO check if a field has changed and edit entry at datacenter_tenants at DB
- else: #result=0
- datacenter_tenants_dict = {}
- #insert at table datacenter_tenants
- else: #if vim_tenant_id==None:
- #create tenant at VIM if not provided
- try:
- vim_tenant_id = myvim.new_tenant(vim_tenant_name, "created by openmano for datacenter "+datacenter_name)
- except vimconn.vimconnException as e:
- raise NfvoException("Not possible to create vim_tenant {} at VIM: {}".format(vim_tenant_id, str(e)), HTTP_Internal_Server_Error)
- datacenter_tenants_dict = {}
- datacenter_tenants_dict["created"]="true"
-
- #fill datacenter_tenants table
- if not vim_tenant_id_exist_atdb:
- datacenter_tenants_dict["vim_tenant_id"] = vim_tenant_id
- datacenter_tenants_dict["vim_tenant_name"] = vim_tenant_name
- datacenter_tenants_dict["user"] = vim_username
- datacenter_tenants_dict["passwd"] = vim_password
- datacenter_tenants_dict["datacenter_id"] = datacenter_id
- if config:
- datacenter_tenants_dict["config"] = yaml.safe_dump(config, default_flow_style=True, width=256)
- id_ = mydb.new_row('datacenter_tenants', datacenter_tenants_dict, add_uuid=True)
- datacenter_tenants_dict["uuid"] = id_
-
- #fill tenants_datacenters table
- tenants_datacenter_dict["datacenter_tenant_id"]=datacenter_tenants_dict["uuid"]
- mydb.new_row('tenants_datacenters', tenants_datacenter_dict)
- # create thread
- datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_dict['uuid'], datacenter_id) # reload data
- thread_name = get_non_used_vim_name(datacenter_name, datacenter_id, tenant_dict['name'], tenant_dict['uuid'])
- new_thread = vim_thread.vim_thread(myvim, task_lock, thread_name, datacenter_name, db=db, db_lock=db_lock)
- new_thread.start()
- thread_id = datacenter_id + "." + tenant_dict['uuid']
- vim_threads["running"][thread_id] = new_thread
- return datacenter_id
-
-
-def deassociate_datacenter_to_tenant(mydb, tenant_id, datacenter, vim_tenant_id=None):
- #get datacenter info
- datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, None, datacenter)
-
- #get nfvo_tenant info
- if not tenant_id or tenant_id=="any":
- tenant_uuid = None
- else:
- tenant_dict = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id)
- tenant_uuid = tenant_dict['uuid']
-
- #check that this association exist before
- tenants_datacenter_dict={"datacenter_id":datacenter_id }
- if tenant_uuid:
- tenants_datacenter_dict["nfvo_tenant_id"] = tenant_uuid
- tenant_datacenter_list = mydb.get_rows(FROM='tenants_datacenters', WHERE=tenants_datacenter_dict)
- if len(tenant_datacenter_list)==0 and tenant_uuid:
- raise NfvoException("datacenter '{}' and tenant '{}' are not attached".format(datacenter_id, tenant_dict['uuid']), HTTP_Not_Found)
-
- #delete this association
- mydb.delete_row(FROM='tenants_datacenters', WHERE=tenants_datacenter_dict)
-
- #get vim_tenant info and deletes
- warning=''
- for tenant_datacenter_item in tenant_datacenter_list:
- vim_tenant_dict = mydb.get_table_by_uuid_name('datacenter_tenants', tenant_datacenter_item['datacenter_tenant_id'])
- #try to delete vim:tenant
- try:
- mydb.delete_row_by_id('datacenter_tenants', tenant_datacenter_item['datacenter_tenant_id'])
- if vim_tenant_dict['created']=='true':
- #delete tenant at VIM if created by NFVO
- try:
- myvim.delete_tenant(vim_tenant_dict['vim_tenant_id'])
- except vimconn.vimconnException as e:
- warning = "Not possible to delete vim_tenant_id {} from VIM: {} ".format(vim_tenant_dict['vim_tenant_id'], str(e))
- logger.warn(warning)
- except db_base_Exception as e:
- logger.error("Cannot delete datacenter_tenants " + str(e))
- pass # the error will be caused because dependencies, vim_tenant can not be deleted
- thread_id = datacenter_id + "." + tenant_datacenter_item["nfvo_tenant_id"]
- thread = vim_threads["running"][thread_id]
- thread.insert_task(new_task("exit", None, store=False))
- vim_threads["deleting"][thread_id] = thread
- return "datacenter {} detached. {}".format(datacenter_id, warning)
-
-
-def datacenter_action(mydb, tenant_id, datacenter, action_dict):
- #DEPRECATED
- #get datacenter info
- datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
-
- if 'net-update' in action_dict:
- try:
- nets = myvim.get_network_list(filter_dict={'shared': True, 'admin_state_up': True, 'status': 'ACTIVE'})
- #print content
- except vimconn.vimconnException as e:
- #logger.error("nfvo.datacenter_action() Not possible to get_network_list from VIM: %s ", str(e))
- raise NfvoException(str(e), HTTP_Internal_Server_Error)
- #update nets Change from VIM format to NFVO format
- net_list=[]
- for net in nets:
- net_nfvo={'datacenter_id': datacenter_id}
- net_nfvo['name'] = net['name']
- #net_nfvo['description']= net['name']
- net_nfvo['vim_net_id'] = net['id']
- net_nfvo['type'] = net['type'][0:6] #change from ('ptp','data','bridge_data','bridge_man') to ('bridge','data','ptp')
- net_nfvo['shared'] = net['shared']
- net_nfvo['multipoint'] = False if net['type']=='ptp' else True
- net_list.append(net_nfvo)
- inserted, deleted = mydb.update_datacenter_nets(datacenter_id, net_list)
- logger.info("Inserted %d nets, deleted %d old nets", inserted, deleted)
- return inserted
- elif 'net-edit' in action_dict:
- net = action_dict['net-edit'].pop('net')
- what = 'vim_net_id' if utils.check_valid_uuid(net) else 'name'
- result = mydb.update_rows('datacenter_nets', action_dict['net-edit'],
- WHERE={'datacenter_id':datacenter_id, what: net})
- return result
- elif 'net-delete' in action_dict:
- net = action_dict['net-deelte'].get('net')
- what = 'vim_net_id' if utils.check_valid_uuid(net) else 'name'
- result = mydb.delete_row(FROM='datacenter_nets',
- WHERE={'datacenter_id':datacenter_id, what: net})
- return result
-
- else:
- raise NfvoException("Unknown action " + str(action_dict), HTTP_Bad_Request)
-
-
-def datacenter_edit_netmap(mydb, tenant_id, datacenter, netmap, action_dict):
- #get datacenter info
- datacenter_id, _ = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
-
- what = 'uuid' if utils.check_valid_uuid(netmap) else 'name'
- result = mydb.update_rows('datacenter_nets', action_dict['netmap'],
- WHERE={'datacenter_id':datacenter_id, what: netmap})
- return result
-
-
-def datacenter_new_netmap(mydb, tenant_id, datacenter, action_dict=None):
- #get datacenter info
- datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
- filter_dict={}
- if action_dict:
- action_dict = action_dict["netmap"]
- if 'vim_id' in action_dict:
- filter_dict["id"] = action_dict['vim_id']
- if 'vim_name' in action_dict:
- filter_dict["name"] = action_dict['vim_name']
- else:
- filter_dict["shared"] = True
-
- try:
- vim_nets = myvim.get_network_list(filter_dict=filter_dict)
- except vimconn.vimconnException as e:
- #logger.error("nfvo.datacenter_new_netmap() Not possible to get_network_list from VIM: %s ", str(e))
- raise NfvoException(str(e), HTTP_Internal_Server_Error)
- if len(vim_nets)>1 and action_dict:
- raise NfvoException("more than two networks found, specify with vim_id", HTTP_Conflict)
- elif len(vim_nets)==0: # and action_dict:
- raise NfvoException("Not found a network at VIM with " + str(filter_dict), HTTP_Not_Found)
- net_list=[]
- for net in vim_nets:
- net_nfvo={'datacenter_id': datacenter_id}
- if action_dict and "name" in action_dict:
- net_nfvo['name'] = action_dict['name']
- else:
- net_nfvo['name'] = net['name']
- #net_nfvo['description']= net['name']
- net_nfvo['vim_net_id'] = net['id']
- net_nfvo['type'] = net['type'][0:6] #change from ('ptp','data','bridge_data','bridge_man') to ('bridge','data','ptp')
- net_nfvo['shared'] = net['shared']
- net_nfvo['multipoint'] = False if net['type']=='ptp' else True
- try:
- net_id = mydb.new_row("datacenter_nets", net_nfvo, add_uuid=True)
- net_nfvo["status"] = "OK"
- net_nfvo["uuid"] = net_id
- except db_base_Exception as e:
- if action_dict:
- raise
- else:
- net_nfvo["status"] = "FAIL: " + str(e)
- net_list.append(net_nfvo)
- return net_list
-
-
-def vim_action_get(mydb, tenant_id, datacenter, item, name):
- #get datacenter info
- datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
- filter_dict={}
- if name:
- if utils.check_valid_uuid(name):
- filter_dict["id"] = name
- else:
- filter_dict["name"] = name
- try:
- if item=="networks":
- #filter_dict['tenant_id'] = myvim['tenant_id']
- content = myvim.get_network_list(filter_dict=filter_dict)
- elif item=="tenants":
- content = myvim.get_tenant_list(filter_dict=filter_dict)
- elif item == "images":
- content = myvim.get_image_list(filter_dict=filter_dict)
- else:
- raise NfvoException(item + "?", HTTP_Method_Not_Allowed)
- logger.debug("vim_action response %s", content) #update nets Change from VIM format to NFVO format
- if name and len(content)==1:
- return {item[:-1]: content[0]}
- elif name and len(content)==0:
- raise NfvoException("No {} found with ".format(item[:-1]) + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), filter_dict.iteritems())),
- datacenter)
- else:
- return {item: content}
- except vimconn.vimconnException as e:
- print "vim_action Not possible to get_%s_list from VIM: %s " % (item, str(e))
- raise NfvoException("Not possible to get_{}_list from VIM: {}".format(item, str(e)), e.http_code)
-
-
-def vim_action_delete(mydb, tenant_id, datacenter, item, name):
- #get datacenter info
- if tenant_id == "any":
- tenant_id=None
-
- datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
- #get uuid name
- content = vim_action_get(mydb, tenant_id, datacenter, item, name)
- logger.debug("vim_action_delete vim response: " + str(content))
- items = content.values()[0]
- if type(items)==list and len(items)==0:
- raise NfvoException("Not found " + item, HTTP_Not_Found)
- elif type(items)==list and len(items)>1:
- raise NfvoException("Found more than one {} with this name. Use uuid.".format(item), HTTP_Not_Found)
- else: # it is a dict
- item_id = items["id"]
- item_name = str(items.get("name"))
-
- try:
- if item=="networks":
- content = myvim.delete_network(item_id)
- elif item=="tenants":
- content = myvim.delete_tenant(item_id)
- elif item == "images":
- content = myvim.delete_image(item_id)
- else:
- raise NfvoException(item + "?", HTTP_Method_Not_Allowed)
- except vimconn.vimconnException as e:
- #logger.error( "vim_action Not possible to delete_{} {}from VIM: {} ".format(item, name, str(e)))
- raise NfvoException("Not possible to delete_{} {} from VIM: {}".format(item, name, str(e)), e.http_code)
-
- return "{} {} {} deleted".format(item[:-1], item_id,item_name)
-
-
-def vim_action_create(mydb, tenant_id, datacenter, item, descriptor):
- #get datacenter info
- logger.debug("vim_action_create descriptor %s", str(descriptor))
- if tenant_id == "any":
- tenant_id=None
- datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
- try:
- if item=="networks":
- net = descriptor["network"]
- net_name = net.pop("name")
- net_type = net.pop("type", "bridge")
- net_public = net.pop("shared", False)
- net_ipprofile = net.pop("ip_profile", None)
- net_vlan = net.pop("vlan", None)
- content = myvim.new_network(net_name, net_type, net_ipprofile, shared=net_public, vlan=net_vlan) #, **net)
- elif item=="tenants":
- tenant = descriptor["tenant"]
- content = myvim.new_tenant(tenant["name"], tenant.get("description"))
- else:
- raise NfvoException(item + "?", HTTP_Method_Not_Allowed)
- except vimconn.vimconnException as e:
- raise NfvoException("Not possible to create {} at VIM: {}".format(item, str(e)), e.http_code)
-
- return vim_action_get(mydb, tenant_id, datacenter, item, content)
+++ /dev/null
-# -*- 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
-##
-
-'''
-NFVO DB engine. It implements all the methods to interact with the Openmano Database
-'''
-__author__="Alfonso Tierno, Gerardo Garcia, Pablo Montes"
-__date__ ="$28-aug-2014 10:05:01$"
-
-import db_base
-import MySQLdb as mdb
-import json
-import yaml
-import time
-#import sys, os
-
-tables_with_createdat_field=["datacenters","instance_nets","instance_scenarios","instance_vms","instance_vnfs",
- "interfaces","nets","nfvo_tenants","scenarios","sce_interfaces","sce_nets",
- "sce_vnfs","tenants_datacenters","datacenter_tenants","vms","vnfs", "datacenter_nets"]
-
-class nfvo_db(db_base.db_base):
- def __init__(self, host=None, user=None, passwd=None, database=None, log_name='openmano.db', log_level=None):
- db_base.db_base.__init__(self, host, user, passwd, database, log_name, log_level)
- db_base.db_base.tables_with_created_field=tables_with_createdat_field
- return
-
- def new_vnf_as_a_whole(self,nfvo_tenant,vnf_name,vnf_descriptor,VNFCDict):
- self.logger.debug("Adding new vnf to the NFVO database")
- tries = 2
- while tries:
- created_time = time.time()
- try:
- with self.con:
-
- myVNFDict = {}
- myVNFDict["name"] = vnf_name
- myVNFDict["descriptor"] = vnf_descriptor['vnf'].get('descriptor')
- myVNFDict["public"] = vnf_descriptor['vnf'].get('public', "false")
- myVNFDict["description"] = vnf_descriptor['vnf']['description']
- myVNFDict["class"] = vnf_descriptor['vnf'].get('class',"MISC")
- myVNFDict["tenant_id"] = vnf_descriptor['vnf'].get("tenant_id")
-
- vnf_id = self._new_row_internal('vnfs', myVNFDict, add_uuid=True, root_uuid=None, created_time=created_time)
- #print "Adding new vms to the NFVO database"
- #For each vm, we must create the appropriate vm in the NFVO database.
- vmDict = {}
- for _,vm in VNFCDict.iteritems():
- #This code could make the name of the vms grow and grow.
- #If we agree to follow this convention, we should check with a regex that the vnfc name is not including yet the vnf name
- #vm['name'] = "%s-%s" % (vnf_name,vm['name'])
- #print "VM name: %s. Description: %s" % (vm['name'], vm['description'])
- vm["vnf_id"] = vnf_id
- created_time += 0.00001
- vm_id = self._new_row_internal('vms', vm, add_uuid=True, root_uuid=vnf_id, created_time=created_time)
- #print "Internal vm id in NFVO DB: %s" % vm_id
- vmDict[vm['name']] = vm_id
-
- #Collect the bridge interfaces of each VM/VNFC under the 'bridge-ifaces' field
- bridgeInterfacesDict = {}
- for vm in vnf_descriptor['vnf']['VNFC']:
- if 'bridge-ifaces' in vm:
- bridgeInterfacesDict[vm['name']] = {}
- for bridgeiface in vm['bridge-ifaces']:
- created_time += 0.00001
- if 'port-security' in bridgeiface:
- bridgeiface['port_security'] = bridgeiface.pop('port-security')
- if 'floating-ip' in bridgeiface:
- bridgeiface['floating_ip'] = bridgeiface.pop('floating-ip')
- db_base._convert_bandwidth(bridgeiface, logger=self.logger)
- bridgeInterfacesDict[vm['name']][bridgeiface['name']] = {}
- bridgeInterfacesDict[vm['name']][bridgeiface['name']]['vpci'] = bridgeiface.get('vpci',None)
- bridgeInterfacesDict[vm['name']][bridgeiface['name']]['mac'] = bridgeiface.get('mac_address',None)
- bridgeInterfacesDict[vm['name']][bridgeiface['name']]['bw'] = bridgeiface.get('bandwidth', None)
- bridgeInterfacesDict[vm['name']][bridgeiface['name']]['model'] = bridgeiface.get('model', None)
- bridgeInterfacesDict[vm['name']][bridgeiface['name']]['port_security'] = \
- int(bridgeiface.get('port_security', True))
- bridgeInterfacesDict[vm['name']][bridgeiface['name']]['floating_ip'] = \
- int(bridgeiface.get('floating_ip', False))
- bridgeInterfacesDict[vm['name']][bridgeiface['name']]['created_time'] = created_time
-
- # Collect the data interfaces of each VM/VNFC under the 'numas' field
- dataifacesDict = {}
- for vm in vnf_descriptor['vnf']['VNFC']:
- dataifacesDict[vm['name']] = {}
- for numa in vm.get('numas', []):
- for dataiface in numa.get('interfaces', []):
- created_time += 0.00001
- db_base._convert_bandwidth(dataiface, logger=self.logger)
- dataifacesDict[vm['name']][dataiface['name']] = {}
- dataifacesDict[vm['name']][dataiface['name']]['vpci'] = dataiface['vpci']
- dataifacesDict[vm['name']][dataiface['name']]['bw'] = dataiface['bandwidth']
- dataifacesDict[vm['name']][dataiface['name']]['model'] = "PF" if dataiface[
- 'dedicated'] == "yes" else (
- "VF" if dataiface['dedicated'] == "no" else "VFnotShared")
- dataifacesDict[vm['name']][dataiface['name']]['created_time'] = created_time
-
- #For each internal connection, we add it to the interfaceDict and we create the appropriate net in the NFVO database.
- #print "Adding new nets (VNF internal nets) to the NFVO database (if any)"
- internalconnList = []
- if 'internal-connections' in vnf_descriptor['vnf']:
- for net in vnf_descriptor['vnf']['internal-connections']:
- #print "Net name: %s. Description: %s" % (net['name'], net['description'])
-
- myNetDict = {}
- myNetDict["name"] = net['name']
- myNetDict["description"] = net['description']
- myNetDict["type"] = net['type']
- myNetDict["vnf_id"] = vnf_id
-
- created_time += 0.00001
- net_id = self._new_row_internal('nets', myNetDict, add_uuid=True, root_uuid=vnf_id, created_time=created_time)
-
- for element in net['elements']:
- ifaceItem = {}
- #ifaceItem["internal_name"] = "%s-%s-%s" % (net['name'],element['VNFC'], element['local_iface_name'])
- ifaceItem["internal_name"] = element['local_iface_name']
- #ifaceItem["vm_id"] = vmDict["%s-%s" % (vnf_name,element['VNFC'])]
- ifaceItem["vm_id"] = vmDict[element['VNFC']]
- ifaceItem["net_id"] = net_id
- ifaceItem["type"] = net['type']
- if ifaceItem ["type"] == "data":
- dataiface = dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ]
- ifaceItem["vpci"] = dataiface['vpci']
- ifaceItem["bw"] = dataiface['bw']
- ifaceItem["model"] = dataiface['model']
- created_time_iface = dataiface['created_time']
- else:
- bridgeiface = bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]
- ifaceItem["vpci"] = bridgeiface['vpci']
- ifaceItem["mac"] = bridgeiface['mac']
- ifaceItem["bw"] = bridgeiface['bw']
- ifaceItem["model"] = bridgeiface['model']
- ifaceItem["port_security"] = bridgeiface['port_security']
- ifaceItem["floating_ip"] = bridgeiface['floating_ip']
- created_time_iface = bridgeiface['created_time']
- internalconnList.append(ifaceItem)
- #print "Internal net id in NFVO DB: %s" % net_id
-
- #print "Adding internal interfaces to the NFVO database (if any)"
- for iface in internalconnList:
- #print "Iface name: %s" % iface['internal_name']
- iface_id = self._new_row_internal('interfaces', iface, add_uuid=True, root_uuid=vnf_id, created_time = created_time_iface)
- #print "Iface id in NFVO DB: %s" % iface_id
-
- #print "Adding external interfaces to the NFVO database"
- for iface in vnf_descriptor['vnf']['external-connections']:
- myIfaceDict = {}
- #myIfaceDict["internal_name"] = "%s-%s-%s" % (vnf_name,iface['VNFC'], iface['local_iface_name'])
- myIfaceDict["internal_name"] = iface['local_iface_name']
- #myIfaceDict["vm_id"] = vmDict["%s-%s" % (vnf_name,iface['VNFC'])]
- myIfaceDict["vm_id"] = vmDict[iface['VNFC']]
- myIfaceDict["external_name"] = iface['name']
- myIfaceDict["type"] = iface['type']
- if iface["type"] == "data":
- dataiface = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]
- myIfaceDict["vpci"] = dataiface['vpci']
- myIfaceDict["bw"] = dataiface['bw']
- myIfaceDict["model"] = dataiface['model']
- created_time_iface = dataiface['created_time']
- else:
- bridgeiface = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]
- myIfaceDict["vpci"] = bridgeiface['vpci']
- myIfaceDict["bw"] = bridgeiface['bw']
- myIfaceDict["model"] = bridgeiface['model']
- myIfaceDict["mac"] = bridgeiface['mac']
- myIfaceDict["port_security"]= bridgeiface['port_security']
- myIfaceDict["floating_ip"] = bridgeiface['floating_ip']
- created_time_iface = bridgeiface['created_time']
- #print "Iface name: %s" % iface['name']
- iface_id = self._new_row_internal('interfaces', myIfaceDict, add_uuid=True, root_uuid=vnf_id, created_time = created_time_iface)
- #print "Iface id in NFVO DB: %s" % iface_id
-
- return vnf_id
-
- except (mdb.Error, AttributeError) as e:
- self._format_error(e, tries)
- tries -= 1
-
- def new_vnf_as_a_whole2(self,nfvo_tenant,vnf_name,vnf_descriptor,VNFCDict):
- self.logger.debug("Adding new vnf to the NFVO database")
- tries = 2
- while tries:
- created_time = time.time()
- try:
- with self.con:
-
- myVNFDict = {}
- myVNFDict["name"] = vnf_name
- myVNFDict["descriptor"] = vnf_descriptor['vnf'].get('descriptor')
- myVNFDict["public"] = vnf_descriptor['vnf'].get('public', "false")
- myVNFDict["description"] = vnf_descriptor['vnf']['description']
- myVNFDict["class"] = vnf_descriptor['vnf'].get('class',"MISC")
- myVNFDict["tenant_id"] = vnf_descriptor['vnf'].get("tenant_id")
-
- vnf_id = self._new_row_internal('vnfs', myVNFDict, add_uuid=True, root_uuid=None, created_time=created_time)
- #print "Adding new vms to the NFVO database"
- #For each vm, we must create the appropriate vm in the NFVO database.
- vmDict = {}
- for _,vm in VNFCDict.iteritems():
- #This code could make the name of the vms grow and grow.
- #If we agree to follow this convention, we should check with a regex that the vnfc name is not including yet the vnf name
- #vm['name'] = "%s-%s" % (vnf_name,vm['name'])
- #print "VM name: %s. Description: %s" % (vm['name'], vm['description'])
- vm["vnf_id"] = vnf_id
- created_time += 0.00001
- vm_id = self._new_row_internal('vms', vm, add_uuid=True, root_uuid=vnf_id, created_time=created_time)
- #print "Internal vm id in NFVO DB: %s" % vm_id
- vmDict[vm['name']] = vm_id
-
- #Collect the bridge interfaces of each VM/VNFC under the 'bridge-ifaces' field
- bridgeInterfacesDict = {}
- for vm in vnf_descriptor['vnf']['VNFC']:
- if 'bridge-ifaces' in vm:
- bridgeInterfacesDict[vm['name']] = {}
- for bridgeiface in vm['bridge-ifaces']:
- created_time += 0.00001
- db_base._convert_bandwidth(bridgeiface, logger=self.logger)
- if 'port-security' in bridgeiface:
- bridgeiface['port_security'] = bridgeiface.pop('port-security')
- if 'floating-ip' in bridgeiface:
- bridgeiface['floating_ip'] = bridgeiface.pop('floating-ip')
- ifaceDict = {}
- ifaceDict['vpci'] = bridgeiface.get('vpci',None)
- ifaceDict['mac'] = bridgeiface.get('mac_address',None)
- ifaceDict['bw'] = bridgeiface.get('bandwidth', None)
- ifaceDict['model'] = bridgeiface.get('model', None)
- ifaceDict['port_security'] = int(bridgeiface.get('port_security', True))
- ifaceDict['floating_ip'] = int(bridgeiface.get('floating_ip', False))
- ifaceDict['created_time'] = created_time
- bridgeInterfacesDict[vm['name']][bridgeiface['name']] = ifaceDict
-
- # Collect the data interfaces of each VM/VNFC under the 'numas' field
- dataifacesDict = {}
- for vm in vnf_descriptor['vnf']['VNFC']:
- dataifacesDict[vm['name']] = {}
- for numa in vm.get('numas', []):
- for dataiface in numa.get('interfaces', []):
- created_time += 0.00001
- db_base._convert_bandwidth(dataiface, logger=self.logger)
- ifaceDict = {}
- ifaceDict['vpci'] = dataiface['vpci']
- ifaceDict['bw'] = dataiface['bandwidth']
- ifaceDict['model'] = "PF" if dataiface['dedicated'] == "yes" else \
- ("VF" if dataiface['dedicated'] == "no" else "VFnotShared")
- ifaceDict['created_time'] = created_time
- dataifacesDict[vm['name']][dataiface['name']] = ifaceDict
-
- #For each internal connection, we add it to the interfaceDict and we create the appropriate net in the NFVO database.
- #print "Adding new nets (VNF internal nets) to the NFVO database (if any)"
- if 'internal-connections' in vnf_descriptor['vnf']:
- for net in vnf_descriptor['vnf']['internal-connections']:
- #print "Net name: %s. Description: %s" % (net['name'], net['description'])
-
- myNetDict = {}
- myNetDict["name"] = net['name']
- myNetDict["description"] = net['description']
- if (net["implementation"] == "overlay"):
- net["type"] = "bridge"
- #It should give an error if the type is e-line. For the moment, we consider it as a bridge
- elif (net["implementation"] == "underlay"):
- if (net["type"] == "e-line"):
- net["type"] = "ptp"
- elif (net["type"] == "e-lan"):
- net["type"] = "data"
- net.pop("implementation")
- myNetDict["type"] = net['type']
- myNetDict["vnf_id"] = vnf_id
-
- created_time += 0.00001
- net_id = self._new_row_internal('nets', myNetDict, add_uuid=True, root_uuid=vnf_id, created_time=created_time)
-
- if "ip-profile" in net:
- ip_profile = net["ip-profile"]
- myIPProfileDict = {}
- myIPProfileDict["net_id"] = net_id
- myIPProfileDict["ip_version"] = ip_profile.get('ip-version',"IPv4")
- myIPProfileDict["subnet_address"] = ip_profile.get('subnet-address',None)
- myIPProfileDict["gateway_address"] = ip_profile.get('gateway-address',None)
- myIPProfileDict["dns_address"] = ip_profile.get('dns-address',None)
- if ("dhcp" in ip_profile):
- myIPProfileDict["dhcp_enabled"] = ip_profile["dhcp"].get('enabled',"true")
- myIPProfileDict["dhcp_start_address"] = ip_profile["dhcp"].get('start-address',None)
- myIPProfileDict["dhcp_count"] = ip_profile["dhcp"].get('count',None)
-
- created_time += 0.00001
- ip_profile_id = self._new_row_internal('ip_profiles', myIPProfileDict)
-
- for element in net['elements']:
- ifaceItem = {}
- #ifaceItem["internal_name"] = "%s-%s-%s" % (net['name'],element['VNFC'], element['local_iface_name'])
- ifaceItem["internal_name"] = element['local_iface_name']
- #ifaceItem["vm_id"] = vmDict["%s-%s" % (vnf_name,element['VNFC'])]
- ifaceItem["vm_id"] = vmDict[element['VNFC']]
- ifaceItem["net_id"] = net_id
- ifaceItem["type"] = net['type']
- ifaceItem["ip_address"] = element.get('ip_address',None)
- if ifaceItem ["type"] == "data":
- ifaceDict = dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ]
- ifaceItem["vpci"] = ifaceDict['vpci']
- ifaceItem["bw"] = ifaceDict['bw']
- ifaceItem["model"] = ifaceDict['model']
- else:
- ifaceDict = bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]
- ifaceItem["vpci"] = ifaceDict['vpci']
- ifaceItem["mac"] = ifaceDict['mac']
- ifaceItem["bw"] = ifaceDict['bw']
- ifaceItem["model"] = ifaceDict['model']
- ifaceItem["port_security"] = ifaceDict['port_security']
- ifaceItem["floating_ip"] = ifaceDict['floating_ip']
- created_time_iface = ifaceDict["created_time"]
- #print "Iface name: %s" % iface['internal_name']
- iface_id = self._new_row_internal('interfaces', ifaceItem, add_uuid=True, root_uuid=vnf_id, created_time=created_time_iface)
- #print "Iface id in NFVO DB: %s" % iface_id
-
- #print "Adding external interfaces to the NFVO database"
- for iface in vnf_descriptor['vnf']['external-connections']:
- myIfaceDict = {}
- #myIfaceDict["internal_name"] = "%s-%s-%s" % (vnf_name,iface['VNFC'], iface['local_iface_name'])
- myIfaceDict["internal_name"] = iface['local_iface_name']
- #myIfaceDict["vm_id"] = vmDict["%s-%s" % (vnf_name,iface['VNFC'])]
- myIfaceDict["vm_id"] = vmDict[iface['VNFC']]
- myIfaceDict["external_name"] = iface['name']
- myIfaceDict["type"] = iface['type']
- if iface["type"] == "data":
- myIfaceDict["vpci"] = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['vpci']
- myIfaceDict["bw"] = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['bw']
- myIfaceDict["model"] = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['model']
- created_time_iface = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['created_time']
- else:
- myIfaceDict["vpci"] = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['vpci']
- myIfaceDict["bw"] = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['bw']
- myIfaceDict["model"] = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['model']
- myIfaceDict["mac"] = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['mac']
- myIfaceDict["port_security"] = \
- bridgeInterfacesDict[iface['VNFC']][iface['local_iface_name']]['port_security']
- myIfaceDict["floating_ip"] = \
- bridgeInterfacesDict[iface['VNFC']][iface['local_iface_name']]['floating_ip']
- created_time_iface = bridgeInterfacesDict[iface['VNFC']][iface['local_iface_name']]['created_time']
- #print "Iface name: %s" % iface['name']
- iface_id = self._new_row_internal('interfaces', myIfaceDict, add_uuid=True, root_uuid=vnf_id, created_time=created_time_iface)
- #print "Iface id in NFVO DB: %s" % iface_id
-
- return vnf_id
-
- except (mdb.Error, AttributeError) as e:
- self._format_error(e, tries)
-# except KeyError as e2:
-# exc_type, exc_obj, exc_tb = sys.exc_info()
-# fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
-# self.logger.debug("Exception type: %s; Filename: %s; Line number: %s", exc_type, fname, exc_tb.tb_lineno)
-# raise KeyError
- tries -= 1
-
- def new_scenario(self, scenario_dict):
- tries = 2
- while tries:
- created_time = time.time()
- try:
- with self.con:
- self.cur = self.con.cursor()
- tenant_id = scenario_dict.get('tenant_id')
- #scenario
- INSERT_={'tenant_id': tenant_id,
- 'name': scenario_dict['name'],
- 'description': scenario_dict['description'],
- 'public': scenario_dict.get('public', "false")}
-
- scenario_uuid = self._new_row_internal('scenarios', INSERT_, add_uuid=True, root_uuid=None, created_time=created_time)
- #sce_nets
- for net in scenario_dict['nets'].values():
- net_dict={'scenario_id': scenario_uuid}
- net_dict["name"] = net["name"]
- net_dict["type"] = net["type"]
- net_dict["description"] = net.get("description")
- net_dict["external"] = net.get("external", False)
- if "graph" in net:
- #net["graph"]=yaml.safe_dump(net["graph"],default_flow_style=True,width=256)
- #TODO, must be json because of the GUI, change to yaml
- net_dict["graph"]=json.dumps(net["graph"])
- created_time += 0.00001
- net_uuid = self._new_row_internal('sce_nets', net_dict, add_uuid=True, root_uuid=scenario_uuid, created_time=created_time)
- net['uuid']=net_uuid
-
- if net.get("ip-profile"):
- ip_profile = net["ip-profile"]
- myIPProfileDict = {
- "sce_net_id": net_uuid,
- "ip_version": ip_profile.get('ip-version', "IPv4"),
- "subnet_address": ip_profile.get('subnet-address'),
- "gateway_address": ip_profile.get('gateway-address'),
- "dns_address": ip_profile.get('dns-address')}
- if "dhcp" in ip_profile:
- myIPProfileDict["dhcp_enabled"] = ip_profile["dhcp"].get('enabled', "true")
- myIPProfileDict["dhcp_start_address"] = ip_profile["dhcp"].get('start-address')
- myIPProfileDict["dhcp_count"] = ip_profile["dhcp"].get('count')
- self._new_row_internal('ip_profiles', myIPProfileDict)
-
- # sce_vnfs
- for k, vnf in scenario_dict['vnfs'].items():
- INSERT_ = {'scenario_id': scenario_uuid,
- 'name': k,
- 'vnf_id': vnf['uuid'],
- # 'description': scenario_dict['name']
- 'description': vnf['description']}
- if "graph" in vnf:
- #I NSERT_["graph"]=yaml.safe_dump(vnf["graph"],default_flow_style=True,width=256)
- # TODO, must be json because of the GUI, change to yaml
- INSERT_["graph"] = json.dumps(vnf["graph"])
- created_time += 0.00001
- scn_vnf_uuid = self._new_row_internal('sce_vnfs', INSERT_, add_uuid=True,
- root_uuid=scenario_uuid, created_time=created_time)
- vnf['scn_vnf_uuid']=scn_vnf_uuid
- # sce_interfaces
- for iface in vnf['ifaces'].values():
- # print 'iface', iface
- if 'net_key' not in iface:
- continue
- iface['net_id'] = scenario_dict['nets'][ iface['net_key'] ]['uuid']
- INSERT_={'sce_vnf_id': scn_vnf_uuid,
- 'sce_net_id': iface['net_id'],
- 'interface_id': iface['uuid'],
- 'ip_address': iface.get('ip_address')}
- created_time += 0.00001
- iface_uuid = self._new_row_internal('sce_interfaces', INSERT_, add_uuid=True,
- root_uuid=scenario_uuid, created_time=created_time)
-
- return scenario_uuid
-
- except (mdb.Error, AttributeError) as e:
- self._format_error(e, tries)
- tries -= 1
-
- def edit_scenario(self, scenario_dict):
- tries = 2
- while tries:
- modified_time = time.time()
- item_changed=0
- try:
- with self.con:
- self.cur = self.con.cursor()
- #check that scenario exist
- tenant_id = scenario_dict.get('tenant_id')
- scenario_uuid = scenario_dict['uuid']
-
- where_text = "uuid='{}'".format(scenario_uuid)
- if not tenant_id and tenant_id != "any":
- where_text += " AND (tenant_id='{}' OR public='True')".format(tenant_id)
- cmd = "SELECT * FROM scenarios WHERE "+ where_text
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- self.cur.fetchall()
- if self.cur.rowcount==0:
- raise db_base.db_base_Exception("No scenario found with this criteria " + where_text, db_base.HTTP_Bad_Request)
- elif self.cur.rowcount>1:
- raise db_base.db_base_Exception("More than one scenario found with this criteria " + where_text, db_base.HTTP_Bad_Request)
-
- #scenario
- nodes = {}
- topology = scenario_dict.pop("topology", None)
- if topology != None and "nodes" in topology:
- nodes = topology.get("nodes",{})
- UPDATE_ = {}
- if "name" in scenario_dict: UPDATE_["name"] = scenario_dict["name"]
- if "description" in scenario_dict: UPDATE_["description"] = scenario_dict["description"]
- if len(UPDATE_)>0:
- WHERE_={'tenant_id': tenant_id, 'uuid': scenario_uuid}
- item_changed += self._update_rows('scenarios', UPDATE_, WHERE_, modified_time=modified_time)
- #sce_nets
- for node_id, node in nodes.items():
- if "graph" in node:
- #node["graph"] = yaml.safe_dump(node["graph"],default_flow_style=True,width=256)
- #TODO, must be json because of the GUI, change to yaml
- node["graph"] = json.dumps(node["graph"])
- WHERE_={'scenario_id': scenario_uuid, 'uuid': node_id}
- #Try to change at sce_nets(version 0 API backward compatibility and sce_vnfs)
- item_changed += self._update_rows('sce_nets', node, WHERE_)
- item_changed += self._update_rows('sce_vnfs', node, WHERE_, modified_time=modified_time)
- return item_changed
-
- except (mdb.Error, AttributeError) as e:
- self._format_error(e, tries)
- tries -= 1
-
-# def get_instance_scenario(self, instance_scenario_id, tenant_id=None):
-# '''Obtain the scenario instance information, filtering by one or serveral of the tenant, uuid or name
-# instance_scenario_id is the uuid or the name if it is not a valid uuid format
-# Only one scenario isntance must mutch the filtering or an error is returned
-# '''
-# print "1******************************************************************"
-# try:
-# with self.con:
-# self.cur = self.con.cursor(mdb.cursors.DictCursor)
-# #scenario table
-# where_list=[]
-# if tenant_id is not None: where_list.append( "tenant_id='" + tenant_id +"'" )
-# if db_base._check_valid_uuid(instance_scenario_id):
-# where_list.append( "uuid='" + instance_scenario_id +"'" )
-# else:
-# where_list.append( "name='" + instance_scenario_id +"'" )
-# where_text = " AND ".join(where_list)
-# self.cur.execute("SELECT * FROM instance_scenarios WHERE "+ where_text)
-# rows = self.cur.fetchall()
-# if self.cur.rowcount==0:
-# return -HTTP_Bad_Request, "No scenario instance found with this criteria " + where_text
-# elif self.cur.rowcount>1:
-# return -HTTP_Bad_Request, "More than one scenario instance found with this criteria " + where_text
-# instance_scenario_dict = rows[0]
-#
-# #instance_vnfs
-# self.cur.execute("SELECT uuid,vnf_id FROM instance_vnfs WHERE instance_scenario_id='"+ instance_scenario_dict['uuid'] + "'")
-# instance_scenario_dict['instance_vnfs'] = self.cur.fetchall()
-# for vnf in instance_scenario_dict['instance_vnfs']:
-# #instance_vms
-# self.cur.execute("SELECT uuid, vim_vm_id "+
-# "FROM instance_vms "+
-# "WHERE instance_vnf_id='" + vnf['uuid'] +"'"
-# )
-# vnf['instance_vms'] = self.cur.fetchall()
-# #instance_nets
-# self.cur.execute("SELECT uuid, vim_net_id FROM instance_nets WHERE instance_scenario_id='"+ instance_scenario_dict['uuid'] + "'")
-# instance_scenario_dict['instance_nets'] = self.cur.fetchall()
-#
-# #instance_interfaces
-# self.cur.execute("SELECT uuid, vim_interface_id, instance_vm_id, instance_net_id FROM instance_interfaces WHERE instance_scenario_id='"+ instance_scenario_dict['uuid'] + "'")
-# instance_scenario_dict['instance_interfaces'] = self.cur.fetchall()
-#
-# db_base._convert_datetime2str(instance_scenario_dict)
-# db_base._convert_str2boolean(instance_scenario_dict, ('public','shared','external') )
-# print "2******************************************************************"
-# return 1, instance_scenario_dict
-# except (mdb.Error, AttributeError) as e:
-# print "nfvo_db.get_instance_scenario DB Exception %d: %s" % (e.args[0], e.args[1])
-# return self._format_error(e)
-
- def get_scenario(self, scenario_id, tenant_id=None, datacenter_id=None):
- '''Obtain the scenario information, filtering by one or serveral of the tenant, uuid or name
- scenario_id is the uuid or the name if it is not a valid uuid format
- if datacenter_id is provided, it supply aditional vim_id fields with the matching vim uuid
- Only one scenario must mutch the filtering or an error is returned
- '''
- tries = 2
- while tries:
- try:
- with self.con:
- self.cur = self.con.cursor(mdb.cursors.DictCursor)
- where_text = "uuid='{}'".format(scenario_id)
- if not tenant_id and tenant_id != "any":
- where_text += " AND (tenant_id='{}' OR public='True')".format(tenant_id)
- cmd = "SELECT * FROM scenarios WHERE " + where_text
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- rows = self.cur.fetchall()
- if self.cur.rowcount==0:
- raise db_base.db_base_Exception("No scenario found with this criteria " + where_text, db_base.HTTP_Bad_Request)
- elif self.cur.rowcount>1:
- raise db_base.db_base_Exception("More than one scenario found with this criteria " + where_text, db_base.HTTP_Bad_Request)
- scenario_dict = rows[0]
- if scenario_dict["cloud_config"]:
- scenario_dict["cloud-config"] = yaml.load(scenario_dict["cloud_config"])
- del scenario_dict["cloud_config"]
- #sce_vnfs
- cmd = "SELECT uuid,name,vnf_id,description FROM sce_vnfs WHERE scenario_id='{}' ORDER BY created_at".format(scenario_dict['uuid'])
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- scenario_dict['vnfs'] = self.cur.fetchall()
- for vnf in scenario_dict['vnfs']:
- #sce_interfaces
- cmd = "SELECT scei.uuid,scei.sce_net_id,scei.interface_id,i.external_name,scei.ip_address FROM sce_interfaces as scei join interfaces as i on scei.interface_id=i.uuid WHERE scei.sce_vnf_id='{}' ORDER BY scei.created_at".format(vnf['uuid'])
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- vnf['interfaces'] = self.cur.fetchall()
- #vms
- cmd = "SELECT vms.uuid as uuid, flavor_id, image_id, vms.name as name, vms.description as description, vms.boot_data as boot_data " \
- " FROM vnfs join vms on vnfs.uuid=vms.vnf_id " \
- " WHERE vnfs.uuid='" + vnf['vnf_id'] +"'" \
- " ORDER BY vms.created_at"
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- vnf['vms'] = self.cur.fetchall()
- for vm in vnf['vms']:
- if vm["boot_data"]:
- vm["boot_data"] = yaml.safe_load(vm["boot_data"])
- else:
- del vm["boot_data"]
- if datacenter_id!=None:
- cmd = "SELECT vim_id FROM datacenters_images WHERE image_id='{}' AND datacenter_id='{}'".format(vm['image_id'],datacenter_id)
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- if self.cur.rowcount==1:
- vim_image_dict = self.cur.fetchone()
- vm['vim_image_id']=vim_image_dict['vim_id']
- cmd = "SELECT vim_id FROM datacenters_flavors WHERE flavor_id='{}' AND datacenter_id='{}'".format(vm['flavor_id'],datacenter_id)
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- if self.cur.rowcount==1:
- vim_flavor_dict = self.cur.fetchone()
- vm['vim_flavor_id']=vim_flavor_dict['vim_id']
-
- #interfaces
- cmd = "SELECT uuid,internal_name,external_name,net_id,type,vpci,mac,bw,model,ip_address," \
- "floating_ip, port_security" \
- " FROM interfaces" \
- " WHERE vm_id='{}'" \
- " ORDER BY created_at".format(vm['uuid'])
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- vm['interfaces'] = self.cur.fetchall()
- for index in range(0,len(vm['interfaces'])):
- vm['interfaces'][index]['port-security'] = vm['interfaces'][index].pop("port_security")
- vm['interfaces'][index]['floating-ip'] = vm['interfaces'][index].pop("floating_ip")
- #nets every net of a vms
- cmd = "SELECT uuid,name,type,description FROM nets WHERE vnf_id='{}'".format(vnf['vnf_id'])
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- vnf['nets'] = self.cur.fetchall()
- for vnf_net in vnf['nets']:
- SELECT_ = "ip_version,subnet_address,gateway_address,dns_address,dhcp_enabled,dhcp_start_address,dhcp_count"
- cmd = "SELECT {} FROM ip_profiles WHERE net_id='{}'".format(SELECT_,vnf_net['uuid'])
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- ipprofiles = self.cur.fetchall()
- if self.cur.rowcount==1:
- vnf_net["ip_profile"] = ipprofiles[0]
- elif self.cur.rowcount>1:
- raise db_base.db_base_Exception("More than one ip-profile found with this criteria: net_id='{}'".format(vnf_net['uuid']), db_base.HTTP_Bad_Request)
-
- #sce_nets
- cmd = "SELECT uuid,name,type,external,description" \
- " FROM sce_nets WHERE scenario_id='{}'" \
- " ORDER BY created_at ".format(scenario_dict['uuid'])
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- scenario_dict['nets'] = self.cur.fetchall()
- #datacenter_nets
- for net in scenario_dict['nets']:
- if str(net['external']) == 'false':
- SELECT_ = "ip_version,subnet_address,gateway_address,dns_address,dhcp_enabled,dhcp_start_address,dhcp_count"
- cmd = "SELECT {} FROM ip_profiles WHERE sce_net_id='{}'".format(SELECT_,net['uuid'])
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- ipprofiles = self.cur.fetchall()
- if self.cur.rowcount==1:
- net["ip_profile"] = ipprofiles[0]
- elif self.cur.rowcount>1:
- raise db_base.db_base_Exception("More than one ip-profile found with this criteria: sce_net_id='{}'".format(net['uuid']), db_base.HTTP_Bad_Request)
- continue
- WHERE_=" WHERE name='{}'".format(net['name'])
- if datacenter_id!=None:
- WHERE_ += " AND datacenter_id='{}'".format(datacenter_id)
- cmd = "SELECT vim_net_id FROM datacenter_nets" + WHERE_
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- d_net = self.cur.fetchone()
- if d_net==None or datacenter_id==None:
- #print "nfvo_db.get_scenario() WARNING external net %s not found" % net['name']
- net['vim_id']=None
- else:
- net['vim_id']=d_net['vim_net_id']
-
- db_base._convert_datetime2str(scenario_dict)
- db_base._convert_str2boolean(scenario_dict, ('public','shared','external','port-security','floating-ip') )
- return scenario_dict
- except (mdb.Error, AttributeError) as e:
- self._format_error(e, tries)
- tries -= 1
-
- def delete_scenario(self, scenario_id, tenant_id=None):
- '''Deletes a scenario, filtering by one or several of the tenant, uuid or name
- scenario_id is the uuid or the name if it is not a valid uuid format
- Only one scenario must mutch the filtering or an error is returned
- '''
- tries = 2
- while tries:
- try:
- with self.con:
- self.cur = self.con.cursor(mdb.cursors.DictCursor)
-
- #scenario table
- where_text = "uuid='{}'".format(scenario_id)
- if not tenant_id and tenant_id != "any":
- where_text += " AND (tenant_id='{}' OR public='True')".format(tenant_id)
- cmd = "SELECT * FROM scenarios WHERE "+ where_text
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- rows = self.cur.fetchall()
- if self.cur.rowcount==0:
- raise db_base.db_base_Exception("No scenario found where " + where_text, db_base.HTTP_Bad_Request)
- elif self.cur.rowcount>1:
- raise db_base.db_base_Exception("More than one scenario found where " + where_text, db_base.HTTP_Bad_Request)
- scenario_uuid = rows[0]["uuid"]
- scenario_name = rows[0]["name"]
-
- #sce_vnfs
- cmd = "DELETE FROM scenarios WHERE uuid='{}'".format(scenario_uuid)
- self.logger.debug(cmd)
- self.cur.execute(cmd)
-
- return scenario_uuid + " " + scenario_name
- except (mdb.Error, AttributeError) as e:
- self._format_error(e, tries, "delete", "instances running")
- tries -= 1
-
- def new_instance_scenario_as_a_whole(self,tenant_id,instance_scenario_name,instance_scenario_description,scenarioDict):
- tries = 2
- while tries:
- created_time = time.time()
- try:
- with self.con:
- self.cur = self.con.cursor()
- #instance_scenarios
- datacenter_id = scenarioDict['datacenter_id']
- INSERT_={'tenant_id': tenant_id,
- 'datacenter_tenant_id': scenarioDict["datacenter2tenant"][datacenter_id],
- 'name': instance_scenario_name,
- 'description': instance_scenario_description,
- 'scenario_id' : scenarioDict['uuid'],
- 'datacenter_id': datacenter_id
- }
- if scenarioDict.get("cloud-config"):
- INSERT_["cloud_config"] = yaml.safe_dump(scenarioDict["cloud-config"], default_flow_style=True, width=256)
-
- instance_uuid = self._new_row_internal('instance_scenarios', INSERT_, add_uuid=True, root_uuid=None, created_time=created_time)
-
- net_scene2instance={}
- #instance_nets #nets interVNF
- for net in scenarioDict['nets']:
- net_scene2instance[ net['uuid'] ] ={}
- datacenter_site_id = net.get('datacenter_id', datacenter_id)
- if not "vim_id_sites" in net:
- net["vim_id_sites"] ={datacenter_site_id: net['vim_id']}
- net["vim_id_sites"]["datacenter_site_id"] = {datacenter_site_id: net['vim_id']}
- sce_net_id = net.get("uuid")
-
- for datacenter_site_id,vim_id in net["vim_id_sites"].iteritems():
- INSERT_={'vim_net_id': vim_id, 'created': net.get('created', False), 'instance_scenario_id':instance_uuid } #, 'type': net['type']
- INSERT_['datacenter_id'] = datacenter_site_id
- INSERT_['datacenter_tenant_id'] = scenarioDict["datacenter2tenant"][datacenter_site_id]
- if sce_net_id:
- INSERT_['sce_net_id'] = sce_net_id
- created_time += 0.00001
- instance_net_uuid = self._new_row_internal('instance_nets', INSERT_, True, instance_uuid, created_time)
- net_scene2instance[ sce_net_id ][datacenter_site_id] = instance_net_uuid
- net['uuid'] = instance_net_uuid #overwrite scnario uuid by instance uuid
-
- if 'ip_profile' in net:
- net['ip_profile']['net_id'] = None
- net['ip_profile']['sce_net_id'] = None
- net['ip_profile']['instance_net_id'] = instance_net_uuid
- created_time += 0.00001
- ip_profile_id = self._new_row_internal('ip_profiles', net['ip_profile'])
-
- #instance_vnfs
- for vnf in scenarioDict['vnfs']:
- datacenter_site_id = vnf.get('datacenter_id', datacenter_id)
- INSERT_={'instance_scenario_id': instance_uuid, 'vnf_id': vnf['vnf_id'] }
- INSERT_['datacenter_id'] = datacenter_site_id
- INSERT_['datacenter_tenant_id'] = scenarioDict["datacenter2tenant"][datacenter_site_id]
- if vnf.get("uuid"):
- INSERT_['sce_vnf_id'] = vnf['uuid']
- created_time += 0.00001
- instance_vnf_uuid = self._new_row_internal('instance_vnfs', INSERT_, True, instance_uuid, created_time)
- vnf['uuid'] = instance_vnf_uuid #overwrite scnario uuid by instance uuid
-
- #instance_nets #nets intraVNF
- for net in vnf['nets']:
- net_scene2instance[ net['uuid'] ] = {}
- INSERT_={'vim_net_id': net['vim_id'], 'created': net.get('created', False), 'instance_scenario_id':instance_uuid } #, 'type': net['type']
- INSERT_['datacenter_id'] = net.get('datacenter_id', datacenter_site_id)
- INSERT_['datacenter_tenant_id'] = scenarioDict["datacenter2tenant"][datacenter_id]
- if net.get("uuid"):
- INSERT_['net_id'] = net['uuid']
- created_time += 0.00001
- instance_net_uuid = self._new_row_internal('instance_nets', INSERT_, True, instance_uuid, created_time)
- net_scene2instance[ net['uuid'] ][datacenter_site_id] = instance_net_uuid
- net['uuid'] = instance_net_uuid #overwrite scnario uuid by instance uuid
-
- if 'ip_profile' in net:
- net['ip_profile']['net_id'] = None
- net['ip_profile']['sce_net_id'] = None
- net['ip_profile']['instance_net_id'] = instance_net_uuid
- created_time += 0.00001
- ip_profile_id = self._new_row_internal('ip_profiles', net['ip_profile'])
-
- #instance_vms
- for vm in vnf['vms']:
- INSERT_={'instance_vnf_id': instance_vnf_uuid, 'vm_id': vm['uuid'], 'vim_vm_id': vm['vim_id'] }
- created_time += 0.00001
- instance_vm_uuid = self._new_row_internal('instance_vms', INSERT_, True, instance_uuid, created_time)
- vm['uuid'] = instance_vm_uuid #overwrite scnario uuid by instance uuid
-
- #instance_interfaces
- for interface in vm['interfaces']:
- net_id = interface.get('net_id', None)
- if net_id is None:
- #check if is connected to a inter VNFs net
- for iface in vnf['interfaces']:
- if iface['interface_id'] == interface['uuid']:
- if 'ip_address' in iface:
- interface['ip_address'] = iface['ip_address']
- net_id = iface.get('sce_net_id', None)
- break
- if net_id is None:
- continue
- interface_type='external' if interface['external_name'] is not None else 'internal'
- INSERT_={'instance_vm_id': instance_vm_uuid, 'instance_net_id': net_scene2instance[net_id][datacenter_site_id],
- 'interface_id': interface['uuid'], 'vim_interface_id': interface.get('vim_id'), 'type': interface_type,
- 'ip_address': interface.get('ip_address'), 'floating_ip': int(interface.get('floating-ip',False)),
- 'port_security': int(interface.get('port-security',True))}
- #created_time += 0.00001
- interface_uuid = self._new_row_internal('instance_interfaces', INSERT_, True, instance_uuid) #, created_time)
- interface['uuid'] = interface_uuid #overwrite scnario uuid by instance uuid
- return instance_uuid
- except (mdb.Error, AttributeError) as e:
- self._format_error(e, tries)
- tries -= 1
-
- def get_instance_scenario(self, instance_id, tenant_id=None, verbose=False):
- '''Obtain the instance information, filtering by one or several of the tenant, uuid or name
- instance_id is the uuid or the name if it is not a valid uuid format
- Only one instance must mutch the filtering or an error is returned
- '''
- tries = 2
- while tries:
- try:
- with self.con:
- self.cur = self.con.cursor(mdb.cursors.DictCursor)
- #instance table
- where_list=[]
- if tenant_id is not None: where_list.append( "inst.tenant_id='" + tenant_id +"'" )
- if db_base._check_valid_uuid(instance_id):
- where_list.append( "inst.uuid='" + instance_id +"'" )
- else:
- where_list.append( "inst.name='" + instance_id +"'" )
- where_text = " AND ".join(where_list)
- cmd = "SELECT inst.uuid as uuid,inst.name as name,inst.scenario_id as scenario_id, datacenter_id" +\
- " ,datacenter_tenant_id, s.name as scenario_name,inst.tenant_id as tenant_id" + \
- " ,inst.description as description,inst.created_at as created_at" +\
- " ,inst.cloud_config as 'cloud_config'" +\
- " FROM instance_scenarios as inst join scenarios as s on inst.scenario_id=s.uuid"+\
- " WHERE " + where_text
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- rows = self.cur.fetchall()
-
- if self.cur.rowcount==0:
- raise db_base.db_base_Exception("No instance found where " + where_text, db_base.HTTP_Not_Found)
- elif self.cur.rowcount>1:
- raise db_base.db_base_Exception("More than one instance found where " + where_text, db_base.HTTP_Bad_Request)
- instance_dict = rows[0]
- if instance_dict["cloud_config"]:
- instance_dict["cloud-config"] = yaml.load(instance_dict["cloud_config"])
- del instance_dict["cloud_config"]
-
- #instance_vnfs
- cmd = "SELECT iv.uuid as uuid,sv.vnf_id as vnf_id,sv.name as vnf_name, sce_vnf_id, datacenter_id, datacenter_tenant_id"\
- " FROM instance_vnfs as iv join sce_vnfs as sv on iv.sce_vnf_id=sv.uuid" \
- " WHERE iv.instance_scenario_id='{}'" \
- " ORDER BY iv.created_at ".format(instance_dict['uuid'])
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- instance_dict['vnfs'] = self.cur.fetchall()
- for vnf in instance_dict['vnfs']:
- vnf_manage_iface_list=[]
- #instance vms
- cmd = "SELECT iv.uuid as uuid, vim_vm_id, status, error_msg, vim_info, iv.created_at as created_at, name "\
- " FROM instance_vms as iv join vms on iv.vm_id=vms.uuid "\
- " WHERE instance_vnf_id='{}' ORDER BY iv.created_at".format(vnf['uuid'])
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- vnf['vms'] = self.cur.fetchall()
- for vm in vnf['vms']:
- vm_manage_iface_list=[]
- #instance_interfaces
- cmd = "SELECT vim_interface_id, instance_net_id, internal_name,external_name, mac_address,"\
- " ii.ip_address as ip_address, vim_info, i.type as type"\
- " FROM instance_interfaces as ii join interfaces as i on ii.interface_id=i.uuid"\
- " WHERE instance_vm_id='{}' ORDER BY created_at".format(vm['uuid'])
- self.logger.debug(cmd)
- self.cur.execute(cmd )
- vm['interfaces'] = self.cur.fetchall()
- for iface in vm['interfaces']:
- if iface["type"] == "mgmt" and iface["ip_address"]:
- vnf_manage_iface_list.append(iface["ip_address"])
- vm_manage_iface_list.append(iface["ip_address"])
- if not verbose:
- del iface["type"]
- if vm_manage_iface_list: vm["ip_address"] = ",".join(vm_manage_iface_list)
- if vnf_manage_iface_list: vnf["ip_address"] = ",".join(vnf_manage_iface_list)
-
- #instance_nets
- #select_text = "instance_nets.uuid as uuid,sce_nets.name as net_name,instance_nets.vim_net_id as net_id,instance_nets.status as status,instance_nets.external as external"
- #from_text = "instance_nets join instance_scenarios on instance_nets.instance_scenario_id=instance_scenarios.uuid " + \
- # "join sce_nets on instance_scenarios.scenario_id=sce_nets.scenario_id"
- #where_text = "instance_nets.instance_scenario_id='"+ instance_dict['uuid'] + "'"
- cmd = "SELECT uuid,vim_net_id,status,error_msg,vim_info,created, sce_net_id, net_id as vnf_net_id, datacenter_id, datacenter_tenant_id"\
- " FROM instance_nets" \
- " WHERE instance_scenario_id='{}' ORDER BY created_at".format(instance_dict['uuid'])
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- instance_dict['nets'] = self.cur.fetchall()
-
- db_base._convert_datetime2str(instance_dict)
- db_base._convert_str2boolean(instance_dict, ('public','shared','created') )
- return instance_dict
- except (mdb.Error, AttributeError) as e:
- self._format_error(e, tries)
- tries -= 1
-
- def delete_instance_scenario(self, instance_id, tenant_id=None):
- '''Deletes a instance_Scenario, filtering by one or serveral of the tenant, uuid or name
- instance_id is the uuid or the name if it is not a valid uuid format
- Only one instance_scenario must mutch the filtering or an error is returned
- '''
- tries = 2
- while tries:
- try:
- with self.con:
- self.cur = self.con.cursor(mdb.cursors.DictCursor)
-
- #instance table
- where_list=[]
- if tenant_id is not None: where_list.append( "tenant_id='" + tenant_id +"'" )
- if db_base._check_valid_uuid(instance_id):
- where_list.append( "uuid='" + instance_id +"'" )
- else:
- where_list.append( "name='" + instance_id +"'" )
- where_text = " AND ".join(where_list)
- cmd = "SELECT * FROM instance_scenarios WHERE "+ where_text
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- rows = self.cur.fetchall()
-
- if self.cur.rowcount==0:
- raise db_base.db_base_Exception("No instance found where " + where_text, db_base.HTTP_Bad_Request)
- elif self.cur.rowcount>1:
- raise db_base.db_base_Exception("More than one instance found where " + where_text, db_base.HTTP_Bad_Request)
- instance_uuid = rows[0]["uuid"]
- instance_name = rows[0]["name"]
-
- #sce_vnfs
- cmd = "DELETE FROM instance_scenarios WHERE uuid='{}'".format(instance_uuid)
- self.logger.debug(cmd)
- self.cur.execute(cmd)
-
- return instance_uuid + " " + instance_name
- except (mdb.Error, AttributeError) as e:
- self._format_error(e, tries, "delete", "No dependences can avoid deleting!!!!")
- tries -= 1
-
- def new_instance_scenario(self, instance_scenario_dict, tenant_id):
- #return self.new_row('vnfs', vnf_dict, None, tenant_id, True, True)
- return self._new_row_internal('instance_scenarios', instance_scenario_dict, tenant_id, add_uuid=True, root_uuid=None, log=True)
-
- def update_instance_scenario(self, instance_scenario_dict):
- #TODO:
- return
-
- def new_instance_vnf(self, instance_vnf_dict, tenant_id, instance_scenario_id = None):
- #return self.new_row('vms', vm_dict, tenant_id, True, True)
- return self._new_row_internal('instance_vnfs', instance_vnf_dict, tenant_id, add_uuid=True, root_uuid=instance_scenario_id, log=True)
-
- def update_instance_vnf(self, instance_vnf_dict):
- #TODO:
- return
-
- def delete_instance_vnf(self, instance_vnf_id):
- #TODO:
- return
-
- def new_instance_vm(self, instance_vm_dict, tenant_id, instance_scenario_id = None):
- #return self.new_row('vms', vm_dict, tenant_id, True, True)
- return self._new_row_internal('instance_vms', instance_vm_dict, tenant_id, add_uuid=True, root_uuid=instance_scenario_id, log=True)
-
- def update_instance_vm(self, instance_vm_dict):
- #TODO:
- return
-
- def delete_instance_vm(self, instance_vm_id):
- #TODO:
- return
-
- def new_instance_net(self, instance_net_dict, tenant_id, instance_scenario_id = None):
- return self._new_row_internal('instance_nets', instance_net_dict, tenant_id, add_uuid=True, root_uuid=instance_scenario_id, log=True)
-
- def update_instance_net(self, instance_net_dict):
- #TODO:
- return
-
- def delete_instance_net(self, instance_net_id):
- #TODO:
- return
-
- def new_instance_interface(self, instance_interface_dict, tenant_id, instance_scenario_id = None):
- return self._new_row_internal('instance_interfaces', instance_interface_dict, tenant_id, add_uuid=True, root_uuid=instance_scenario_id, log=True)
-
- def update_instance_interface(self, instance_interface_dict):
- #TODO:
- return
-
- def delete_instance_interface(self, instance_interface_dict):
- #TODO:
- return
-
- def update_datacenter_nets(self, datacenter_id, new_net_list=[]):
- ''' Removes the old and adds the new net list at datacenter list for one datacenter.
- Attribute
- datacenter_id: uuid of the datacenter to act upon
- table: table where to insert
- new_net_list: the new values to be inserted. If empty it only deletes the existing nets
- Return: (Inserted items, Deleted items) if OK, (-Error, text) if error
- '''
- tries = 2
- while tries:
- created_time = time.time()
- try:
- with self.con:
- self.cur = self.con.cursor()
- cmd="DELETE FROM datacenter_nets WHERE datacenter_id='{}'".format(datacenter_id)
- self.logger.debug(cmd)
- self.cur.execute(cmd)
- deleted = self.cur.rowcount
- inserted = 0
- for new_net in new_net_list:
- created_time += 0.00001
- self._new_row_internal('datacenter_nets', new_net, add_uuid=True, created_time=created_time)
- inserted += 1
- return inserted, deleted
- except (mdb.Error, AttributeError) as e:
- self._format_error(e, tries)
- tries -= 1
-
-
+++ /dev/null
-[Unit]
-Description=openmano server (OSM RO)
-
-[Service]
-User=${USER_OWNER}
-ExecStart=openmanod -c /etc/osm/openmanod.cfg --log-file=/var/log/osm/openmano.log
-Restart=always
-
-[Install]
-WantedBy=multi-user.target
-
+++ /dev/null
-# -*- 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
-##
-
-'''
-JSON schemas used by openmano httpserver.py module to parse the different files and messages sent through the API
-'''
-__author__="Alfonso Tierno, Gerardo Garcia, Pablo Montes"
-__date__ ="$09-oct-2014 09:09:48$"
-
-#Basis schemas
-patern_name="^[ -~]+$"
-passwd_schema={"type" : "string", "minLength":1, "maxLength":60}
-nameshort_schema={"type" : "string", "minLength":1, "maxLength":60, "pattern" : "^[^,;()'\"]+$"}
-name_schema={"type" : "string", "minLength":1, "maxLength":255, "pattern" : "^[^,;()'\"]+$"}
-xml_text_schema={"type" : "string", "minLength":1, "maxLength":1000, "pattern" : "^[^']+$"}
-description_schema={"type" : ["string","null"], "maxLength":255, "pattern" : "^[^'\"]+$"}
-id_schema_fake = {"type" : "string", "minLength":2, "maxLength":36 } #"pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
-id_schema = {"type" : "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"}
-pci_schema={"type":"string", "pattern":"^[0-9a-fA-F]{4}(:[0-9a-fA-F]{2}){2}\.[0-9a-fA-F]$"}
-http_schema={"type":"string", "pattern":"^https?://[^'\"=]+$"}
-bandwidth_schema={"type":"string", "pattern" : "^[0-9]+ *([MG]bps)?$"}
-memory_schema={"type":"string", "pattern" : "^[0-9]+ *([MG]i?[Bb])?$"}
-integer0_schema={"type":"integer","minimum":0}
-integer1_schema={"type":"integer","minimum":1}
-path_schema={"type":"string", "pattern":"^(\.){0,2}(/[^/\"':{}\(\)]+)+$"}
-vlan_schema={"type":"integer","minimum":1,"maximum":4095}
-vlan1000_schema={"type":"integer","minimum":1000,"maximum":4095}
-mac_schema={"type":"string", "pattern":"^[0-9a-fA-F][02468aceACE](:[0-9a-fA-F]{2}){5}$"} #must be unicast LSB bit of MSB byte ==0
-#mac_schema={"type":"string", "pattern":"^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$"}
-ip_schema={"type":"string","pattern":"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"}
-ip_prefix_schema={"type":"string","pattern":"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/(30|[12]?[0-9])$"}
-port_schema={"type":"integer","minimum":1,"maximum":65534}
-object_schema={"type":"object"}
-schema_version_2={"type":"integer","minimum":2,"maximum":2}
-#schema_version_string={"type":"string","enum": ["0.1", "2", "0.2", "3", "0.3"]}
-log_level_schema={"type":"string", "enum":["DEBUG", "INFO", "WARNING","ERROR","CRITICAL"]}
-checksum_schema={"type":"string", "pattern":"^[0-9a-fA-F]{32}$"}
-size_schema={"type":"integer","minimum":1,"maximum":100}
-
-metadata_schema={
- "type":"object",
- "properties":{
- "architecture": {"type":"string"},
- "use_incremental": {"type":"string","enum":["yes","no"]},
- "vpci": pci_schema,
- "os_distro": {"type":"string"},
- "os_type": {"type":"string"},
- "os_version": {"type":"string"},
- "bus": {"type":"string"},
- "topology": {"type":"string", "enum": ["oneSocket"]}
- }
-}
-
-#Schema for the configuration file
-config_schema = {
- "title":"configuration response information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "http_port": port_schema,
- "http_admin_port": port_schema,
- "http_host": nameshort_schema,
- "auto_push_VNF_to_VIMs": {"type":"boolean"},
- "vnf_repository": path_schema,
- "db_host": nameshort_schema,
- "db_user": nameshort_schema,
- "db_passwd": {"type":"string"},
- "db_name": nameshort_schema,
- # Next fields will disappear once the MANO API includes appropriate primitives
- "vim_url": http_schema,
- "vim_url_admin": http_schema,
- "vim_name": nameshort_schema,
- "vim_tenant_name": nameshort_schema,
- "mano_tenant_name": nameshort_schema,
- "mano_tenant_id": id_schema,
- "http_console_proxy": {"type":"boolean"},
- "http_console_host": nameshort_schema,
- "http_console_ports": {
- "type": "array",
- "items": {"OneOf" : [
- port_schema,
- {"type":"object", "properties":{"from": port_schema, "to": port_schema}, "required": ["from","to"]}
- ]}
- },
- "log_level": log_level_schema,
- "log_socket_level": log_level_schema,
- "log_level_db": log_level_schema,
- "log_level_vim": log_level_schema,
- "log_level_nfvo": log_level_schema,
- "log_level_http": log_level_schema,
- "log_level_console": log_level_schema,
- "log_file_db": path_schema,
- "log_file_vim": path_schema,
- "log_file_nfvo": path_schema,
- "log_file_http": path_schema,
- "log_file_console": path_schema,
- "log_socket_host": nameshort_schema,
- "log_socket_port": port_schema,
- "log_file": path_schema,
- },
- "required": ['db_host', 'db_user', 'db_passwd', 'db_name'],
- "additionalProperties": False
-}
-
-tenant_schema = {
- "title":"tenant information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "tenant":{
- "type":"object",
- "properties":{
- "name": nameshort_schema,
- "description": description_schema,
- },
- "required": ["name"],
- "additionalProperties": True
- }
- },
- "required": ["tenant"],
- "additionalProperties": False
-}
-
-tenant_edit_schema = {
- "title":"tenant edit information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "tenant":{
- "type":"object",
- "properties":{
- "name": name_schema,
- "description": description_schema,
- },
- "additionalProperties": False
- }
- },
- "required": ["tenant"],
- "additionalProperties": False
-}
-
-datacenter_schema_properties={
- "name": name_schema,
- "description": description_schema,
- "type": nameshort_schema, #currently "openvim" or "openstack", can be enlarged with plugins
- "vim_url": description_schema,
- "vim_url_admin": description_schema,
- "config": { "type":"object" }
-}
-
-datacenter_schema = {
- "title":"datacenter information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "datacenter":{
- "type":"object",
- "properties":datacenter_schema_properties,
- "required": ["name", "vim_url"],
- "additionalProperties": True
- }
- },
- "required": ["datacenter"],
- "additionalProperties": False
-}
-
-
-datacenter_edit_schema = {
- "title":"datacenter edit nformation schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "datacenter":{
- "type":"object",
- "properties":datacenter_schema_properties,
- "additionalProperties": False
- }
- },
- "required": ["datacenter"],
- "additionalProperties": False
-}
-
-
-netmap_new_schema = {
- "title":"netmap new information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "netmap":{ #delete from datacenter
- "type":"object",
- "properties":{
- "name": name_schema, #name or uuid of net to change
- "vim_id": id_schema,
- "vim_name": name_schema
- },
- "minProperties": 1,
- "additionalProperties": False
- },
- },
- "required": ["netmap"],
- "additionalProperties": False
-}
-
-netmap_edit_schema = {
- "title":"netmap edit information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "netmap":{ #delete from datacenter
- "type":"object",
- "properties":{
- "name": name_schema, #name or uuid of net to change
- },
- "minProperties": 1,
- "additionalProperties": False
- },
- },
- "required": ["netmap"],
- "additionalProperties": False
-}
-
-datacenter_action_schema = {
- "title":"datacenter action information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "net-update":{"type":"null",},
- "net-edit":{
- "type":"object",
- "properties":{
- "net": name_schema, #name or uuid of net to change
- "name": name_schema,
- "description": description_schema,
- "shared": {"type": "boolean"}
- },
- "minProperties": 1,
- "additionalProperties": False
- },
- "net-delete":{
- "type":"object",
- "properties":{
- "net": name_schema, #name or uuid of net to change
- },
- "required": ["net"],
- "additionalProperties": False
- },
- },
- "minProperties": 1,
- "maxProperties": 1,
- "additionalProperties": False
-}
-
-
-datacenter_associate_schema={
- "title":"datacenter associate information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "datacenter":{
- "type":"object",
- "properties":{
- "vim_tenant": name_schema,
- "vim_tenant_name": name_schema,
- "vim_username": nameshort_schema,
- "vim_password": nameshort_schema,
- "config": {"type": "object"}
- },
-# "required": ["vim_tenant"],
- "additionalProperties": True
- }
- },
- "required": ["datacenter"],
- "additionalProperties": False
-}
-
-dhcp_schema = {
- "title":"DHCP schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "enabled": {"type": "boolean"},
- "start-address": ip_schema,
- "count": integer1_schema
- },
- "required": ["enabled", "start-address", "count"],
-}
-
-ip_profile_schema = {
- "title":"IP profile schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "ip-version": {"type":"string", "enum":["IPv4","IPv6"]},
- "subnet-address": ip_prefix_schema,
- "gateway-address": ip_schema,
- "dns-address": ip_schema,
- "dhcp": dhcp_schema
- },
-}
-
-key_pair_schema = {
- "title": "Key-pair schema for cloud-init configuration schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "name": name_schema,
- "key": {"type":"string"}
- },
- "required": ["key"],
- "additionalProperties": False
-}
-
-cloud_config_user_schema = {
- "title": "User schema for cloud-init configuration schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "name": nameshort_schema,
- "user-info": {"type":"string"},
- #"key-pairs": {"type" : "array", "items": key_pair_schema}
- "key-pairs": {"type" : "array", "items": {"type":"string"}}
- },
- "required": ["name"],
- "additionalProperties": False
-}
-
-cloud_config_schema = {
- "title": "Cloud-init configuration schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- #"key-pairs": {"type" : "array", "items": key_pair_schema},
- "key-pairs": {"type" : "array", "items": {"type":"string"}},
- "users": {"type" : "array", "items": cloud_config_user_schema}
- },
- "additionalProperties": False
-}
-
-internal_connection_element_schema = {
- "type":"object",
- "properties":{
- "VNFC": name_schema,
- "local_iface_name": name_schema
- }
-}
-
-internal_connection_element_schema_v02 = {
- "type":"object",
- "properties":{
- "VNFC": name_schema,
- "local_iface_name": name_schema,
- "ip_address": ip_schema
- }
-}
-
-internal_connection_schema = {
- "type":"object",
- "properties":{
- "name": name_schema,
- "description":description_schema,
- "type":{"type":"string", "enum":["bridge","data","ptp"]},
- "elements": {"type" : "array", "items": internal_connection_element_schema, "minItems":2}
- },
- "required": ["name", "type", "elements"],
- "additionalProperties": False
-}
-
-internal_connection_schema_v02 = {
- "type":"object",
- "properties":{
- "name": name_schema,
- "description":description_schema,
- "type": {"type": "string", "enum":["e-line", "e-lan"]},
- "implementation": {"type": "string", "enum":["overlay", "underlay"]},
- "ip-profile": ip_profile_schema,
- "elements": {"type" : "array", "items": internal_connection_element_schema_v02, "minItems":2}
- },
- "required": ["name", "type", "implementation", "elements"],
- "additionalProperties": False
-}
-
-external_connection_schema = {
- "type":"object",
- "properties":{
- "name": name_schema,
- "type":{"type":"string", "enum":["mgmt","bridge","data"]},
- "VNFC": name_schema,
- "local_iface_name": name_schema ,
- "description":description_schema
- },
- "required": ["name", "type", "VNFC", "local_iface_name"],
- "additionalProperties": False
-}
-
-#Not yet used
-external_connection_schema_v02 = {
- "type":"object",
- "properties":{
- "name": name_schema,
- "mgmt": {"type":"boolean"},
- "type": {"type": "string", "enum":["e-line", "e-lan"]},
- "implementation": {"type": "string", "enum":["overlay", "underlay"]},
- "VNFC": name_schema,
- "local_iface_name": name_schema ,
- "description":description_schema
- },
- "required": ["name", "type", "VNFC", "local_iface_name"],
- "additionalProperties": False
-}
-
-interfaces_schema={
- "type":"array",
- "items":{
- "type":"object",
- "properties":{
- "name":name_schema,
- "dedicated":{"type":"string","enum":["yes","no","yes:sriov"]},
- "bandwidth":bandwidth_schema,
- "vpci":pci_schema,
- "mac_address": mac_schema
- },
- "additionalProperties": False,
- "required": ["name","dedicated", "bandwidth"]
- }
-}
-
-bridge_interfaces_schema={
- "type":"array",
- "items":{
- "type":"object",
- "properties":{
- "name": name_schema,
- "bandwidth":bandwidth_schema,
- "vpci":pci_schema,
- "mac_address": mac_schema,
- "model": {"type":"string", "enum":["virtio","e1000","ne2k_pci","pcnet","rtl8139"]},
- "port-security": {"type" : "boolean"},
- "floating-ip": {"type" : "boolean"}
- },
- "additionalProperties": False,
- "required": ["name"]
- }
-}
-
-devices_schema={
- "type":"array",
- "items":{
- "type":"object",
- "properties":{
- "type":{"type":"string", "enum":["disk","cdrom","xml"] },
- "image": path_schema,
- "image name": name_schema,
- "image checksum": checksum_schema,
- "image metadata": metadata_schema,
- "size": size_schema,
- "vpci":pci_schema,
- "xml":xml_text_schema,
- },
- "additionalProperties": False,
- "required": ["type"]
- }
-}
-
-
-numa_schema = {
- "type": "object",
- "properties": {
- "memory":integer1_schema,
- "cores":integer1_schema,
- "paired-threads":integer1_schema,
- "threads":integer1_schema,
- "cores-id":{"type":"array","items":integer0_schema},
- "paired-threads-id":{"type":"array","items":{"type":"array","minItems":2,"maxItems":2,"items":integer0_schema}},
- "threads-id":{"type":"array","items":integer0_schema},
- "interfaces":interfaces_schema
- },
- "additionalProperties": False,
- #"required": ["memory"]
-}
-
-config_files_schema = {
- "title": "Config files for cloud init schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type": "object",
- "properties": {
- "dest": path_schema,
- "encoding": {"type": "string", "enum": ["b64", "base64", "gz", "gz+b64", "gz+base64", "gzip+b64", "gzip+base64"]}, #by default text
- "content": {"type": "string"},
- "permissions": {"type": "string"}, # tiypically octal notation '0644'
- "owner": {"type": "string"}, # format: owner:group
-
- },
- "additionalProperties": False,
- "required": ["dest", "content"],
-}
-
-boot_data_vdu_schema = {
- "title": "Boot data (Cloud-init) configuration schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type": "object",
- "properties":{
- "key-pairs": {"type" : "array", "items": {"type":"string"}},
- "users": {"type" : "array", "items": cloud_config_user_schema},
- "user-data": {"type" : "string"}, # scrip to run
- "config-files": {"type": "array", "items": config_files_schema},
- # NOTE: “user-data” are mutually exclusive with users and config-files because user/files are injected using user-data
- "boot-data-drive": {"type": "boolean"},
- },
- "additionalProperties": False,
-}
-
-vnfc_schema = {
- "type":"object",
- "properties":{
- "name": name_schema,
- "description": description_schema,
- "VNFC image": {"oneOf": [path_schema, http_schema]},
- "image name": name_schema,
- "image checksum": checksum_schema,
- "image metadata": metadata_schema,
- #"cloud-config": cloud_config_schema, #common for all vnfs in the scenario
- "processor": {
- "type":"object",
- "properties":{
- "model":description_schema,
- "features":{"type":"array","items":nameshort_schema}
- },
- "required": ["model"],
- "additionalProperties": False
- },
- "hypervisor": {
- "type":"object",
- "properties":{
- "type":nameshort_schema,
- "version":description_schema
- },
- },
- "ram":integer0_schema,
- "vcpus":integer0_schema,
- "disk": integer1_schema,
- "numas": {
- "type": "array",
- "items": numa_schema
- },
- "bridge-ifaces": bridge_interfaces_schema,
- "devices": devices_schema,
- "boot-data" : boot_data_vdu_schema
-
- },
- "required": ["name"],
- "oneOf": [
- {"required": ["VNFC image"]},
- {"required": ["image name"]}
- ],
- "additionalProperties": False
-}
-
-vnfd_schema_v01 = {
- "title":"vnfd information schema v0.1",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "vnf":{
- "type":"object",
- "properties":{
- "name": name_schema,
- "description": description_schema,
- "class": nameshort_schema,
- "public": {"type" : "boolean"},
- "physical": {"type" : "boolean"},
- "tenant_id": id_schema, #only valid for admin
- "external-connections": {"type" : "array", "items": external_connection_schema, "minItems":1},
- "internal-connections": {"type" : "array", "items": internal_connection_schema, "minItems":1},
- "VNFC":{"type" : "array", "items": vnfc_schema, "minItems":1}
- },
- "required": ["name","external-connections"],
- "additionalProperties": True
- }
- },
- "required": ["vnf"],
- "additionalProperties": False
-}
-
-#VNFD schema for OSM R1
-vnfd_schema_v02 = {
- "title":"vnfd information schema v0.2",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "schema_version": {"type": "string", "enum": ["0.2"]},
- "vnf":{
- "type":"object",
- "properties":{
- "name": name_schema,
- "description": description_schema,
- "class": nameshort_schema,
- "public": {"type" : "boolean"},
- "physical": {"type" : "boolean"},
- "tenant_id": id_schema, #only valid for admin
- "external-connections": {"type" : "array", "items": external_connection_schema, "minItems":1},
- "internal-connections": {"type" : "array", "items": internal_connection_schema_v02, "minItems":1},
- # "cloud-config": cloud_config_schema, #common for all vnfcs
- "VNFC":{"type" : "array", "items": vnfc_schema, "minItems":1}
- },
- "required": ["name"],
- "additionalProperties": True
- }
- },
- "required": ["vnf", "schema_version"],
- "additionalProperties": False
-}
-
-#vnfd_schema = vnfd_schema_v01
-#{
-# "title":"vnfd information schema v0.2",
-# "$schema": "http://json-schema.org/draft-04/schema#",
-# "oneOf": [vnfd_schema_v01, vnfd_schema_v02]
-#}
-
-graph_schema = {
- "title":"graphical scenario descriptor information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "x": integer0_schema,
- "y": integer0_schema,
- "ifaces": {
- "type":"object",
- "properties":{
- "left": {"type":"array"},
- "right": {"type":"array"},
- "bottom": {"type":"array"},
- }
- }
- },
- "required": ["x","y"]
-}
-
-nsd_schema_v01 = {
- "title":"network scenario descriptor information schema v0.1",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "name":name_schema,
- "description": description_schema,
- "tenant_id": id_schema, #only valid for admin
- "public": {"type": "boolean"},
- "topology":{
- "type":"object",
- "properties":{
- "nodes": {
- "type":"object",
- "patternProperties":{
- ".": {
- "type": "object",
- "properties":{
- "type":{"type":"string", "enum":["VNF", "other_network", "network", "external_network"]},
- "vnf_id": id_schema,
- "graph": graph_schema,
- },
- "patternProperties":{
- "^(VNF )?model$": {"type": "string"}
- },
- "required": ["type"]
- }
- }
- },
- "connections": {
- "type":"object",
- "patternProperties":{
- ".": {
- "type": "object",
- "properties":{
- "nodes":{"oneOf":[{"type":"object", "minProperties":2}, {"type":"array", "minLength":1}]},
- "type": {"type": "string", "enum":["link", "external_network", "dataplane_net", "bridge_net"]},
- "graph": graph_schema
- },
- "required": ["nodes"]
- },
- }
- }
- },
- "required": ["nodes"],
- "additionalProperties": False
- }
- },
- "required": ["name","topology"],
- "additionalProperties": False
-}
-
-nsd_schema_v02 = {
- "title":"network scenario descriptor information schema v0.2",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "schema_version": schema_version_2,
- "scenario":{
- "type":"object",
- "properties":{
- "name": name_schema,
- "description": description_schema,
- "tenant_id": id_schema, #only valid for admin
- "public": {"type": "boolean"},
- "vnfs": {
- "type":"object",
- "patternProperties":{
- ".": {
- "type": "object",
- "properties":{
- "vnf_id": id_schema,
- "graph": graph_schema,
- "vnf_name": name_schema,
- },
- }
- },
- "minProperties": 1
- },
- "networks": {
- "type":"object",
- "patternProperties":{
- ".": {
- "type": "object",
- "properties":{
- "interfaces":{"type":"array", "minLength":1},
- "type": {"type": "string", "enum":["dataplane", "bridge"]},
- "external" : {"type": "boolean"},
- "graph": graph_schema
- },
- "required": ["interfaces"]
- },
- }
- },
-
- },
- "required": ["vnfs", "name"],
- "additionalProperties": False
- }
- },
- "required": ["scenario","schema_version"],
- "additionalProperties": False
-}
-
-#NSD schema for OSM R1
-nsd_schema_v03 = {
- "title":"network scenario descriptor information schema v0.3",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "schema_version": {"type": "string", "enum": ["0.3"]},
- "scenario":{
- "type":"object",
- "properties":{
- "name": name_schema,
- "description": description_schema,
- "tenant_id": id_schema, #only valid for admin
- "public": {"type": "boolean"},
- "cloud-config": cloud_config_schema, #common for all vnfs in the scenario
- #"datacenter": name_schema,
- "vnfs": {
- "type":"object",
- "patternProperties":{
- ".": {
- "type": "object",
- "properties":{
- "vnf_id": id_schema,
- "graph": graph_schema,
- "vnf_name": name_schema,
- #"cloud-config": cloud_config_schema, #particular for a vnf
- #"datacenter": name_schema,
- "internal-connections": {
- "type": "object",
- "patternProperties": {
- ".": {
- "type": "object",
- "properties": {
- "ip-profile": ip_profile_schema,
- "elements": {
- "type" : "array",
- "items":{
- "type":"object",
- "properties":{
- "VNFC": name_schema,
- "local_iface_name": name_schema,
- "ip_address": ip_schema
- },
- "required": ["VNFC", "local_iface_name"],
- }
- }
- }
- }
- }
- }
- },
- }
- },
- "minProperties": 1
- },
- "networks": {
- "type":"object",
- "patternProperties":{
- ".": {
- "type": "object",
- "properties":{
- "interfaces":{
- "type":"array",
- "minLength":1,
- "items":{
- "type":"object",
- "properties":{
- "vnf": name_schema,
- "vnf_interface": name_schema,
- "ip_address": ip_schema
- },
- "required": ["vnf", "vnf_interface"],
- }
- },
- "type": {"type": "string", "enum":["e-line", "e-lan"]},
- "implementation": {"type": "string", "enum":["overlay", "underlay"]},
- "external" : {"type": "boolean"},
- "graph": graph_schema,
- "ip-profile": ip_profile_schema
- },
- "required": ["interfaces"]
- },
- }
- },
-
- },
- "required": ["vnfs", "networks","name"],
- "additionalProperties": False
- }
- },
- "required": ["scenario","schema_version"],
- "additionalProperties": False
-}
-
-#scenario_new_schema = {
-# "title":"new scenario information schema",
-# "$schema": "http://json-schema.org/draft-04/schema#",
-# #"oneOf": [nsd_schema_v01, nsd_schema_v02]
-# "oneOf": [nsd_schema_v01]
-#}
-
-scenario_edit_schema = {
- "title":"edit scenario information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "name":name_schema,
- "description": description_schema,
- "topology":{
- "type":"object",
- "properties":{
- "nodes": {
- "type":"object",
- "patternProperties":{
- "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$": {
- "type":"object",
- "properties":{
- "graph":{
- "type": "object",
- "properties":{
- "x": integer0_schema,
- "y": integer0_schema,
- "ifaces":{ "type": "object"}
- }
- },
- "description": description_schema,
- "name": name_schema
- }
- }
- }
- }
- },
- "required": ["nodes"],
- "additionalProperties": False
- }
- },
- "additionalProperties": False
-}
-
-scenario_action_schema = {
- "title":"scenario action information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "start":{
- "type": "object",
- "properties": {
- "instance_name":name_schema,
- "description":description_schema,
- "datacenter": {"type": "string"}
- },
- "required": ["instance_name"]
- },
- "deploy":{
- "type": "object",
- "properties": {
- "instance_name":name_schema,
- "description":description_schema,
- "datacenter": {"type": "string"}
- },
- "required": ["instance_name"]
- },
- "reserve":{
- "type": "object",
- "properties": {
- "instance_name":name_schema,
- "description":description_schema,
- "datacenter": {"type": "string"}
- },
- "required": ["instance_name"]
- },
- "verify":{
- "type": "object",
- "properties": {
- "instance_name":name_schema,
- "description":description_schema,
- "datacenter": {"type": "string"}
- },
- "required": ["instance_name"]
- }
- },
- "minProperties": 1,
- "maxProperties": 1,
- "additionalProperties": False
-}
-
-instance_scenario_create_schema_v01 = {
- "title":"instance scenario create information schema v0.1",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "schema_version": {"type": "string", "enum": ["0.1"]},
- "instance":{
- "type":"object",
- "properties":{
- "name":name_schema,
- "description":description_schema,
- "datacenter": name_schema,
- "scenario" : name_schema, #can be an UUID or name
- "action":{"enum": ["deploy","reserve","verify" ]},
- "connect_mgmt_interfaces": {"oneOf": [{"type":"boolean"}, {"type":"object"}]},# can be true or a dict with datacenter: net_name
- "cloud-config": cloud_config_schema, #common to all vnfs in the instance scenario
- "vnfs":{ #mapping from scenario to datacenter
- "type": "object",
- "patternProperties":{
- ".": {
- "type": "object",
- "properties":{
- "name": name_schema, #override vnf name
- "datacenter": name_schema,
- #"metadata": {"type": "object"},
- #"user_data": {"type": "string"}
- #"cloud-config": cloud_config_schema, #particular for a vnf
- "external-connections": {
- "type": "object",
- "patternProperties": {
- ".": {
- "type": "object",
- "properties": {
- "vim-network-name": name_schema,
- "ip_address": ip_schema
- }
- }
- }
- },
- "internal-connections": {
- "type": "object",
- "patternProperties": {
- ".": {
- "type": "object",
- "properties": {
- "ip-profile": ip_profile_schema,
- "elements": {
- "type" : "array",
- "items":{
- "type":"object",
- "properties":{
- "VNFC": name_schema,
- "local_iface_name": name_schema,
- "ip_address": ip_schema
- },
- "required": ["VNFC", "local_iface_name"],
- }
- }
- }
- }
- }
- }
- }
- }
- },
- },
- "networks":{ #mapping from scenario to datacenter
- "type": "object",
- "patternProperties":{
- ".": {
- "type": "object",
- "properties":{
- "interfaces":{
- "type":"array",
- "minLength":1,
- "items":{
- "type":"object",
- "properties":{
- "ip_address": ip_schema,
- "datacenter": name_schema,
- "vim-network-name": name_schema
- },
- "patternProperties":{
- ".": {"type": "string"}
- }
- }
- },
- "ip-profile": ip_profile_schema,
- #if the network connects VNFs deployed at different sites, you must specify one entry per site that this network connect to
- "sites": {
- "type":"array",
- "minLength":1,
- "items":{
- "type":"object",
- "properties":{
- # By default for an scenario 'external' network openmano looks for an existing VIM network to map this external scenario network,
- # for other networks openamno creates at VIM
- # Use netmap-create to force to create an external scenario network
- "netmap-create": {"oneOf":[name_schema,{"type": "null"}]}, #datacenter network to use. Null if must be created as an internal net
- #netmap-use: Indicates an existing VIM network that must be used for this scenario network.
- #Can use both the VIM network name (if it is not ambiguous) or the VIM net UUID
- #If both 'netmap-create' and 'netmap-use'are supplied, netmap-use precedes, but if fails openmano follows the netmap-create
- #In oder words, it is the same as 'try to map to the VIM network (netmap-use) if exist, and if not create the network (netmap-create)
- "netmap-use": name_schema, #
- "vim-network-name": name_schema, #override network name
- #"ip-profile": ip_profile_schema,
- "datacenter": name_schema,
- }
- }
- },
-
-
-
- }
- }
- },
- },
- },
- "additionalProperties": False,
- "required": ["name"]
- },
- },
- "required": ["instance"],
- "additionalProperties": False
-}
-
-instance_scenario_action_schema = {
- "title":"instance scenario action information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "start":{"type": "null"},
- "pause":{"type": "null"},
- "resume":{"type": "null"},
- "shutoff":{"type": "null"},
- "shutdown":{"type": "null"},
- "forceOff":{"type": "null"},
- "rebuild":{"type": "null"},
- "reboot":{
- "type": ["object","null"],
- },
- "console": {"type": ["string", "null"], "enum": ["novnc", "xvpvnc", "rdp-html5", "spice-html5", None]},
- "vnfs":{"type": "array", "items":{"type":"string"}},
- "vms":{"type": "array", "items":{"type":"string"}}
- },
- "minProperties": 1,
- #"maxProperties": 1,
- "additionalProperties": False
-}
+++ /dev/null
-#!/usr/bin/env python3
-# -*- 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
-##
-
-'''
-openmano python client used to interact with openmano-server
-'''
-__author__="Alfonso Tierno"
-__date__ ="$09-Mar-2016 09:09:48$"
-__version__="0.0.1-r467"
-version_date="Mar 2016"
-
-import requests
-import json
-import yaml
-import logging
-import sys
-if sys.version_info.major == 3:
- from urllib.parse import quote
-elif sys.version_info.major == 2:
- from urllib import quote
-
-class OpenmanoException(Exception):
- '''Common Exception for all openmano client exceptions'''
-
-class OpenmanoBadParamsException(OpenmanoException):
- '''Bad or missing input parameters'''
-
-class OpenmanoResponseException(OpenmanoException):
- '''Unexpected response from openmano server'''
-
-class OpenmanoNotFoundException(OpenmanoException):
- '''Not found at server'''
-
-# class vnf():
-# def __init__(self, message):
-# print "Error: %s" %message
-# print
-# self.print_usage()
-# #self.print_help()
-# print
-# print "Type 'openmano -h' for help"
-
-class openmanoclient():
- headers_req = {'Accept': 'application/yaml', 'content-type': 'application/yaml'}
-
- def __init__(self, **kwargs):
- self.username = kwargs.get("username")
- self.password = kwargs.get("password")
- self.endpoint_url = kwargs.get("endpoint_url")
- self.tenant_id = kwargs.get("tenant_id")
- self.tenant_name = kwargs.get("tenant_name")
- self.tenant = None
- self.datacenter_id = kwargs.get("datacenter_id")
- self.datacenter_name = kwargs.get("datacenter_name")
- self.datacenter = None
- self.logger = logging.getLogger(kwargs.get('logger','manoclient'))
- if kwargs.get("debug"):
- self.logger.setLevel(logging.DEBUG)
-
- def __getitem__(self, index):
- if index=='tenant_name':
- return self.tenant_name
- elif index=='tenant_id':
- return self.tenant_id
- elif index=='datacenter_name':
- return self.datacenter_name
- elif index=='datacenter_id':
- return self.datacenter_id
- elif index=='username':
- return self.username
- elif index=='password':
- return self.password
- elif index=='endpoint_url':
- return self.endpoint_url
- else:
- raise KeyError("Invalid key '%s'" %str(index))
-
- def __setitem__(self,index, value):
- if index=='tenant_name':
- self.tenant_name = value
- elif index=='tenant_id':
- self.tenant_id = value
- elif index=='datacenter_name':
- self.datacenter_name = value
- elif index=='datacenter_id':
- self.datacenter_id = value
- elif index=='username':
- self.username = value
- elif index=='password':
- self.password = value
- elif index=='endpoint_url':
- self.endpoint_url = value
- else:
- raise KeyError("Invalid key '%s'" %str(index))
- self.tenant = None # force to reload tenant with different credentials
- self.datacenter = None # force to reload datacenter with different credentials
-
- def _parse(self, descriptor, descriptor_format, response=False):
- #try yaml
- if descriptor_format and descriptor_format != "json" and descriptor_format != "yaml":
- raise OpenmanoBadParamsException("'descriptor_format' must be a 'json' or 'yaml' text")
- if descriptor_format != "json":
- try:
- return yaml.load(descriptor)
- except yaml.YAMLError as exc:
- error_pos = ""
- if hasattr(exc, 'problem_mark'):
- mark = exc.problem_mark
- error_pos = " at line:{} column:{}s".format(mark.line+1, mark.column+1)
- error_text = "yaml format error" + error_pos
- elif descriptor_format != "yaml":
- try:
- return json.loads(descriptor)
- except Exception as e:
- if response:
- error_text = "json format error" + str(e)
-
- if response:
- raise OpenmanoResponseException(error_text)
- raise OpenmanoBadParamsException(error_text)
-
- def _parse_yaml(self, descriptor, response=False):
- try:
- return yaml.load(descriptor)
- except yaml.YAMLError as exc:
- error_pos = ""
- if hasattr(exc, 'problem_mark'):
- mark = exc.problem_mark
- error_pos = " at line:{} column:{}s".format(mark.line+1, mark.column+1)
- error_text = "yaml format error" + error_pos
- if response:
- raise OpenmanoResponseException(error_text)
- raise OpenmanoBadParamsException(error_text)
-
-
- def _get_item_uuid(self, item, item_id=None, item_name=None, all_tenants=False):
- if all_tenants == None:
- tenant_text = ""
- elif all_tenants == False:
- tenant_text = "/" + self.tenant
- else:
- tenant_text = "/any"
- URLrequest = "{}{}/{}".format(self.endpoint_url, tenant_text, item)
- self.logger.debug("GET %s", URLrequest )
- mano_response = requests.get(URLrequest, headers=self.headers_req)
- self.logger.debug("openmano response: %s", mano_response.text )
- content = self._parse_yaml(mano_response.text, response=True)
- #print content
- found = 0
- if not item_id and not item_name:
- raise OpenmanoResponseException("Missing either {0}_name or {0}_id".format(item[:-1]))
- for i in content[item]:
- if item_id and i["uuid"] == item_id:
- return item_id
- elif item_name and i["name"] == item_name:
- uuid = i["uuid"]
- found += 1
-
- if found == 0:
- if item_id:
- raise OpenmanoNotFoundException("No {} found with id '{}'".format(item[:-1], item_id))
- else:
- #print(item, item_name)
- raise OpenmanoNotFoundException("No {} found with name '{}'".format(item[:-1], item_name) )
- elif found > 1:
- raise OpenmanoNotFoundException("{} {} found with name '{}'. uuid must be used".format(found, item, item_name))
- return uuid
-
- def _get_item(self, item, uuid=None, name=None, all_tenants=False):
- if all_tenants:
- tenant_text = "/any"
- elif all_tenants==None:
- tenant_text = ""
- else:
- tenant_text = "/"+self._get_tenant()
- if not uuid:
- #check that exist
- uuid = self._get_item_uuid(item, uuid, name, all_tenants)
-
- URLrequest = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, uuid)
- self.logger.debug("GET %s", URLrequest )
- mano_response = requests.get(URLrequest, headers=self.headers_req)
- self.logger.debug("openmano response: %s", mano_response.text )
-
- content = self._parse_yaml(mano_response.text, response=True)
- if mano_response.status_code==200:
- return content
- else:
- raise OpenmanoResponseException(str(content))
-
- def _get_tenant(self):
- if not self.tenant:
- self.tenant = self._get_item_uuid("tenants", self.tenant_id, self.tenant_name, None)
- return self.tenant
-
- def _get_datacenter(self):
- if not self.tenant:
- self._get_tenant()
- if not self.datacenter:
- self.datacenter = self._get_item_uuid("datacenters", self.datacenter_id, self.datacenter_name, False)
- return self.datacenter
-
- def _create_item(self, item, descriptor, all_tenants=False):
- if all_tenants:
- tenant_text = "/any"
- elif all_tenants==None:
- tenant_text = ""
- else:
- tenant_text = "/"+self._get_tenant()
- payload_req = yaml.safe_dump(descriptor)
-
- #print payload_req
-
- URLrequest = "{}{}/{}".format(self.endpoint_url, tenant_text, item)
- self.logger.debug("openmano POST %s %s", URLrequest, payload_req)
- mano_response = requests.post(URLrequest, headers = self.headers_req, data=payload_req)
- self.logger.debug("openmano response: %s", mano_response.text )
-
- content = self._parse_yaml(mano_response.text, response=True)
- if mano_response.status_code==200:
- return content
- else:
- raise OpenmanoResponseException(str(content))
-
- def _del_item(self, item, uuid=None, name=None, all_tenants=False):
- if all_tenants:
- tenant_text = "/any"
- elif all_tenants==None:
- tenant_text = ""
- else:
- tenant_text = "/"+self._get_tenant()
- if not uuid:
- #check that exist
- uuid = self._get_item_uuid(item, uuid, name, all_tenants)
-
- URLrequest = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, uuid)
- self.logger.debug("DELETE %s", URLrequest )
- mano_response = requests.delete(URLrequest, headers = self.headers_req)
- self.logger.debug("openmano response: %s", mano_response.text )
-
- content = self._parse_yaml(mano_response.text, response=True)
- if mano_response.status_code==200:
- return content
- else:
- raise OpenmanoResponseException(str(content))
-
- def _list_item(self, item, all_tenants=False, filter_dict=None):
- if all_tenants:
- tenant_text = "/any"
- elif all_tenants==None:
- tenant_text = ""
- else:
- tenant_text = "/"+self._get_tenant()
-
- URLrequest = "{}{}/{}".format(self.endpoint_url, tenant_text, item)
- separator="?"
- if filter_dict:
- for k in filter_dict:
- URLrequest += separator + quote(str(k)) + "=" + quote(str(filter_dict[k]))
- separator = "&"
- self.logger.debug("openmano GET %s", URLrequest)
- mano_response = requests.get(URLrequest, headers=self.headers_req)
- self.logger.debug("openmano response: %s", mano_response.text )
-
- content = self._parse_yaml(mano_response.text, response=True)
- if mano_response.status_code==200:
- return content
- else:
- raise OpenmanoResponseException(str(content))
-
- def _edit_item(self, item, descriptor, uuid=None, name=None, all_tenants=False):
- if all_tenants:
- tenant_text = "/any"
- elif all_tenants==None:
- tenant_text = ""
- else:
- tenant_text = "/"+self._get_tenant()
-
- if not uuid:
- #check that exist
- uuid = self._get_item_uuid("tenants", uuid, name, all_tenants)
-
- payload_req = yaml.safe_dump(descriptor)
-
- #print payload_req
-
- URLrequest = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, uuid)
- self.logger.debug("openmano PUT %s %s", URLrequest, payload_req)
- mano_response = requests.put(URLrequest, headers = self.headers_req, data=payload_req)
- self.logger.debug("openmano response: %s", mano_response.text )
-
- content = self._parse_yaml(mano_response.text, response=True)
- if mano_response.status_code==200:
- return content
- else:
- raise OpenmanoResponseException(str(content))
-
- #TENANTS
- def list_tenants(self, **kwargs):
- '''Obtain a list of tenants
- Params: can be filtered by 'uuid','name','description'
- Return: Raises an exception on error
- Obtain a dictionary with format {'tenants':[{tenant1_info},{tenant2_info},...]}}
- '''
- return self._list_item("tenants", all_tenants=None, filter_dict=kwargs)
-
- def get_tenant(self, uuid=None, name=None):
- '''Obtain the information of a tenant
- Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
- Return: Raises an exception on error, not found, found several
- Obtain a dictionary with format {'tenant':{tenant_info}}
- '''
- return self._get_item("tenants", uuid, name, all_tenants=None)
-
- def delete_tenant(self, uuid=None, name=None):
- '''Delete a tenant
- Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
- Return: Raises an exception on error, not found, found several
- Obtain a dictionary with format {'result': text indicating deleted}
- '''
- return self._del_item("tenants", uuid, name, all_tenants=None)
-
- def create_tenant(self, descriptor=None, descriptor_format=None, name=None, description=None):
- '''Creates a tenant
- Params: must supply a descriptor or/and just a name
- descriptor: with format {'tenant':{new_tenant_info}}
- newtenant_info must contain 'name', and optionally 'description'
- must be a dictionary or a json/yaml text.
- name: the tenant name. Overwrite descriptor name if any
- description: tenant descriptor.. Overwrite descriptor description if any
- Return: Raises an exception on error
- Obtain a dictionary with format {'tenant':{new_tenant_info}}
- '''
- if isinstance(descriptor, str):
- descriptor = self._parse(descriptor, descriptor_format)
- elif descriptor:
- pass
- elif name:
- descriptor={"tenant": {"name": name}}
- else:
- raise OpenmanoBadParamsException("Missing descriptor")
-
- if 'tenant' not in descriptor or len(descriptor)!=1:
- raise OpenmanoBadParamsException("Descriptor must contain only one 'tenant' field")
- if name:
- descriptor['tenant']['name'] = name
- if description:
- descriptor['tenant']['description'] = description
-
- return self._create_item("tenants", descriptor, all_tenants=None)
-
- def edit_tenant(self, uuid=None, name=None, descriptor=None, descriptor_format=None, new_name=None, new_description=None):
- '''Edit the parameters of a tenant
- Params: must supply a descriptor or/and a new_name or new_description
- uuid or/and name. If only name is supplied, there must be only one or an exception is raised
- descriptor: with format {'tenant':{params to change info}}
- must be a dictionary or a json/yaml text.
- name: the tenant name. Overwrite descriptor name if any
- description: tenant descriptor.. Overwrite descriptor description if any
- Return: Raises an exception on error, not found or found several
- Obtain a dictionary with format {'tenant':{newtenant_info}}
- '''
-
- if isinstance(descriptor, str):
- descriptor = self.parse(descriptor, descriptor_format)
- elif descriptor:
- pass
- elif new_name or new_description:
- descriptor={"tenant": {}}
- else:
- raise OpenmanoBadParamsException("Missing descriptor")
-
- if 'tenant' not in descriptor or len(descriptor)!=1:
- raise OpenmanoBadParamsException("Descriptor must contain only one 'tenant' field")
- if new_name:
- descriptor['tenant']['name'] = new_name
- if new_description:
- descriptor['tenant']['description'] = new_description
-
- return self._edit_item("tenants", descriptor, uuid, name, all_tenants=None)
-
- #DATACENTERS
-
- def list_datacenters(self, all_tenants=False, **kwargs):
- '''Obtain a list of datacenters, that are the VIM information at openmano
- Params: can be filtered by 'uuid','name','vim_url','type'
- Return: Raises an exception on error
- Obtain a dictionary with format {'datacenters':[{datacenter1_info},{datacenter2_info},...]}}
- '''
- return self._list_item("datacenters", all_tenants, filter_dict=kwargs)
-
- def get_datacenter(self, uuid=None, name=None, all_tenants=False):
- '''Obtain the information of a datacenter
- Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
- Return: Raises an exception on error, not found, found several
- Obtain a dictionary with format {'datacenter':{datacenter_info}}
- '''
- return self._get_item("datacenters", uuid, name, all_tenants)
-
- def delete_datacenter(self, uuid=None, name=None):
- '''Delete a datacenter
- Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
- Return: Raises an exception on error, not found, found several, not free
- Obtain a dictionary with format {'result': text indicating deleted}
- '''
- if not uuid:
- # check that exist
- uuid = self._get_item_uuid("datacenters", uuid, name, all_tenants=True)
- return self._del_item("datacenters", uuid, name, all_tenants=None)
-
- def create_datacenter(self, descriptor=None, descriptor_format=None, name=None, vim_url=None, **kwargs):
-#, type="openvim", public=False, description=None):
- '''Creates a datacenter
- Params: must supply a descriptor or/and just a name and vim_url
- descriptor: with format {'datacenter':{new_datacenter_info}}
- newdatacenter_info must contain 'name', 'vim_url', and optionally 'description'
- must be a dictionary or a json/yaml text.
- name: the datacenter name. Overwrite descriptor name if any
- vim_url: the datacenter URL. Overwrite descriptor vim_url if any
- vim_url_admin: the datacenter URL for administrative issues. Overwrite descriptor vim_url if any
- vim_type: the datacenter type, can be openstack or openvim. Overwrite descriptor type if any
- public: boolean, by default not public
- description: datacenter description. Overwrite descriptor description if any
- config: dictionary with extra configuration for the concrete datacenter
- Return: Raises an exception on error
- Obtain a dictionary with format {'datacenter':{new_datacenter_info}}
- '''
- if isinstance(descriptor, str):
- descriptor = self.parse(descriptor, descriptor_format)
- elif descriptor:
- pass
- elif name and vim_url:
- descriptor={"datacenter": {"name": name, "vim_url": vim_url}}
- else:
- raise OpenmanoBadParamsException("Missing descriptor, or name and vim_url")
-
- if 'datacenter' not in descriptor or len(descriptor)!=1:
- raise OpenmanoBadParamsException("Descriptor must contain only one 'datacenter' field")
- if name:
- descriptor['datacenter']['name'] = name
- if vim_url:
- descriptor['datacenter']['vim_url'] = vim_url
- for param in kwargs:
- descriptor['datacenter'][param] = kwargs[param]
-
- return self._create_item("datacenters", descriptor, all_tenants=None)
-
- def edit_datacenter(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False, **kwargs):
- '''Edit the parameters of a datacenter
- Params: must supply a descriptor or/and a parameter to change
- uuid or/and name. If only name is supplied, there must be only one or an exception is raised
- descriptor: with format {'datacenter':{params to change info}}
- must be a dictionary or a json/yaml text.
- parameters to change can be supplyied by the descriptor or as parameters:
- new_name: the datacenter name
- vim_url: the datacenter URL
- vim_url_admin: the datacenter URL for administrative issues
- vim_type: the datacenter type, can be openstack or openvim.
- public: boolean, available to other tenants
- description: datacenter description
- Return: Raises an exception on error, not found or found several
- Obtain a dictionary with format {'datacenter':{new_datacenter_info}}
- '''
-
- if isinstance(descriptor, str):
- descriptor = self.parse(descriptor, descriptor_format)
- elif descriptor:
- pass
- elif kwargs:
- descriptor={"datacenter": {}}
- else:
- raise OpenmanoBadParamsException("Missing descriptor")
-
- if 'datacenter' not in descriptor or len(descriptor)!=1:
- raise OpenmanoBadParamsException("Descriptor must contain only one 'datacenter' field")
- for param in kwargs:
- if param=='new_name':
- descriptor['datacenter']['name'] = kwargs[param]
- else:
- descriptor['datacenter'][param] = kwargs[param]
- return self._edit_item("datacenters", descriptor, uuid, name, all_tenants=None)
-
- def attach_datacenter(self, uuid=None, name=None, descriptor=None, descriptor_format=None, vim_user=None, vim_password=None, vim_tenant_name=None, vim_tenant_id=None):
- #check that exist
- uuid = self._get_item_uuid("datacenters", uuid, name, all_tenants=True)
- tenant_text = "/"+self._get_tenant()
-
- if isinstance(descriptor, str):
- descriptor = self.parse(descriptor, descriptor_format)
- elif descriptor:
- pass
- elif vim_user or vim_password or vim_tenant_name or vim_tenant_id:
- descriptor={"datacenter": {}}
- else:
- raise OpenmanoBadParamsException("Missing descriptor or params")
-
- if vim_user or vim_password or vim_tenant_name or vim_tenant_id:
- #print args.name
- try:
- if vim_user:
- descriptor['datacenter']['vim_user'] = vim_user
- if vim_password:
- descriptor['datacenter']['vim_password'] = vim_password
- if vim_tenant_name:
- descriptor['datacenter']['vim_tenant_name'] = vim_tenant_name
- if vim_tenant_id:
- descriptor['datacenter']['vim_tenant'] = vim_tenant_id
- except (KeyError, TypeError) as e:
- if str(e)=='datacenter': error_pos= "missing field 'datacenter'"
- else: error_pos="wrong format"
- raise OpenmanoBadParamsException("Wrong datacenter descriptor: " + error_pos)
-
- payload_req = yaml.safe_dump(descriptor)
- #print payload_req
- URLrequest = "{}{}/datacenters/{}".format(self.endpoint_url, tenant_text, uuid)
- self.logger.debug("openmano POST %s %s", URLrequest, payload_req)
- mano_response = requests.post(URLrequest, headers = self.headers_req, data=payload_req)
- self.logger.debug("openmano response: %s", mano_response.text )
-
- content = self._parse_yaml(mano_response.text, response=True)
- if mano_response.status_code==200:
- return content
- else:
- raise OpenmanoResponseException(str(content))
-
- def detach_datacenter(self, uuid=None, name=None):
- if not uuid:
- #check that exist
- uuid = self._get_item_uuid("datacenters", uuid, name, all_tenants=False)
- tenant_text = "/"+self._get_tenant()
- URLrequest = "{}{}/datacenters/{}".format(self.endpoint_url, tenant_text, uuid)
- self.logger.debug("openmano DELETE %s", URLrequest)
- mano_response = requests.delete(URLrequest, headers = self.headers_req)
- self.logger.debug("openmano response: %s", mano_response.text )
-
- content = self._parse_yaml(mano_response.text, response=True)
- if mano_response.status_code==200:
- return content
- else:
- raise OpenmanoResponseException(str(content))
-
- #VNFS
- def list_vnfs(self, all_tenants=False, **kwargs):
- '''Obtain a list of vnfs
- Params: can be filtered by 'uuid','name','description','public', "tenant_id"
- Return: Raises an exception on error
- Obtain a dictionary with format {'vnfs':[{vnf1_info},{vnf2_info},...]}}
- '''
- return self._list_item("vnfs", all_tenants, kwargs)
-
- def get_vnf(self, uuid=None, name=None, all_tenants=False):
- '''Obtain the information of a vnf
- Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
- Return: Raises an exception on error, not found, found several
- Obtain a dictionary with format {'vnf':{vnf_info}}
- '''
- return self._get_item("vnfs", uuid, name, all_tenants)
-
- def delete_vnf(self, uuid=None, name=None, all_tenants=False):
- '''Delete a vnf
- Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
- Return: Raises an exception on error, not found, found several, not free
- Obtain a dictionary with format {'result': text indicating deleted}
- '''
- return self._del_item("vnfs", uuid, name, all_tenants)
-
- def create_vnf(self, descriptor=None, descriptor_format=None, **kwargs):
- '''Creates a vnf
- Params: must supply a descriptor
- descriptor: with format {'vnf':{new_vnf_info}}
- must be a dictionary or a json/yaml text.
- must be a dictionary or a json/yaml text.
- Other parameters can be:
- name: the vnf name. Overwrite descriptor name if any
- image_path: Can be a string or a string list. Overwrite the image_path at descriptor
- description: vnf descriptor.. Overwrite descriptor description if any
- public: boolean, available to other tenants
- class: user text for vnf classification
- tenant_id: Propietary tenant
- ...
- Return: Raises an exception on error
- Obtain a dictionary with format {'vnf':{new_vnf_info}}
- '''
- if isinstance(descriptor, str):
- descriptor = self.parse(descriptor, descriptor_format)
- elif descriptor:
- pass
- else:
- raise OpenmanoBadParamsException("Missing descriptor")
-
- if 'vnf' not in descriptor or len(descriptor)>2:
- raise OpenmanoBadParamsException("Descriptor must contain only one 'vnf' field, and an optional version")
- for param in kwargs:
- if param == 'image_path':
- #print args.name
- try:
- if isinstance(kwargs[param], str):
- descriptor['vnf']['VNFC'][0]['VNFC image']=kwargs[param]
- elif isinstance(kwargs[param], tuple) or isinstance(kwargs[param], list):
- index=0
- for image_path_ in kwargs[param]:
- #print "image-path", image_path_
- descriptor['vnf']['VNFC'][index]['VNFC image']=image_path_
- index=index+1
- else:
- raise OpenmanoBadParamsException("Wrong image_path type. Expected text or a text list")
- except (KeyError, TypeError) as e:
- if str(e)=='vnf': error_pos= "missing field 'vnf'"
- elif str(e)=='VNFC': error_pos= "missing field 'vnf':'VNFC'"
- elif str(e)==str(index): error_pos= "field 'vnf':'VNFC' must be an array"
- elif str(e)=='VNFC image': error_pos= "missing field 'vnf':'VNFC'['VNFC image']"
- else: error_pos="wrong format"
- raise OpenmanoBadParamsException("Wrong VNF descriptor: " + error_pos)
- else:
- descriptor['vnf'][param] = kwargs[param]
- return self._create_item("vnfs", descriptor)
-
-# def edit_vnf(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False, **kwargs):
-# '''Edit the parameters of a vnf
-# Params: must supply a descriptor or/and a parameters to change
-# uuid or/and name. If only name is supplied, there must be only one or an exception is raised
-# descriptor: with format {'vnf':{params to change info}}
-# parameters to change can be supplyied by the descriptor or as parameters:
-# new_name: the vnf name
-# vim_url: the vnf URL
-# vim_url_admin: the vnf URL for administrative issues
-# vim_type: the vnf type, can be openstack or openvim.
-# public: boolean, available to other tenants
-# description: vnf description
-# Return: Raises an exception on error, not found or found several
-# Obtain a dictionary with format {'vnf':{new_vnf_info}}
-# '''
-#
-# if isinstance(descriptor, str):
-# descriptor = self.parse(descriptor, descriptor_format)
-# elif descriptor:
-# pass
-# elif kwargs:
-# descriptor={"vnf": {}}
-# else:
-# raise OpenmanoBadParamsException("Missing descriptor")
-#
-# if 'vnf' not in descriptor or len(descriptor)>2:
-# raise OpenmanoBadParamsException("Descriptor must contain only one 'vnf' field")
-# for param in kwargs:
-# if param=='new_name':
-# descriptor['vnf']['name'] = kwargs[param]
-# else:
-# descriptor['vnf'][param] = kwargs[param]
-# return self._edit_item("vnfs", descriptor, uuid, name, all_tenants=None)
-
- #SCENARIOS
- def list_scenarios(self, all_tenants=False, **kwargs):
- '''Obtain a list of scenarios
- Params: can be filtered by 'uuid','name','description','public', "tenant_id"
- Return: Raises an exception on error
- Obtain a dictionary with format {'scenarios':[{scenario1_info},{scenario2_info},...]}}
- '''
- return self._list_item("scenarios", all_tenants, kwargs)
-
- def get_scenario(self, uuid=None, name=None, all_tenants=False):
- '''Obtain the information of a scenario
- Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
- Return: Raises an exception on error, not found, found several
- Obtain a dictionary with format {'scenario':{scenario_info}}
- '''
- return self._get_item("scenarios", uuid, name, all_tenants)
-
- def delete_scenario(self, uuid=None, name=None, all_tenants=False):
- '''Delete a scenario
- Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
- Return: Raises an exception on error, not found, found several, not free
- Obtain a dictionary with format {'result': text indicating deleted}
- '''
- return self._del_item("scenarios", uuid, name, all_tenants)
-
- def create_scenario(self, descriptor=None, descriptor_format=None, **kwargs):
- '''Creates a scenario
- Params: must supply a descriptor
- descriptor: with format {'scenario':{new_scenario_info}}
- must be a dictionary or a json/yaml text.
- Other parameters can be:
- name: the scenario name. Overwrite descriptor name if any
- description: scenario descriptor.. Overwrite descriptor description if any
- public: boolean, available to other tenants
- tenant_id. Propietary tenant
- Return: Raises an exception on error
- Obtain a dictionary with format {'scenario':{new_scenario_info}}
- '''
- if isinstance(descriptor, str):
- descriptor = self.parse(descriptor, descriptor_format)
- elif descriptor:
- pass
- else:
- raise OpenmanoBadParamsException("Missing descriptor")
-
- if 'scenario' not in descriptor or len(descriptor)>2:
- raise OpenmanoBadParamsException("Descriptor must contain only one 'scenario' field, and an optional version")
- for param in kwargs:
- descriptor['scenario'][param] = kwargs[param]
- return self._create_item("scenarios", descriptor)
-
- def edit_scenario(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False, **kwargs):
- '''Edit the parameters of a scenario
- Params: must supply a descriptor or/and a parameters to change
- uuid or/and name. If only name is supplied, there must be only one or an exception is raised
- descriptor: with format {'scenario':{params to change info}}
- must be a dictionary or a json/yaml text.
- parameters to change can be supplyied by the descriptor or as parameters:
- new_name: the scenario name
- public: boolean, available to other tenants
- description: scenario description
- tenant_id. Propietary tenant
- Return: Raises an exception on error, not found or found several
- Obtain a dictionary with format {'scenario':{new_scenario_info}}
- '''
-
- if isinstance(descriptor, str):
- descriptor = self.parse(descriptor, descriptor_format)
- elif descriptor:
- pass
- elif kwargs:
- descriptor={"scenario": {}}
- else:
- raise OpenmanoBadParamsException("Missing descriptor")
-
- if 'scenario' not in descriptor or len(descriptor)>2:
- raise OpenmanoBadParamsException("Descriptor must contain only one 'scenario' field")
- for param in kwargs:
- if param=='new_name':
- descriptor['scenario']['name'] = kwargs[param]
- else:
- descriptor['scenario'][param] = kwargs[param]
- return self._edit_item("scenarios", descriptor, uuid, name, all_tenants=None)
-
-
- #INSTANCE-SCENARIOS
- def list_instances(self, all_tenants=False, **kwargs):
- '''Obtain a list of instances
- Params: can be filtered by 'uuid','name','description','scenario_id', "tenant_id"
- Return: Raises an exception on error
- Obtain a dictionary with format {'instances':[{instance1_info},{instance2_info},...]}}
- '''
- return self._list_item("instances", all_tenants, kwargs)
-
- def get_instance(self, uuid=None, name=None, all_tenants=False):
- '''Obtain the information of a instance
- Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
- Return: Raises an exception on error, not found, found several
- Obtain a dictionary with format {'instance':{instance_info}}
- '''
- return self._get_item("instances", uuid, name, all_tenants)
-
- def delete_instance(self, uuid=None, name=None, all_tenants=False):
- '''Delete a instance
- Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
- Return: Raises an exception on error, not found, found several, not free
- Obtain a dictionary with format {'result': text indicating deleted}
- '''
- return self._del_item("instances", uuid, name, all_tenants)
-
- def create_instance(self, descriptor=None, descriptor_format=None, name=None, **kwargs):
- '''Creates a instance
- Params: must supply a descriptor or/and a name and scenario
- descriptor: with format {'instance':{new_instance_info}}
- must be a dictionary or a json/yaml text.
- name: the instance name. Overwrite descriptor name if any
- Other parameters can be:
- description: instance descriptor.. Overwrite descriptor description if any
- datacenter_name, datacenter_id: datacenter where to be deployed
- scenario_name, scenario_id: Scenario this instance is based on
- Return: Raises an exception on error
- Obtain a dictionary with format {'instance':{new_instance_info}}
- '''
- if isinstance(descriptor, str):
- descriptor = self.parse(descriptor, descriptor_format)
- elif descriptor:
- pass
- elif name and ("scenario_name" in kwargs or "scenario_id" in kwargs):
- descriptor = {"instance":{"name": name}}
- else:
- raise OpenmanoBadParamsException("Missing descriptor")
-
- if 'instance' not in descriptor or len(descriptor)>2:
- raise OpenmanoBadParamsException("Descriptor must contain only one 'instance' field, and an optional version")
- if name:
- descriptor['instance']["name"] = name
- if "scenario_name" in kwargs or "scenario_id" in kwargs:
- descriptor['instance']["scenario"] = self._get_item_uuid("scenarios", kwargs.get("scenario_id"), kwargs.get("scenario_name"))
- if "datacenter_name" in kwargs or "datacenter_id" in kwargs:
- descriptor['instance']["datacenter"] = self._get_item_uuid("datacenters", kwargs.get("datacenter_id"), kwargs.get("datacenter_name"))
- if "description" in kwargs:
- descriptor['instance']["description"] = kwargs.get("description")
- #for param in kwargs:
- # descriptor['instance'][param] = kwargs[param]
- if "datacenter" not in descriptor['instance']:
- descriptor['instance']["datacenter"] = self._get_datacenter()
- return self._create_item("instances", descriptor)
-
- #VIM ACTIONS
- def vim_action(self, action, item, uuid=None, all_tenants=False, **kwargs):
- '''Perform an action over a vim
- Params:
- action: can be 'list', 'get'/'show', 'delete' or 'create'
- item: can be 'tenants' or 'networks'
- uuid: uuid of the tenant/net to show or to delete. Ignore otherwise
- other parameters:
- datacenter_name, datacenter_id: datacenters to act on, if missing uses classes store datacenter
- descriptor, descriptor_format: descriptor needed on creation, can be a dict or a yaml/json str
- must be a dictionary or a json/yaml text.
- name: for created tenant/net Overwrite descriptor name if any
- description: tenant descriptor. Overwrite descriptor description if any
-
- Return: Raises an exception on error
- Obtain a dictionary with format {'tenant':{new_tenant_info}}
- '''
- if item not in ("tenants", "networks", "images"):
- raise OpenmanoBadParamsException("Unknown value for item '{}', must be 'tenants', 'nets' or "
- "images".format(str(item)))
-
- image_actions = ['list','get','show','delete']
- if item == "images" and action not in image_actions:
- raise OpenmanoBadParamsException("Only available actions for item '{}' are {}\n"
- "Requested action was '{}'".format(item, ', '.join(image_actions), action))
- if all_tenants:
- tenant_text = "/any"
- else:
- tenant_text = "/"+self._get_tenant()
-
- if "datacenter_id" in kwargs or "datacenter_name" in kwargs:
- datacenter = self._get_item_uuid("datacenters", kwargs.get("datacenter_id"), kwargs.get("datacenter_name"), all_tenants=all_tenants)
- else:
- datacenter = self._get_datacenter()
-
- if action=="list":
- URLrequest = "{}{}/vim/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item)
- self.logger.debug("GET %s", URLrequest )
- mano_response = requests.get(URLrequest, headers=self.headers_req)
- self.logger.debug("openmano response: %s", mano_response.text )
- content = self._parse_yaml(mano_response.text, response=True)
- if mano_response.status_code==200:
- return content
- else:
- raise OpenmanoResponseException(str(content))
- elif action=="get" or action=="show":
- URLrequest = "{}{}/vim/{}/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item, uuid)
- self.logger.debug("GET %s", URLrequest )
- mano_response = requests.get(URLrequest, headers=self.headers_req)
- self.logger.debug("openmano response: %s", mano_response.text )
- content = self._parse_yaml(mano_response.text, response=True)
- if mano_response.status_code==200:
- return content
- else:
- raise OpenmanoResponseException(str(content))
- elif action=="delete":
- URLrequest = "{}{}/vim/{}/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item, uuid)
- self.logger.debug("DELETE %s", URLrequest )
- mano_response = requests.delete(URLrequest, headers=self.headers_req)
- self.logger.debug("openmano response: %s", mano_response.text )
- content = self._parse_yaml(mano_response.text, response=True)
- if mano_response.status_code==200:
- return content
- else:
- raise OpenmanoResponseException(str(content))
- elif action=="create":
- if "descriptor" in kwargs:
- if isinstance(kwargs["descriptor"], str):
- descriptor = self._parse(kwargs["descriptor"], kwargs.get("descriptor_format") )
- else:
- descriptor = kwargs["descriptor"]
- elif "name" in kwargs:
- descriptor={item[:-1]: {"name": kwargs["name"]}}
- else:
- raise OpenmanoResponseException("Missing descriptor")
-
- if item[:-1] not in descriptor or len(descriptor)!=1:
- raise OpenmanoBadParamsException("Descriptor must contain only one 'tenant' field")
- if "name" in kwargs:
- descriptor[ item[:-1] ]['name'] = kwargs["name"]
- if "description" in kwargs:
- descriptor[ item[:-1] ]['description'] = kwargs["description"]
- payload_req = yaml.safe_dump(descriptor)
- #print payload_req
- URLrequest = "{}{}/vim/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item)
- self.logger.debug("openmano POST %s %s", URLrequest, payload_req)
- mano_response = requests.post(URLrequest, headers = self.headers_req, data=payload_req)
- self.logger.debug("openmano response: %s", mano_response.text )
- content = self._parse_yaml(mano_response.text, response=True)
- if mano_response.status_code==200:
- return content
- else:
- raise OpenmanoResponseException(str(content))
- else:
- raise OpenmanoBadParamsException("Unknown value for action '{}".format(str(action)))
-
version_date="Jan 2017"
database_version="0.19" #expected database schema version
-import httpserver
import time
import sys
import getopt
import yaml
-import nfvo_db
from jsonschema import validate as js_v, exceptions as js_e
-from openmano_schemas import config_schema
-from db_base import db_base_Exception
-import nfvo
import logging
import logging.handlers as log_handlers
import socket
+from osm_ro import httpserver, nfvo, nfvo_db
+from osm_ro.openmano_schemas import config_schema
+from osm_ro.db_base import db_base_Exception
global global_config
global logger
--- /dev/null
+[Unit]
+Description=openmano server (OSM RO)
+
+[Service]
+User=${USER_OWNER}
+ExecStart=openmanod.py -c /etc/osm/openmanod.cfg --log-file=/var/log/osm/openmano.log
+Restart=always
+
+[Install]
+WantedBy=multi-user.target
+
--- /dev/null
+# -*- 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
+##
+
+'''
+Implement like a proxy for TCP/IP in a separated thread.
+It creates two sockets to bypass the TCP/IP packets among the fix console
+server specified at class construction (console_host, console_port)
+and a client that connect against the (host, port) specified also at construction
+
+ --------------------- -------------------------------
+ | OPENMANO | | VIM |
+client 1 ----> | ConsoleProxyThread | ------> | Console server |
+client 2 ----> | (host, port) | ------> |(console_host, console_server)|
+ ... -------------------- ------------------------------
+'''
+__author__="Alfonso Tierno"
+__date__ ="$19-nov-2015 09:07:15$"
+
+import socket
+import select
+import threading
+import logging
+
+
+class ConsoleProxyException(Exception):
+ '''raise when an exception has found'''
+class ConsoleProxyExceptionPortUsed(ConsoleProxyException):
+ '''raise when the port is used'''
+
+class ConsoleProxyThread(threading.Thread):
+ buffer_size = 4096
+ check_finish = 1 #frequency to check if requested to end in seconds
+
+ def __init__(self, host, port, console_host, console_port, log_level=None):
+ try:
+ threading.Thread.__init__(self)
+ self.console_host = console_host
+ self.console_port = console_port
+ self.host = host
+ self.port = port
+ self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self.server.bind((host, port))
+ self.server.listen(200)
+ #TODO timeout in a lock section can be used to autoterminate the thread
+ #when inactivity and timeout<time : set timeout=0 and terminate
+ #from outside, close class when timeout==0; set timeout=time+120 when adding a new console on this thread
+ #set self.timeout = time.time() + 120 at init
+ self.name = "ConsoleProxy " + console_host + ":" + str(console_port)
+ self.input_list = [self.server]
+ self.channel = {}
+ self.terminate = False #put at True from outside to force termination
+ self.logger = logging.getLogger('openmano.console')
+ if log_level:
+ self.logger.setLevel( getattr(logging, log_level) )
+
+ except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e:
+ if e is socket.error and e.errno==98:
+ raise ConsoleProxyExceptionPortUsed("socket.error " + str(e))
+ raise ConsoleProxyException(type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) )
+
+ def run(self):
+ while 1:
+ try:
+ inputready, _, _ = select.select(self.input_list, [], [], self.check_finish)
+ except select.error as e:
+ self.logger.error("Exception on select %s: %s", type(e).__name__, str(e) )
+ self.on_terminate()
+
+ if self.terminate:
+ self.on_terminate()
+ self.logger.debug("Terminate because commanded")
+ break
+
+ for sock in inputready:
+ if sock == self.server:
+ self.on_accept()
+ else:
+ self.on_recv(sock)
+
+ def on_terminate(self):
+ while self.input_list:
+ if self.input_list[0] is self.server:
+ self.server.close()
+ del self.input_list[0]
+ else:
+ self.on_close(self.input_list[0], "Terminating thread")
+
+ def on_accept(self):
+ #accept
+ try:
+ clientsock, clientaddr = self.server.accept()
+ except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e:
+ self.logger.error("Exception on_accept %s: %s", type(e).__name__, str(e) )
+ return False
+ #print self.name, ": Accept new client ", clientaddr
+
+ #connect
+ try:
+ forward = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ forward.connect((self.console_host, self.console_port))
+ name = "%s:%d => (%s:%d => %s:%d) => %s:%d" %\
+ (clientsock.getpeername() + clientsock.getsockname() + forward.getsockname() + forward.getpeername() )
+ self.logger.warn("new connection " + name)
+
+ self.input_list.append(clientsock)
+ self.input_list.append(forward)
+ info = { "name": name,
+ "clientsock" : clientsock,
+ "serversock" : forward
+ }
+ self.channel[clientsock] = info
+ self.channel[forward] = info
+ return True
+ except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e:
+ self.logger.error("Exception on_connect to server %s:%d; %s: %s Close client side %s",
+ self.console_host, self.console_port, type(e).__name__, str(e), str(clientaddr) )
+ clientsock.close()
+ return False
+
+ def on_close(self, sock, cause):
+ if sock not in self.channel:
+ return #can happen if there is data ready to received at both sides and the channel has been deleted. QUITE IMPROBABLE but just in case
+ info = self.channel[sock]
+ #debug info
+ sockname = "client" if sock is info["clientsock"] else "server"
+ self.logger.warn("del connection %s %s at %s side", info["name"], str(cause), str(sockname) )
+ #close sockets
+ try:
+ # close the connection with client
+ info["clientsock"].close() # equivalent to do self.s.close()
+ except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e:
+ self.logger.error("Exception on_close client socket %s: %s", type(e).__name__, str(e) )
+ try:
+ # close the connection with remote server
+ info["serversock"].close()
+ except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e:
+ self.logger.error("Exception on_close server socket %s: %s", type(e).__name__, str(e) )
+
+ #remove objects from input_list
+ self.input_list.remove(info["clientsock"])
+ self.input_list.remove(info["serversock"])
+ # delete both objects from channel dict
+ del self.channel[info["clientsock"]]
+ del self.channel[info["serversock"]]
+
+ def on_recv(self, sock):
+ if sock not in self.channel:
+ return #can happen if there is data ready to received at both sides and the channel has been deleted. QUITE IMPROBABLE but just in case
+ info = self.channel[sock]
+ peersock = info["serversock"] if sock is info["clientsock"] else info["clientsock"]
+ try:
+ data = sock.recv(self.buffer_size)
+ if len(data) == 0:
+ self.on_close(sock, "peer closed")
+ else:
+ #print self.data
+ sock = peersock
+ peersock.send(data)
+ except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e:
+ #print self.name, ": Exception %s: %s" % (type(e).__name__, str(e) )
+ self.on_close(sock, "Exception %s: %s" % (type(e).__name__, str(e) ))
+
+
+
+ #def start_timeout(self):
+ # self.timeout = time.time() + 120
+
--- /dev/null
+# -*- 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
+##
+
+'''
+Base class for openmano database manipulation
+'''
+__author__="Alfonso Tierno"
+__date__ ="$4-Apr-2016 10:05:01$"
+
+import MySQLdb as mdb
+import uuid as myUuid
+import utils as af
+import json
+#import yaml
+import time
+import logging
+import datetime
+from jsonschema import validate as js_v, exceptions as js_e
+
+HTTP_Bad_Request = 400
+HTTP_Unauthorized = 401
+HTTP_Not_Found = 404
+HTTP_Method_Not_Allowed = 405
+HTTP_Request_Timeout = 408
+HTTP_Conflict = 409
+HTTP_Service_Unavailable = 503
+HTTP_Internal_Server_Error = 500
+
+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}$"}
+ id_schema2 = {"type" : "string", "pattern": "^[a-fA-F0-9]{32}$"}
+ try:
+ js_v(uuid, id_schema)
+ return True
+ except js_e.ValidationError:
+ try:
+ js_v(uuid, id_schema2)
+ return True
+ except js_e.ValidationError:
+ return False
+ return False
+
+def _convert_datetime2str(var):
+ '''Converts a datetime variable to a string with the format '%Y-%m-%dT%H:%i:%s'
+ It enters recursively in the dict var finding this kind of variables
+ '''
+ if type(var) is dict:
+ for k,v in var.items():
+ if type(v) is datetime.datetime:
+ var[k]= v.strftime('%Y-%m-%dT%H:%M:%S')
+ elif type(v) is dict or type(v) is list or type(v) is tuple:
+ _convert_datetime2str(v)
+ if len(var) == 0: return True
+ elif type(var) is list or type(var) is tuple:
+ for v in var:
+ _convert_datetime2str(v)
+
+def _convert_bandwidth(data, reverse=False, logger=None):
+ '''Check the field bandwidth recursivelly and when found, it removes units and convert to number
+ It assumes that bandwidth is well formed
+ Attributes:
+ 'data': dictionary bottle.FormsDict variable to be checked. None or empty is consideted valid
+ 'reverse': by default convert form str to int (Mbps), if True it convert from number to units
+ Return:
+ None
+ '''
+ 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:
+ _convert_bandwidth(data[k], reverse, logger)
+ if "bandwidth" in data:
+ try:
+ value=str(data["bandwidth"])
+ if not reverse:
+ pos = value.find("bps")
+ if pos>0:
+ if value[pos-1]=="G": data["bandwidth"] = int(data["bandwidth"][:pos-1]) * 1000
+ elif value[pos-1]=="k": data["bandwidth"]= int(data["bandwidth"][:pos-1]) / 1000
+ else: data["bandwidth"]= int(data["bandwidth"][:pos-1])
+ else:
+ value = int(data["bandwidth"])
+ if value % 1000 == 0: data["bandwidth"]=str(value/1000) + " Gbps"
+ else: data["bandwidth"]=str(value) + " Mbps"
+ except:
+ if logger:
+ logger.error("convert_bandwidth exception for type '%s' data '%s'", type(data["bandwidth"]), data["bandwidth"])
+ return
+ if type(data) is tuple or type(data) is list:
+ for k in data:
+ if type(k) is dict or type(k) is tuple or type(k) is list:
+ _convert_bandwidth(k, reverse, logger)
+
+def _convert_str2boolean(data, items):
+ '''Check recursively the content of data, and if there is an key contained in items, convert value from string to boolean
+ Done recursively
+ Attributes:
+ 'data': dictionary variable to be checked. None or empty is considered valid
+ 'items': tuple of keys to convert
+ Return:
+ None
+ '''
+ 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:
+ _convert_str2boolean(data[k], items)
+ if k in items:
+ if type(data[k]) is str:
+ if data[k]=="false" or data[k]=="False" or data[k]=="0": data[k]=False
+ elif data[k]=="true" or data[k]=="True" or data[k]=="1": data[k]=True
+ elif type(data[k]) is int:
+ if data[k]==0: data[k]=False
+ elif data[k]==1: data[k]=True
+ if type(data) is tuple or type(data) is list:
+ for k in data:
+ if type(k) is dict or type(k) is tuple or type(k) is list:
+ _convert_str2boolean(k, items)
+
+class db_base_Exception(Exception):
+ '''Common Exception for all database exceptions'''
+
+ def __init__(self, message, http_code=HTTP_Bad_Request):
+ Exception.__init__(self, message)
+ self.http_code = http_code
+
+class db_base():
+ tables_with_created_field=()
+
+ def __init__(self, host=None, user=None, passwd=None, database=None, log_name='db', log_level=None):
+ self.host = host
+ self.user = user
+ self.passwd = passwd
+ self.database = database
+ self.con = None
+ self.log_level=log_level
+ self.logger = logging.getLogger(log_name)
+ if self.log_level:
+ self.logger.setLevel( getattr(logging, log_level) )
+
+ def connect(self, host=None, user=None, passwd=None, database=None):
+ '''Connect to specific data base.
+ The first time a valid host, user, passwd and database must be provided,
+ Following calls can skip this parameters
+ '''
+ try:
+ if host: self.host = host
+ if user: self.user = user
+ if passwd: self.passwd = passwd
+ if database: self.database = database
+
+ self.con = mdb.connect(self.host, self.user, self.passwd, self.database)
+ self.logger.debug("DB: connected to '%s' at '%s@%s'", self.database, self.user, self.host)
+ except mdb.Error as e:
+ raise db_base_Exception("Cannot connect to DataBase '{}' at '{}@{}' Error {}: {}".format(
+ self.database, self.user, self.host, e.args[0], e.args[1]),
+ http_code = HTTP_Unauthorized )
+
+ def get_db_version(self):
+ ''' Obtain the database schema version.
+ Return: (negative, text) if error or version 0.0 where schema_version table is missing
+ (version_int, version_text) if ok
+ '''
+ cmd = "SELECT version_int,version FROM schema_version"
+ tries = 2
+ while tries:
+ try:
+ with self.con:
+ self.cur = self.con.cursor()
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ rows = self.cur.fetchall()
+ highest_version_int=0
+ highest_version=""
+ for row in rows: #look for the latest version
+ if row[0]>highest_version_int:
+ highest_version_int, highest_version = row[0:2]
+ return highest_version_int, highest_version
+ except (mdb.Error, AttributeError) as e:
+ #self.logger.error("get_db_version DB Exception %d: %s. Command %s",e.args[0], e.args[1], cmd)
+ self._format_error(e, tries)
+ tries -= 1
+
+ def disconnect(self):
+ '''disconnect from specific data base'''
+ try:
+ self.con.close()
+ self.con = None
+ except mdb.Error as e:
+ self.logger.error("while disconnecting from DB: Error %d: %s",e.args[0], e.args[1])
+ return
+ except AttributeError as e: #self.con not defined
+ if e[0][-5:] == "'con'":
+ self.logger.warn("while disconnecting from DB: Error %d: %s",e.args[0], e.args[1])
+ return
+ else:
+ raise
+
+ def _format_error(self, e, tries=1, command=None, extra=None):
+ '''Creates a text error base on the produced exception
+ Params:
+ e: mdb exception
+ retry: in case of timeout, if reconnecting to database and retry, or raise and exception
+ cmd: database command that produce the exception
+ command: if the intention is update or delete
+ extra: extra information to add to some commands
+ Return
+ HTTP error in negative, formatted error text
+ '''
+ if isinstance(e,AttributeError ):
+ raise db_base_Exception("DB Exception " + str(e), HTTP_Internal_Server_Error)
+ if e.args[0]==2006 or e.args[0]==2013 : #MySQL server has gone away (((or))) Exception 2013: Lost connection to MySQL server during query
+ if tries>1:
+ self.logger.warn("DB Exception '%s'. Retry", str(e))
+ #reconnect
+ self.connect()
+ return
+ else:
+ raise db_base_Exception("Database connection timeout Try Again", HTTP_Request_Timeout)
+
+ fk=e.args[1].find("foreign key constraint fails")
+ if fk>=0:
+ if command=="update":
+ raise db_base_Exception("tenant_id '{}' not found.".format(extra), HTTP_Not_Found)
+ elif command=="delete":
+ raise db_base_Exception("Resource is not free. There are {} that prevent deleting it.".format(extra), HTTP_Conflict)
+ de = e.args[1].find("Duplicate entry")
+ fk = e.args[1].find("for key")
+ uk = e.args[1].find("Unknown column")
+ wc = e.args[1].find("in 'where clause'")
+ fl = e.args[1].find("in 'field list'")
+ #print de, fk, uk, wc,fl
+ if de>=0:
+ if fk>=0: #error 1062
+ raise db_base_Exception("Value {} already in use for {}".format(e.args[1][de+15:fk], e.args[1][fk+7:]), HTTP_Conflict)
+ if uk>=0:
+ if wc>=0:
+ raise db_base_Exception("Field {} can not be used for filtering".format(e.args[1][uk+14:wc]), HTTP_Bad_Request)
+ if fl>=0:
+ raise db_base_Exception("Field {} does not exist".format(e.args[1][uk+14:wc]), HTTP_Bad_Request)
+ raise db_base_Exception("Database internal Error {}: {}".format(e.args[0], e.args[1]), HTTP_Internal_Server_Error)
+
+ def __str2db_format(self, data):
+ '''Convert string data to database format.
+ If data is None it returns the 'Null' text,
+ otherwise it returns the text surrounded by quotes ensuring internal quotes are escaped.
+ '''
+ if data==None:
+ return 'Null'
+ else:
+ return json.dumps(str(data))
+
+ def __tuple2db_format_set(self, data):
+ '''Compose the needed text for a SQL SET, parameter 'data' is a pair tuple (A,B),
+ and it returns the text 'A="B"', where A is a field of a table and B is the value
+ If B is None it returns the 'A=Null' text, without surrounding Null by quotes
+ If B is not None it returns the text "A='B'" or 'A="B"' where B is surrounded by quotes,
+ and it ensures internal quotes of B are escaped.
+ '''
+ if data[1]==None:
+ return str(data[0]) + "=Null"
+ else:
+ return str(data[0]) + '=' + json.dumps(str(data[1]))
+
+ def __tuple2db_format_where(self, data):
+ '''Compose the needed text for a SQL WHERE, parameter 'data' is a pair tuple (A,B),
+ and it returns the text 'A="B"', where A is a field of a table and B is the value
+ If B is None it returns the 'A is Null' text, without surrounding Null by quotes
+ If B is not None it returns the text "A='B'" or 'A="B"' where B is surrounded by quotes,
+ and it ensures internal quotes of B are escaped.
+ '''
+ if data[1]==None:
+ return str(data[0]) + " is Null"
+
+# if type(data[1]) is tuple: #this can only happen in a WHERE_OR clause
+# text =[]
+# for d in data[1]:
+# if d==None:
+# text.append(str(data[0]) + " is Null")
+# continue
+# out=str(d)
+# if "'" not in out:
+# text.append( str(data[0]) + "='" + out + "'" )
+# elif '"' not in out:
+# text.append( str(data[0]) + '="' + out + '"' )
+# else:
+# text.append( str(data[0]) + '=' + json.dumps(out) )
+# return " OR ".join(text)
+
+ out=str(data[1])
+ return str(data[0]) + '=' + json.dumps(out)
+
+ def __tuple2db_format_where_not(self, data):
+ '''Compose the needed text for a SQL WHERE(not). parameter 'data' is a pair tuple (A,B),
+ and it returns the text 'A<>"B"', where A is a field of a table and B is the value
+ If B is None it returns the 'A is not Null' text, without surrounding Null by quotes
+ If B is not None it returns the text "A<>'B'" or 'A<>"B"' where B is surrounded by quotes,
+ and it ensures internal quotes of B are escaped.
+ '''
+ if data[1]==None:
+ return str(data[0]) + " is not Null"
+ out=str(data[1])
+ return str(data[0]) + '<>' + json.dumps(out)
+
+ def __remove_quotes(self, data):
+ '''remove single quotes ' of any string content of data dictionary'''
+ for k,v in data.items():
+ if type(v) == str:
+ if "'" in v:
+ data[k] = data[k].replace("'","_")
+
+ def _update_rows(self, table, UPDATE, WHERE, modified_time=0):
+ ''' Update one or several rows into a table.
+ Atributes
+ UPDATE: dictionary with the key: value to change
+ table: table where to update
+ WHERE: dictionary of elements to update
+ Return: the number of updated rows, exception if error
+ '''
+ #gettting uuid
+ values = ",".join(map(self.__tuple2db_format_set, UPDATE.iteritems() ))
+ if modified_time:
+ values += ",modified_at={:f}".format(modified_time)
+ cmd= "UPDATE " + table +" SET " + values +\
+ " WHERE " + " and ".join(map(self.__tuple2db_format_where, WHERE.iteritems() ))
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ return self.cur.rowcount
+
+ def _new_row_internal(self, table, INSERT, add_uuid=False, root_uuid=None, created_time=0):
+ ''' Add one row into a table. It DOES NOT begin or end the transaction, so self.con.cursor must be created
+ Attribute
+ INSERT: dictionary with the key:value to insert
+ table: table where to insert
+ add_uuid: if True, it will create an uuid key entry at INSERT if not provided
+ created_time: time to add to the created_time column
+ It checks presence of uuid and add one automatically otherwise
+ Return: uuid
+ '''
+
+ if add_uuid:
+ #create uuid if not provided
+ if 'uuid' not in INSERT:
+ uuid = INSERT['uuid'] = str(myUuid.uuid1()) # create_uuid
+ else:
+ uuid = str(INSERT['uuid'])
+ else:
+ uuid=None
+ if add_uuid:
+ #defining root_uuid if not provided
+ if root_uuid is None:
+ root_uuid = uuid
+ if created_time:
+ created_at = created_time
+ else:
+ created_at=time.time()
+ #inserting new uuid
+ cmd = "INSERT INTO uuids (uuid, root_uuid, used_at, created_at) VALUES ('{:s}','{:s}','{:s}', {:f})".format(uuid, root_uuid, table, created_at)
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ #insertion
+ cmd= "INSERT INTO " + table +" SET " + \
+ ",".join(map(self.__tuple2db_format_set, INSERT.iteritems() ))
+ if created_time:
+ cmd += ",created_at=%f" % created_time
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ self.cur.rowcount
+ return uuid
+
+ def _get_rows(self,table,uuid):
+ cmd = "SELECT * FROM {} WHERE uuid='{}'".format(str(table), str(uuid))
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ rows = self.cur.fetchall()
+ return rows
+
+ def new_row(self, table, INSERT, add_uuid=False, created_time=0):
+ ''' Add one row into a table.
+ Attribute
+ INSERT: dictionary with the key: value to insert
+ table: table where to insert
+ tenant_id: only useful for logs. If provided, logs will use this tenant_id
+ add_uuid: if True, it will create an uuid key entry at INSERT if not provided
+ It checks presence of uuid and add one automatically otherwise
+ Return: (result, uuid) where result can be 0 if error, or 1 if ok
+ '''
+ if table in self.tables_with_created_field and created_time==0:
+ created_time=time.time()
+ tries = 2
+ while tries:
+ try:
+ with self.con:
+ self.cur = self.con.cursor()
+ return self._new_row_internal(table, INSERT, add_uuid, None, created_time)
+
+ except (mdb.Error, AttributeError) as e:
+ self._format_error(e, tries)
+ tries -= 1
+
+ def update_rows(self, table, UPDATE, WHERE, modified_time=0):
+ ''' Update one or several rows into a table.
+ Atributes
+ UPDATE: dictionary with the key: value to change
+ table: table where to update
+ WHERE: dictionary of elements to update
+ Return: (result, descriptive text) where result indicates the number of updated files
+ '''
+ if table in self.tables_with_created_field and modified_time==0:
+ modified_time=time.time()
+ tries = 2
+ while tries:
+ try:
+ with self.con:
+ self.cur = self.con.cursor()
+ return self._update_rows(table, UPDATE, WHERE)
+
+ except (mdb.Error, AttributeError) as e:
+ self._format_error(e, tries)
+ tries -= 1
+
+ def delete_row_by_id(self, table, uuid):
+ tries = 2
+ while tries:
+ try:
+ with self.con:
+ #delete host
+ self.cur = self.con.cursor()
+ cmd = "DELETE FROM {} WHERE uuid = '{}'".format(table, uuid)
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ deleted = self.cur.rowcount
+ if deleted:
+ #delete uuid
+ self.cur = self.con.cursor()
+ cmd = "DELETE FROM uuids WHERE root_uuid = '{}'".format(uuid)
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ return deleted
+ except (mdb.Error, AttributeError) as e:
+ self._format_error(e, tries, "delete", "dependencies")
+ tries -= 1
+
+ def delete_row(self, **sql_dict):
+ ''' Deletes rows from a table.
+ Attribute sql_dir: dictionary with the following key: value
+ 'FROM': string of table name (Mandatory)
+ 'WHERE': dict of key:values, translated to key=value AND ... (Optional)
+ 'WHERE_NOT': dict of key:values, translated to key<>value AND ... (Optional)
+ if value is None, it is translated to key is not null
+ 'LIMIT': limit of number of rows (Optional)
+ Return: the number of deleted or exception if error
+ '''
+ #print sql_dict
+ from_ = "FROM " + str(sql_dict['FROM'])
+ #print 'from_', from_
+ if 'WHERE' in sql_dict and len(sql_dict['WHERE']) > 0:
+ w=sql_dict['WHERE']
+ where_ = "WHERE " + " AND ".join(map(self.__tuple2db_format_where, w.iteritems()))
+ else: where_ = ""
+ if 'WHERE_NOT' in sql_dict and len(sql_dict['WHERE_NOT']) > 0:
+ w=sql_dict['WHERE_NOT']
+ where_2 = " AND ".join(map(self.__tuple2db_format_where_not, w.iteritems()))
+ if len(where_)==0: where_ = "WHERE " + where_2
+ else: where_ = where_ + " AND " + where_2
+ #print 'where_', where_
+ limit_ = "LIMIT " + str(sql_dict['LIMIT']) if 'LIMIT' in sql_dict else ""
+ #print 'limit_', limit_
+ cmd = " ".join( ("DELETE", from_, where_, limit_) )
+ tries = 2
+ while tries:
+ try:
+ with self.con:
+ self.cur = self.con.cursor()
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ deleted = self.cur.rowcount
+ return deleted
+ except (mdb.Error, AttributeError) as e:
+ self._format_error(e, tries)
+ tries -= 1
+
+ def get_rows_by_id(self, table, uuid):
+ '''get row from a table based on uuid'''
+ tries = 2
+ while tries:
+ try:
+ with self.con:
+ self.cur = self.con.cursor(mdb.cursors.DictCursor)
+ cmd="SELECT * FROM {} where uuid='{}'".format(str(table), str(uuid))
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ rows = self.cur.fetchall()
+ return rows
+ except (mdb.Error, AttributeError) as e:
+ self._format_error(e, tries)
+ tries -= 1
+
+ def get_rows(self, **sql_dict):
+ ''' Obtain rows from a table.
+ Attribute sql_dir: dictionary with the following key: value
+ 'SELECT': list or tuple of fields to retrieve) (by default all)
+ 'FROM': string of table name (Mandatory)
+ 'WHERE': dict of key:values, translated to key=value (key is null) AND ... (Optional)
+ 'WHERE_NOT': dict of key:values, translated to key<>value (key is not null) AND ... (Optional)
+ 'WHERE_OR': dict of key:values, translated to key=value OR ... (Optional)
+ 'WHERE_AND_OR: str 'AND' or 'OR'(by default) mark the priority to 'WHERE AND (WHERE_OR)' or (WHERE) OR WHERE_OR' (Optional)
+ 'LIMIT': limit of number of rows (Optional)
+ 'ORDER_BY': list or tuple of fields to order
+ Return: a list with dictionaries at each row
+ '''
+ #print sql_dict
+ select_= "SELECT " + ("*" if 'SELECT' not in sql_dict else ",".join(map(str,sql_dict['SELECT'])) )
+ #print 'select_', select_
+ from_ = "FROM " + str(sql_dict['FROM'])
+ #print 'from_', from_
+ where_and = ""
+ where_or = ""
+ w=sql_dict.get('WHERE')
+ if w:
+ where_and = " AND ".join(map(self.__tuple2db_format_where, w.iteritems() ))
+ w=sql_dict.get('WHERE_NOT')
+ if w:
+ if where_and: where_and += " AND "
+ where_and += " AND ".join(map(self.__tuple2db_format_where_not, w.iteritems() ) )
+ w=sql_dict.get('WHERE_OR')
+ if w:
+ where_or = " OR ".join(map(self.__tuple2db_format_where, w.iteritems() ))
+ if where_and and where_or:
+ if sql_dict.get("WHERE_AND_OR") == "AND":
+ where_ = "WHERE " + where_and + " AND (" + where_or + ")"
+ else:
+ where_ = "WHERE (" + where_and + ") OR " + where_or
+ elif where_and and not where_or:
+ where_ = "WHERE " + where_and
+ elif not where_and and where_or:
+ where_ = "WHERE " + where_or
+ else:
+ where_ = ""
+ #print 'where_', where_
+ limit_ = "LIMIT " + str(sql_dict['LIMIT']) if 'LIMIT' in sql_dict else ""
+ order_ = "ORDER BY " + ",".join(map(str,sql_dict['SELECT'])) if 'ORDER_BY' in sql_dict else ""
+
+ #print 'limit_', limit_
+ cmd = " ".join( (select_, from_, where_, limit_, order_) )
+ tries = 2
+ while tries:
+ try:
+ with self.con:
+ self.cur = self.con.cursor(mdb.cursors.DictCursor)
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ rows = self.cur.fetchall()
+ return rows
+ except (mdb.Error, AttributeError) as e:
+ self._format_error(e, tries)
+ tries -= 1
+
+ def get_table_by_uuid_name(self, table, uuid_name, error_item_text=None, allow_serveral=False, WHERE_OR={}, WHERE_AND_OR="OR"):
+ ''' Obtain One row from a table based on name or uuid.
+ Attribute:
+ table: string of table name
+ uuid_name: name or uuid. If not uuid format is found, it is considered a name
+ allow_severeral: if False return ERROR if more than one row are founded
+ error_item_text: in case of error it identifies the 'item' name for a proper output text
+ 'WHERE_OR': dict of key:values, translated to key=value OR ... (Optional)
+ 'WHERE_AND_OR: str 'AND' or 'OR'(by default) mark the priority to 'WHERE AND (WHERE_OR)' or (WHERE) OR WHERE_OR' (Optional
+ Return: if allow_several==False, a dictionary with this row, or error if no item is found or more than one is found
+ if allow_several==True, a list of dictionaries with the row or rows, error if no item is found
+ '''
+
+ if error_item_text==None:
+ error_item_text = table
+ what = 'uuid' if af.check_valid_uuid(uuid_name) else 'name'
+ cmd = " SELECT * FROM {} WHERE {}='{}'".format(table, what, uuid_name)
+ if WHERE_OR:
+ where_or = " OR ".join(map(self.__tuple2db_format_where, WHERE_OR.iteritems() ))
+ if WHERE_AND_OR == "AND":
+ cmd += " AND (" + where_or + ")"
+ else:
+ cmd += " OR " + where_or
+
+
+ tries = 2
+ while tries:
+ try:
+ with self.con:
+ self.cur = self.con.cursor(mdb.cursors.DictCursor)
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ number = self.cur.rowcount
+ if number==0:
+ return -HTTP_Not_Found, "No %s found with %s '%s'" %(error_item_text, what, uuid_name)
+ elif number>1 and not allow_serveral:
+ return -HTTP_Bad_Request, "More than one %s found with %s '%s'" %(error_item_text, what, uuid_name)
+ if allow_serveral:
+ rows = self.cur.fetchall()
+ else:
+ rows = self.cur.fetchone()
+ return rows
+ except (mdb.Error, AttributeError) as e:
+ self._format_error(e, tries)
+ tries -= 1
+
+ def get_uuid(self, uuid):
+ '''check in the database if this uuid is already present'''
+ for retry_ in range(0,2):
+ try:
+ with self.con:
+ self.cur = self.con.cursor(mdb.cursors.DictCursor)
+ self.cur.execute("SELECT * FROM uuids where uuid='" + str(uuid) + "'")
+ rows = self.cur.fetchall()
+ return self.cur.rowcount, rows
+ except (mdb.Error, AttributeError) as e:
+ print "nfvo_db.get_uuid DB Exception %d: %s" % (e.args[0], e.args[1])
+ r,c = self._format_error(e)
+ if r!=-HTTP_Request_Timeout or retry_==1: return r,c
+
+ def get_uuid_from_name(self, table, name):
+ '''Searchs in table the name and returns the uuid
+ '''
+ tries = 2
+ while tries:
+ try:
+ with self.con:
+ self.cur = self.con.cursor(mdb.cursors.DictCursor)
+ where_text = "name='" + name +"'"
+ self.cur.execute("SELECT * FROM " + table + " WHERE "+ where_text)
+ rows = self.cur.fetchall()
+ if self.cur.rowcount==0:
+ return 0, "Name %s not found in table %s" %(name, table)
+ elif self.cur.rowcount>1:
+ return self.cur.rowcount, "More than one VNF with name %s found in table %s" %(name, table)
+ return self.cur.rowcount, rows[0]["uuid"]
+ except (mdb.Error, AttributeError) as e:
+ self._format_error(e, tries)
+ tries -= 1
+
--- /dev/null
+# -*- 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
+##
+
+'''
+HTTP server implementing the openmano API. It will answer to POST, PUT, GET methods in the appropriate URLs
+and will use the nfvo.py module to run the appropriate method.
+Every YAML/JSON file is checked against a schema in openmano_schemas.py module.
+'''
+__author__="Alfonso Tierno, Gerardo Garcia"
+__date__ ="$17-sep-2014 09:07:15$"
+
+import bottle
+import yaml
+import json
+import threading
+import time
+import logging
+
+from jsonschema import validate as js_v, exceptions as js_e
+from openmano_schemas import vnfd_schema_v01, vnfd_schema_v02, \
+ nsd_schema_v01, nsd_schema_v02, nsd_schema_v03, scenario_edit_schema, \
+ scenario_action_schema, instance_scenario_action_schema, instance_scenario_create_schema_v01, \
+ tenant_schema, tenant_edit_schema,\
+ datacenter_schema, datacenter_edit_schema, datacenter_action_schema, datacenter_associate_schema,\
+ object_schema, netmap_new_schema, netmap_edit_schema
+import nfvo
+import utils
+from db_base import db_base_Exception
+from functools import wraps
+
+global mydb
+global url_base
+global logger
+url_base="/openmano"
+logger = None
+
+HTTP_Bad_Request = 400
+HTTP_Unauthorized = 401
+HTTP_Not_Found = 404
+HTTP_Forbidden = 403
+HTTP_Method_Not_Allowed = 405
+HTTP_Not_Acceptable = 406
+HTTP_Service_Unavailable = 503
+HTTP_Internal_Server_Error= 500
+
+def delete_nulls(var):
+ if type(var) is dict:
+ for k in var.keys():
+ if var[k] is None: del var[k]
+ elif type(var[k]) is dict or type(var[k]) is list or type(var[k]) is tuple:
+ if delete_nulls(var[k]): del var[k]
+ if len(var) == 0: return True
+ elif type(var) is list or type(var) is tuple:
+ for k in var:
+ if type(k) is dict: delete_nulls(k)
+ if len(var) == 0: return True
+ return False
+
+def convert_datetime2str(var):
+ '''Converts a datetime variable to a string with the format '%Y-%m-%dT%H:%i:%s'
+ It enters recursively in the dict var finding this kind of variables
+ '''
+ if type(var) is dict:
+ for k,v in var.items():
+ if type(v) is float and k in ("created_at", "modified_at"):
+ var[k] = time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime(v) )
+ elif type(v) is dict or type(v) is list or type(v) is tuple:
+ convert_datetime2str(v)
+ if len(var) == 0: return True
+ elif type(var) is list or type(var) is tuple:
+ for v in var:
+ convert_datetime2str(v)
+
+def log_to_logger(fn):
+ '''
+ Wrap a Bottle request so that a log line is emitted after it's handled.
+ (This decorator can be extended to take the desired logger as a param.)
+ '''
+ @wraps(fn)
+ def _log_to_logger(*args, **kwargs):
+ actual_response = fn(*args, **kwargs)
+ # modify this to log exactly what you need:
+ logger.info('FROM %s %s %s %s' % (bottle.request.remote_addr,
+ bottle.request.method,
+ bottle.request.url,
+ bottle.response.status))
+ return actual_response
+ return _log_to_logger
+
+class httpserver(threading.Thread):
+ def __init__(self, db, admin=False, host='localhost', port=9090):
+ #global url_base
+ global mydb
+ global logger
+ #initialization
+ if not logger:
+ logger = logging.getLogger('openmano.http')
+ threading.Thread.__init__(self)
+ self.host = host
+ self.port = port #Port where the listen service must be started
+ if admin==True:
+ self.name = "http_admin"
+ else:
+ self.name = "http"
+ #self.url_preffix = 'http://' + host + ':' + str(port) + url_base
+ mydb = db
+ #self.first_usable_connection_index = 10
+ #self.next_connection_index = self.first_usable_connection_index #The next connection index to be used
+ #Ensure that when the main program exits the thread will also exit
+ self.daemon = True
+ self.setDaemon(True)
+
+ def run(self):
+ bottle.install(log_to_logger)
+ bottle.run(host=self.host, port=self.port, debug=False, quiet=True)
+
+def run_bottle(db, host_='localhost', port_=9090):
+ '''used for launching in main thread, so that it can be debugged'''
+ global mydb
+ mydb = db
+ bottle.run(host=host_, port=port_, debug=True) #quiet=True
+
+
+@bottle.route(url_base + '/', method='GET')
+def http_get():
+ #print
+ return 'works' #TODO: to be completed
+
+#
+# Util functions
+#
+
+def change_keys_http2db(data, http_db, reverse=False):
+ '''Change keys of dictionary data acording to the key_dict values
+ This allow change from http interface names to database names.
+ When reverse is True, the change is otherwise
+ Attributes:
+ data: can be a dictionary or a list
+ http_db: is a dictionary with hhtp names as keys and database names as value
+ reverse: by default change is done from http api to database. If True change is done otherwise
+ Return: None, but data is modified'''
+ if type(data) is tuple or type(data) is list:
+ for d in data:
+ change_keys_http2db(d, http_db, reverse)
+ elif type(data) is dict or type(data) is bottle.FormsDict:
+ if reverse:
+ for k,v in http_db.items():
+ if v in data: data[k]=data.pop(v)
+ else:
+ for k,v in http_db.items():
+ if k in data: data[v]=data.pop(k)
+
+def format_out(data):
+ '''return string of dictionary data according to requested json, yaml, xml. By default json'''
+ logger.debug("OUT: " + yaml.safe_dump(data, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True) )
+ if 'application/yaml' in bottle.request.headers.get('Accept'):
+ bottle.response.content_type='application/yaml'
+ return yaml.safe_dump(data, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True) #, canonical=True, default_style='"'
+ else: #by default json
+ bottle.response.content_type='application/json'
+ #return data #json no style
+ return json.dumps(data, indent=4) + "\n"
+
+def format_in(default_schema, version_fields=None, version_dict_schema=None):
+ ''' Parse the content of HTTP request against a json_schema
+ Parameters
+ default_schema: The schema to be parsed by default if no version field is found in the client data
+ version_fields: If provided it contains a tuple or list with the fields to iterate across the client data to obtain the version
+ version_dict_schema: It contains a dictionary with the version as key, and json schema to apply as value
+ It can contain a None as key, and this is apply if the client data version does not match any key
+ Return:
+ user_data, used_schema: if the data is successfully decoded and matches the schema
+ launch a bottle abort if fails
+ '''
+ #print "HEADERS :" + str(bottle.request.headers.items())
+ try:
+ error_text = "Invalid header format "
+ format_type = bottle.request.headers.get('Content-Type', 'application/json')
+ if 'application/json' in format_type:
+ error_text = "Invalid json format "
+ #Use the json decoder instead of bottle decoder because it informs about the location of error formats with a ValueError exception
+ client_data = json.load(bottle.request.body)
+ #client_data = bottle.request.json()
+ elif 'application/yaml' in format_type:
+ error_text = "Invalid yaml format "
+ client_data = yaml.load(bottle.request.body)
+ elif 'application/xml' in format_type:
+ bottle.abort(501, "Content-Type: application/xml not supported yet.")
+ else:
+ logger.warning('Content-Type ' + str(format_type) + ' not supported.')
+ bottle.abort(HTTP_Not_Acceptable, 'Content-Type ' + str(format_type) + ' not supported.')
+ return
+ #if client_data == None:
+ # bottle.abort(HTTP_Bad_Request, "Content error, empty")
+ # return
+
+ logger.debug('IN: %s', yaml.safe_dump(client_data, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True) )
+ #look for the client provider version
+ error_text = "Invalid content "
+ client_version = None
+ used_schema = None
+ if version_fields != None:
+ client_version = client_data
+ for field in version_fields:
+ if field in client_version:
+ client_version = client_version[field]
+ else:
+ client_version=None
+ break
+ if client_version==None:
+ used_schema=default_schema
+ elif version_dict_schema!=None:
+ if client_version in version_dict_schema:
+ used_schema = version_dict_schema[client_version]
+ elif None in version_dict_schema:
+ used_schema = version_dict_schema[None]
+ if used_schema==None:
+ bottle.abort(HTTP_Bad_Request, "Invalid schema version or missing version field")
+
+ js_v(client_data, used_schema)
+ return client_data, used_schema
+ except (ValueError, yaml.YAMLError) as exc:
+ error_text += str(exc)
+ logger.error(error_text)
+ bottle.abort(HTTP_Bad_Request, error_text)
+ except js_e.ValidationError as exc:
+ logger.error("validate_in error, jsonschema exception at '%s' '%s' ", str(exc.path), str(exc.message))
+ error_pos = ""
+ if len(exc.path)>0: error_pos=" at " + ":".join(map(json.dumps, exc.path))
+ bottle.abort(HTTP_Bad_Request, error_text + exc.message + error_pos)
+ #except:
+ # bottle.abort(HTTP_Bad_Request, "Content error: Failed to parse Content-Type", error_pos)
+ # raise
+
+def filter_query_string(qs, http2db, allowed):
+ '''Process query string (qs) checking that contains only valid tokens for avoiding SQL injection
+ Attributes:
+ 'qs': bottle.FormsDict variable to be processed. None or empty is considered valid
+ 'http2db': dictionary with change from http API naming (dictionary key) to database naming(dictionary value)
+ 'allowed': list of allowed string tokens (API http naming). All the keys of 'qs' must be one of 'allowed'
+ Return: A tuple with the (select,where,limit) to be use in a database query. All of then transformed to the database naming
+ select: list of items to retrieve, filtered by query string 'field=token'. If no 'field' is present, allowed list is returned
+ where: dictionary with key, value, taken from the query string token=value. Empty if nothing is provided
+ limit: limit dictated by user with the query string 'limit'. 100 by default
+ abort if not permited, using bottel.abort
+ '''
+ where={}
+ limit=100
+ select=[]
+ #if type(qs) is not bottle.FormsDict:
+ # bottle.abort(HTTP_Internal_Server_Error, '!!!!!!!!!!!!!!invalid query string not a dictionary')
+ # #bottle.abort(HTTP_Internal_Server_Error, "call programmer")
+ for k in qs:
+ if k=='field':
+ select += qs.getall(k)
+ for v in select:
+ if v not in allowed:
+ bottle.abort(HTTP_Bad_Request, "Invalid query string at 'field="+v+"'")
+ elif k=='limit':
+ try:
+ limit=int(qs[k])
+ except:
+ bottle.abort(HTTP_Bad_Request, "Invalid query string at 'limit="+qs[k]+"'")
+ else:
+ if k not in allowed:
+ bottle.abort(HTTP_Bad_Request, "Invalid query string at '"+k+"="+qs[k]+"'")
+ if qs[k]!="null": where[k]=qs[k]
+ else: where[k]=None
+ if len(select)==0: select += allowed
+ #change from http api to database naming
+ for i in range(0,len(select)):
+ k=select[i]
+ if http2db and k in http2db:
+ select[i] = http2db[k]
+ if http2db:
+ change_keys_http2db(where, http2db)
+ #print "filter_query_string", select,where,limit
+
+ return select,where,limit
+
+@bottle.hook('after_request')
+def enable_cors():
+ '''Don't know yet if really needed. Keep it just in case'''
+ bottle.response.headers['Access-Control-Allow-Origin'] = '*'
+
+#
+# VNFs
+#
+
+@bottle.route(url_base + '/tenants', method='GET')
+def http_get_tenants():
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ select_,where_,limit_ = filter_query_string(bottle.request.query, None,
+ ('uuid','name','description','created_at') )
+ try:
+ tenants = mydb.get_rows(FROM='nfvo_tenants', SELECT=select_,WHERE=where_,LIMIT=limit_)
+ #change_keys_http2db(content, http2db_tenant, reverse=True)
+ convert_datetime2str(tenants)
+ data={'tenants' : tenants}
+ return format_out(data)
+ except db_base_Exception as e:
+ logger.error("http_get_tenants error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/tenants/<tenant_id>', method='GET')
+def http_get_tenant_id(tenant_id):
+ '''get tenant details, can use both uuid or name'''
+ #obtain data
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ try:
+ tenant = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id, "tenant")
+ #change_keys_http2db(content, http2db_tenant, reverse=True)
+ convert_datetime2str(tenant)
+ data={'tenant' : tenant}
+ return format_out(data)
+ except db_base_Exception as e:
+ logger.error("http_get_tenant_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/tenants', method='POST')
+def http_post_tenants():
+ '''insert a tenant into the catalogue. '''
+ #parse input data
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ http_content,_ = format_in( tenant_schema )
+ r = utils.remove_extra_items(http_content, tenant_schema)
+ if r:
+ logger.debug("Remove received extra items %s", str(r))
+ try:
+ data = nfvo.new_tenant(mydb, http_content['tenant'])
+ return http_get_tenant_id(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_post_tenants error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/tenants/<tenant_id>', method='PUT')
+def http_edit_tenant_id(tenant_id):
+ '''edit tenant details, can use both uuid or name'''
+ #parse input data
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ http_content,_ = format_in( tenant_edit_schema )
+ r = utils.remove_extra_items(http_content, tenant_edit_schema)
+ if r:
+ logger.debug("Remove received extra items %s", str(r))
+
+ #obtain data, check that only one exist
+ try:
+ tenant = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id)
+ #edit data
+ tenant_id = tenant['uuid']
+ where={'uuid': tenant['uuid']}
+ mydb.update_rows('nfvo_tenants', http_content['tenant'], where)
+ return http_get_tenant_id(tenant_id)
+ except db_base_Exception as e:
+ logger.error("http_edit_tenant_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/tenants/<tenant_id>', method='DELETE')
+def http_delete_tenant_id(tenant_id):
+ '''delete a tenant from database, can use both uuid or name'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ try:
+ data = nfvo.delete_tenant(mydb, tenant_id)
+ return format_out({"result":"tenant " + data + " deleted"})
+ except db_base_Exception as e:
+ logger.error("http_delete_tenant_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/datacenters', method='GET')
+def http_get_datacenters(tenant_id):
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ try:
+ if tenant_id != 'any':
+ #check valid tenant_id
+ nfvo.check_tenant(mydb, tenant_id)
+ select_,where_,limit_ = filter_query_string(bottle.request.query, None,
+ ('uuid','name','vim_url','type','created_at') )
+ if tenant_id != 'any':
+ where_['nfvo_tenant_id'] = tenant_id
+ if 'created_at' in select_:
+ select_[ select_.index('created_at') ] = 'd.created_at as created_at'
+ if 'created_at' in where_:
+ where_['d.created_at'] = where_.pop('created_at')
+ datacenters = mydb.get_rows(FROM='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id',
+ SELECT=select_,WHERE=where_,LIMIT=limit_)
+ else:
+ datacenters = mydb.get_rows(FROM='datacenters',
+ SELECT=select_,WHERE=where_,LIMIT=limit_)
+ #change_keys_http2db(content, http2db_tenant, reverse=True)
+ convert_datetime2str(datacenters)
+ data={'datacenters' : datacenters}
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_get_datacenters error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='GET')
+def http_get_datacenter_id(tenant_id, datacenter_id):
+ '''get datacenter details, can use both uuid or name'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ try:
+ if tenant_id != 'any':
+ #check valid tenant_id
+ nfvo.check_tenant(mydb, tenant_id)
+ #obtain data
+ what = 'uuid' if utils.check_valid_uuid(datacenter_id) else 'name'
+ where_={}
+ where_[what] = datacenter_id
+ select_=['uuid', 'name','vim_url', 'vim_url_admin', 'type', 'd.config as config', 'description', 'd.created_at as created_at']
+ if tenant_id != 'any':
+ select_.append("datacenter_tenant_id")
+ where_['td.nfvo_tenant_id']= tenant_id
+ from_='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id'
+ else:
+ from_='datacenters as d'
+ datacenters = mydb.get_rows(
+ SELECT=select_,
+ FROM=from_,
+ WHERE=where_)
+
+ if len(datacenters)==0:
+ bottle.abort( HTTP_Not_Found, "No datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
+ elif len(datacenters)>1:
+ bottle.abort( HTTP_Bad_Request, "More than one datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
+ datacenter = datacenters[0]
+ if tenant_id != 'any':
+ #get vim tenant info
+ vim_tenants = mydb.get_rows(
+ SELECT=("vim_tenant_name", "vim_tenant_id", "user", "passwd", "config"),
+ FROM="datacenter_tenants",
+ WHERE={"uuid": datacenters[0]["datacenter_tenant_id"]},
+ ORDER_BY=("created", ) )
+ del datacenter["datacenter_tenant_id"]
+ datacenter["vim_tenants"] = vim_tenants
+ for vim_tenant in vim_tenants:
+ if vim_tenant["passwd"]:
+ vim_tenant["passwd"] = "******"
+ if vim_tenant['config'] != None:
+ try:
+ config_dict = yaml.load(vim_tenant['config'])
+ vim_tenant['config'] = config_dict
+ except Exception as e:
+ logger.error("Exception '%s' while trying to load config information", str(e))
+
+ if datacenter['config'] != None:
+ try:
+ config_dict = yaml.load(datacenter['config'])
+ datacenter['config'] = config_dict
+ except Exception as e:
+ logger.error("Exception '%s' while trying to load config information", str(e))
+ #change_keys_http2db(content, http2db_datacenter, reverse=True)
+ convert_datetime2str(datacenter)
+ data={'datacenter' : datacenter}
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_get_datacenter_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/datacenters', method='POST')
+def http_post_datacenters():
+ '''insert a tenant into the catalogue. '''
+ #parse input data
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ http_content,_ = format_in( datacenter_schema )
+ r = utils.remove_extra_items(http_content, datacenter_schema)
+ if r:
+ logger.debug("Remove received extra items %s", str(r))
+ try:
+ data = nfvo.new_datacenter(mydb, http_content['datacenter'])
+ return http_get_datacenter_id('any', data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_post_datacenters error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/datacenters/<datacenter_id_name>', method='PUT')
+def http_edit_datacenter_id(datacenter_id_name):
+ '''edit datacenter details, can use both uuid or name'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ #parse input data
+ http_content,_ = format_in( datacenter_edit_schema )
+ r = utils.remove_extra_items(http_content, datacenter_edit_schema)
+ if r:
+ logger.debug("Remove received extra items %s", str(r))
+
+ try:
+ datacenter_id = nfvo.edit_datacenter(mydb, datacenter_id_name, http_content['datacenter'])
+ return http_get_datacenter_id('any', datacenter_id)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_edit_datacenter_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/networks', method='GET') #deprecated
+@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='GET')
+@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='GET')
+def http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
+ '''get datacenter networks, can use both uuid or name'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ #obtain data
+ try:
+ datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
+ where_= {"datacenter_id":datacenter_dict['uuid']}
+ if netmap_id:
+ if utils.check_valid_uuid(netmap_id):
+ where_["uuid"] = netmap_id
+ else:
+ where_["name"] = netmap_id
+ netmaps =mydb.get_rows(FROM='datacenter_nets',
+ SELECT=('name','vim_net_id as vim_id', 'uuid', 'type','multipoint','shared','description', 'created_at'),
+ WHERE=where_ )
+ convert_datetime2str(netmaps)
+ utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
+ if netmap_id and len(netmaps)==1:
+ data={'netmap' : netmaps[0]}
+ elif netmap_id and len(netmaps)==0:
+ bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), where_.iteritems())) )
+ return
+ else:
+ data={'netmaps' : netmaps}
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_getnetwork_datacenter_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='DELETE')
+@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='DELETE')
+def http_delnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
+ '''get datacenter networks, can use both uuid or name'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ #obtain data
+ try:
+ datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
+ where_= {"datacenter_id":datacenter_dict['uuid']}
+ if netmap_id:
+ if utils.check_valid_uuid(netmap_id):
+ where_["uuid"] = netmap_id
+ else:
+ where_["name"] = netmap_id
+ #change_keys_http2db(content, http2db_tenant, reverse=True)
+ deleted = mydb.delete_row(FROM='datacenter_nets', WHERE= where_)
+ if deleted == 0 and netmap_id :
+ bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), where_.iteritems())) )
+ if netmap_id:
+ return format_out({"result": "netmap %s deleted" % netmap_id})
+ else:
+ return format_out({"result": "%d netmap deleted" % deleted})
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_delnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/upload', method='POST')
+def http_uploadnetmap_datacenter_id(tenant_id, datacenter_id):
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ try:
+ netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, None)
+ convert_datetime2str(netmaps)
+ utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
+ data={'netmaps' : netmaps}
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_uploadnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='POST')
+def http_postnetmap_datacenter_id(tenant_id, datacenter_id):
+ '''creates a new netmap'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ #parse input data
+ http_content,_ = format_in( netmap_new_schema )
+ r = utils.remove_extra_items(http_content, netmap_new_schema)
+ if r:
+ logger.debug("Remove received extra items %s", str(r))
+ try:
+ #obtain data, check that only one exist
+ netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, http_content)
+ convert_datetime2str(netmaps)
+ utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
+ data={'netmaps' : netmaps}
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_postnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='PUT')
+def http_putnettmap_datacenter_id(tenant_id, datacenter_id, netmap_id):
+ '''edit a netmap'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ #parse input data
+ http_content,_ = format_in( netmap_edit_schema )
+ r = utils.remove_extra_items(http_content, netmap_edit_schema)
+ if r:
+ logger.debug("Remove received extra items %s", str(r))
+
+ #obtain data, check that only one exist
+ try:
+ nfvo.datacenter_edit_netmap(mydb, tenant_id, datacenter_id, netmap_id, http_content)
+ return http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_putnettmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/action', method='POST')
+def http_action_datacenter_id(tenant_id, datacenter_id):
+ '''perform an action over datacenter, can use both uuid or name'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ #parse input data
+ http_content,_ = format_in( datacenter_action_schema )
+ r = utils.remove_extra_items(http_content, datacenter_action_schema)
+ if r:
+ logger.debug("Remove received extra items %s", str(r))
+ try:
+ #obtain data, check that only one exist
+ result = nfvo.datacenter_action(mydb, tenant_id, datacenter_id, http_content)
+ if 'net-update' in http_content:
+ return http_getnetmap_datacenter_id(datacenter_id)
+ else:
+ return format_out(result)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_action_datacenter_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/datacenters/<datacenter_id>', method='DELETE')
+def http_delete_datacenter_id( datacenter_id):
+ '''delete a tenant from database, can use both uuid or name'''
+
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ try:
+ data = nfvo.delete_datacenter(mydb, datacenter_id)
+ return format_out({"result":"datacenter '" + data + "' deleted"})
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_delete_datacenter_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='POST')
+def http_associate_datacenters(tenant_id, datacenter_id):
+ '''associate an existing datacenter to a this tenant. '''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ #parse input data
+ http_content,_ = format_in( datacenter_associate_schema )
+ r = utils.remove_extra_items(http_content, datacenter_associate_schema)
+ if r:
+ logger.debug("Remove received extra items %s", str(r))
+ try:
+ id_ = nfvo.associate_datacenter_to_tenant(mydb, tenant_id, datacenter_id,
+ http_content['datacenter'].get('vim_tenant'),
+ http_content['datacenter'].get('vim_tenant_name'),
+ http_content['datacenter'].get('vim_username'),
+ http_content['datacenter'].get('vim_password'),
+ http_content['datacenter'].get('config')
+ )
+ return http_get_datacenter_id(tenant_id, id_)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_associate_datacenters error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='DELETE')
+def http_deassociate_datacenters(tenant_id, datacenter_id):
+ '''deassociate an existing datacenter to a this tenant. '''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ try:
+ data = nfvo.deassociate_datacenter_to_tenant(mydb, tenant_id, datacenter_id)
+ return format_out({"result": data})
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_deassociate_datacenters error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='GET')
+@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='GET')
+def http_get_vim_items(tenant_id, datacenter_id, item, name=None):
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ try:
+ data = nfvo.vim_action_get(mydb, tenant_id, datacenter_id, item, name)
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_get_vim_items error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='DELETE')
+def http_del_vim_items(tenant_id, datacenter_id, item, name):
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ try:
+ data = nfvo.vim_action_delete(mydb, tenant_id, datacenter_id, item, name)
+ return format_out({"result":data})
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_del_vim_items error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='POST')
+def http_post_vim_items(tenant_id, datacenter_id, item):
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ http_content,_ = format_in( object_schema )
+ try:
+ data = nfvo.vim_action_create(mydb, tenant_id, datacenter_id, item, http_content)
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_post_vim_items error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/vnfs', method='GET')
+def http_get_vnfs(tenant_id):
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ try:
+ if tenant_id != 'any':
+ #check valid tenant_id
+ nfvo.check_tenant(mydb, tenant_id)
+ select_,where_,limit_ = filter_query_string(bottle.request.query, None,
+ ('uuid','name','description','public', "tenant_id", "created_at") )
+ where_or = {}
+ if tenant_id != "any":
+ where_or["tenant_id"] = tenant_id
+ where_or["public"] = True
+ vnfs = mydb.get_rows(FROM='vnfs', SELECT=select_,WHERE=where_,WHERE_OR=where_or, WHERE_AND_OR="AND",LIMIT=limit_)
+ #change_keys_http2db(content, http2db_vnf, reverse=True)
+ utils.convert_str2boolean(vnfs, ('public',))
+ convert_datetime2str(vnfs)
+ data={'vnfs' : vnfs}
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_get_vnfs error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='GET')
+def http_get_vnf_id(tenant_id,vnf_id):
+ '''get vnf details, can use both uuid or name'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ try:
+ vnf = nfvo.get_vnf_id(mydb,tenant_id,vnf_id)
+ utils.convert_str2boolean(vnf, ('public',))
+ convert_datetime2str(vnf)
+ return format_out(vnf)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_get_vnf_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/vnfs', method='POST')
+def http_post_vnfs(tenant_id):
+ '''insert a vnf into the catalogue. Creates the flavor and images in the VIM, and creates the VNF and its internal structure in the OPENMANO DB'''
+ #print "Parsing the YAML file of the VNF"
+ #parse input data
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ http_content, used_schema = format_in( vnfd_schema_v01, ("schema_version",), {"0.2": vnfd_schema_v02})
+ r = utils.remove_extra_items(http_content, used_schema)
+ if r:
+ logger.debug("Remove received extra items %s", str(r))
+ try:
+ if used_schema == vnfd_schema_v01:
+ vnf_id = nfvo.new_vnf(mydb,tenant_id,http_content)
+ elif used_schema == vnfd_schema_v02:
+ vnf_id = nfvo.new_vnf_v02(mydb,tenant_id,http_content)
+ else:
+ logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
+ bottle.abort(HTTP_Bad_Request, "Invalid schema version")
+ return http_get_vnf_id(tenant_id, vnf_id)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_post_vnfs error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='DELETE')
+def http_delete_vnf_id(tenant_id,vnf_id):
+ '''delete a vnf from database, and images and flavors in VIM when appropriate, can use both uuid or name'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ #check valid tenant_id and deletes the vnf, including images,
+ try:
+ data = nfvo.delete_vnf(mydb,tenant_id,vnf_id)
+ #print json.dumps(data, indent=4)
+ return format_out({"result":"VNF " + data + " deleted"})
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_delete_vnf_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+#@bottle.route(url_base + '/<tenant_id>/hosts/topology', method='GET')
+#@bottle.route(url_base + '/<tenant_id>/physicalview/Madrid-Alcantara', method='GET')
+@bottle.route(url_base + '/<tenant_id>/physicalview/<datacenter>', method='GET')
+def http_get_hosts(tenant_id, datacenter):
+ '''get the tidvim host hopology from the vim.'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ #print "http_get_hosts received by tenant " + tenant_id + ' datacenter ' + datacenter
+ try:
+ if datacenter == 'treeview':
+ data = nfvo.get_hosts(mydb, tenant_id)
+ else:
+ #openmano-gui is using a hardcoded value for the datacenter
+ result, data = nfvo.get_hosts_info(mydb, tenant_id) #, datacenter)
+
+ if result < 0:
+ #print "http_get_hosts error %d %s" % (-result, data)
+ bottle.abort(-result, data)
+ else:
+ convert_datetime2str(data)
+ #print json.dumps(data, indent=4)
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_get_hosts error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<path:path>', method='OPTIONS')
+def http_options_deploy(path):
+ '''For some reason GUI web ask for OPTIONS that must be responded'''
+ #TODO: check correct path, and correct headers request
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ bottle.response.set_header('Access-Control-Allow-Methods','POST, GET, PUT, DELETE, OPTIONS')
+ bottle.response.set_header('Accept','application/yaml,application/json')
+ bottle.response.set_header('Content-Type','application/yaml,application/json')
+ bottle.response.set_header('Access-Control-Allow-Headers','content-type')
+ bottle.response.set_header('Access-Control-Allow-Origin','*')
+ return
+
+@bottle.route(url_base + '/<tenant_id>/topology/deploy', method='POST')
+def http_post_deploy(tenant_id):
+ '''post topology deploy.'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+
+ http_content, used_schema = format_in( nsd_schema_v01, ("schema_version",), {2: nsd_schema_v02})
+ #r = utils.remove_extra_items(http_content, used_schema)
+ #if r is not None: print "http_post_deploy: Warning: remove extra items ", r
+ #print "http_post_deploy input: ", http_content
+
+ try:
+ scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
+ instance = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['name'], http_content['name'])
+ #print json.dumps(data, indent=4)
+ return format_out(instance)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_post_deploy error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/topology/verify', method='POST')
+def http_post_verify(tenant_id):
+ #TODO:
+# '''post topology verify'''
+# print "http_post_verify by tenant " + tenant_id + ' datacenter ' + datacenter
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ return
+
+#
+# SCENARIOS
+#
+
+@bottle.route(url_base + '/<tenant_id>/scenarios', method='POST')
+def http_post_scenarios(tenant_id):
+ '''add a scenario into the catalogue. Creates the scenario and its internal structure in the OPENMANO DB'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ http_content, used_schema = format_in( nsd_schema_v01, ("schema_version",), {2: nsd_schema_v02, "0.3": nsd_schema_v03})
+ #r = utils.remove_extra_items(http_content, used_schema)
+ #if r is not None: print "http_post_scenarios: Warning: remove extra items ", r
+ #print "http_post_scenarios input: ", http_content
+ try:
+ if used_schema == nsd_schema_v01:
+ scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
+ elif used_schema == nsd_schema_v02:
+ scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content, "0.2")
+ elif used_schema == nsd_schema_v03:
+ scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content, "0.3")
+ else:
+ logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
+ bottle.abort(HTTP_Bad_Request, "Invalid schema version")
+ #print json.dumps(data, indent=4)
+ #return format_out(data)
+ return http_get_scenario_id(tenant_id, scenario_id)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_post_scenarios error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>/action', method='POST')
+def http_post_scenario_action(tenant_id, scenario_id):
+ '''take an action over a scenario'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ # parse input data
+ http_content, _ = format_in(scenario_action_schema)
+ r = utils.remove_extra_items(http_content, scenario_action_schema)
+ if r:
+ logger.debug("Remove received extra items %s", str(r))
+ try:
+ # check valid tenant_id
+ nfvo.check_tenant(mydb, tenant_id)
+ if "start" in http_content:
+ data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['start']['instance_name'], \
+ http_content['start'].get('description',http_content['start']['instance_name']),
+ http_content['start'].get('datacenter') )
+ return format_out(data)
+ elif "deploy" in http_content: #Equivalent to start
+ data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['deploy']['instance_name'],
+ http_content['deploy'].get('description',http_content['deploy']['instance_name']),
+ http_content['deploy'].get('datacenter') )
+ return format_out(data)
+ elif "reserve" in http_content: #Reserve resources
+ data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['reserve']['instance_name'],
+ http_content['reserve'].get('description',http_content['reserve']['instance_name']),
+ http_content['reserve'].get('datacenter'), startvms=False )
+ return format_out(data)
+ elif "verify" in http_content: #Equivalent to start and then delete
+ data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['verify']['instance_name'],
+ http_content['verify'].get('description',http_content['verify']['instance_name']),
+ http_content['verify'].get('datacenter'), startvms=False )
+ instance_id = data['uuid']
+ nfvo.delete_instance(mydb, tenant_id,instance_id)
+ return format_out({"result":"Verify OK"})
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_post_scenario_action error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/scenarios', method='GET')
+def http_get_scenarios(tenant_id):
+ '''get scenarios list'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ try:
+ #check valid tenant_id
+ if tenant_id != "any":
+ nfvo.check_tenant(mydb, tenant_id)
+ #obtain data
+ s,w,l=filter_query_string(bottle.request.query, None, ('uuid', 'name', 'description', 'tenant_id', 'created_at', 'public'))
+ where_or={}
+ if tenant_id != "any":
+ where_or["tenant_id"] = tenant_id
+ where_or["public"] = True
+ scenarios = mydb.get_rows(SELECT=s, WHERE=w, WHERE_OR=where_or, WHERE_AND_OR="AND", LIMIT=l, FROM='scenarios')
+ convert_datetime2str(scenarios)
+ utils.convert_str2boolean(scenarios, ('public',) )
+ data={'scenarios':scenarios}
+ #print json.dumps(scenarios, indent=4)
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='GET')
+def http_get_scenario_id(tenant_id, scenario_id):
+ '''get scenario details, can use both uuid or name'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ try:
+ #check valid tenant_id
+ if tenant_id != "any":
+ nfvo.check_tenant(mydb, tenant_id)
+ #obtain data
+ scenario = mydb.get_scenario(scenario_id, tenant_id)
+ convert_datetime2str(scenario)
+ data={'scenario' : scenario}
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='DELETE')
+def http_delete_scenario_id(tenant_id, scenario_id):
+ '''delete a scenario from database, can use both uuid or name'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ try:
+ #check valid tenant_id
+ if tenant_id != "any":
+ nfvo.check_tenant(mydb, tenant_id)
+ #obtain data
+ data = mydb.delete_scenario(scenario_id, tenant_id)
+ #print json.dumps(data, indent=4)
+ return format_out({"result":"scenario " + data + " deleted"})
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_delete_scenario_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='PUT')
+def http_put_scenario_id(tenant_id, scenario_id):
+ '''edit an existing scenario id'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ http_content,_ = format_in( scenario_edit_schema )
+ #r = utils.remove_extra_items(http_content, scenario_edit_schema)
+ #if r is not None: print "http_put_scenario_id: Warning: remove extra items ", r
+ #print "http_put_scenario_id input: ", http_content
+ try:
+ nfvo.edit_scenario(mydb, tenant_id, scenario_id, http_content)
+ #print json.dumps(data, indent=4)
+ #return format_out(data)
+ return http_get_scenario_id(tenant_id, scenario_id)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_put_scenario_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+@bottle.route(url_base + '/<tenant_id>/instances', method='POST')
+def http_post_instances(tenant_id):
+ '''create an instance-scenario'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ # parse input data
+ http_content, used_schema = format_in(instance_scenario_create_schema_v01)
+ r = utils.remove_extra_items(http_content, used_schema)
+ if r is not None:
+ logger.warning("http_post_instances: Warning: remove extra items %s", str(r))
+ try:
+ #check valid tenant_id
+ if tenant_id != "any":
+ nfvo.check_tenant(mydb, tenant_id)
+ data = nfvo.create_instance(mydb, tenant_id, http_content["instance"])
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_post_instances error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+#
+# INSTANCES
+#
+@bottle.route(url_base + '/<tenant_id>/instances', method='GET')
+def http_get_instances(tenant_id):
+ '''get instance list'''
+ try:
+ #check valid tenant_id
+ if tenant_id != "any":
+ nfvo.check_tenant(mydb, tenant_id)
+ #obtain data
+ s,w,l=filter_query_string(bottle.request.query, None, ('uuid', 'name', 'scenario_id', 'tenant_id', 'description', 'created_at'))
+ if tenant_id != "any":
+ w['tenant_id'] = tenant_id
+ instances = mydb.get_rows(SELECT=s, WHERE=w, LIMIT=l, FROM='instance_scenarios')
+ convert_datetime2str(instances)
+ utils.convert_str2boolean(instances, ('public',) )
+ data={'instances':instances}
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_get_instances error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='GET')
+def http_get_instance_id(tenant_id, instance_id):
+ '''get instances details, can use both uuid or name'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ try:
+ #check valid tenant_id
+ if tenant_id != "any":
+ nfvo.check_tenant(mydb, tenant_id)
+ if tenant_id == "any":
+ tenant_id = None
+ #obtain data (first time is only to check that the instance exists)
+ instance_dict = mydb.get_instance_scenario(instance_id, tenant_id, verbose=True)
+ try:
+ nfvo.refresh_instance(mydb, tenant_id, instance_dict)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.warn("nfvo.refresh_instance couldn't refresh the status of the instance: %s" % str(e))
+ #obtain data with results upated
+ instance = mydb.get_instance_scenario(instance_id, tenant_id)
+ convert_datetime2str(instance)
+ #print json.dumps(instance, indent=4)
+ return format_out(instance)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_get_instance_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='DELETE')
+def http_delete_instance_id(tenant_id, instance_id):
+ '''delete instance from VIM and from database, can use both uuid or name'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ try:
+ #check valid tenant_id
+ if tenant_id != "any":
+ nfvo.check_tenant(mydb, tenant_id)
+ if tenant_id == "any":
+ tenant_id = None
+ #obtain data
+ message = nfvo.delete_instance(mydb, tenant_id,instance_id)
+ return format_out({"result":message})
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_delete_instance_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action', method='POST')
+def http_post_instance_scenario_action(tenant_id, instance_id):
+ '''take an action over a scenario instance'''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ # parse input data
+ http_content, _ = format_in(instance_scenario_action_schema)
+ r = utils.remove_extra_items(http_content, instance_scenario_action_schema)
+ if r:
+ logger.debug("Remove received extra items %s", str(r))
+ try:
+ #check valid tenant_id
+ if tenant_id != "any":
+ nfvo.check_tenant(mydb, tenant_id)
+
+ #print "http_post_instance_scenario_action input: ", http_content
+ #obtain data
+ instance = mydb.get_instance_scenario(instance_id, tenant_id)
+ instance_id = instance["uuid"]
+
+ data = nfvo.instance_action(mydb, tenant_id, instance_id, http_content)
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_post_instance_scenario_action error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
+@bottle.error(400)
+@bottle.error(401)
+@bottle.error(404)
+@bottle.error(403)
+@bottle.error(405)
+@bottle.error(406)
+@bottle.error(409)
+@bottle.error(503)
+@bottle.error(500)
+def error400(error):
+ e={"error":{"code":error.status_code, "type":error.status, "description":error.body}}
+ bottle.response.headers['Access-Control-Allow-Origin'] = '*'
+ return format_out(e)
+
--- /dev/null
+# -*- 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
+##
+
+'''
+NFVO engine, implementing all the methods for the creation, deletion and management of vnfs, scenarios and instances
+'''
+__author__="Alfonso Tierno, Gerardo Garcia, Pablo Montes"
+__date__ ="$16-sep-2014 22:05:01$"
+
+import imp
+#import json
+import yaml
+import utils
+import vim_thread
+from db_base import HTTP_Unauthorized, HTTP_Bad_Request, HTTP_Internal_Server_Error, HTTP_Not_Found,\
+ HTTP_Conflict, HTTP_Method_Not_Allowed
+import console_proxy_thread as cli
+import vimconn
+import logging
+import collections
+from db_base import db_base_Exception
+import nfvo_db
+from threading import Lock
+from time import time
+
+global global_config
+global vimconn_imported
+global logger
+global default_volume_size
+default_volume_size = '5' #size in GB
+
+
+vimconn_imported = {} # dictionary with VIM type as key, loaded module as value
+vim_threads = {"running":{}, "deleting": {}, "names": []} # threads running for attached-VIMs
+vim_persistent_info = {}
+logger = logging.getLogger('openmano.nfvo')
+task_lock = Lock()
+task_dict = {}
+last_task_id = 0.0
+db=None
+db_lock=Lock()
+
+class NfvoException(Exception):
+ def __init__(self, message, http_code):
+ self.http_code = http_code
+ Exception.__init__(self, message)
+
+
+def get_task_id():
+ global last_task_id
+ task_id = time()
+ if task_id <= last_task_id:
+ task_id = last_task_id + 0.000001
+ last_task_id = task_id
+ return "TASK.{:.6f}".format(task_id)
+
+
+def new_task(name, params, store=True, depends=None):
+ task_id = get_task_id()
+ task = {"status": "enqueued", "id": task_id, "name": name, "params": params}
+ if depends:
+ task["depends"] = depends
+ if store:
+ task_dict[task_id] = task
+ return task
+
+
+def is_task_id(id):
+ return True if id[:5] == "TASK." else False
+
+
+def get_non_used_vim_name(datacenter_name, datacenter_id, tenant_name, tenant_id):
+ name = datacenter_name[:16]
+ if name not in vim_threads["names"]:
+ vim_threads["names"].append(name)
+ return name
+ name = datacenter_name[:16] + "." + tenant_name[:16]
+ if name not in vim_threads["names"]:
+ vim_threads["names"].append(name)
+ return name
+ name = datacenter_id + "-" + tenant_id
+ vim_threads["names"].append(name)
+ return name
+
+
+def start_service(mydb):
+ global db, global_config
+ db = nfvo_db.nfvo_db()
+ db.connect(global_config['db_host'], global_config['db_user'], global_config['db_passwd'], global_config['db_name'])
+ from_= 'tenants_datacenters as td join datacenters as d on td.datacenter_id=d.uuid join datacenter_tenants as dt on td.datacenter_tenant_id=dt.uuid'
+ select_ = ('type','d.config as config','d.uuid as datacenter_id', 'vim_url', 'vim_url_admin', 'd.name as datacenter_name',
+ 'dt.uuid as datacenter_tenant_id','dt.vim_tenant_name as vim_tenant_name','dt.vim_tenant_id as vim_tenant_id',
+ 'user','passwd', 'dt.config as dt_config', 'nfvo_tenant_id')
+ try:
+ vims = mydb.get_rows(FROM=from_, SELECT=select_)
+ for vim in vims:
+ extra={'datacenter_tenant_id': vim.get('datacenter_tenant_id')}
+ if vim["config"]:
+ extra.update(yaml.load(vim["config"]))
+ if vim.get('dt_config'):
+ extra.update(yaml.load(vim["dt_config"]))
+ if vim["type"] not in vimconn_imported:
+ module_info=None
+ try:
+ module = "vimconn_" + vim["type"]
+ module_info = imp.find_module(module)
+ vim_conn = imp.load_module(vim["type"], *module_info)
+ vimconn_imported[vim["type"]] = vim_conn
+ except (IOError, ImportError) as e:
+ if module_info and module_info[0]:
+ file.close(module_info[0])
+ raise NfvoException("Unknown vim type '{}'. Can not open file '{}.py'; {}: {}".format(
+ vim["type"], module, type(e).__name__, str(e)), HTTP_Bad_Request)
+
+ thread_id = vim["datacenter_id"] + "." + vim['nfvo_tenant_id']
+ vim_persistent_info[thread_id] = {}
+ try:
+ #if not tenant:
+ # return -HTTP_Bad_Request, "You must provide a valid tenant name or uuid for VIM %s" % ( vim["type"])
+ myvim = vimconn_imported[ vim["type"] ].vimconnector(
+ uuid=vim['datacenter_id'], name=vim['datacenter_name'],
+ tenant_id=vim['vim_tenant_id'], tenant_name=vim['vim_tenant_name'],
+ url=vim['vim_url'], url_admin=vim['vim_url_admin'],
+ user=vim['user'], passwd=vim['passwd'],
+ config=extra, persistent_info=vim_persistent_info[thread_id]
+ )
+ except Exception as e:
+ raise NfvoException("Error at VIM {}; {}: {}".format(vim["type"], type(e).__name__, str(e)), HTTP_Internal_Server_Error)
+ thread_name = get_non_used_vim_name(vim['datacenter_name'], vim['vim_tenant_id'], vim['vim_tenant_name'], vim['vim_tenant_id'])
+ new_thread = vim_thread.vim_thread(myvim, task_lock, thread_name, vim['datacenter_name'],
+ vim.get('datacenter_tenant_id'), db=db, db_lock=db_lock)
+ new_thread.start()
+ vim_threads["running"][thread_id] = new_thread
+ except db_base_Exception as e:
+ raise NfvoException(str(e) + " at nfvo.get_vim", e.http_code)
+
+
+def stop_service():
+ for thread_id,thread in vim_threads["running"].items():
+ thread.insert_task(new_task("exit", None, store=False))
+ vim_threads["deleting"][thread_id] = thread
+ vim_threads["running"] = {}
+
+
+def get_flavorlist(mydb, vnf_id, nfvo_tenant=None):
+ '''Obtain flavorList
+ return result, content:
+ <0, error_text upon error
+ nb_records, flavor_list on success
+ '''
+ WHERE_dict={}
+ WHERE_dict['vnf_id'] = vnf_id
+ if nfvo_tenant is not None:
+ WHERE_dict['nfvo_tenant_id'] = nfvo_tenant
+
+ #result, content = mydb.get_table(FROM='vms join vnfs on vms.vnf_id = vnfs.uuid',SELECT=('uuid'),WHERE=WHERE_dict )
+ #result, content = mydb.get_table(FROM='vms',SELECT=('vim_flavor_id',),WHERE=WHERE_dict )
+ flavors = mydb.get_rows(FROM='vms join flavors on vms.flavor_id=flavors.uuid',SELECT=('flavor_id',),WHERE=WHERE_dict )
+ #print "get_flavor_list result:", result
+ #print "get_flavor_list content:", content
+ flavorList=[]
+ for flavor in flavors:
+ flavorList.append(flavor['flavor_id'])
+ return flavorList
+
+
+def get_imagelist(mydb, vnf_id, nfvo_tenant=None):
+ '''Obtain imageList
+ return result, content:
+ <0, error_text upon error
+ nb_records, flavor_list on success
+ '''
+ WHERE_dict={}
+ WHERE_dict['vnf_id'] = vnf_id
+ if nfvo_tenant is not None:
+ WHERE_dict['nfvo_tenant_id'] = nfvo_tenant
+
+ #result, content = mydb.get_table(FROM='vms join vnfs on vms-vnf_id = vnfs.uuid',SELECT=('uuid'),WHERE=WHERE_dict )
+ images = mydb.get_rows(FROM='vms join images on vms.image_id=images.uuid',SELECT=('image_id',),WHERE=WHERE_dict )
+ imageList=[]
+ for image in images:
+ imageList.append(image['image_id'])
+ return imageList
+
+
+def get_vim(mydb, nfvo_tenant=None, datacenter_id=None, datacenter_name=None, datacenter_tenant_id=None,
+ vim_tenant=None, vim_tenant_name=None, vim_user=None, vim_passwd=None):
+ '''Obtain a dictionary of VIM (datacenter) classes with some of the input parameters
+ return dictionary with {datacenter_id: vim_class, ... }. vim_class contain:
+ 'nfvo_tenant_id','datacenter_id','vim_tenant_id','vim_url','vim_url_admin','datacenter_name','type','user','passwd'
+ raise exception upon error
+ '''
+ WHERE_dict={}
+ if nfvo_tenant is not None: WHERE_dict['nfvo_tenant_id'] = nfvo_tenant
+ if datacenter_id is not None: WHERE_dict['d.uuid'] = datacenter_id
+ if datacenter_tenant_id is not None: WHERE_dict['datacenter_tenant_id'] = datacenter_tenant_id
+ if datacenter_name is not None: WHERE_dict['d.name'] = datacenter_name
+ if vim_tenant is not None: WHERE_dict['dt.vim_tenant_id'] = vim_tenant
+ if vim_tenant_name is not None: WHERE_dict['vim_tenant_name'] = vim_tenant_name
+ if nfvo_tenant or vim_tenant or vim_tenant_name or datacenter_tenant_id:
+ from_= 'tenants_datacenters as td join datacenters as d on td.datacenter_id=d.uuid join datacenter_tenants as dt on td.datacenter_tenant_id=dt.uuid'
+ select_ = ('type','d.config as config','d.uuid as datacenter_id', 'vim_url', 'vim_url_admin', 'd.name as datacenter_name',
+ 'dt.uuid as datacenter_tenant_id','dt.vim_tenant_name as vim_tenant_name','dt.vim_tenant_id as vim_tenant_id',
+ 'user','passwd', 'dt.config as dt_config')
+ else:
+ from_ = 'datacenters as d'
+ select_ = ('type','config','d.uuid as datacenter_id', 'vim_url', 'vim_url_admin', 'd.name as datacenter_name')
+ try:
+ vims = mydb.get_rows(FROM=from_, SELECT=select_, WHERE=WHERE_dict )
+ vim_dict={}
+ for vim in vims:
+ extra={'datacenter_tenant_id': vim.get('datacenter_tenant_id')}
+ if vim["config"]:
+ extra.update(yaml.load(vim["config"]))
+ if vim.get('dt_config'):
+ extra.update(yaml.load(vim["dt_config"]))
+ if vim["type"] not in vimconn_imported:
+ module_info=None
+ try:
+ module = "vimconn_" + vim["type"]
+ module_info = imp.find_module(module)
+ vim_conn = imp.load_module(vim["type"], *module_info)
+ vimconn_imported[vim["type"]] = vim_conn
+ except (IOError, ImportError) as e:
+ if module_info and module_info[0]:
+ file.close(module_info[0])
+ raise NfvoException("Unknown vim type '{}'. Can not open file '{}.py'; {}: {}".format(
+ vim["type"], module, type(e).__name__, str(e)), HTTP_Bad_Request)
+
+ try:
+ if 'nfvo_tenant_id' in vim:
+ thread_id = vim["datacenter_id"] + "." + vim['nfvo_tenant_id']
+ if thread_id not in vim_persistent_info:
+ vim_persistent_info[thread_id] = {}
+ persistent_info = vim_persistent_info[thread_id]
+ else:
+ persistent_info = {}
+ #if not tenant:
+ # return -HTTP_Bad_Request, "You must provide a valid tenant name or uuid for VIM %s" % ( vim["type"])
+ vim_dict[ vim['datacenter_id'] ] = vimconn_imported[ vim["type"] ].vimconnector(
+ uuid=vim['datacenter_id'], name=vim['datacenter_name'],
+ tenant_id=vim.get('vim_tenant_id',vim_tenant),
+ tenant_name=vim.get('vim_tenant_name',vim_tenant_name),
+ url=vim['vim_url'], url_admin=vim['vim_url_admin'],
+ user=vim.get('user',vim_user), passwd=vim.get('passwd',vim_passwd),
+ config=extra, persistent_info=persistent_info
+ )
+ except Exception as e:
+ raise NfvoException("Error at VIM {}; {}: {}".format(vim["type"], type(e).__name__, str(e)), HTTP_Internal_Server_Error)
+ return vim_dict
+ except db_base_Exception as e:
+ raise NfvoException(str(e) + " at nfvo.get_vim", e.http_code)
+
+
+def rollback(mydb, vims, rollback_list):
+ undeleted_items=[]
+ #delete things by reverse order
+ for i in range(len(rollback_list)-1, -1, -1):
+ item = rollback_list[i]
+ if item["where"]=="vim":
+ if item["vim_id"] not in vims:
+ continue
+ vim=vims[ item["vim_id"] ]
+ try:
+ if item["what"]=="image":
+ vim.delete_image(item["uuid"])
+ mydb.delete_row(FROM="datacenters_images", WHERE={"datacenter_id": vim["id"], "vim_id":item["uuid"]})
+ elif item["what"]=="flavor":
+ vim.delete_flavor(item["uuid"])
+ mydb.delete_row(FROM="datacenters_flavors", WHERE={"datacenter_id": vim["id"], "vim_id":item["uuid"]})
+ elif item["what"]=="network":
+ vim.delete_network(item["uuid"])
+ elif item["what"]=="vm":
+ vim.delete_vminstance(item["uuid"])
+ except vimconn.vimconnException as e:
+ logger.error("Error in rollback. Not possible to delete VIM %s '%s'. Message: %s", item['what'], item["uuid"], str(e))
+ undeleted_items.append("{} {} from VIM {}".format(item['what'], item["uuid"], vim["name"]))
+ except db_base_Exception as e:
+ logger.error("Error in rollback. Not possible to delete %s '%s' from DB.datacenters Message: %s", item['what'], item["uuid"], str(e))
+
+ else: # where==mano
+ try:
+ if item["what"]=="image":
+ mydb.delete_row(FROM="images", WHERE={"uuid": item["uuid"]})
+ elif item["what"]=="flavor":
+ mydb.delete_row(FROM="flavors", WHERE={"uuid": item["uuid"]})
+ except db_base_Exception as e:
+ logger.error("Error in rollback. Not possible to delete %s '%s' from DB. Message: %s", item['what'], item["uuid"], str(e))
+ undeleted_items.append("{} '{}'".format(item['what'], item["uuid"]))
+ if len(undeleted_items)==0:
+ return True," Rollback successful."
+ else:
+ return False," Rollback fails to delete: " + str(undeleted_items)
+
+
+def check_vnf_descriptor(vnf_descriptor, vnf_descriptor_version=1):
+ global global_config
+ #create a dictionary with vnfc-name: vnfc:interface-list key:values pairs
+ vnfc_interfaces={}
+ for vnfc in vnf_descriptor["vnf"]["VNFC"]:
+ name_dict = {}
+ #dataplane interfaces
+ for numa in vnfc.get("numas",() ):
+ for interface in numa.get("interfaces",()):
+ if interface["name"] in name_dict:
+ raise NfvoException(
+ "Error at vnf:VNFC[name:'{}']:numas:interfaces:name, interface name '{}' already used in this VNFC".format(
+ vnfc["name"], interface["name"]),
+ HTTP_Bad_Request)
+ name_dict[ interface["name"] ] = "underlay"
+ #bridge interfaces
+ for interface in vnfc.get("bridge-ifaces",() ):
+ if interface["name"] in name_dict:
+ raise NfvoException(
+ "Error at vnf:VNFC[name:'{}']:bridge-ifaces:name, interface name '{}' already used in this VNFC".format(
+ vnfc["name"], interface["name"]),
+ HTTP_Bad_Request)
+ name_dict[ interface["name"] ] = "overlay"
+ vnfc_interfaces[ vnfc["name"] ] = name_dict
+ # check bood-data info
+ if "boot-data" in vnfc:
+ # check that user-data is incompatible with users and config-files
+ if (vnfc["boot-data"].get("users") or vnfc["boot-data"].get("config-files")) and vnfc["boot-data"].get("user-data"):
+ raise NfvoException(
+ "Error at vnf:VNFC:boot-data, fields 'users' and 'config-files' are not compatible with 'user-data'",
+ HTTP_Bad_Request)
+
+ #check if the info in external_connections matches with the one in the vnfcs
+ name_list=[]
+ for external_connection in vnf_descriptor["vnf"].get("external-connections",() ):
+ if external_connection["name"] in name_list:
+ raise NfvoException(
+ "Error at vnf:external-connections:name, value '{}' already used as an external-connection".format(
+ external_connection["name"]),
+ HTTP_Bad_Request)
+ name_list.append(external_connection["name"])
+ if external_connection["VNFC"] not in vnfc_interfaces:
+ raise NfvoException(
+ "Error at vnf:external-connections[name:'{}']:VNFC, value '{}' does not match any VNFC".format(
+ external_connection["name"], external_connection["VNFC"]),
+ HTTP_Bad_Request)
+
+ if external_connection["local_iface_name"] not in vnfc_interfaces[ external_connection["VNFC"] ]:
+ raise NfvoException(
+ "Error at vnf:external-connections[name:'{}']:local_iface_name, value '{}' does not match any interface of this VNFC".format(
+ external_connection["name"],
+ external_connection["local_iface_name"]),
+ HTTP_Bad_Request )
+
+ #check if the info in internal_connections matches with the one in the vnfcs
+ name_list=[]
+ for internal_connection in vnf_descriptor["vnf"].get("internal-connections",() ):
+ if internal_connection["name"] in name_list:
+ raise NfvoException(
+ "Error at vnf:internal-connections:name, value '%s' already used as an internal-connection".format(
+ internal_connection["name"]),
+ HTTP_Bad_Request)
+ name_list.append(internal_connection["name"])
+ #We should check that internal-connections of type "ptp" have only 2 elements
+
+ if len(internal_connection["elements"])>2 and (internal_connection.get("type") == "ptp" or internal_connection.get("type") == "e-line"):
+ raise NfvoException(
+ "Error at 'vnf:internal-connections[name:'{}']:elements', size must be 2 for a '{}' type. Consider change it to '{}' type".format(
+ internal_connection["name"],
+ 'ptp' if vnf_descriptor_version==1 else 'e-line',
+ 'data' if vnf_descriptor_version==1 else "e-lan"),
+ HTTP_Bad_Request)
+ for port in internal_connection["elements"]:
+ vnf = port["VNFC"]
+ iface = port["local_iface_name"]
+ if vnf not in vnfc_interfaces:
+ raise NfvoException(
+ "Error at vnf:internal-connections[name:'{}']:elements[]:VNFC, value '{}' does not match any VNFC".format(
+ internal_connection["name"], vnf),
+ HTTP_Bad_Request)
+ if iface not in vnfc_interfaces[ vnf ]:
+ raise NfvoException(
+ "Error at vnf:internal-connections[name:'{}']:elements[]:local_iface_name, value '{}' does not match any interface of this VNFC".format(
+ internal_connection["name"], iface),
+ HTTP_Bad_Request)
+ return -HTTP_Bad_Request,
+ if vnf_descriptor_version==1 and "type" not in internal_connection:
+ if vnfc_interfaces[vnf][iface] == "overlay":
+ internal_connection["type"] = "bridge"
+ else:
+ internal_connection["type"] = "data"
+ if vnf_descriptor_version==2 and "implementation" not in internal_connection:
+ if vnfc_interfaces[vnf][iface] == "overlay":
+ internal_connection["implementation"] = "overlay"
+ else:
+ internal_connection["implementation"] = "underlay"
+ if (internal_connection.get("type") == "data" or internal_connection.get("type") == "ptp" or \
+ internal_connection.get("implementation") == "underlay") and vnfc_interfaces[vnf][iface] == "overlay":
+ raise NfvoException(
+ "Error at vnf:internal-connections[name:'{}']:elements[]:{}, interface of type {} connected to an {} network".format(
+ internal_connection["name"],
+ iface, 'bridge' if vnf_descriptor_version==1 else 'overlay',
+ 'data' if vnf_descriptor_version==1 else 'underlay'),
+ HTTP_Bad_Request)
+ if (internal_connection.get("type") == "bridge" or internal_connection.get("implementation") == "overlay") and \
+ vnfc_interfaces[vnf][iface] == "underlay":
+ raise NfvoException(
+ "Error at vnf:internal-connections[name:'{}']:elements[]:{}, interface of type {} connected to an {} network".format(
+ internal_connection["name"], iface,
+ 'data' if vnf_descriptor_version==1 else 'underlay',
+ 'bridge' if vnf_descriptor_version==1 else 'overlay'),
+ HTTP_Bad_Request)
+
+
+def create_or_use_image(mydb, vims, image_dict, rollback_list, only_create_at_vim=False, return_on_error = None):
+ #look if image exist
+ if only_create_at_vim:
+ image_mano_id = image_dict['uuid']
+ if return_on_error == None:
+ return_on_error = True
+ else:
+ if image_dict['location']:
+ images = mydb.get_rows(FROM="images", WHERE={'location':image_dict['location'], 'metadata':image_dict['metadata']})
+ else:
+ images = mydb.get_rows(FROM="images", WHERE={'universal_name':image_dict['universal_name'], 'checksum':image_dict['checksum']})
+ if len(images)>=1:
+ image_mano_id = images[0]['uuid']
+ else:
+ #create image in MANO DB
+ temp_image_dict={'name':image_dict['name'], 'description':image_dict.get('description',None),
+ 'location':image_dict['location'], 'metadata':image_dict.get('metadata',None),
+ 'universal_name':image_dict['universal_name'] , 'checksum':image_dict['checksum']
+ }
+ #temp_image_dict['location'] = image_dict.get('new_location') if image_dict['location'] is None
+ image_mano_id = mydb.new_row('images', temp_image_dict, add_uuid=True)
+ rollback_list.append({"where":"mano", "what":"image","uuid":image_mano_id})
+ #create image at every vim
+ for vim_id,vim in vims.iteritems():
+ image_created="false"
+ #look at database
+ image_db = mydb.get_rows(FROM="datacenters_images", WHERE={'datacenter_id':vim_id, 'image_id':image_mano_id})
+ #look at VIM if this image exist
+ try:
+ if image_dict['location'] is not None:
+ image_vim_id = vim.get_image_id_from_path(image_dict['location'])
+ else:
+ filter_dict = {}
+ filter_dict['name'] = image_dict['universal_name']
+ if image_dict.get('checksum') != None:
+ filter_dict['checksum'] = image_dict['checksum']
+ #logger.debug('>>>>>>>> Filter dict: %s', str(filter_dict))
+ vim_images = vim.get_image_list(filter_dict)
+ #logger.debug('>>>>>>>> VIM images: %s', str(vim_images))
+ if len(vim_images) > 1:
+ raise vimconn.vimconnException("More than one candidate VIM image found for filter: {}".format(str(filter_dict)), HTTP_Conflict)
+ elif len(vim_images) == 0:
+ raise vimconn.vimconnNotFoundException("Image not found at VIM with filter: '{}'".format(str(filter_dict)))
+ else:
+ #logger.debug('>>>>>>>> VIM image 0: %s', str(vim_images[0]))
+ image_vim_id = vim_images[0]['id']
+
+ except vimconn.vimconnNotFoundException as e:
+ #Create the image in VIM only if image_dict['location'] or image_dict['new_location'] is not None
+ try:
+ #image_dict['location']=image_dict.get('new_location') if image_dict['location'] is None
+ if image_dict['location']:
+ image_vim_id = vim.new_image(image_dict)
+ rollback_list.append({"where":"vim", "vim_id": vim_id, "what":"image","uuid":image_vim_id})
+ image_created="true"
+ else:
+ #If we reach this point, then the image has image name, and optionally checksum, and could not be found
+ raise vimconn.vimconnException(str(e))
+ except vimconn.vimconnException as e:
+ if return_on_error:
+ logger.error("Error creating image at VIM '%s': %s", vim["name"], str(e))
+ raise
+ image_vim_id = None
+ logger.warn("Error creating image at VIM '%s': %s", vim["name"], str(e))
+ continue
+ except vimconn.vimconnException as e:
+ if return_on_error:
+ logger.error("Error contacting VIM to know if the image exists at VIM: %s", str(e))
+ raise
+ logger.warn("Error contacting VIM to know if the image exists at VIM: %s", str(e))
+ image_vim_id = None
+ continue
+ #if we reach here, the image has been created or existed
+ if len(image_db)==0:
+ #add new vim_id at datacenters_images
+ mydb.new_row('datacenters_images', {'datacenter_id':vim_id, 'image_id':image_mano_id, 'vim_id': image_vim_id, 'created':image_created})
+ elif image_db[0]["vim_id"]!=image_vim_id:
+ #modify existing vim_id at datacenters_images
+ mydb.update_rows('datacenters_images', UPDATE={'vim_id':image_vim_id}, WHERE={'datacenter_id':vim_id, 'image_id':image_mano_id})
+
+ return image_vim_id if only_create_at_vim else image_mano_id
+
+
+def create_or_use_flavor(mydb, vims, flavor_dict, rollback_list, only_create_at_vim=False, return_on_error = None):
+ temp_flavor_dict= {'disk':flavor_dict.get('disk',1),
+ 'ram':flavor_dict.get('ram'),
+ 'vcpus':flavor_dict.get('vcpus'),
+ }
+ if 'extended' in flavor_dict and flavor_dict['extended']==None:
+ del flavor_dict['extended']
+ if 'extended' in flavor_dict:
+ temp_flavor_dict['extended']=yaml.safe_dump(flavor_dict['extended'],default_flow_style=True,width=256)
+
+ #look if flavor exist
+ if only_create_at_vim:
+ flavor_mano_id = flavor_dict['uuid']
+ if return_on_error == None:
+ return_on_error = True
+ else:
+ flavors = mydb.get_rows(FROM="flavors", WHERE=temp_flavor_dict)
+ if len(flavors)>=1:
+ flavor_mano_id = flavors[0]['uuid']
+ else:
+ #create flavor
+ #create one by one the images of aditional disks
+ dev_image_list=[] #list of images
+ if 'extended' in flavor_dict and flavor_dict['extended']!=None:
+ dev_nb=0
+ for device in flavor_dict['extended'].get('devices',[]):
+ if "image" not in device and "image name" not in device:
+ continue
+ image_dict={}
+ image_dict['name']=device.get('image name',flavor_dict['name']+str(dev_nb)+"-img")
+ image_dict['universal_name']=device.get('image name')
+ image_dict['description']=flavor_dict['name']+str(dev_nb)+"-img"
+ image_dict['location']=device.get('image')
+ #image_dict['new_location']=vnfc.get('image location')
+ image_dict['checksum']=device.get('image checksum')
+ image_metadata_dict = device.get('image metadata', None)
+ image_metadata_str = None
+ if image_metadata_dict != None:
+ image_metadata_str = yaml.safe_dump(image_metadata_dict,default_flow_style=True,width=256)
+ image_dict['metadata']=image_metadata_str
+ image_id = create_or_use_image(mydb, vims, image_dict, rollback_list)
+ #print "Additional disk image id for VNFC %s: %s" % (flavor_dict['name']+str(dev_nb)+"-img", image_id)
+ dev_image_list.append(image_id)
+ dev_nb += 1
+ temp_flavor_dict['name'] = flavor_dict['name']
+ temp_flavor_dict['description'] = flavor_dict.get('description',None)
+ content = mydb.new_row('flavors', temp_flavor_dict, add_uuid=True)
+ flavor_mano_id= content
+ rollback_list.append({"where":"mano", "what":"flavor","uuid":flavor_mano_id})
+ #create flavor at every vim
+ if 'uuid' in flavor_dict:
+ del flavor_dict['uuid']
+ flavor_vim_id=None
+ for vim_id,vim in vims.items():
+ flavor_created="false"
+ #look at database
+ flavor_db = mydb.get_rows(FROM="datacenters_flavors", WHERE={'datacenter_id':vim_id, 'flavor_id':flavor_mano_id})
+ #look at VIM if this flavor exist SKIPPED
+ #res_vim, flavor_vim_id = vim.get_flavor_id_from_path(flavor_dict['location'])
+ #if res_vim < 0:
+ # print "Error contacting VIM to know if the flavor %s existed previously." %flavor_vim_id
+ # continue
+ #elif res_vim==0:
+
+ #Create the flavor in VIM
+ #Translate images at devices from MANO id to VIM id
+ disk_list = []
+ if 'extended' in flavor_dict and flavor_dict['extended']!=None and "devices" in flavor_dict['extended']:
+ #make a copy of original devices
+ devices_original=[]
+
+ for device in flavor_dict["extended"].get("devices",[]):
+ dev={}
+ dev.update(device)
+ devices_original.append(dev)
+ if 'image' in device:
+ del device['image']
+ if 'image metadata' in device:
+ del device['image metadata']
+ dev_nb=0
+ for index in range(0,len(devices_original)) :
+ device=devices_original[index]
+ if "image" not in device and "image name" not in device:
+ if 'size' in device:
+ disk_list.append({'size': device.get('size', default_volume_size)})
+ continue
+ image_dict={}
+ image_dict['name']=device.get('image name',flavor_dict['name']+str(dev_nb)+"-img")
+ image_dict['universal_name']=device.get('image name')
+ image_dict['description']=flavor_dict['name']+str(dev_nb)+"-img"
+ image_dict['location']=device.get('image')
+ #image_dict['new_location']=device.get('image location')
+ image_dict['checksum']=device.get('image checksum')
+ image_metadata_dict = device.get('image metadata', None)
+ image_metadata_str = None
+ if image_metadata_dict != None:
+ image_metadata_str = yaml.safe_dump(image_metadata_dict,default_flow_style=True,width=256)
+ image_dict['metadata']=image_metadata_str
+ image_mano_id=create_or_use_image(mydb, vims, image_dict, rollback_list, only_create_at_vim=False, return_on_error=return_on_error )
+ image_dict["uuid"]=image_mano_id
+ image_vim_id=create_or_use_image(mydb, vims, image_dict, rollback_list, only_create_at_vim=True, return_on_error=return_on_error)
+
+ #save disk information (image must be based on and size
+ disk_list.append({'image_id': image_vim_id, 'size': device.get('size', default_volume_size)})
+
+ flavor_dict["extended"]["devices"][index]['imageRef']=image_vim_id
+ dev_nb += 1
+ if len(flavor_db)>0:
+ #check that this vim_id exist in VIM, if not create
+ flavor_vim_id=flavor_db[0]["vim_id"]
+ try:
+ vim.get_flavor(flavor_vim_id)
+ continue #flavor exist
+ except vimconn.vimconnException:
+ pass
+ #create flavor at vim
+ logger.debug("nfvo.create_or_use_flavor() adding flavor to VIM %s", vim["name"])
+ try:
+ flavor_vim_id = None
+ flavor_vim_id=vim.get_flavor_id_from_data(flavor_dict)
+ flavor_create="false"
+ except vimconn.vimconnException as e:
+ pass
+ try:
+ if not flavor_vim_id:
+ flavor_vim_id = vim.new_flavor(flavor_dict)
+ rollback_list.append({"where":"vim", "vim_id": vim_id, "what":"flavor","uuid":flavor_vim_id})
+ flavor_created="true"
+ except vimconn.vimconnException as e:
+ if return_on_error:
+ logger.error("Error creating flavor at VIM %s: %s.", vim["name"], str(e))
+ raise
+ logger.warn("Error creating flavor at VIM %s: %s.", vim["name"], str(e))
+ flavor_vim_id = None
+ continue
+ #if reach here the flavor has been create or exist
+ if len(flavor_db)==0:
+ #add new vim_id at datacenters_flavors
+ extended_devices_yaml = None
+ if len(disk_list) > 0:
+ extended_devices = dict()
+ extended_devices['disks'] = disk_list
+ extended_devices_yaml = yaml.safe_dump(extended_devices,default_flow_style=True,width=256)
+ mydb.new_row('datacenters_flavors',
+ {'datacenter_id':vim_id, 'flavor_id':flavor_mano_id, 'vim_id': flavor_vim_id,
+ 'created':flavor_created,'extended': extended_devices_yaml})
+ elif flavor_db[0]["vim_id"]!=flavor_vim_id:
+ #modify existing vim_id at datacenters_flavors
+ mydb.update_rows('datacenters_flavors', UPDATE={'vim_id':flavor_vim_id}, WHERE={'datacenter_id':vim_id, 'flavor_id':flavor_mano_id})
+
+ return flavor_vim_id if only_create_at_vim else flavor_mano_id
+
+
+def new_vnf(mydb, tenant_id, vnf_descriptor):
+ global global_config
+
+ # Step 1. Check the VNF descriptor
+ check_vnf_descriptor(vnf_descriptor, vnf_descriptor_version=1)
+ # Step 2. Check tenant exist
+ vims = {}
+ if tenant_id != "any":
+ check_tenant(mydb, tenant_id)
+ if "tenant_id" in vnf_descriptor["vnf"]:
+ if vnf_descriptor["vnf"]["tenant_id"] != tenant_id:
+ raise NfvoException("VNF can not have a different tenant owner '{}', must be '{}'".format(vnf_descriptor["vnf"]["tenant_id"], tenant_id),
+ HTTP_Unauthorized)
+ else:
+ vnf_descriptor['vnf']['tenant_id'] = tenant_id
+ # Step 3. Get the URL of the VIM from the nfvo_tenant and the datacenter
+ if global_config["auto_push_VNF_to_VIMs"]:
+ vims = get_vim(mydb, tenant_id)
+
+ # Step 4. Review the descriptor and add missing fields
+ #print vnf_descriptor
+ #logger.debug("Refactoring VNF descriptor with fields: description, public (default: true)")
+ vnf_name = vnf_descriptor['vnf']['name']
+ vnf_descriptor['vnf']['description'] = vnf_descriptor['vnf'].get("description", vnf_name)
+ if "physical" in vnf_descriptor['vnf']:
+ del vnf_descriptor['vnf']['physical']
+ #print vnf_descriptor
+
+ # Step 6. For each VNFC in the descriptor, flavors and images are created in the VIM
+ logger.debug('BEGIN creation of VNF "%s"' % vnf_name)
+ logger.debug("VNF %s: consisting of %d VNFC(s)" % (vnf_name,len(vnf_descriptor['vnf']['VNFC'])))
+
+ #For each VNFC, we add it to the VNFCDict and we create a flavor.
+ VNFCDict = {} # Dictionary, key: VNFC name, value: dict with the relevant information to create the VNF and VMs in the MANO database
+ rollback_list = [] # It will contain the new images created in mano. It is used for rollback
+ try:
+ logger.debug("Creating additional disk images and new flavors in the VIM for each VNFC")
+ for vnfc in vnf_descriptor['vnf']['VNFC']:
+ VNFCitem={}
+ VNFCitem["name"] = vnfc['name']
+ VNFCitem["description"] = vnfc.get("description", 'VM %s of the VNF %s' %(vnfc['name'],vnf_name))
+
+ #print "Flavor name: %s. Description: %s" % (VNFCitem["name"]+"-flv", VNFCitem["description"])
+
+ myflavorDict = {}
+ myflavorDict["name"] = vnfc['name']+"-flv" #Maybe we could rename the flavor by using the field "image name" if exists
+ myflavorDict["description"] = VNFCitem["description"]
+ myflavorDict["ram"] = vnfc.get("ram", 0)
+ myflavorDict["vcpus"] = vnfc.get("vcpus", 0)
+ myflavorDict["disk"] = vnfc.get("disk", 1)
+ myflavorDict["extended"] = {}
+
+ devices = vnfc.get("devices")
+ if devices != None:
+ myflavorDict["extended"]["devices"] = devices
+
+ # TODO:
+ # Mapping from processor models to rankings should be available somehow in the NFVO. They could be taken from VIM or directly from a new database table
+ # Another option is that the processor in the VNF descriptor specifies directly the ranking of the host
+
+ # Previous code has been commented
+ #if vnfc['processor']['model'] == "Intel(R) Xeon(R) CPU E5-4620 0 @ 2.20GHz" :
+ # myflavorDict["flavor"]['extended']['processor_ranking'] = 200
+ #elif vnfc['processor']['model'] == "Intel(R) Xeon(R) CPU E5-2697 v2 @ 2.70GHz" :
+ # myflavorDict["flavor"]['extended']['processor_ranking'] = 300
+ #else:
+ # result2, message = rollback(myvim, myvimURL, myvim_tenant, flavorList, imageList)
+ # if result2:
+ # print "Error creating flavor: unknown processor model. Rollback successful."
+ # return -HTTP_Bad_Request, "Error creating flavor: unknown processor model. Rollback successful."
+ # else:
+ # return -HTTP_Bad_Request, "Error creating flavor: unknown processor model. Rollback fail: you need to access VIM and delete the following %s" % message
+ myflavorDict['extended']['processor_ranking'] = 100 #Hardcoded value, while we decide when the mapping is done
+
+ if 'numas' in vnfc and len(vnfc['numas'])>0:
+ myflavorDict['extended']['numas'] = vnfc['numas']
+
+ #print myflavorDict
+
+ # Step 6.2 New flavors are created in the VIM
+ flavor_id = create_or_use_flavor(mydb, vims, myflavorDict, rollback_list)
+
+ #print "Flavor id for VNFC %s: %s" % (vnfc['name'],flavor_id)
+ VNFCitem["flavor_id"] = flavor_id
+ VNFCDict[vnfc['name']] = VNFCitem
+
+ logger.debug("Creating new images in the VIM for each VNFC")
+ # Step 6.3 New images are created in the VIM
+ #For each VNFC, we must create the appropriate image.
+ #This "for" loop might be integrated with the previous one
+ #In case this integration is made, the VNFCDict might become a VNFClist.
+ for vnfc in vnf_descriptor['vnf']['VNFC']:
+ #print "Image name: %s. Description: %s" % (vnfc['name']+"-img", VNFCDict[vnfc['name']]['description'])
+ image_dict={}
+ image_dict['name']=vnfc.get('image name',vnf_name+"-"+vnfc['name']+"-img")
+ image_dict['universal_name']=vnfc.get('image name')
+ image_dict['description']=vnfc.get('image name', VNFCDict[vnfc['name']]['description'])
+ image_dict['location']=vnfc.get('VNFC image')
+ #image_dict['new_location']=vnfc.get('image location')
+ image_dict['checksum']=vnfc.get('image checksum')
+ image_metadata_dict = vnfc.get('image metadata', None)
+ image_metadata_str = None
+ if image_metadata_dict is not None:
+ image_metadata_str = yaml.safe_dump(image_metadata_dict,default_flow_style=True,width=256)
+ image_dict['metadata']=image_metadata_str
+ #print "create_or_use_image", mydb, vims, image_dict, rollback_list
+ image_id = create_or_use_image(mydb, vims, image_dict, rollback_list)
+ #print "Image id for VNFC %s: %s" % (vnfc['name'],image_id)
+ VNFCDict[vnfc['name']]["image_id"] = image_id
+ VNFCDict[vnfc['name']]["image_path"] = vnfc.get('VNFC image')
+ if vnfc.get("boot-data"):
+ VNFCDict[vnfc['name']]["boot_data"] = yaml.safe_dump(vnfc["boot-data"], default_flow_style=True, width=256)
+
+
+ # Step 7. Storing the VNF descriptor in the repository
+ if "descriptor" not in vnf_descriptor["vnf"]:
+ vnf_descriptor["vnf"]["descriptor"] = yaml.safe_dump(vnf_descriptor, indent=4, explicit_start=True, default_flow_style=False)
+
+ # Step 8. Adding the VNF to the NFVO DB
+ vnf_id = mydb.new_vnf_as_a_whole(tenant_id,vnf_name,vnf_descriptor,VNFCDict)
+ return vnf_id
+ except (db_base_Exception, vimconn.vimconnException, KeyError) as e:
+ _, message = rollback(mydb, vims, rollback_list)
+ if isinstance(e, db_base_Exception):
+ error_text = "Exception at database"
+ elif isinstance(e, KeyError):
+ error_text = "KeyError exception "
+ e.http_code = HTTP_Internal_Server_Error
+ else:
+ error_text = "Exception at VIM"
+ error_text += " {} {}. {}".format(type(e).__name__, str(e), message)
+ #logger.error("start_scenario %s", error_text)
+ raise NfvoException(error_text, e.http_code)
+
+
+def new_vnf_v02(mydb, tenant_id, vnf_descriptor):
+ global global_config
+
+ # Step 1. Check the VNF descriptor
+ check_vnf_descriptor(vnf_descriptor, vnf_descriptor_version=2)
+ # Step 2. Check tenant exist
+ vims = {}
+ if tenant_id != "any":
+ check_tenant(mydb, tenant_id)
+ if "tenant_id" in vnf_descriptor["vnf"]:
+ if vnf_descriptor["vnf"]["tenant_id"] != tenant_id:
+ raise NfvoException("VNF can not have a different tenant owner '{}', must be '{}'".format(vnf_descriptor["vnf"]["tenant_id"], tenant_id),
+ HTTP_Unauthorized)
+ else:
+ vnf_descriptor['vnf']['tenant_id'] = tenant_id
+ # Step 3. Get the URL of the VIM from the nfvo_tenant and the datacenter
+ if global_config["auto_push_VNF_to_VIMs"]:
+ vims = get_vim(mydb, tenant_id)
+
+ # Step 4. Review the descriptor and add missing fields
+ #print vnf_descriptor
+ #logger.debug("Refactoring VNF descriptor with fields: description, public (default: true)")
+ vnf_name = vnf_descriptor['vnf']['name']
+ vnf_descriptor['vnf']['description'] = vnf_descriptor['vnf'].get("description", vnf_name)
+ if "physical" in vnf_descriptor['vnf']:
+ del vnf_descriptor['vnf']['physical']
+ #print vnf_descriptor
+
+ # Step 6. For each VNFC in the descriptor, flavors and images are created in the VIM
+ logger.debug('BEGIN creation of VNF "%s"' % vnf_name)
+ logger.debug("VNF %s: consisting of %d VNFC(s)" % (vnf_name,len(vnf_descriptor['vnf']['VNFC'])))
+
+ #For each VNFC, we add it to the VNFCDict and we create a flavor.
+ VNFCDict = {} # Dictionary, key: VNFC name, value: dict with the relevant information to create the VNF and VMs in the MANO database
+ rollback_list = [] # It will contain the new images created in mano. It is used for rollback
+ try:
+ logger.debug("Creating additional disk images and new flavors in the VIM for each VNFC")
+ for vnfc in vnf_descriptor['vnf']['VNFC']:
+ VNFCitem={}
+ VNFCitem["name"] = vnfc['name']
+ VNFCitem["description"] = vnfc.get("description", 'VM %s of the VNF %s' %(vnfc['name'],vnf_name))
+
+ #print "Flavor name: %s. Description: %s" % (VNFCitem["name"]+"-flv", VNFCitem["description"])
+
+ myflavorDict = {}
+ myflavorDict["name"] = vnfc['name']+"-flv" #Maybe we could rename the flavor by using the field "image name" if exists
+ myflavorDict["description"] = VNFCitem["description"]
+ myflavorDict["ram"] = vnfc.get("ram", 0)
+ myflavorDict["vcpus"] = vnfc.get("vcpus", 0)
+ myflavorDict["disk"] = vnfc.get("disk", 1)
+ myflavorDict["extended"] = {}
+
+ devices = vnfc.get("devices")
+ if devices != None:
+ myflavorDict["extended"]["devices"] = devices
+
+ # TODO:
+ # Mapping from processor models to rankings should be available somehow in the NFVO. They could be taken from VIM or directly from a new database table
+ # Another option is that the processor in the VNF descriptor specifies directly the ranking of the host
+
+ # Previous code has been commented
+ #if vnfc['processor']['model'] == "Intel(R) Xeon(R) CPU E5-4620 0 @ 2.20GHz" :
+ # myflavorDict["flavor"]['extended']['processor_ranking'] = 200
+ #elif vnfc['processor']['model'] == "Intel(R) Xeon(R) CPU E5-2697 v2 @ 2.70GHz" :
+ # myflavorDict["flavor"]['extended']['processor_ranking'] = 300
+ #else:
+ # result2, message = rollback(myvim, myvimURL, myvim_tenant, flavorList, imageList)
+ # if result2:
+ # print "Error creating flavor: unknown processor model. Rollback successful."
+ # return -HTTP_Bad_Request, "Error creating flavor: unknown processor model. Rollback successful."
+ # else:
+ # return -HTTP_Bad_Request, "Error creating flavor: unknown processor model. Rollback fail: you need to access VIM and delete the following %s" % message
+ myflavorDict['extended']['processor_ranking'] = 100 #Hardcoded value, while we decide when the mapping is done
+
+ if 'numas' in vnfc and len(vnfc['numas'])>0:
+ myflavorDict['extended']['numas'] = vnfc['numas']
+
+ #print myflavorDict
+
+ # Step 6.2 New flavors are created in the VIM
+ flavor_id = create_or_use_flavor(mydb, vims, myflavorDict, rollback_list)
+
+ #print "Flavor id for VNFC %s: %s" % (vnfc['name'],flavor_id)
+ VNFCitem["flavor_id"] = flavor_id
+ VNFCDict[vnfc['name']] = VNFCitem
+
+ logger.debug("Creating new images in the VIM for each VNFC")
+ # Step 6.3 New images are created in the VIM
+ #For each VNFC, we must create the appropriate image.
+ #This "for" loop might be integrated with the previous one
+ #In case this integration is made, the VNFCDict might become a VNFClist.
+ for vnfc in vnf_descriptor['vnf']['VNFC']:
+ #print "Image name: %s. Description: %s" % (vnfc['name']+"-img", VNFCDict[vnfc['name']]['description'])
+ image_dict={}
+ image_dict['name']=vnfc.get('image name',vnf_name+"-"+vnfc['name']+"-img")
+ image_dict['universal_name']=vnfc.get('image name')
+ image_dict['description']=vnfc.get('image name', VNFCDict[vnfc['name']]['description'])
+ image_dict['location']=vnfc.get('VNFC image')
+ #image_dict['new_location']=vnfc.get('image location')
+ image_dict['checksum']=vnfc.get('image checksum')
+ image_metadata_dict = vnfc.get('image metadata', None)
+ image_metadata_str = None
+ if image_metadata_dict is not None:
+ image_metadata_str = yaml.safe_dump(image_metadata_dict,default_flow_style=True,width=256)
+ image_dict['metadata']=image_metadata_str
+ #print "create_or_use_image", mydb, vims, image_dict, rollback_list
+ image_id = create_or_use_image(mydb, vims, image_dict, rollback_list)
+ #print "Image id for VNFC %s: %s" % (vnfc['name'],image_id)
+ VNFCDict[vnfc['name']]["image_id"] = image_id
+ VNFCDict[vnfc['name']]["image_path"] = vnfc.get('VNFC image')
+ if vnfc.get("boot-data"):
+ VNFCDict[vnfc['name']]["boot_data"] = yaml.safe_dump(vnfc["boot-data"], default_flow_style=True, width=256)
+
+ # Step 7. Storing the VNF descriptor in the repository
+ if "descriptor" not in vnf_descriptor["vnf"]:
+ vnf_descriptor["vnf"]["descriptor"] = yaml.safe_dump(vnf_descriptor, indent=4, explicit_start=True, default_flow_style=False)
+
+ # Step 8. Adding the VNF to the NFVO DB
+ vnf_id = mydb.new_vnf_as_a_whole2(tenant_id,vnf_name,vnf_descriptor,VNFCDict)
+ return vnf_id
+ except (db_base_Exception, vimconn.vimconnException, KeyError) as e:
+ _, message = rollback(mydb, vims, rollback_list)
+ if isinstance(e, db_base_Exception):
+ error_text = "Exception at database"
+ elif isinstance(e, KeyError):
+ error_text = "KeyError exception "
+ e.http_code = HTTP_Internal_Server_Error
+ else:
+ error_text = "Exception at VIM"
+ error_text += " {} {}. {}".format(type(e).__name__, str(e), message)
+ #logger.error("start_scenario %s", error_text)
+ raise NfvoException(error_text, e.http_code)
+
+
+def get_vnf_id(mydb, tenant_id, vnf_id):
+ #check valid tenant_id
+ check_tenant(mydb, tenant_id)
+ #obtain data
+ where_or = {}
+ if tenant_id != "any":
+ where_or["tenant_id"] = tenant_id
+ where_or["public"] = True
+ vnf = mydb.get_table_by_uuid_name('vnfs', vnf_id, "VNF", WHERE_OR=where_or, WHERE_AND_OR="AND")
+
+ vnf_id=vnf["uuid"]
+ filter_keys = ('uuid','name','description','public', "tenant_id", "created_at")
+ filtered_content = dict( (k,v) for k,v in vnf.iteritems() if k in filter_keys )
+ #change_keys_http2db(filtered_content, http2db_vnf, reverse=True)
+ data={'vnf' : filtered_content}
+ #GET VM
+ content = mydb.get_rows(FROM='vnfs join vms on vnfs.uuid=vms.vnf_id',
+ SELECT=('vms.uuid as uuid','vms.name as name', 'vms.description as description', 'boot_data'),
+ WHERE={'vnfs.uuid': vnf_id} )
+ if len(content)==0:
+ raise NfvoException("vnf '{}' not found".format(vnf_id), HTTP_Not_Found)
+ # change boot_data into boot-data
+ for vm in content:
+ if vm.get("boot_data"):
+ vm["boot-data"] = yaml.safe_load(vm["boot_data"])
+ del vm["boot_data"]
+
+ data['vnf']['VNFC'] = content
+ #TODO: GET all the information from a VNFC and include it in the output.
+
+ #GET NET
+ content = mydb.get_rows(FROM='vnfs join nets on vnfs.uuid=nets.vnf_id',
+ SELECT=('nets.uuid as uuid','nets.name as name','nets.description as description', 'nets.type as type', 'nets.multipoint as multipoint'),
+ WHERE={'vnfs.uuid': vnf_id} )
+ data['vnf']['nets'] = content
+
+ #GET ip-profile for each net
+ for net in data['vnf']['nets']:
+ ipprofiles = mydb.get_rows(FROM='ip_profiles',
+ SELECT=('ip_version','subnet_address','gateway_address','dns_address','dhcp_enabled','dhcp_start_address','dhcp_count'),
+ WHERE={'net_id': net["uuid"]} )
+ if len(ipprofiles)==1:
+ net["ip_profile"] = ipprofiles[0]
+ elif len(ipprofiles)>1:
+ raise NfvoException("More than one ip-profile found with this criteria: net_id='{}'".format(net['uuid']), HTTP_Bad_Request)
+
+
+ #TODO: For each net, GET its elements and relevant info per element (VNFC, iface, ip_address) and include them in the output.
+
+ #GET External Interfaces
+ content = mydb.get_rows(FROM='vnfs join vms on vnfs.uuid=vms.vnf_id join interfaces on vms.uuid=interfaces.vm_id',\
+ SELECT=('interfaces.uuid as uuid','interfaces.external_name as external_name', 'vms.name as vm_name', 'interfaces.vm_id as vm_id', \
+ 'interfaces.internal_name as internal_name', 'interfaces.type as type', 'interfaces.vpci as vpci','interfaces.bw as bw'),\
+ WHERE={'vnfs.uuid': vnf_id},
+ WHERE_NOT={'interfaces.external_name': None} )
+ #print content
+ data['vnf']['external-connections'] = content
+
+ return data
+
+
+def delete_vnf(mydb,tenant_id,vnf_id,datacenter=None,vim_tenant=None):
+ # Check tenant exist
+ if tenant_id != "any":
+ check_tenant(mydb, tenant_id)
+ # Get the URL of the VIM from the nfvo_tenant and the datacenter
+ vims = get_vim(mydb, tenant_id)
+ else:
+ vims={}
+
+ # Checking if it is a valid uuid and, if not, getting the uuid assuming that the name was provided"
+ where_or = {}
+ if tenant_id != "any":
+ where_or["tenant_id"] = tenant_id
+ where_or["public"] = True
+ vnf = mydb.get_table_by_uuid_name('vnfs', vnf_id, "VNF", WHERE_OR=where_or, WHERE_AND_OR="AND")
+ vnf_id = vnf["uuid"]
+
+ # "Getting the list of flavors and tenants of the VNF"
+ flavorList = get_flavorlist(mydb, vnf_id)
+ if len(flavorList)==0:
+ logger.warn("delete_vnf error. No flavors found for the VNF id '%s'", vnf_id)
+
+ imageList = get_imagelist(mydb, vnf_id)
+ if len(imageList)==0:
+ logger.warn( "delete_vnf error. No images found for the VNF id '%s'", vnf_id)
+
+ deleted = mydb.delete_row_by_id('vnfs', vnf_id)
+ if deleted == 0:
+ raise NfvoException("vnf '{}' not found".format(vnf_id), HTTP_Not_Found)
+
+ undeletedItems = []
+ for flavor in flavorList:
+ #check if flavor is used by other vnf
+ try:
+ c = mydb.get_rows(FROM='vms', WHERE={'flavor_id':flavor} )
+ if len(c) > 0:
+ logger.debug("Flavor '%s' not deleted because it is being used by another VNF", flavor)
+ continue
+ #flavor not used, must be deleted
+ #delelte at VIM
+ c = mydb.get_rows(FROM='datacenters_flavors', WHERE={'flavor_id':flavor})
+ for flavor_vim in c:
+ if flavor_vim["datacenter_id"] not in vims:
+ continue
+ if flavor_vim['created']=='false': #skip this flavor because not created by openmano
+ continue
+ myvim=vims[ flavor_vim["datacenter_id"] ]
+ try:
+ myvim.delete_flavor(flavor_vim["vim_id"])
+ except vimconn.vimconnNotFoundException as e:
+ logger.warn("VIM flavor %s not exist at datacenter %s", flavor_vim["vim_id"], flavor_vim["datacenter_id"] )
+ except vimconn.vimconnException as e:
+ logger.error("Not possible to delete VIM flavor %s from datacenter %s: %s %s",
+ flavor_vim["vim_id"], flavor_vim["datacenter_id"], type(e).__name__, str(e))
+ undeletedItems.append("flavor {} from VIM {}".format(flavor_vim["vim_id"], flavor_vim["datacenter_id"] ))
+ #delete flavor from Database, using table flavors and with cascade foreign key also at datacenters_flavors
+ mydb.delete_row_by_id('flavors', flavor)
+ except db_base_Exception as e:
+ logger.error("delete_vnf_error. Not possible to get flavor details and delete '%s'. %s", flavor, str(e))
+ undeletedItems.append("flavor %s" % flavor)
+
+
+ for image in imageList:
+ try:
+ #check if image is used by other vnf
+ c = mydb.get_rows(FROM='vms', WHERE={'image_id':image} )
+ if len(c) > 0:
+ logger.debug("Image '%s' not deleted because it is being used by another VNF", image)
+ continue
+ #image not used, must be deleted
+ #delelte at VIM
+ c = mydb.get_rows(FROM='datacenters_images', WHERE={'image_id':image})
+ for image_vim in c:
+ if image_vim["datacenter_id"] not in vims:
+ continue
+ if image_vim['created']=='false': #skip this image because not created by openmano
+ continue
+ myvim=vims[ image_vim["datacenter_id"] ]
+ try:
+ myvim.delete_image(image_vim["vim_id"])
+ except vimconn.vimconnNotFoundException as e:
+ logger.warn("VIM image %s not exist at datacenter %s", image_vim["vim_id"], image_vim["datacenter_id"] )
+ except vimconn.vimconnException as e:
+ logger.error("Not possible to delete VIM image %s from datacenter %s: %s %s",
+ image_vim["vim_id"], image_vim["datacenter_id"], type(e).__name__, str(e))
+ undeletedItems.append("image {} from VIM {}".format(image_vim["vim_id"], image_vim["datacenter_id"] ))
+ #delete image from Database, using table images and with cascade foreign key also at datacenters_images
+ mydb.delete_row_by_id('images', image)
+ except db_base_Exception as e:
+ logger.error("delete_vnf_error. Not possible to get image details and delete '%s'. %s", image, str(e))
+ undeletedItems.append("image %s" % image)
+
+ return vnf_id + " " + vnf["name"]
+ #if undeletedItems:
+ # return "delete_vnf. Undeleted: %s" %(undeletedItems)
+
+
+def get_hosts_info(mydb, nfvo_tenant_id, datacenter_name=None):
+ result, vims = get_vim(mydb, nfvo_tenant_id, None, datacenter_name)
+ if result < 0:
+ return result, vims
+ elif result == 0:
+ return -HTTP_Not_Found, "datacenter '%s' not found" % datacenter_name
+ myvim = vims.values()[0]
+ result,servers = myvim.get_hosts_info()
+ if result < 0:
+ return result, servers
+ topology = {'name':myvim['name'] , 'servers': servers}
+ return result, topology
+
+
+def get_hosts(mydb, nfvo_tenant_id):
+ vims = get_vim(mydb, nfvo_tenant_id)
+ if len(vims) == 0:
+ raise NfvoException("No datacenter found for tenant '{}'".format(str(nfvo_tenant_id)), HTTP_Not_Found)
+ elif len(vims)>1:
+ #print "nfvo.datacenter_action() error. Several datacenters found"
+ raise NfvoException("More than one datacenters found, try to identify with uuid", HTTP_Conflict)
+ myvim = vims.values()[0]
+ try:
+ hosts = myvim.get_hosts()
+ logger.debug('VIM hosts response: '+ yaml.safe_dump(hosts, indent=4, default_flow_style=False))
+
+ datacenter = {'Datacenters': [ {'name':myvim['name'],'servers':[]} ] }
+ for host in hosts:
+ server={'name':host['name'], 'vms':[]}
+ for vm in host['instances']:
+ #get internal name and model
+ try:
+ c = mydb.get_rows(SELECT=('name',), FROM='instance_vms as iv join vms on iv.vm_id=vms.uuid',\
+ WHERE={'vim_vm_id':vm['id']} )
+ if len(c) == 0:
+ logger.warn("nfvo.get_hosts virtual machine at VIM '{}' not found at tidnfvo".format(vm['id']))
+ continue
+ server['vms'].append( {'name':vm['name'] , 'model':c[0]['name']} )
+
+ except db_base_Exception as e:
+ logger.warn("nfvo.get_hosts virtual machine at VIM '{}' error {}".format(vm['id'], str(e)))
+ datacenter['Datacenters'][0]['servers'].append(server)
+ #return -400, "en construccion"
+
+ #print 'datacenters '+ json.dumps(datacenter, indent=4)
+ return datacenter
+ except vimconn.vimconnException as e:
+ raise NfvoException("Not possible to get_host_list from VIM: {}".format(str(e)), e.http_code)
+
+
+def new_scenario(mydb, tenant_id, topo):
+
+# result, vims = get_vim(mydb, tenant_id)
+# if result < 0:
+# return result, vims
+#1: parse input
+ if tenant_id != "any":
+ check_tenant(mydb, tenant_id)
+ if "tenant_id" in topo:
+ if topo["tenant_id"] != tenant_id:
+ raise NfvoException("VNF can not have a different tenant owner '{}', must be '{}'".format(topo["tenant_id"], tenant_id),
+ HTTP_Unauthorized)
+ else:
+ tenant_id=None
+
+#1.1: get VNFs and external_networks (other_nets).
+ vnfs={}
+ other_nets={} #external_networks, bridge_networks and data_networkds
+ nodes = topo['topology']['nodes']
+ for k in nodes.keys():
+ if nodes[k]['type'] == 'VNF':
+ vnfs[k] = nodes[k]
+ vnfs[k]['ifaces'] = {}
+ elif nodes[k]['type'] == 'other_network' or nodes[k]['type'] == 'external_network':
+ other_nets[k] = nodes[k]
+ other_nets[k]['external']=True
+ elif nodes[k]['type'] == 'network':
+ other_nets[k] = nodes[k]
+ other_nets[k]['external']=False
+
+
+#1.2: Check that VNF are present at database table vnfs. Insert uuid, description and external interfaces
+ for name,vnf in vnfs.items():
+ where={}
+ where_or={"tenant_id": tenant_id, 'public': "true"}
+ error_text = ""
+ error_pos = "'topology':'nodes':'" + name + "'"
+ if 'vnf_id' in vnf:
+ error_text += " 'vnf_id' " + vnf['vnf_id']
+ where['uuid'] = vnf['vnf_id']
+ if 'VNF model' in vnf:
+ error_text += " 'VNF model' " + vnf['VNF model']
+ where['name'] = vnf['VNF model']
+ if len(where) == 0:
+ raise NfvoException("Descriptor need a 'vnf_id' or 'VNF model' field at " + error_pos, HTTP_Bad_Request)
+
+ vnf_db = mydb.get_rows(SELECT=('uuid','name','description'),
+ FROM='vnfs',
+ WHERE=where,
+ WHERE_OR=where_or,
+ WHERE_AND_OR="AND")
+ if len(vnf_db)==0:
+ raise NfvoException("unknown" + error_text + " at " + error_pos, HTTP_Not_Found)
+ elif len(vnf_db)>1:
+ raise NfvoException("more than one" + error_text + " at " + error_pos + " Concrete with 'vnf_id'", HTTP_Conflict)
+ vnf['uuid']=vnf_db[0]['uuid']
+ vnf['description']=vnf_db[0]['description']
+ #get external interfaces
+ ext_ifaces = mydb.get_rows(SELECT=('external_name as name','i.uuid as iface_uuid', 'i.type as type'),
+ FROM='vnfs join vms on vnfs.uuid=vms.vnf_id join interfaces as i on vms.uuid=i.vm_id',
+ WHERE={'vnfs.uuid':vnf['uuid']}, WHERE_NOT={'external_name':None} )
+ for ext_iface in ext_ifaces:
+ vnf['ifaces'][ ext_iface['name'] ] = {'uuid':ext_iface['iface_uuid'], 'type':ext_iface['type']}
+
+#1.4 get list of connections
+ conections = topo['topology']['connections']
+ conections_list = []
+ conections_list_name = []
+ for k in conections.keys():
+ if type(conections[k]['nodes'])==dict: #dict with node:iface pairs
+ ifaces_list = conections[k]['nodes'].items()
+ elif type(conections[k]['nodes'])==list: #list with dictionary
+ ifaces_list=[]
+ conection_pair_list = map(lambda x: x.items(), conections[k]['nodes'] )
+ for k2 in conection_pair_list:
+ ifaces_list += k2
+
+ con_type = conections[k].get("type", "link")
+ if con_type != "link":
+ if k in other_nets:
+ raise NfvoException("Format error. Reapeted network name at 'topology':'connections':'{}'".format(str(k)), HTTP_Bad_Request)
+ other_nets[k] = {'external': False}
+ if conections[k].get("graph"):
+ other_nets[k]["graph"] = conections[k]["graph"]
+ ifaces_list.append( (k, None) )
+
+
+ if con_type == "external_network":
+ other_nets[k]['external'] = True
+ if conections[k].get("model"):
+ other_nets[k]["model"] = conections[k]["model"]
+ else:
+ other_nets[k]["model"] = k
+ if con_type == "dataplane_net" or con_type == "bridge_net":
+ other_nets[k]["model"] = con_type
+
+ conections_list_name.append(k)
+ conections_list.append(set(ifaces_list)) #from list to set to operate as a set (this conversion removes elements that are repeated in a list)
+ #print set(ifaces_list)
+ #check valid VNF and iface names
+ for iface in ifaces_list:
+ if iface[0] not in vnfs and iface[0] not in other_nets :
+ raise NfvoException("format error. Invalid VNF name at 'topology':'connections':'{}':'nodes':'{}'".format(
+ str(k), iface[0]), HTTP_Not_Found)
+ if iface[0] in vnfs and iface[1] not in vnfs[ iface[0] ]['ifaces']:
+ raise NfvoException("format error. Invalid interface name at 'topology':'connections':'{}':'nodes':'{}':'{}'".format(
+ str(k), iface[0], iface[1]), HTTP_Not_Found)
+
+#1.5 unify connections from the pair list to a consolidated list
+ index=0
+ while index < len(conections_list):
+ index2 = index+1
+ while index2 < len(conections_list):
+ if len(conections_list[index] & conections_list[index2])>0: #common interface, join nets
+ conections_list[index] |= conections_list[index2]
+ del conections_list[index2]
+ del conections_list_name[index2]
+ else:
+ index2 += 1
+ conections_list[index] = list(conections_list[index]) # from set to list again
+ index += 1
+ #for k in conections_list:
+ # print k
+
+
+
+#1.6 Delete non external nets
+# for k in other_nets.keys():
+# if other_nets[k]['model']=='bridge' or other_nets[k]['model']=='dataplane_net' or other_nets[k]['model']=='bridge_net':
+# for con in conections_list:
+# delete_indexes=[]
+# for index in range(0,len(con)):
+# if con[index][0] == k: delete_indexes.insert(0,index) #order from higher to lower
+# for index in delete_indexes:
+# del con[index]
+# del other_nets[k]
+#1.7: Check external_ports are present at database table datacenter_nets
+ for k,net in other_nets.items():
+ error_pos = "'topology':'nodes':'" + k + "'"
+ if net['external']==False:
+ if 'name' not in net:
+ net['name']=k
+ if 'model' not in net:
+ raise NfvoException("needed a 'model' at " + error_pos, HTTP_Bad_Request)
+ if net['model']=='bridge_net':
+ net['type']='bridge';
+ elif net['model']=='dataplane_net':
+ net['type']='data';
+ else:
+ raise NfvoException("unknown 'model' '"+ net['model'] +"' at " + error_pos, HTTP_Not_Found)
+ else: #external
+#IF we do not want to check that external network exist at datacenter
+ pass
+#ELSE
+# error_text = ""
+# WHERE_={}
+# if 'net_id' in net:
+# error_text += " 'net_id' " + net['net_id']
+# WHERE_['uuid'] = net['net_id']
+# if 'model' in net:
+# error_text += " 'model' " + net['model']
+# WHERE_['name'] = net['model']
+# if len(WHERE_) == 0:
+# return -HTTP_Bad_Request, "needed a 'net_id' or 'model' at " + error_pos
+# r,net_db = mydb.get_table(SELECT=('uuid','name','description','type','shared'),
+# FROM='datacenter_nets', WHERE=WHERE_ )
+# if r<0:
+# print "nfvo.new_scenario Error getting datacenter_nets",r,net_db
+# elif r==0:
+# print "nfvo.new_scenario Error" +error_text+ " is not present at database"
+# return -HTTP_Bad_Request, "unknown " +error_text+ " at " + error_pos
+# elif r>1:
+# print "nfvo.new_scenario Error more than one external_network for " +error_text+ " is present at database"
+# return -HTTP_Bad_Request, "more than one external_network for " +error_text+ "at "+ error_pos + " Concrete with 'net_id'"
+# other_nets[k].update(net_db[0])
+#ENDIF
+ net_list={}
+ net_nb=0 #Number of nets
+ for con in conections_list:
+ #check if this is connected to a external net
+ other_net_index=-1
+ #print
+ #print "con", con
+ for index in range(0,len(con)):
+ #check if this is connected to a external net
+ for net_key in other_nets.keys():
+ if con[index][0]==net_key:
+ if other_net_index>=0:
+ error_text="There is some interface connected both to net '%s' and net '%s'" % (con[other_net_index][0], net_key)
+ #print "nfvo.new_scenario " + error_text
+ raise NfvoException(error_text, HTTP_Bad_Request)
+ else:
+ other_net_index = index
+ net_target = net_key
+ break
+ #print "other_net_index", other_net_index
+ try:
+ if other_net_index>=0:
+ del con[other_net_index]
+#IF we do not want to check that external network exist at datacenter
+ if other_nets[net_target]['external'] :
+ if "name" not in other_nets[net_target]:
+ other_nets[net_target]['name'] = other_nets[net_target]['model']
+ if other_nets[net_target]["type"] == "external_network":
+ if vnfs[ con[0][0] ]['ifaces'][ con[0][1] ]["type"] == "data":
+ other_nets[net_target]["type"] = "data"
+ else:
+ other_nets[net_target]["type"] = "bridge"
+#ELSE
+# if other_nets[net_target]['external'] :
+# type_='data' if len(con)>1 else 'ptp' #an external net is connected to a external port, so it is ptp if only one connection is done to this net
+# if type_=='data' and other_nets[net_target]['type']=="ptp":
+# error_text = "Error connecting %d nodes on a not multipoint net %s" % (len(con), net_target)
+# print "nfvo.new_scenario " + error_text
+# return -HTTP_Bad_Request, error_text
+#ENDIF
+ for iface in con:
+ vnfs[ iface[0] ]['ifaces'][ iface[1] ]['net_key'] = net_target
+ else:
+ #create a net
+ net_type_bridge=False
+ net_type_data=False
+ net_target = "__-__net"+str(net_nb)
+ net_list[net_target] = {'name': conections_list_name[net_nb], #"net-"+str(net_nb),
+ 'description':"net-%s in scenario %s" %(net_nb,topo['name']),
+ 'external':False}
+ for iface in con:
+ vnfs[ iface[0] ]['ifaces'][ iface[1] ]['net_key'] = net_target
+ iface_type = vnfs[ iface[0] ]['ifaces'][ iface[1] ]['type']
+ if iface_type=='mgmt' or iface_type=='bridge':
+ net_type_bridge = True
+ else:
+ net_type_data = True
+ if net_type_bridge and net_type_data:
+ error_text = "Error connection interfaces of bridge type with data type. Firs node %s, iface %s" % (iface[0], iface[1])
+ #print "nfvo.new_scenario " + error_text
+ raise NfvoException(error_text, HTTP_Bad_Request)
+ elif net_type_bridge:
+ type_='bridge'
+ else:
+ type_='data' if len(con)>2 else 'ptp'
+ net_list[net_target]['type'] = type_
+ net_nb+=1
+ except Exception:
+ error_text = "Error connection node %s : %s does not match any VNF or interface" % (iface[0], iface[1])
+ #print "nfvo.new_scenario " + error_text
+ #raise e
+ raise NfvoException(error_text, HTTP_Bad_Request)
+
+#1.8: Connect to management net all not already connected interfaces of type 'mgmt'
+ #1.8.1 obtain management net
+ mgmt_net = mydb.get_rows(SELECT=('uuid','name','description','type','shared'),
+ FROM='datacenter_nets', WHERE={'name':'mgmt'} )
+ #1.8.2 check all interfaces from all vnfs
+ if len(mgmt_net)>0:
+ add_mgmt_net = False
+ for vnf in vnfs.values():
+ for iface in vnf['ifaces'].values():
+ if iface['type']=='mgmt' and 'net_key' not in iface:
+ #iface not connected
+ iface['net_key'] = 'mgmt'
+ add_mgmt_net = True
+ if add_mgmt_net and 'mgmt' not in net_list:
+ net_list['mgmt']=mgmt_net[0]
+ net_list['mgmt']['external']=True
+ net_list['mgmt']['graph']={'visible':False}
+
+ net_list.update(other_nets)
+ #print
+ #print 'net_list', net_list
+ #print
+ #print 'vnfs', vnfs
+ #print
+
+#2: insert scenario. filling tables scenarios,sce_vnfs,sce_interfaces,sce_nets
+ c = mydb.new_scenario( { 'vnfs':vnfs, 'nets':net_list,
+ 'tenant_id':tenant_id, 'name':topo['name'],
+ 'description':topo.get('description',topo['name']),
+ 'public': topo.get('public', False)
+ })
+
+ return c
+
+
+def new_scenario_v02(mydb, tenant_id, scenario_dict, version):
+ """ This creates a new scenario for version 0.2 and 0.3"""
+ scenario = scenario_dict["scenario"]
+ if tenant_id != "any":
+ check_tenant(mydb, tenant_id)
+ if "tenant_id" in scenario:
+ if scenario["tenant_id"] != tenant_id:
+ # print "nfvo.new_scenario_v02() tenant '%s' not found" % tenant_id
+ raise NfvoException("VNF can not have a different tenant owner '{}', must be '{}'".format(
+ scenario["tenant_id"], tenant_id), HTTP_Unauthorized)
+ else:
+ tenant_id=None
+
+ # 1: Check that VNF are present at database table vnfs and update content into scenario dict
+ for name,vnf in scenario["vnfs"].iteritems():
+ where={}
+ where_or={"tenant_id": tenant_id, 'public': "true"}
+ error_text = ""
+ error_pos = "'scenario':'vnfs':'" + name + "'"
+ if 'vnf_id' in vnf:
+ error_text += " 'vnf_id' " + vnf['vnf_id']
+ where['uuid'] = vnf['vnf_id']
+ if 'vnf_name' in vnf:
+ error_text += " 'vnf_name' " + vnf['vnf_name']
+ where['name'] = vnf['vnf_name']
+ if len(where) == 0:
+ raise NfvoException("Needed a 'vnf_id' or 'vnf_name' at " + error_pos, HTTP_Bad_Request)
+ vnf_db = mydb.get_rows(SELECT=('uuid', 'name', 'description'),
+ FROM='vnfs',
+ WHERE=where,
+ WHERE_OR=where_or,
+ WHERE_AND_OR="AND")
+ if len(vnf_db) == 0:
+ raise NfvoException("Unknown" + error_text + " at " + error_pos, HTTP_Not_Found)
+ elif len(vnf_db) > 1:
+ raise NfvoException("More than one" + error_text + " at " + error_pos + " Concrete with 'vnf_id'", HTTP_Conflict)
+ vnf['uuid'] = vnf_db[0]['uuid']
+ vnf['description'] = vnf_db[0]['description']
+ vnf['ifaces'] = {}
+ # get external interfaces
+ ext_ifaces = mydb.get_rows(SELECT=('external_name as name', 'i.uuid as iface_uuid', 'i.type as type'),
+ FROM='vnfs join vms on vnfs.uuid=vms.vnf_id join interfaces as i on vms.uuid=i.vm_id',
+ WHERE={'vnfs.uuid':vnf['uuid']}, WHERE_NOT={'external_name': None} )
+ for ext_iface in ext_ifaces:
+ vnf['ifaces'][ ext_iface['name'] ] = {'uuid':ext_iface['iface_uuid'], 'type': ext_iface['type']}
+ # TODO? get internal-connections from db.nets and their profiles, and update scenario[vnfs][internal-connections] accordingly
+
+ # 2: Insert net_key and ip_address at every vnf interface
+ for net_name, net in scenario["networks"].items():
+ net_type_bridge = False
+ net_type_data = False
+ for iface_dict in net["interfaces"]:
+ if version == "0.2":
+ temp_dict = iface_dict
+ ip_address = None
+ elif version == "0.3":
+ temp_dict = {iface_dict["vnf"] : iface_dict["vnf_interface"]}
+ ip_address = iface_dict.get('ip_address', None)
+ for vnf, iface in temp_dict.items():
+ if vnf not in scenario["vnfs"]:
+ error_text = "Error at 'networks':'{}':'interfaces' VNF '{}' not match any VNF at 'vnfs'".format(
+ net_name, vnf)
+ # logger.debug("nfvo.new_scenario_v02 " + error_text)
+ raise NfvoException(error_text, HTTP_Not_Found)
+ if iface not in scenario["vnfs"][vnf]['ifaces']:
+ error_text = "Error at 'networks':'{}':'interfaces':'{}' interface not match any VNF interface"\
+ .format(net_name, iface)
+ # logger.debug("nfvo.new_scenario_v02 " + error_text)
+ raise NfvoException(error_text, HTTP_Bad_Request)
+ if "net_key" in scenario["vnfs"][vnf]['ifaces'][iface]:
+ error_text = "Error at 'networks':'{}':'interfaces':'{}' interface already connected at network"\
+ "'{}'".format(net_name, iface,scenario["vnfs"][vnf]['ifaces'][iface]['net_key'])
+ # logger.debug("nfvo.new_scenario_v02 " + error_text)
+ raise NfvoException(error_text, HTTP_Bad_Request)
+ scenario["vnfs"][vnf]['ifaces'][ iface ]['net_key'] = net_name
+ scenario["vnfs"][vnf]['ifaces'][iface]['ip_address'] = ip_address
+ iface_type = scenario["vnfs"][vnf]['ifaces'][iface]['type']
+ if iface_type == 'mgmt' or iface_type == 'bridge':
+ net_type_bridge = True
+ else:
+ net_type_data = True
+
+ if net_type_bridge and net_type_data:
+ error_text = "Error connection interfaces of 'bridge' type and 'data' type at 'networks':'{}':'interfaces'"\
+ .format(net_name)
+ # logger.debug("nfvo.new_scenario " + error_text)
+ raise NfvoException(error_text, HTTP_Bad_Request)
+ elif net_type_bridge:
+ type_ = 'bridge'
+ else:
+ type_ = 'data' if len(net["interfaces"]) > 2 else 'ptp'
+
+ if net.get("implementation"): # for v0.3
+ if type_ == "bridge" and net["implementation"] == "underlay":
+ error_text = "Error connecting interfaces of data type to a network declared as 'underlay' at "\
+ "'network':'{}'".format(net_name)
+ # logger.debug(error_text)
+ raise NfvoException(error_text, HTTP_Bad_Request)
+ elif type_ != "bridge" and net["implementation"] == "overlay":
+ error_text = "Error connecting interfaces of data type to a network declared as 'overlay' at "\
+ "'network':'{}'".format(net_name)
+ # logger.debug(error_text)
+ raise NfvoException(error_text, HTTP_Bad_Request)
+ net.pop("implementation")
+ if "type" in net and version == "0.3": # for v0.3
+ if type_ == "data" and net["type"] == "e-line":
+ error_text = "Error connecting more than 2 interfaces of data type to a network declared as type "\
+ "'e-line' at 'network':'{}'".format(net_name)
+ # logger.debug(error_text)
+ raise NfvoException(error_text, HTTP_Bad_Request)
+ elif type_ == "ptp" and net["type"] == "e-lan":
+ type_ = "data"
+
+ net['type'] = type_
+ net['name'] = net_name
+ net['external'] = net.get('external', False)
+
+ # 3: insert at database
+ scenario["nets"] = scenario["networks"]
+ scenario['tenant_id'] = tenant_id
+ scenario_id = mydb.new_scenario(scenario)
+ return scenario_id
+
+
+def edit_scenario(mydb, tenant_id, scenario_id, data):
+ data["uuid"] = scenario_id
+ data["tenant_id"] = tenant_id
+ c = mydb.edit_scenario( data )
+ return c
+
+
+def start_scenario(mydb, tenant_id, scenario_id, instance_scenario_name, instance_scenario_description, datacenter=None,vim_tenant=None, startvms=True):
+ #print "Checking that nfvo_tenant_id exists and getting the VIM URI and the VIM tenant_id"
+ datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter, vim_tenant=vim_tenant)
+ vims = {datacenter_id: myvim}
+ myvim_tenant = myvim['tenant_id']
+ datacenter_name = myvim['name']
+
+ rollbackList=[]
+ try:
+ #print "Checking that the scenario_id exists and getting the scenario dictionary"
+ scenarioDict = mydb.get_scenario(scenario_id, tenant_id, datacenter_id)
+ scenarioDict['datacenter2tenant'] = { datacenter_id: myvim['config']['datacenter_tenant_id'] }
+ scenarioDict['datacenter_id'] = datacenter_id
+ #print '================scenarioDict======================='
+ #print json.dumps(scenarioDict, indent=4)
+ #print 'BEGIN launching instance scenario "%s" based on "%s"' % (instance_scenario_name,scenarioDict['name'])
+
+ logger.debug("start_scenario Scenario %s: consisting of %d VNF(s)", scenarioDict['name'],len(scenarioDict['vnfs']))
+ #print yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False)
+
+ auxNetDict = {} #Auxiliar dictionary. First key:'scenario' or sce_vnf uuid. Second Key: uuid of the net/sce_net. Value: vim_net_id
+ auxNetDict['scenario'] = {}
+
+ logger.debug("start_scenario 1. Creating new nets (sce_nets) in the VIM")
+ for sce_net in scenarioDict['nets']:
+ #print "Net name: %s. Description: %s" % (sce_net["name"], sce_net["description"])
+
+ myNetName = "%s.%s" % (instance_scenario_name, sce_net['name'])
+ myNetName = myNetName[0:255] #limit length
+ myNetType = sce_net['type']
+ myNetDict = {}
+ myNetDict["name"] = myNetName
+ myNetDict["type"] = myNetType
+ myNetDict["tenant_id"] = myvim_tenant
+ myNetIPProfile = sce_net.get('ip_profile', None)
+ #TODO:
+ #We should use the dictionary as input parameter for new_network
+ #print myNetDict
+ if not sce_net["external"]:
+ network_id = myvim.new_network(myNetName, myNetType, myNetIPProfile)
+ #print "New VIM network created for scenario %s. Network id: %s" % (scenarioDict['name'],network_id)
+ sce_net['vim_id'] = network_id
+ auxNetDict['scenario'][sce_net['uuid']] = network_id
+ rollbackList.append({'what':'network','where':'vim','vim_id':datacenter_id,'uuid':network_id})
+ sce_net["created"] = True
+ else:
+ if sce_net['vim_id'] == None:
+ error_text = "Error, datacenter '%s' does not have external network '%s'." % (datacenter_name, sce_net['name'])
+ _, message = rollback(mydb, vims, rollbackList)
+ logger.error("nfvo.start_scenario: %s", error_text)
+ raise NfvoException(error_text, HTTP_Bad_Request)
+ logger.debug("Using existent VIM network for scenario %s. Network id %s", scenarioDict['name'],sce_net['vim_id'])
+ auxNetDict['scenario'][sce_net['uuid']] = sce_net['vim_id']
+
+ logger.debug("start_scenario 2. Creating new nets (vnf internal nets) in the VIM")
+ #For each vnf net, we create it and we add it to instanceNetlist.
+ for sce_vnf in scenarioDict['vnfs']:
+ for net in sce_vnf['nets']:
+ #print "Net name: %s. Description: %s" % (net["name"], net["description"])
+
+ myNetName = "%s.%s" % (instance_scenario_name,net['name'])
+ myNetName = myNetName[0:255] #limit length
+ myNetType = net['type']
+ myNetDict = {}
+ myNetDict["name"] = myNetName
+ myNetDict["type"] = myNetType
+ myNetDict["tenant_id"] = myvim_tenant
+ myNetIPProfile = net.get('ip_profile', None)
+ #print myNetDict
+ #TODO:
+ #We should use the dictionary as input parameter for new_network
+ network_id = myvim.new_network(myNetName, myNetType, myNetIPProfile)
+ #print "VIM network id for scenario %s: %s" % (scenarioDict['name'],network_id)
+ net['vim_id'] = network_id
+ if sce_vnf['uuid'] not in auxNetDict:
+ auxNetDict[sce_vnf['uuid']] = {}
+ auxNetDict[sce_vnf['uuid']][net['uuid']] = network_id
+ rollbackList.append({'what':'network','where':'vim','vim_id':datacenter_id,'uuid':network_id})
+ net["created"] = True
+
+ #print "auxNetDict:"
+ #print yaml.safe_dump(auxNetDict, indent=4, default_flow_style=False)
+
+ logger.debug("start_scenario 3. Creating new vm instances in the VIM")
+ #myvim.new_vminstance(self,vimURI,tenant_id,name,description,image_id,flavor_id,net_dict)
+ i = 0
+ for sce_vnf in scenarioDict['vnfs']:
+ for vm in sce_vnf['vms']:
+ i += 1
+ myVMDict = {}
+ #myVMDict['name'] = "%s-%s-%s" % (scenarioDict['name'],sce_vnf['name'], vm['name'])
+ myVMDict['name'] = "{}.{}.{}".format(instance_scenario_name,sce_vnf['name'],chr(96+i))
+ #myVMDict['description'] = vm['description']
+ myVMDict['description'] = myVMDict['name'][0:99]
+ if not startvms:
+ myVMDict['start'] = "no"
+ myVMDict['name'] = myVMDict['name'][0:255] #limit name length
+ #print "VM name: %s. Description: %s" % (myVMDict['name'], myVMDict['name'])
+
+ #create image at vim in case it not exist
+ image_dict = mydb.get_table_by_uuid_name("images", vm['image_id'])
+ image_id = create_or_use_image(mydb, vims, image_dict, [], True)
+ vm['vim_image_id'] = image_id
+
+ #create flavor at vim in case it not exist
+ flavor_dict = mydb.get_table_by_uuid_name("flavors", vm['flavor_id'])
+ if flavor_dict['extended']!=None:
+ flavor_dict['extended']= yaml.load(flavor_dict['extended'])
+ flavor_id = create_or_use_flavor(mydb, vims, flavor_dict, [], True)
+ vm['vim_flavor_id'] = flavor_id
+
+
+ myVMDict['imageRef'] = vm['vim_image_id']
+ myVMDict['flavorRef'] = vm['vim_flavor_id']
+ myVMDict['networks'] = []
+ for iface in vm['interfaces']:
+ netDict = {}
+ if iface['type']=="data":
+ netDict['type'] = iface['model']
+ elif "model" in iface and iface["model"]!=None:
+ netDict['model']=iface['model']
+ #TODO in future, remove this because mac_address will not be set, and the type of PV,VF is obtained from iterface table model
+ #discover type of interface looking at flavor
+ for numa in flavor_dict.get('extended',{}).get('numas',[]):
+ for flavor_iface in numa.get('interfaces',[]):
+ if flavor_iface.get('name') == iface['internal_name']:
+ if flavor_iface['dedicated'] == 'yes':
+ netDict['type']="PF" #passthrough
+ elif flavor_iface['dedicated'] == 'no':
+ netDict['type']="VF" #siov
+ elif flavor_iface['dedicated'] == 'yes:sriov':
+ netDict['type']="VFnotShared" #sriov but only one sriov on the PF
+ netDict["mac_address"] = flavor_iface.get("mac_address")
+ break;
+ netDict["use"]=iface['type']
+ if netDict["use"]=="data" and not netDict.get("type"):
+ #print "netDict", netDict
+ #print "iface", iface
+ e_text = "Cannot determine the interface type PF or VF of VNF '%s' VM '%s' iface '%s'" %(sce_vnf['name'], vm['name'], iface['internal_name'])
+ if flavor_dict.get('extended')==None:
+ raise NfvoException(e_text + "After database migration some information is not available. \
+ Try to delete and create the scenarios and VNFs again", HTTP_Conflict)
+ else:
+ raise NfvoException(e_text, HTTP_Internal_Server_Error)
+ if netDict["use"]=="mgmt" or netDict["use"]=="bridge":
+ netDict["type"]="virtual"
+ if "vpci" in iface and iface["vpci"] is not None:
+ netDict['vpci'] = iface['vpci']
+ if "mac" in iface and iface["mac"] is not None:
+ netDict['mac_address'] = iface['mac']
+ if "port-security" in iface and iface["port-security"] is not None:
+ netDict['port_security'] = iface['port-security']
+ if "floating-ip" in iface and iface["floating-ip"] is not None:
+ netDict['floating_ip'] = iface['floating-ip']
+ netDict['name'] = iface['internal_name']
+ if iface['net_id'] is None:
+ for vnf_iface in sce_vnf["interfaces"]:
+ #print iface
+ #print vnf_iface
+ if vnf_iface['interface_id']==iface['uuid']:
+ netDict['net_id'] = auxNetDict['scenario'][ vnf_iface['sce_net_id'] ]
+ break
+ else:
+ netDict['net_id'] = auxNetDict[ sce_vnf['uuid'] ][ iface['net_id'] ]
+ #skip bridge ifaces not connected to any net
+ #if 'net_id' not in netDict or netDict['net_id']==None:
+ # continue
+ myVMDict['networks'].append(netDict)
+ #print ">>>>>>>>>>>>>>>>>>>>>>>>>>>"
+ #print myVMDict['name']
+ #print "networks", yaml.safe_dump(myVMDict['networks'], indent=4, default_flow_style=False)
+ #print "interfaces", yaml.safe_dump(vm['interfaces'], indent=4, default_flow_style=False)
+ #print ">>>>>>>>>>>>>>>>>>>>>>>>>>>"
+ vm_id = myvim.new_vminstance(myVMDict['name'],myVMDict['description'],myVMDict.get('start', None),
+ myVMDict['imageRef'],myVMDict['flavorRef'],myVMDict['networks'])
+ #print "VIM vm instance id (server id) for scenario %s: %s" % (scenarioDict['name'],vm_id)
+ vm['vim_id'] = vm_id
+ rollbackList.append({'what':'vm','where':'vim','vim_id':datacenter_id,'uuid':vm_id})
+ #put interface uuid back to scenario[vnfs][vms[[interfaces]
+ for net in myVMDict['networks']:
+ if "vim_id" in net:
+ for iface in vm['interfaces']:
+ if net["name"]==iface["internal_name"]:
+ iface["vim_id"]=net["vim_id"]
+ break
+
+ logger.debug("start scenario Deployment done")
+ #print yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False)
+ #r,c = mydb.new_instance_scenario_as_a_whole(nfvo_tenant,scenarioDict['name'],scenarioDict)
+ instance_id = mydb.new_instance_scenario_as_a_whole(tenant_id,instance_scenario_name, instance_scenario_description, scenarioDict)
+ return mydb.get_instance_scenario(instance_id)
+
+ except (db_base_Exception, vimconn.vimconnException) as e:
+ _, message = rollback(mydb, vims, rollbackList)
+ if isinstance(e, db_base_Exception):
+ error_text = "Exception at database"
+ else:
+ error_text = "Exception at VIM"
+ error_text += " {} {}. {}".format(type(e).__name__, str(e), message)
+ #logger.error("start_scenario %s", error_text)
+ raise NfvoException(error_text, e.http_code)
+
+
+def unify_cloud_config(cloud_config_preserve, cloud_config):
+ ''' join the cloud config information into cloud_config_preserve.
+ In case of conflict cloud_config_preserve preserves
+ None is admited
+ '''
+ if not cloud_config_preserve and not cloud_config:
+ return None
+
+ new_cloud_config = {"key-pairs":[], "users":[]}
+ # key-pairs
+ if cloud_config_preserve:
+ for key in cloud_config_preserve.get("key-pairs", () ):
+ if key not in new_cloud_config["key-pairs"]:
+ new_cloud_config["key-pairs"].append(key)
+ if cloud_config:
+ for key in cloud_config.get("key-pairs", () ):
+ if key not in new_cloud_config["key-pairs"]:
+ new_cloud_config["key-pairs"].append(key)
+ if not new_cloud_config["key-pairs"]:
+ del new_cloud_config["key-pairs"]
+
+ # users
+ if cloud_config:
+ new_cloud_config["users"] += cloud_config.get("users", () )
+ if cloud_config_preserve:
+ new_cloud_config["users"] += cloud_config_preserve.get("users", () )
+ index_to_delete = []
+ users = new_cloud_config.get("users", [])
+ for index0 in range(0,len(users)):
+ if index0 in index_to_delete:
+ continue
+ for index1 in range(index0+1,len(users)):
+ if index1 in index_to_delete:
+ continue
+ if users[index0]["name"] == users[index1]["name"]:
+ index_to_delete.append(index1)
+ for key in users[index1].get("key-pairs",()):
+ if "key-pairs" not in users[index0]:
+ users[index0]["key-pairs"] = [key]
+ elif key not in users[index0]["key-pairs"]:
+ users[index0]["key-pairs"].append(key)
+ index_to_delete.sort(reverse=True)
+ for index in index_to_delete:
+ del users[index]
+ if not new_cloud_config["users"]:
+ del new_cloud_config["users"]
+
+ #boot-data-drive
+ if cloud_config and cloud_config.get("boot-data-drive") != None:
+ new_cloud_config["boot-data-drive"] = cloud_config["boot-data-drive"]
+ if cloud_config_preserve and cloud_config_preserve.get("boot-data-drive") != None:
+ new_cloud_config["boot-data-drive"] = cloud_config_preserve["boot-data-drive"]
+
+ # user-data
+ if cloud_config and cloud_config.get("user-data") != None:
+ new_cloud_config["user-data"] = cloud_config["user-data"]
+ if cloud_config_preserve and cloud_config_preserve.get("user-data") != None:
+ new_cloud_config["user-data"] = cloud_config_preserve["user-data"]
+
+ # config files
+ new_cloud_config["config-files"] = []
+ if cloud_config and cloud_config.get("config-files") != None:
+ new_cloud_config["config-files"] += cloud_config["config-files"]
+ if cloud_config_preserve:
+ for file in cloud_config_preserve.get("config-files", ()):
+ for index in range(0, len(new_cloud_config["config-files"])):
+ if new_cloud_config["config-files"][index]["dest"] == file["dest"]:
+ new_cloud_config["config-files"][index] = file
+ break
+ else:
+ new_cloud_config["config-files"].append(file)
+ if not new_cloud_config["config-files"]:
+ del new_cloud_config["config-files"]
+ return new_cloud_config
+
+
+def get_vim_thread(tenant_id, datacenter_id_name=None, datacenter_tenant_id=None):
+ datacenter_id = None
+ datacenter_name = None
+ thread = None
+ if datacenter_id_name:
+ if utils.check_valid_uuid(datacenter_id_name):
+ datacenter_id = datacenter_id_name
+ else:
+ datacenter_name = datacenter_id_name
+ if datacenter_id:
+ thread = vim_threads["running"].get(datacenter_id + "." + tenant_id)
+ else:
+ for k, v in vim_threads["running"].items():
+ datacenter_tenant = k.split(".")
+ if datacenter_tenant[0] == datacenter_id and datacenter_tenant[1] == tenant_id:
+ if thread:
+ raise NfvoException("More than one datacenters found, try to identify with uuid", HTTP_Conflict)
+ thread = v
+ elif not datacenter_id and datacenter_tenant[1] == tenant_id:
+ if thread.datacenter_name == datacenter_name:
+ if thread:
+ raise NfvoException("More than one datacenters found, try to identify with uuid", HTTP_Conflict)
+ thread = v
+ if not thread:
+ raise NfvoException("datacenter '{}' not found".format(str(datacenter_id_name)), HTTP_Not_Found)
+ return thread
+
+
+def get_datacenter_by_name_uuid(mydb, tenant_id, datacenter_id_name=None, **extra_filter):
+ datacenter_id = None
+ datacenter_name = None
+ if datacenter_id_name:
+ if utils.check_valid_uuid(datacenter_id_name):
+ datacenter_id = datacenter_id_name
+ else:
+ datacenter_name = datacenter_id_name
+ vims = get_vim(mydb, tenant_id, datacenter_id, datacenter_name, **extra_filter)
+ if len(vims) == 0:
+ raise NfvoException("datacenter '{}' not found".format(str(datacenter_id_name)), HTTP_Not_Found)
+ elif len(vims)>1:
+ #print "nfvo.datacenter_action() error. Several datacenters found"
+ raise NfvoException("More than one datacenters found, try to identify with uuid", HTTP_Conflict)
+ return vims.keys()[0], vims.values()[0]
+
+
+def update(d, u):
+ '''Takes dict d and updates it with the values in dict u.'''
+ '''It merges all depth levels'''
+ for k, v in u.iteritems():
+ if isinstance(v, collections.Mapping):
+ r = update(d.get(k, {}), v)
+ d[k] = r
+ else:
+ d[k] = u[k]
+ return d
+
+
+def create_instance(mydb, tenant_id, instance_dict):
+ # print "Checking that nfvo_tenant_id exists and getting the VIM URI and the VIM tenant_id"
+ # logger.debug("Creating instance...")
+ scenario = instance_dict["scenario"]
+
+ #find main datacenter
+ myvims = {}
+ myvim_threads = {}
+ datacenter2tenant = {}
+ datacenter = instance_dict.get("datacenter")
+ default_datacenter_id, vim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
+ myvims[default_datacenter_id] = vim
+ myvim_threads[default_datacenter_id] = get_vim_thread(tenant_id, default_datacenter_id)
+ datacenter2tenant[default_datacenter_id] = vim['config']['datacenter_tenant_id']
+ #myvim_tenant = myvim['tenant_id']
+# default_datacenter_name = vim['name']
+ rollbackList=[]
+
+ #print "Checking that the scenario exists and getting the scenario dictionary"
+ scenarioDict = mydb.get_scenario(scenario, tenant_id, default_datacenter_id)
+
+ #logger.debug(">>>>>>> Dictionaries before merging")
+ #logger.debug(">>>>>>> InstanceDict:\n{}".format(yaml.safe_dump(instance_dict,default_flow_style=False, width=256)))
+ #logger.debug(">>>>>>> ScenarioDict:\n{}".format(yaml.safe_dump(scenarioDict,default_flow_style=False, width=256)))
+
+ scenarioDict['datacenter_id'] = default_datacenter_id
+
+ auxNetDict = {} #Auxiliar dictionary. First key:'scenario' or sce_vnf uuid. Second Key: uuid of the net/sce_net. Value: vim_net_id
+ auxNetDict['scenario'] = {}
+
+ logger.debug("Creating instance from scenario-dict:\n%s", yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False)) #TODO remove
+ instance_name = instance_dict["name"]
+ instance_description = instance_dict.get("description")
+ instance_tasks={}
+ try:
+ # 0 check correct parameters
+ for net_name, net_instance_desc in instance_dict.get("networks",{}).iteritems():
+ found = False
+ for scenario_net in scenarioDict['nets']:
+ if net_name == scenario_net["name"]:
+ found = True
+ break
+ if not found:
+ raise NfvoException("Invalid scenario network name '{}' at instance:networks".format(net_name), HTTP_Bad_Request)
+ if "sites" not in net_instance_desc:
+ net_instance_desc["sites"] = [ {} ]
+ site_without_datacenter_field = False
+ for site in net_instance_desc["sites"]:
+ if site.get("datacenter"):
+ if site["datacenter"] not in myvims:
+ #Add this datacenter to myvims
+ d, v = get_datacenter_by_name_uuid(mydb, tenant_id, site["datacenter"])
+ myvims[d] = v
+ myvim_threads[d] = get_vim_thread(tenant_id, site["datacenter"])
+ datacenter2tenant[d] = v['config']['datacenter_tenant_id']
+ site["datacenter"] = d #change name to id
+ else:
+ if site_without_datacenter_field:
+ raise NfvoException("Found more than one entries without datacenter field at instance:networks:{}:sites".format(net_name), HTTP_Bad_Request)
+ site_without_datacenter_field = True
+ site["datacenter"] = default_datacenter_id #change name to id
+
+ for vnf_name, vnf_instance_desc in instance_dict.get("vnfs",{}).iteritems():
+ found=False
+ for scenario_vnf in scenarioDict['vnfs']:
+ if vnf_name == scenario_vnf['name']:
+ found = True
+ break
+ if not found:
+ raise NfvoException("Invalid vnf name '{}' at instance:vnfs".format(vnf_instance_desc), HTTP_Bad_Request)
+ if "datacenter" in vnf_instance_desc:
+ # Add this datacenter to myvims
+ if vnf_instance_desc["datacenter"] not in myvims:
+ d, v = get_datacenter_by_name_uuid(mydb, tenant_id, vnf_instance_desc["datacenter"])
+ myvims[d] = v
+ myvim_threads[d] = get_vim_thread(tenant_id, vnf_instance_desc["datacenter"])
+ datacenter2tenant[d] = v['config']['datacenter_tenant_id']
+ scenario_vnf["datacenter"] = vnf_instance_desc["datacenter"]
+
+ #0.1 parse cloud-config parameters
+ cloud_config = unify_cloud_config(instance_dict.get("cloud-config"), scenarioDict.get("cloud-config"))
+
+ #0.2 merge instance information into scenario
+ #Ideally, the operation should be as simple as: update(scenarioDict,instance_dict)
+ #However, this is not possible yet.
+ for net_name, net_instance_desc in instance_dict.get("networks",{}).iteritems():
+ for scenario_net in scenarioDict['nets']:
+ if net_name == scenario_net["name"]:
+ if 'ip-profile' in net_instance_desc:
+ ipprofile = net_instance_desc['ip-profile']
+ ipprofile['subnet_address'] = ipprofile.pop('subnet-address',None)
+ ipprofile['ip_version'] = ipprofile.pop('ip-version','IPv4')
+ ipprofile['gateway_address'] = ipprofile.pop('gateway-address',None)
+ ipprofile['dns_address'] = ipprofile.pop('dns-address',None)
+ if 'dhcp' in ipprofile:
+ ipprofile['dhcp_start_address'] = ipprofile['dhcp'].get('start-address',None)
+ ipprofile['dhcp_enabled'] = ipprofile['dhcp'].get('enabled',True)
+ ipprofile['dhcp_count'] = ipprofile['dhcp'].get('count',None)
+ del ipprofile['dhcp']
+ if 'ip_profile' not in scenario_net:
+ scenario_net['ip_profile'] = ipprofile
+ else:
+ update(scenario_net['ip_profile'],ipprofile)
+ for interface in net_instance_desc.get('interfaces', () ):
+ if 'ip_address' in interface:
+ for vnf in scenarioDict['vnfs']:
+ if interface['vnf'] == vnf['name']:
+ for vnf_interface in vnf['interfaces']:
+ if interface['vnf_interface'] == vnf_interface['external_name']:
+ vnf_interface['ip_address']=interface['ip_address']
+
+ #logger.debug(">>>>>>>> Merged dictionary")
+ logger.debug("Creating instance scenario-dict MERGED:\n%s", yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False))
+
+
+ # 1. Creating new nets (sce_nets) in the VIM"
+ for sce_net in scenarioDict['nets']:
+ sce_net["vim_id_sites"]={}
+ descriptor_net = instance_dict.get("networks",{}).get(sce_net["name"],{})
+ net_name = descriptor_net.get("vim-network-name")
+ auxNetDict['scenario'][sce_net['uuid']] = {}
+
+ sites = descriptor_net.get("sites", [ {} ])
+ for site in sites:
+ if site.get("datacenter"):
+ vim = myvims[ site["datacenter"] ]
+ datacenter_id = site["datacenter"]
+ myvim_thread = myvim_threads[ site["datacenter"] ]
+ else:
+ vim = myvims[ default_datacenter_id ]
+ datacenter_id = default_datacenter_id
+ myvim_thread = myvim_threads[default_datacenter_id]
+ net_type = sce_net['type']
+ lookfor_filter = {'admin_state_up': True, 'status': 'ACTIVE'} #'shared': True
+ if sce_net["external"]:
+ if not net_name:
+ net_name = sce_net["name"]
+ if "netmap-use" in site or "netmap-create" in site:
+ create_network = False
+ lookfor_network = False
+ if "netmap-use" in site:
+ lookfor_network = True
+ if utils.check_valid_uuid(site["netmap-use"]):
+ filter_text = "scenario id '%s'" % site["netmap-use"]
+ lookfor_filter["id"] = site["netmap-use"]
+ else:
+ filter_text = "scenario name '%s'" % site["netmap-use"]
+ lookfor_filter["name"] = site["netmap-use"]
+ if "netmap-create" in site:
+ create_network = True
+ net_vim_name = net_name
+ if site["netmap-create"]:
+ net_vim_name = site["netmap-create"]
+
+ elif sce_net['vim_id'] != None:
+ #there is a netmap at datacenter_nets database #TODO REVISE!!!!
+ create_network = False
+ lookfor_network = True
+ lookfor_filter["id"] = sce_net['vim_id']
+ filter_text = "vim_id '%s' datacenter_netmap name '%s'. Try to reload vims with datacenter-net-update" % (sce_net['vim_id'], sce_net["name"])
+ #look for network at datacenter and return error
+ else:
+ #There is not a netmap, look at datacenter for a net with this name and create if not found
+ create_network = True
+ lookfor_network = True
+ lookfor_filter["name"] = sce_net["name"]
+ net_vim_name = sce_net["name"]
+ filter_text = "scenario name '%s'" % sce_net["name"]
+ else:
+ if not net_name:
+ net_name = "%s.%s" %(instance_name, sce_net["name"])
+ net_name = net_name[:255] #limit length
+ net_vim_name = net_name
+ create_network = True
+ lookfor_network = False
+
+ if lookfor_network:
+ vim_nets = vim.get_network_list(filter_dict=lookfor_filter)
+ if len(vim_nets) > 1:
+ raise NfvoException("More than one candidate VIM network found for " + filter_text, HTTP_Bad_Request )
+ elif len(vim_nets) == 0:
+ if not create_network:
+ raise NfvoException("No candidate VIM network found for " + filter_text, HTTP_Bad_Request )
+ else:
+ sce_net["vim_id_sites"][datacenter_id] = vim_nets[0]['id']
+ auxNetDict['scenario'][sce_net['uuid']][datacenter_id] = vim_nets[0]['id']
+ create_network = False
+ if create_network:
+ #if network is not external
+ task = new_task("new-net", (net_vim_name, net_type, sce_net.get('ip_profile',None)))
+ task_id = myvim_thread.insert_task(task)
+ instance_tasks[task_id] = task
+ #network_id = vim.new_network(net_vim_name, net_type, sce_net.get('ip_profile',None))
+ sce_net["vim_id_sites"][datacenter_id] = task_id
+ auxNetDict['scenario'][sce_net['uuid']][datacenter_id] = task_id
+ rollbackList.append({'what':'network', 'where':'vim', 'vim_id':datacenter_id, 'uuid':task_id})
+ sce_net["created"] = True
+
+ # 2. Creating new nets (vnf internal nets) in the VIM"
+ #For each vnf net, we create it and we add it to instanceNetlist.
+ for sce_vnf in scenarioDict['vnfs']:
+ for net in sce_vnf['nets']:
+ if sce_vnf.get("datacenter"):
+ vim = myvims[ sce_vnf["datacenter"] ]
+ datacenter_id = sce_vnf["datacenter"]
+ myvim_thread = myvim_threads[ sce_vnf["datacenter"]]
+ else:
+ vim = myvims[ default_datacenter_id ]
+ datacenter_id = default_datacenter_id
+ myvim_thread = myvim_threads[default_datacenter_id]
+ descriptor_net = instance_dict.get("vnfs",{}).get(sce_vnf["name"],{})
+ net_name = descriptor_net.get("name")
+ if not net_name:
+ net_name = "%s.%s" %(instance_name, net["name"])
+ net_name = net_name[:255] #limit length
+ net_type = net['type']
+ task = new_task("new-net", (net_name, net_type, net.get('ip_profile',None)))
+ task_id = myvim_thread.insert_task(task)
+ instance_tasks[task_id] = task
+ # network_id = vim.new_network(net_name, net_type, net.get('ip_profile',None))
+ net['vim_id'] = task_id
+ if sce_vnf['uuid'] not in auxNetDict:
+ auxNetDict[sce_vnf['uuid']] = {}
+ auxNetDict[sce_vnf['uuid']][net['uuid']] = task_id
+ rollbackList.append({'what':'network','where':'vim','vim_id':datacenter_id,'uuid':task_id})
+ net["created"] = True
+
+
+ #print "auxNetDict:"
+ #print yaml.safe_dump(auxNetDict, indent=4, default_flow_style=False)
+
+ # 3. Creating new vm instances in the VIM
+ #myvim.new_vminstance(self,vimURI,tenant_id,name,description,image_id,flavor_id,net_dict)
+ for sce_vnf in scenarioDict['vnfs']:
+ if sce_vnf.get("datacenter"):
+ vim = myvims[ sce_vnf["datacenter"] ]
+ myvim_thread = myvim_threads[ sce_vnf["datacenter"] ]
+ datacenter_id = sce_vnf["datacenter"]
+ else:
+ vim = myvims[ default_datacenter_id ]
+ myvim_thread = myvim_threads[ default_datacenter_id ]
+ datacenter_id = default_datacenter_id
+ sce_vnf["datacenter_id"] = datacenter_id
+ i = 0
+ for vm in sce_vnf['vms']:
+ i += 1
+ myVMDict = {}
+ myVMDict['name'] = "{}.{}.{}".format(instance_name,sce_vnf['name'],chr(96+i))
+ myVMDict['description'] = myVMDict['name'][0:99]
+# if not startvms:
+# myVMDict['start'] = "no"
+ myVMDict['name'] = myVMDict['name'][0:255] #limit name length
+ #create image at vim in case it not exist
+ image_dict = mydb.get_table_by_uuid_name("images", vm['image_id'])
+ image_id = create_or_use_image(mydb, {datacenter_id: vim}, image_dict, [], True)
+ vm['vim_image_id'] = image_id
+
+ #create flavor at vim in case it not exist
+ flavor_dict = mydb.get_table_by_uuid_name("flavors", vm['flavor_id'])
+ if flavor_dict['extended']!=None:
+ flavor_dict['extended']= yaml.load(flavor_dict['extended'])
+ flavor_id = create_or_use_flavor(mydb, {datacenter_id: vim}, flavor_dict, rollbackList, True)
+
+ #Obtain information for additional disks
+ extended_flavor_dict = mydb.get_rows(FROM='datacenters_flavors', SELECT=('extended',), WHERE={'vim_id': flavor_id})
+ if not extended_flavor_dict:
+ raise NfvoException("flavor '{}' not found".format(flavor_id), HTTP_Not_Found)
+ return
+
+ #extended_flavor_dict_yaml = yaml.load(extended_flavor_dict[0])
+ myVMDict['disks'] = None
+ extended_info = extended_flavor_dict[0]['extended']
+ if extended_info != None:
+ extended_flavor_dict_yaml = yaml.load(extended_info)
+ if 'disks' in extended_flavor_dict_yaml:
+ myVMDict['disks'] = extended_flavor_dict_yaml['disks']
+
+ vm['vim_flavor_id'] = flavor_id
+ myVMDict['imageRef'] = vm['vim_image_id']
+ myVMDict['flavorRef'] = vm['vim_flavor_id']
+ myVMDict['networks'] = []
+ task_depends = {}
+ #TODO ALF. connect_mgmt_interfaces. Connect management interfaces if this is true
+ for iface in vm['interfaces']:
+ netDict = {}
+ if iface['type']=="data":
+ netDict['type'] = iface['model']
+ elif "model" in iface and iface["model"]!=None:
+ netDict['model']=iface['model']
+ #TODO in future, remove this because mac_address will not be set, and the type of PV,VF is obtained from iterface table model
+ #discover type of interface looking at flavor
+ for numa in flavor_dict.get('extended',{}).get('numas',[]):
+ for flavor_iface in numa.get('interfaces',[]):
+ if flavor_iface.get('name') == iface['internal_name']:
+ if flavor_iface['dedicated'] == 'yes':
+ netDict['type']="PF" #passthrough
+ elif flavor_iface['dedicated'] == 'no':
+ netDict['type']="VF" #siov
+ elif flavor_iface['dedicated'] == 'yes:sriov':
+ netDict['type']="VFnotShared" #sriov but only one sriov on the PF
+ netDict["mac_address"] = flavor_iface.get("mac_address")
+ break;
+ netDict["use"]=iface['type']
+ if netDict["use"]=="data" and not netDict.get("type"):
+ #print "netDict", netDict
+ #print "iface", iface
+ e_text = "Cannot determine the interface type PF or VF of VNF '%s' VM '%s' iface '%s'" %(sce_vnf['name'], vm['name'], iface['internal_name'])
+ if flavor_dict.get('extended')==None:
+ raise NfvoException(e_text + "After database migration some information is not available. \
+ Try to delete and create the scenarios and VNFs again", HTTP_Conflict)
+ else:
+ raise NfvoException(e_text, HTTP_Internal_Server_Error)
+ if netDict["use"]=="mgmt" or netDict["use"]=="bridge":
+ netDict["type"]="virtual"
+ if "vpci" in iface and iface["vpci"] is not None:
+ netDict['vpci'] = iface['vpci']
+ if "mac" in iface and iface["mac"] is not None:
+ netDict['mac_address'] = iface['mac']
+ if "port-security" in iface and iface["port-security"] is not None:
+ netDict['port_security'] = iface['port-security']
+ if "floating-ip" in iface and iface["floating-ip"] is not None:
+ netDict['floating_ip'] = iface['floating-ip']
+ netDict['name'] = iface['internal_name']
+ if iface['net_id'] is None:
+ for vnf_iface in sce_vnf["interfaces"]:
+ #print iface
+ #print vnf_iface
+ if vnf_iface['interface_id']==iface['uuid']:
+ netDict['net_id'] = auxNetDict['scenario'][ vnf_iface['sce_net_id'] ][datacenter_id]
+ break
+ else:
+ netDict['net_id'] = auxNetDict[ sce_vnf['uuid'] ][ iface['net_id'] ]
+ if is_task_id(netDict['net_id']):
+ task_depends[netDict['net_id']] = instance_tasks[netDict['net_id']]
+ #skip bridge ifaces not connected to any net
+ #if 'net_id' not in netDict or netDict['net_id']==None:
+ # continue
+ myVMDict['networks'].append(netDict)
+ #print ">>>>>>>>>>>>>>>>>>>>>>>>>>>"
+ #print myVMDict['name']
+ #print "networks", yaml.safe_dump(myVMDict['networks'], indent=4, default_flow_style=False)
+ #print "interfaces", yaml.safe_dump(vm['interfaces'], indent=4, default_flow_style=False)
+ #print ">>>>>>>>>>>>>>>>>>>>>>>>>>>"
+ if vm.get("boot_data"):
+ cloud_config_vm = unify_cloud_config(vm["boot_data"], cloud_config)
+ else:
+ cloud_config_vm = cloud_config
+ task = new_task("new-vm", (myVMDict['name'], myVMDict['description'], myVMDict.get('start', None),
+ myVMDict['imageRef'], myVMDict['flavorRef'], myVMDict['networks'],
+ cloud_config_vm, myVMDict['disks']), depends=task_depends)
+ vm_id = myvim_thread.insert_task(task)
+ instance_tasks[vm_id] = task
+
+ vm['vim_id'] = vm_id
+ rollbackList.append({'what':'vm','where':'vim','vim_id':datacenter_id,'uuid':vm_id})
+ #put interface uuid back to scenario[vnfs][vms[[interfaces]
+ for net in myVMDict['networks']:
+ if "vim_id" in net:
+ for iface in vm['interfaces']:
+ if net["name"]==iface["internal_name"]:
+ iface["vim_id"]=net["vim_id"]
+ break
+ scenarioDict["datacenter2tenant"] = datacenter2tenant
+ logger.debug("create_instance Deployment done scenarioDict: %s",
+ yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False) )
+ instance_id = mydb.new_instance_scenario_as_a_whole(tenant_id,instance_name, instance_description, scenarioDict)
+ # Update database with those ended tasks
+ for task in instance_tasks.values():
+ if task["status"] == "ok":
+ if task["name"] == "new-vm":
+ mydb.update_rows("instance_vms", UPDATE={"vim_vm_id": task["result"]},
+ WHERE={"vim_vm_id": task["id"]})
+ elif task["name"] == "new-net":
+ mydb.update_rows("instance_nets", UPDATE={"vim_net_id": task["result"]},
+ WHERE={"vim_net_id": task["id"]})
+ return mydb.get_instance_scenario(instance_id)
+ except (NfvoException, vimconn.vimconnException,db_base_Exception) as e:
+ message = rollback(mydb, myvims, rollbackList)
+ if isinstance(e, db_base_Exception):
+ error_text = "database Exception"
+ elif isinstance(e, vimconn.vimconnException):
+ error_text = "VIM Exception"
+ else:
+ error_text = "Exception"
+ error_text += " {} {}. {}".format(type(e).__name__, str(e), message)
+ #logger.error("create_instance: %s", error_text)
+ raise NfvoException(error_text, e.http_code)
+
+
+def delete_instance(mydb, tenant_id, instance_id):
+ #print "Checking that the instance_id exists and getting the instance dictionary"
+ instanceDict = mydb.get_instance_scenario(instance_id, tenant_id)
+ #print yaml.safe_dump(instanceDict, indent=4, default_flow_style=False)
+ tenant_id = instanceDict["tenant_id"]
+ #print "Checking that nfvo_tenant_id exists and getting the VIM URI and the VIM tenant_id"
+
+ #1. Delete from Database
+ message = mydb.delete_instance_scenario(instance_id, tenant_id)
+
+ #2. delete from VIM
+ error_msg = ""
+ myvims = {}
+ myvim_threads = {}
+
+ #2.1 deleting VMs
+ #vm_fail_list=[]
+ for sce_vnf in instanceDict['vnfs']:
+ datacenter_key = (sce_vnf["datacenter_id"], sce_vnf["datacenter_tenant_id"])
+ if datacenter_key not in myvims:
+ try:
+ myvim_thread = get_vim_thread(tenant_id, sce_vnf["datacenter_id"], sce_vnf["datacenter_tenant_id"])
+ except NfvoException as e:
+ logger.error(str(e))
+ myvim_thread = None
+ myvim_threads[datacenter_key] = myvim_thread
+ vims = get_vim(mydb, tenant_id, datacenter_id=sce_vnf["datacenter_id"],
+ datacenter_tenant_id=sce_vnf["datacenter_tenant_id"])
+ if len(vims) == 0:
+ logger.error("datacenter '{}' with datacenter_tenant_id '{}' not found".format(sce_vnf["datacenter_id"],
+ sce_vnf["datacenter_tenant_id"]))
+ myvims[datacenter_key] = None
+ else:
+ myvims[datacenter_key] = vims.values()[0]
+ myvim = myvims[datacenter_key]
+ myvim_thread = myvim_threads[datacenter_key]
+ for vm in sce_vnf['vms']:
+ if not myvim:
+ error_msg += "\n VM id={} cannot be deleted because datacenter={} not found".format(vm['vim_vm_id'], sce_vnf["datacenter_id"])
+ continue
+ try:
+ task=None
+ if is_task_id(vm['vim_vm_id']):
+ task_id = vm['vim_vm_id']
+ old_task = task_dict.get(task_id)
+ if not old_task:
+ error_msg += "\n VM was scheduled for create, but task {} is not found".format(task_id)
+ continue
+ with task_lock:
+ if old_task["status"] == "enqueued":
+ old_task["status"] = "deleted"
+ elif old_task["status"] == "error":
+ continue
+ elif old_task["status"] == "processing":
+ task = new_task("del-vm", task_id, depends={task_id: old_task})
+ else: #ok
+ task = new_task("del-vm", old_task["result"])
+ else:
+ task = new_task("del-vm", vm['vim_vm_id'], store=False)
+ if task:
+ myvim_thread.insert_task(task)
+ except vimconn.vimconnNotFoundException as e:
+ error_msg+="\n VM VIM_id={} not found at datacenter={}".format(vm['vim_vm_id'], sce_vnf["datacenter_id"])
+ logger.warn("VM instance '%s'uuid '%s', VIM id '%s', from VNF_id '%s' not found",
+ vm['name'], vm['uuid'], vm['vim_vm_id'], sce_vnf['vnf_id'])
+ except vimconn.vimconnException as e:
+ error_msg+="\n VM VIM_id={} at datacenter={} Error: {} {}".format(vm['vim_vm_id'], sce_vnf["datacenter_id"], e.http_code, str(e))
+ logger.error("Error %d deleting VM instance '%s'uuid '%s', VIM_id '%s', from VNF_id '%s': %s",
+ e.http_code, vm['name'], vm['uuid'], vm['vim_vm_id'], sce_vnf['vnf_id'], str(e))
+
+ #2.2 deleting NETS
+ #net_fail_list=[]
+ for net in instanceDict['nets']:
+ if not net['created']:
+ continue #skip not created nets
+ datacenter_key = (net["datacenter_id"], net["datacenter_tenant_id"])
+ if datacenter_key not in myvims:
+ try:
+ myvim_thread = get_vim_thread(tenant_id, sce_vnf["datacenter_id"], sce_vnf["datacenter_tenant_id"])
+ except NfvoException as e:
+ logger.error(str(e))
+ myvim_thread = None
+ myvim_threads[datacenter_key] = myvim_thread
+ vims = get_vim(mydb, tenant_id, datacenter_id=net["datacenter_id"],
+ datacenter_tenant_id=net["datacenter_tenant_id"])
+ if len(vims) == 0:
+ logger.error("datacenter '{}' with datacenter_tenant_id '{}' not found".format(net["datacenter_id"], net["datacenter_tenant_id"]))
+ myvims[datacenter_key] = None
+ else:
+ myvims[datacenter_key] = vims.values()[0]
+ myvim = myvims[datacenter_key]
+ myvim_thread = myvim_threads[datacenter_key]
+
+ if not myvim:
+ error_msg += "\n Net VIM_id={} cannot be deleted because datacenter={} not found".format(net['vim_net_id'], net["datacenter_id"])
+ continue
+ try:
+ task = None
+ if is_task_id(net['vim_net_id']):
+ task_id = net['vim_net_id']
+ old_task = task_dict.get(task_id)
+ if not old_task:
+ error_msg += "\n NET was scheduled for create, but task {} is not found".format(task_id)
+ continue
+ with task_lock:
+ if old_task["status"] == "enqueued":
+ old_task["status"] = "deleted"
+ elif old_task["status"] == "error":
+ continue
+ elif old_task["status"] == "processing":
+ task = new_task("del-net", task_id, depends={task_id: old_task})
+ else: # ok
+ task = new_task("del-net", old_task["result"])
+ else:
+ task = new_task("del-net", net['vim_net_id'], store=False)
+ if task:
+ myvim_thread.insert_task(task)
+ except vimconn.vimconnNotFoundException as e:
+ error_msg += "\n NET VIM_id={} not found at datacenter={}".format(net['vim_net_id'], net["datacenter_id"])
+ logger.warn("NET '%s', VIM_id '%s', from VNF_net_id '%s' not found",
+ net['uuid'], net['vim_net_id'], str(net['vnf_net_id']))
+ except vimconn.vimconnException as e:
+ error_msg += "\n NET VIM_id={} at datacenter={} Error: {} {}".format(net['vim_net_id'],
+ net["datacenter_id"],
+ e.http_code, str(e))
+ logger.error("Error %d deleting NET '%s', VIM_id '%s', from VNF_net_id '%s': %s",
+ e.http_code, net['uuid'], net['vim_net_id'], str(net['vnf_net_id']), str(e))
+ if len(error_msg) > 0:
+ return 'instance ' + message + ' deleted but some elements could not be deleted, or already deleted (error: 404) from VIM: ' + error_msg
+ else:
+ return 'instance ' + message + ' deleted'
+
+
+def refresh_instance(mydb, nfvo_tenant, instanceDict, datacenter=None, vim_tenant=None):
+ '''Refreshes a scenario instance. It modifies instanceDict'''
+ '''Returns:
+ - result: <0 if there is any unexpected error, n>=0 if no errors where n is the number of vms and nets that couldn't be updated in the database
+ - error_msg
+ '''
+ # Assumption: nfvo_tenant and instance_id were checked before entering into this function
+ #print "nfvo.refresh_instance begins"
+ #print json.dumps(instanceDict, indent=4)
+
+ #print "Getting the VIM URL and the VIM tenant_id"
+ myvims={}
+
+ # 1. Getting VIM vm and net list
+ vms_updated = [] #List of VM instance uuids in openmano that were updated
+ vms_notupdated=[]
+ vm_list = {}
+ for sce_vnf in instanceDict['vnfs']:
+ datacenter_key = (sce_vnf["datacenter_id"], sce_vnf["datacenter_tenant_id"])
+ if datacenter_key not in vm_list:
+ vm_list[datacenter_key] = []
+ if datacenter_key not in myvims:
+ vims = get_vim(mydb, nfvo_tenant, datacenter_id=sce_vnf["datacenter_id"],
+ datacenter_tenant_id=sce_vnf["datacenter_tenant_id"])
+ if len(vims) == 0:
+ logger.error("datacenter '{}' with datacenter_tenant_id '{}' not found".format(sce_vnf["datacenter_id"], sce_vnf["datacenter_tenant_id"]))
+ myvims[datacenter_key] = None
+ else:
+ myvims[datacenter_key] = vims.values()[0]
+ for vm in sce_vnf['vms']:
+ vm_list[datacenter_key].append(vm['vim_vm_id'])
+ vms_notupdated.append(vm["uuid"])
+
+ nets_updated = [] #List of VM instance uuids in openmano that were updated
+ nets_notupdated=[]
+ net_list = {}
+ for net in instanceDict['nets']:
+ datacenter_key = (net["datacenter_id"], net["datacenter_tenant_id"])
+ if datacenter_key not in net_list:
+ net_list[datacenter_key] = []
+ if datacenter_key not in myvims:
+ vims = get_vim(mydb, nfvo_tenant, datacenter_id=net["datacenter_id"],
+ datacenter_tenant_id=net["datacenter_tenant_id"])
+ if len(vims) == 0:
+ logger.error("datacenter '{}' with datacenter_tenant_id '{}' not found".format(net["datacenter_id"], net["datacenter_tenant_id"]))
+ myvims[datacenter_key] = None
+ else:
+ myvims[datacenter_key] = vims.values()[0]
+
+ net_list[datacenter_key].append(net['vim_net_id'])
+ nets_notupdated.append(net["uuid"])
+
+ # 1. Getting the status of all VMs
+ vm_dict={}
+ for datacenter_key in myvims:
+ if not vm_list.get(datacenter_key):
+ continue
+ failed = True
+ failed_message=""
+ if not myvims[datacenter_key]:
+ failed_message = "datacenter '{}' with datacenter_tenant_id '{}' not found".format(net["datacenter_id"], net["datacenter_tenant_id"])
+ else:
+ try:
+ vm_dict.update(myvims[datacenter_key].refresh_vms_status(vm_list[datacenter_key]) )
+ failed = False
+ except vimconn.vimconnException as e:
+ logger.error("VIM exception %s %s", type(e).__name__, str(e))
+ failed_message = str(e)
+ if failed:
+ for vm in vm_list[datacenter_key]:
+ vm_dict[vm] = {'status': "VIM_ERROR", 'error_msg': failed_message}
+
+ # 2. Update the status of VMs in the instanceDict, while collects the VMs whose status changed
+ for sce_vnf in instanceDict['vnfs']:
+ for vm in sce_vnf['vms']:
+ vm_id = vm['vim_vm_id']
+ interfaces = vm_dict[vm_id].pop('interfaces', [])
+ #2.0 look if contain manamgement interface, and if not change status from ACTIVE:NoMgmtIP to ACTIVE
+ has_mgmt_iface = False
+ for iface in vm["interfaces"]:
+ if iface["type"]=="mgmt":
+ has_mgmt_iface = True
+ if vm_dict[vm_id]['status'] == "ACTIVE:NoMgmtIP" and not has_mgmt_iface:
+ vm_dict[vm_id]['status'] = "ACTIVE"
+ if vm_dict[vm_id].get('error_msg') and len(vm_dict[vm_id]['error_msg']) >= 1024:
+ vm_dict[vm_id]['error_msg'] = vm_dict[vm_id]['error_msg'][:516] + " ... " + vm_dict[vm_id]['error_msg'][-500:]
+ if vm['status'] != vm_dict[vm_id]['status'] or vm.get('error_msg')!=vm_dict[vm_id].get('error_msg') or vm.get('vim_info')!=vm_dict[vm_id].get('vim_info'):
+ vm['status'] = vm_dict[vm_id]['status']
+ vm['error_msg'] = vm_dict[vm_id].get('error_msg')
+ vm['vim_info'] = vm_dict[vm_id].get('vim_info')
+ # 2.1. Update in openmano DB the VMs whose status changed
+ try:
+ updates = mydb.update_rows('instance_vms', UPDATE=vm_dict[vm_id], WHERE={'uuid':vm["uuid"]})
+ vms_notupdated.remove(vm["uuid"])
+ if updates>0:
+ vms_updated.append(vm["uuid"])
+ except db_base_Exception as e:
+ logger.error("nfvo.refresh_instance error database update: %s", str(e))
+ # 2.2. Update in openmano DB the interface VMs
+ for interface in interfaces:
+ #translate from vim_net_id to instance_net_id
+ network_id_list=[]
+ for net in instanceDict['nets']:
+ if net["vim_net_id"] == interface["vim_net_id"]:
+ network_id_list.append(net["uuid"])
+ if not network_id_list:
+ continue
+ del interface["vim_net_id"]
+ try:
+ for network_id in network_id_list:
+ mydb.update_rows('instance_interfaces', UPDATE=interface, WHERE={'instance_vm_id':vm["uuid"], "instance_net_id":network_id})
+ except db_base_Exception as e:
+ logger.error( "nfvo.refresh_instance error with vm=%s, interface_net_id=%s", vm["uuid"], network_id)
+
+ # 3. Getting the status of all nets
+ net_dict = {}
+ for datacenter_key in myvims:
+ if not net_list.get(datacenter_key):
+ continue
+ failed = True
+ failed_message = ""
+ if not myvims[datacenter_key]:
+ failed_message = "datacenter '{}' with datacenter_tenant_id '{}' not found".format(net["datacenter_id"], net["datacenter_tenant_id"])
+ else:
+ try:
+ net_dict.update(myvims[datacenter_key].refresh_nets_status(net_list[datacenter_key]) )
+ failed = False
+ except vimconn.vimconnException as e:
+ logger.error("VIM exception %s %s", type(e).__name__, str(e))
+ failed_message = str(e)
+ if failed:
+ for net in net_list[datacenter_key]:
+ net_dict[net] = {'status': "VIM_ERROR", 'error_msg': failed_message}
+
+ # 4. Update the status of nets in the instanceDict, while collects the nets whose status changed
+ # TODO: update nets inside a vnf
+ for net in instanceDict['nets']:
+ net_id = net['vim_net_id']
+ if net_dict[net_id].get('error_msg') and len(net_dict[net_id]['error_msg']) >= 1024:
+ net_dict[net_id]['error_msg'] = net_dict[net_id]['error_msg'][:516] + " ... " + net_dict[vm_id]['error_msg'][-500:]
+ if net['status'] != net_dict[net_id]['status'] or net.get('error_msg')!=net_dict[net_id].get('error_msg') or net.get('vim_info')!=net_dict[net_id].get('vim_info'):
+ net['status'] = net_dict[net_id]['status']
+ net['error_msg'] = net_dict[net_id].get('error_msg')
+ net['vim_info'] = net_dict[net_id].get('vim_info')
+ # 5.1. Update in openmano DB the nets whose status changed
+ try:
+ updated = mydb.update_rows('instance_nets', UPDATE=net_dict[net_id], WHERE={'uuid':net["uuid"]})
+ nets_notupdated.remove(net["uuid"])
+ if updated>0:
+ nets_updated.append(net["uuid"])
+ except db_base_Exception as e:
+ logger.error("nfvo.refresh_instance error database update: %s", str(e))
+
+ # Returns appropriate output
+ #print "nfvo.refresh_instance finishes"
+ logger.debug("VMs updated in the database: %s; nets updated in the database %s; VMs not updated: %s; nets not updated: %s",
+ str(vms_updated), str(nets_updated), str(vms_notupdated), str(nets_notupdated))
+ instance_id = instanceDict['uuid']
+ if len(vms_notupdated)+len(nets_notupdated)>0:
+ error_msg = "VMs not updated: " + str(vms_notupdated) + "; nets not updated: " + str(nets_notupdated)
+ return len(vms_notupdated)+len(nets_notupdated), 'Scenario instance ' + instance_id + ' refreshed but some elements could not be updated in the database: ' + error_msg
+
+ return 0, 'Scenario instance ' + instance_id + ' refreshed.'
+
+
+def instance_action(mydb,nfvo_tenant,instance_id, action_dict):
+ #print "Checking that the instance_id exists and getting the instance dictionary"
+ instanceDict = mydb.get_instance_scenario(instance_id, nfvo_tenant)
+ #print yaml.safe_dump(instanceDict, indent=4, default_flow_style=False)
+
+ #print "Checking that nfvo_tenant_id exists and getting the VIM URI and the VIM tenant_id"
+ vims = get_vim(mydb, nfvo_tenant, instanceDict['datacenter_id'])
+ if len(vims) == 0:
+ raise NfvoException("datacenter '{}' not found".format(str(instanceDict['datacenter_id'])), HTTP_Not_Found)
+ myvim = vims.values()[0]
+
+
+ input_vnfs = action_dict.pop("vnfs", [])
+ input_vms = action_dict.pop("vms", [])
+ action_over_all = True if len(input_vnfs)==0 and len (input_vms)==0 else False
+ vm_result = {}
+ vm_error = 0
+ vm_ok = 0
+ for sce_vnf in instanceDict['vnfs']:
+ for vm in sce_vnf['vms']:
+ if not action_over_all:
+ if sce_vnf['uuid'] not in input_vnfs and sce_vnf['vnf_name'] not in input_vnfs and \
+ vm['uuid'] not in input_vms and vm['name'] not in input_vms:
+ continue
+ try:
+ data = myvim.action_vminstance(vm['vim_vm_id'], action_dict)
+ if "console" in action_dict:
+ if not global_config["http_console_proxy"]:
+ vm_result[ vm['uuid'] ] = {"vim_result": 200,
+ "description": "{protocol}//{ip}:{port}/{suffix}".format(
+ protocol=data["protocol"],
+ ip = data["server"],
+ port = data["port"],
+ suffix = data["suffix"]),
+ "name":vm['name']
+ }
+ vm_ok +=1
+ elif data["server"]=="127.0.0.1" or data["server"]=="localhost":
+ vm_result[ vm['uuid'] ] = {"vim_result": -HTTP_Unauthorized,
+ "description": "this console is only reachable by local interface",
+ "name":vm['name']
+ }
+ vm_error+=1
+ else:
+ #print "console data", data
+ try:
+ console_thread = create_or_use_console_proxy_thread(data["server"], data["port"])
+ vm_result[ vm['uuid'] ] = {"vim_result": 200,
+ "description": "{protocol}//{ip}:{port}/{suffix}".format(
+ protocol=data["protocol"],
+ ip = global_config["http_console_host"],
+ port = console_thread.port,
+ suffix = data["suffix"]),
+ "name":vm['name']
+ }
+ vm_ok +=1
+ except NfvoException as e:
+ vm_result[ vm['uuid'] ] = {"vim_result": e.http_code, "name":vm['name'], "description": str(e)}
+ vm_error+=1
+
+ else:
+ vm_result[ vm['uuid'] ] = {"vim_result": 200, "description": "ok", "name":vm['name']}
+ vm_ok +=1
+ except vimconn.vimconnException as e:
+ vm_result[ vm['uuid'] ] = {"vim_result": e.http_code, "name":vm['name'], "description": str(e)}
+ vm_error+=1
+
+ if vm_ok==0: #all goes wrong
+ return vm_result
+ else:
+ return vm_result
+
+
+def create_or_use_console_proxy_thread(console_server, console_port):
+ #look for a non-used port
+ console_thread_key = console_server + ":" + str(console_port)
+ if console_thread_key in global_config["console_thread"]:
+ #global_config["console_thread"][console_thread_key].start_timeout()
+ return global_config["console_thread"][console_thread_key]
+
+ for port in global_config["console_port_iterator"]():
+ #print "create_or_use_console_proxy_thread() port:", port
+ if port in global_config["console_ports"]:
+ continue
+ try:
+ clithread = cli.ConsoleProxyThread(global_config['http_host'], port, console_server, console_port)
+ clithread.start()
+ global_config["console_thread"][console_thread_key] = clithread
+ global_config["console_ports"][port] = console_thread_key
+ return clithread
+ except cli.ConsoleProxyExceptionPortUsed as e:
+ #port used, try with onoher
+ continue
+ except cli.ConsoleProxyException as e:
+ raise NfvoException(str(e), HTTP_Bad_Request)
+ raise NfvoException("Not found any free 'http_console_ports'", HTTP_Conflict)
+
+
+def check_tenant(mydb, tenant_id):
+ '''check that tenant exists at database'''
+ tenant = mydb.get_rows(FROM='nfvo_tenants', SELECT=('uuid',), WHERE={'uuid': tenant_id})
+ if not tenant:
+ raise NfvoException("tenant '{}' not found".format(tenant_id), HTTP_Not_Found)
+ return
+
+
+def new_tenant(mydb, tenant_dict):
+ tenant_id = mydb.new_row("nfvo_tenants", tenant_dict, add_uuid=True)
+ return tenant_id
+
+
+def delete_tenant(mydb, tenant):
+ #get nfvo_tenant info
+
+ tenant_dict = mydb.get_table_by_uuid_name('nfvo_tenants', tenant, 'tenant')
+ mydb.delete_row_by_id("nfvo_tenants", tenant_dict['uuid'])
+ return tenant_dict['uuid'] + " " + tenant_dict["name"]
+
+
+def new_datacenter(mydb, datacenter_descriptor):
+ if "config" in datacenter_descriptor:
+ datacenter_descriptor["config"]=yaml.safe_dump(datacenter_descriptor["config"],default_flow_style=True,width=256)
+ #Check that datacenter-type is correct
+ datacenter_type = datacenter_descriptor.get("type", "openvim");
+ module_info = None
+ try:
+ module = "vimconn_" + datacenter_type
+ module_info = imp.find_module(module)
+ except (IOError, ImportError):
+ if module_info and module_info[0]:
+ file.close(module_info[0])
+ raise NfvoException("Incorrect datacenter type '{}'. Plugin '{}'.py not installed".format(datacenter_type, module), HTTP_Bad_Request)
+
+ datacenter_id = mydb.new_row("datacenters", datacenter_descriptor, add_uuid=True)
+ return datacenter_id
+
+
+def edit_datacenter(mydb, datacenter_id_name, datacenter_descriptor):
+ #obtain data, check that only one exist
+ datacenter = mydb.get_table_by_uuid_name('datacenters', datacenter_id_name)
+ #edit data
+ datacenter_id = datacenter['uuid']
+ where={'uuid': datacenter['uuid']}
+ if "config" in datacenter_descriptor:
+ if datacenter_descriptor['config']!=None:
+ try:
+ new_config_dict = datacenter_descriptor["config"]
+ #delete null fields
+ to_delete=[]
+ for k in new_config_dict:
+ if new_config_dict[k]==None:
+ to_delete.append(k)
+
+ config_dict = yaml.load(datacenter["config"])
+ config_dict.update(new_config_dict)
+ #delete null fields
+ for k in to_delete:
+ del config_dict[k]
+ except Exception as e:
+ raise NfvoException("Bad format at datacenter:config " + str(e), HTTP_Bad_Request)
+ datacenter_descriptor["config"]= yaml.safe_dump(config_dict,default_flow_style=True,width=256) if len(config_dict)>0 else None
+ mydb.update_rows('datacenters', datacenter_descriptor, where)
+ return datacenter_id
+
+
+def delete_datacenter(mydb, datacenter):
+ #get nfvo_tenant info
+ datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter, 'datacenter')
+ mydb.delete_row_by_id("datacenters", datacenter_dict['uuid'])
+ return datacenter_dict['uuid'] + " " + datacenter_dict['name']
+
+
+def associate_datacenter_to_tenant(mydb, nfvo_tenant, datacenter, vim_tenant_id=None, vim_tenant_name=None, vim_username=None, vim_password=None, config=None):
+ #get datacenter info
+ datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, None, datacenter)
+ datacenter_name = myvim["name"]
+
+ create_vim_tenant = True if not vim_tenant_id and not vim_tenant_name else False
+
+ # get nfvo_tenant info
+ tenant_dict = mydb.get_table_by_uuid_name('nfvo_tenants', nfvo_tenant)
+ if vim_tenant_name==None:
+ vim_tenant_name=tenant_dict['name']
+
+ #check that this association does not exist before
+ tenants_datacenter_dict={"nfvo_tenant_id":tenant_dict['uuid'], "datacenter_id":datacenter_id }
+ tenants_datacenters = mydb.get_rows(FROM='tenants_datacenters', WHERE=tenants_datacenter_dict)
+ if len(tenants_datacenters)>0:
+ raise NfvoException("datacenter '{}' and tenant'{}' are already attached".format(datacenter_id, tenant_dict['uuid']), HTTP_Conflict)
+
+ vim_tenant_id_exist_atdb=False
+ if not create_vim_tenant:
+ where_={"datacenter_id": datacenter_id}
+ if vim_tenant_id!=None:
+ where_["vim_tenant_id"] = vim_tenant_id
+ if vim_tenant_name!=None:
+ where_["vim_tenant_name"] = vim_tenant_name
+ #check if vim_tenant_id is already at database
+ datacenter_tenants_dict = mydb.get_rows(FROM='datacenter_tenants', WHERE=where_)
+ if len(datacenter_tenants_dict)>=1:
+ datacenter_tenants_dict = datacenter_tenants_dict[0]
+ vim_tenant_id_exist_atdb=True
+ #TODO check if a field has changed and edit entry at datacenter_tenants at DB
+ else: #result=0
+ datacenter_tenants_dict = {}
+ #insert at table datacenter_tenants
+ else: #if vim_tenant_id==None:
+ #create tenant at VIM if not provided
+ try:
+ vim_tenant_id = myvim.new_tenant(vim_tenant_name, "created by openmano for datacenter "+datacenter_name)
+ except vimconn.vimconnException as e:
+ raise NfvoException("Not possible to create vim_tenant {} at VIM: {}".format(vim_tenant_id, str(e)), HTTP_Internal_Server_Error)
+ datacenter_tenants_dict = {}
+ datacenter_tenants_dict["created"]="true"
+
+ #fill datacenter_tenants table
+ if not vim_tenant_id_exist_atdb:
+ datacenter_tenants_dict["vim_tenant_id"] = vim_tenant_id
+ datacenter_tenants_dict["vim_tenant_name"] = vim_tenant_name
+ datacenter_tenants_dict["user"] = vim_username
+ datacenter_tenants_dict["passwd"] = vim_password
+ datacenter_tenants_dict["datacenter_id"] = datacenter_id
+ if config:
+ datacenter_tenants_dict["config"] = yaml.safe_dump(config, default_flow_style=True, width=256)
+ id_ = mydb.new_row('datacenter_tenants', datacenter_tenants_dict, add_uuid=True)
+ datacenter_tenants_dict["uuid"] = id_
+
+ #fill tenants_datacenters table
+ tenants_datacenter_dict["datacenter_tenant_id"]=datacenter_tenants_dict["uuid"]
+ mydb.new_row('tenants_datacenters', tenants_datacenter_dict)
+ # create thread
+ datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_dict['uuid'], datacenter_id) # reload data
+ thread_name = get_non_used_vim_name(datacenter_name, datacenter_id, tenant_dict['name'], tenant_dict['uuid'])
+ new_thread = vim_thread.vim_thread(myvim, task_lock, thread_name, datacenter_name, db=db, db_lock=db_lock)
+ new_thread.start()
+ thread_id = datacenter_id + "." + tenant_dict['uuid']
+ vim_threads["running"][thread_id] = new_thread
+ return datacenter_id
+
+
+def deassociate_datacenter_to_tenant(mydb, tenant_id, datacenter, vim_tenant_id=None):
+ #get datacenter info
+ datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, None, datacenter)
+
+ #get nfvo_tenant info
+ if not tenant_id or tenant_id=="any":
+ tenant_uuid = None
+ else:
+ tenant_dict = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id)
+ tenant_uuid = tenant_dict['uuid']
+
+ #check that this association exist before
+ tenants_datacenter_dict={"datacenter_id":datacenter_id }
+ if tenant_uuid:
+ tenants_datacenter_dict["nfvo_tenant_id"] = tenant_uuid
+ tenant_datacenter_list = mydb.get_rows(FROM='tenants_datacenters', WHERE=tenants_datacenter_dict)
+ if len(tenant_datacenter_list)==0 and tenant_uuid:
+ raise NfvoException("datacenter '{}' and tenant '{}' are not attached".format(datacenter_id, tenant_dict['uuid']), HTTP_Not_Found)
+
+ #delete this association
+ mydb.delete_row(FROM='tenants_datacenters', WHERE=tenants_datacenter_dict)
+
+ #get vim_tenant info and deletes
+ warning=''
+ for tenant_datacenter_item in tenant_datacenter_list:
+ vim_tenant_dict = mydb.get_table_by_uuid_name('datacenter_tenants', tenant_datacenter_item['datacenter_tenant_id'])
+ #try to delete vim:tenant
+ try:
+ mydb.delete_row_by_id('datacenter_tenants', tenant_datacenter_item['datacenter_tenant_id'])
+ if vim_tenant_dict['created']=='true':
+ #delete tenant at VIM if created by NFVO
+ try:
+ myvim.delete_tenant(vim_tenant_dict['vim_tenant_id'])
+ except vimconn.vimconnException as e:
+ warning = "Not possible to delete vim_tenant_id {} from VIM: {} ".format(vim_tenant_dict['vim_tenant_id'], str(e))
+ logger.warn(warning)
+ except db_base_Exception as e:
+ logger.error("Cannot delete datacenter_tenants " + str(e))
+ pass # the error will be caused because dependencies, vim_tenant can not be deleted
+ thread_id = datacenter_id + "." + tenant_datacenter_item["nfvo_tenant_id"]
+ thread = vim_threads["running"][thread_id]
+ thread.insert_task(new_task("exit", None, store=False))
+ vim_threads["deleting"][thread_id] = thread
+ return "datacenter {} detached. {}".format(datacenter_id, warning)
+
+
+def datacenter_action(mydb, tenant_id, datacenter, action_dict):
+ #DEPRECATED
+ #get datacenter info
+ datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
+
+ if 'net-update' in action_dict:
+ try:
+ nets = myvim.get_network_list(filter_dict={'shared': True, 'admin_state_up': True, 'status': 'ACTIVE'})
+ #print content
+ except vimconn.vimconnException as e:
+ #logger.error("nfvo.datacenter_action() Not possible to get_network_list from VIM: %s ", str(e))
+ raise NfvoException(str(e), HTTP_Internal_Server_Error)
+ #update nets Change from VIM format to NFVO format
+ net_list=[]
+ for net in nets:
+ net_nfvo={'datacenter_id': datacenter_id}
+ net_nfvo['name'] = net['name']
+ #net_nfvo['description']= net['name']
+ net_nfvo['vim_net_id'] = net['id']
+ net_nfvo['type'] = net['type'][0:6] #change from ('ptp','data','bridge_data','bridge_man') to ('bridge','data','ptp')
+ net_nfvo['shared'] = net['shared']
+ net_nfvo['multipoint'] = False if net['type']=='ptp' else True
+ net_list.append(net_nfvo)
+ inserted, deleted = mydb.update_datacenter_nets(datacenter_id, net_list)
+ logger.info("Inserted %d nets, deleted %d old nets", inserted, deleted)
+ return inserted
+ elif 'net-edit' in action_dict:
+ net = action_dict['net-edit'].pop('net')
+ what = 'vim_net_id' if utils.check_valid_uuid(net) else 'name'
+ result = mydb.update_rows('datacenter_nets', action_dict['net-edit'],
+ WHERE={'datacenter_id':datacenter_id, what: net})
+ return result
+ elif 'net-delete' in action_dict:
+ net = action_dict['net-deelte'].get('net')
+ what = 'vim_net_id' if utils.check_valid_uuid(net) else 'name'
+ result = mydb.delete_row(FROM='datacenter_nets',
+ WHERE={'datacenter_id':datacenter_id, what: net})
+ return result
+
+ else:
+ raise NfvoException("Unknown action " + str(action_dict), HTTP_Bad_Request)
+
+
+def datacenter_edit_netmap(mydb, tenant_id, datacenter, netmap, action_dict):
+ #get datacenter info
+ datacenter_id, _ = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
+
+ what = 'uuid' if utils.check_valid_uuid(netmap) else 'name'
+ result = mydb.update_rows('datacenter_nets', action_dict['netmap'],
+ WHERE={'datacenter_id':datacenter_id, what: netmap})
+ return result
+
+
+def datacenter_new_netmap(mydb, tenant_id, datacenter, action_dict=None):
+ #get datacenter info
+ datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
+ filter_dict={}
+ if action_dict:
+ action_dict = action_dict["netmap"]
+ if 'vim_id' in action_dict:
+ filter_dict["id"] = action_dict['vim_id']
+ if 'vim_name' in action_dict:
+ filter_dict["name"] = action_dict['vim_name']
+ else:
+ filter_dict["shared"] = True
+
+ try:
+ vim_nets = myvim.get_network_list(filter_dict=filter_dict)
+ except vimconn.vimconnException as e:
+ #logger.error("nfvo.datacenter_new_netmap() Not possible to get_network_list from VIM: %s ", str(e))
+ raise NfvoException(str(e), HTTP_Internal_Server_Error)
+ if len(vim_nets)>1 and action_dict:
+ raise NfvoException("more than two networks found, specify with vim_id", HTTP_Conflict)
+ elif len(vim_nets)==0: # and action_dict:
+ raise NfvoException("Not found a network at VIM with " + str(filter_dict), HTTP_Not_Found)
+ net_list=[]
+ for net in vim_nets:
+ net_nfvo={'datacenter_id': datacenter_id}
+ if action_dict and "name" in action_dict:
+ net_nfvo['name'] = action_dict['name']
+ else:
+ net_nfvo['name'] = net['name']
+ #net_nfvo['description']= net['name']
+ net_nfvo['vim_net_id'] = net['id']
+ net_nfvo['type'] = net['type'][0:6] #change from ('ptp','data','bridge_data','bridge_man') to ('bridge','data','ptp')
+ net_nfvo['shared'] = net['shared']
+ net_nfvo['multipoint'] = False if net['type']=='ptp' else True
+ try:
+ net_id = mydb.new_row("datacenter_nets", net_nfvo, add_uuid=True)
+ net_nfvo["status"] = "OK"
+ net_nfvo["uuid"] = net_id
+ except db_base_Exception as e:
+ if action_dict:
+ raise
+ else:
+ net_nfvo["status"] = "FAIL: " + str(e)
+ net_list.append(net_nfvo)
+ return net_list
+
+
+def vim_action_get(mydb, tenant_id, datacenter, item, name):
+ #get datacenter info
+ datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
+ filter_dict={}
+ if name:
+ if utils.check_valid_uuid(name):
+ filter_dict["id"] = name
+ else:
+ filter_dict["name"] = name
+ try:
+ if item=="networks":
+ #filter_dict['tenant_id'] = myvim['tenant_id']
+ content = myvim.get_network_list(filter_dict=filter_dict)
+ elif item=="tenants":
+ content = myvim.get_tenant_list(filter_dict=filter_dict)
+ elif item == "images":
+ content = myvim.get_image_list(filter_dict=filter_dict)
+ else:
+ raise NfvoException(item + "?", HTTP_Method_Not_Allowed)
+ logger.debug("vim_action response %s", content) #update nets Change from VIM format to NFVO format
+ if name and len(content)==1:
+ return {item[:-1]: content[0]}
+ elif name and len(content)==0:
+ raise NfvoException("No {} found with ".format(item[:-1]) + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), filter_dict.iteritems())),
+ datacenter)
+ else:
+ return {item: content}
+ except vimconn.vimconnException as e:
+ print "vim_action Not possible to get_%s_list from VIM: %s " % (item, str(e))
+ raise NfvoException("Not possible to get_{}_list from VIM: {}".format(item, str(e)), e.http_code)
+
+
+def vim_action_delete(mydb, tenant_id, datacenter, item, name):
+ #get datacenter info
+ if tenant_id == "any":
+ tenant_id=None
+
+ datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
+ #get uuid name
+ content = vim_action_get(mydb, tenant_id, datacenter, item, name)
+ logger.debug("vim_action_delete vim response: " + str(content))
+ items = content.values()[0]
+ if type(items)==list and len(items)==0:
+ raise NfvoException("Not found " + item, HTTP_Not_Found)
+ elif type(items)==list and len(items)>1:
+ raise NfvoException("Found more than one {} with this name. Use uuid.".format(item), HTTP_Not_Found)
+ else: # it is a dict
+ item_id = items["id"]
+ item_name = str(items.get("name"))
+
+ try:
+ if item=="networks":
+ content = myvim.delete_network(item_id)
+ elif item=="tenants":
+ content = myvim.delete_tenant(item_id)
+ elif item == "images":
+ content = myvim.delete_image(item_id)
+ else:
+ raise NfvoException(item + "?", HTTP_Method_Not_Allowed)
+ except vimconn.vimconnException as e:
+ #logger.error( "vim_action Not possible to delete_{} {}from VIM: {} ".format(item, name, str(e)))
+ raise NfvoException("Not possible to delete_{} {} from VIM: {}".format(item, name, str(e)), e.http_code)
+
+ return "{} {} {} deleted".format(item[:-1], item_id,item_name)
+
+
+def vim_action_create(mydb, tenant_id, datacenter, item, descriptor):
+ #get datacenter info
+ logger.debug("vim_action_create descriptor %s", str(descriptor))
+ if tenant_id == "any":
+ tenant_id=None
+ datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
+ try:
+ if item=="networks":
+ net = descriptor["network"]
+ net_name = net.pop("name")
+ net_type = net.pop("type", "bridge")
+ net_public = net.pop("shared", False)
+ net_ipprofile = net.pop("ip_profile", None)
+ net_vlan = net.pop("vlan", None)
+ content = myvim.new_network(net_name, net_type, net_ipprofile, shared=net_public, vlan=net_vlan) #, **net)
+ elif item=="tenants":
+ tenant = descriptor["tenant"]
+ content = myvim.new_tenant(tenant["name"], tenant.get("description"))
+ else:
+ raise NfvoException(item + "?", HTTP_Method_Not_Allowed)
+ except vimconn.vimconnException as e:
+ raise NfvoException("Not possible to create {} at VIM: {}".format(item, str(e)), e.http_code)
+
+ return vim_action_get(mydb, tenant_id, datacenter, item, content)
--- /dev/null
+# -*- 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
+##
+
+'''
+NFVO DB engine. It implements all the methods to interact with the Openmano Database
+'''
+__author__="Alfonso Tierno, Gerardo Garcia, Pablo Montes"
+__date__ ="$28-aug-2014 10:05:01$"
+
+import db_base
+import MySQLdb as mdb
+import json
+import yaml
+import time
+#import sys, os
+
+tables_with_createdat_field=["datacenters","instance_nets","instance_scenarios","instance_vms","instance_vnfs",
+ "interfaces","nets","nfvo_tenants","scenarios","sce_interfaces","sce_nets",
+ "sce_vnfs","tenants_datacenters","datacenter_tenants","vms","vnfs", "datacenter_nets"]
+
+class nfvo_db(db_base.db_base):
+ def __init__(self, host=None, user=None, passwd=None, database=None, log_name='openmano.db', log_level=None):
+ db_base.db_base.__init__(self, host, user, passwd, database, log_name, log_level)
+ db_base.db_base.tables_with_created_field=tables_with_createdat_field
+ return
+
+ def new_vnf_as_a_whole(self,nfvo_tenant,vnf_name,vnf_descriptor,VNFCDict):
+ self.logger.debug("Adding new vnf to the NFVO database")
+ tries = 2
+ while tries:
+ created_time = time.time()
+ try:
+ with self.con:
+
+ myVNFDict = {}
+ myVNFDict["name"] = vnf_name
+ myVNFDict["descriptor"] = vnf_descriptor['vnf'].get('descriptor')
+ myVNFDict["public"] = vnf_descriptor['vnf'].get('public', "false")
+ myVNFDict["description"] = vnf_descriptor['vnf']['description']
+ myVNFDict["class"] = vnf_descriptor['vnf'].get('class',"MISC")
+ myVNFDict["tenant_id"] = vnf_descriptor['vnf'].get("tenant_id")
+
+ vnf_id = self._new_row_internal('vnfs', myVNFDict, add_uuid=True, root_uuid=None, created_time=created_time)
+ #print "Adding new vms to the NFVO database"
+ #For each vm, we must create the appropriate vm in the NFVO database.
+ vmDict = {}
+ for _,vm in VNFCDict.iteritems():
+ #This code could make the name of the vms grow and grow.
+ #If we agree to follow this convention, we should check with a regex that the vnfc name is not including yet the vnf name
+ #vm['name'] = "%s-%s" % (vnf_name,vm['name'])
+ #print "VM name: %s. Description: %s" % (vm['name'], vm['description'])
+ vm["vnf_id"] = vnf_id
+ created_time += 0.00001
+ vm_id = self._new_row_internal('vms', vm, add_uuid=True, root_uuid=vnf_id, created_time=created_time)
+ #print "Internal vm id in NFVO DB: %s" % vm_id
+ vmDict[vm['name']] = vm_id
+
+ #Collect the bridge interfaces of each VM/VNFC under the 'bridge-ifaces' field
+ bridgeInterfacesDict = {}
+ for vm in vnf_descriptor['vnf']['VNFC']:
+ if 'bridge-ifaces' in vm:
+ bridgeInterfacesDict[vm['name']] = {}
+ for bridgeiface in vm['bridge-ifaces']:
+ created_time += 0.00001
+ if 'port-security' in bridgeiface:
+ bridgeiface['port_security'] = bridgeiface.pop('port-security')
+ if 'floating-ip' in bridgeiface:
+ bridgeiface['floating_ip'] = bridgeiface.pop('floating-ip')
+ db_base._convert_bandwidth(bridgeiface, logger=self.logger)
+ bridgeInterfacesDict[vm['name']][bridgeiface['name']] = {}
+ bridgeInterfacesDict[vm['name']][bridgeiface['name']]['vpci'] = bridgeiface.get('vpci',None)
+ bridgeInterfacesDict[vm['name']][bridgeiface['name']]['mac'] = bridgeiface.get('mac_address',None)
+ bridgeInterfacesDict[vm['name']][bridgeiface['name']]['bw'] = bridgeiface.get('bandwidth', None)
+ bridgeInterfacesDict[vm['name']][bridgeiface['name']]['model'] = bridgeiface.get('model', None)
+ bridgeInterfacesDict[vm['name']][bridgeiface['name']]['port_security'] = \
+ int(bridgeiface.get('port_security', True))
+ bridgeInterfacesDict[vm['name']][bridgeiface['name']]['floating_ip'] = \
+ int(bridgeiface.get('floating_ip', False))
+ bridgeInterfacesDict[vm['name']][bridgeiface['name']]['created_time'] = created_time
+
+ # Collect the data interfaces of each VM/VNFC under the 'numas' field
+ dataifacesDict = {}
+ for vm in vnf_descriptor['vnf']['VNFC']:
+ dataifacesDict[vm['name']] = {}
+ for numa in vm.get('numas', []):
+ for dataiface in numa.get('interfaces', []):
+ created_time += 0.00001
+ db_base._convert_bandwidth(dataiface, logger=self.logger)
+ dataifacesDict[vm['name']][dataiface['name']] = {}
+ dataifacesDict[vm['name']][dataiface['name']]['vpci'] = dataiface['vpci']
+ dataifacesDict[vm['name']][dataiface['name']]['bw'] = dataiface['bandwidth']
+ dataifacesDict[vm['name']][dataiface['name']]['model'] = "PF" if dataiface[
+ 'dedicated'] == "yes" else (
+ "VF" if dataiface['dedicated'] == "no" else "VFnotShared")
+ dataifacesDict[vm['name']][dataiface['name']]['created_time'] = created_time
+
+ #For each internal connection, we add it to the interfaceDict and we create the appropriate net in the NFVO database.
+ #print "Adding new nets (VNF internal nets) to the NFVO database (if any)"
+ internalconnList = []
+ if 'internal-connections' in vnf_descriptor['vnf']:
+ for net in vnf_descriptor['vnf']['internal-connections']:
+ #print "Net name: %s. Description: %s" % (net['name'], net['description'])
+
+ myNetDict = {}
+ myNetDict["name"] = net['name']
+ myNetDict["description"] = net['description']
+ myNetDict["type"] = net['type']
+ myNetDict["vnf_id"] = vnf_id
+
+ created_time += 0.00001
+ net_id = self._new_row_internal('nets', myNetDict, add_uuid=True, root_uuid=vnf_id, created_time=created_time)
+
+ for element in net['elements']:
+ ifaceItem = {}
+ #ifaceItem["internal_name"] = "%s-%s-%s" % (net['name'],element['VNFC'], element['local_iface_name'])
+ ifaceItem["internal_name"] = element['local_iface_name']
+ #ifaceItem["vm_id"] = vmDict["%s-%s" % (vnf_name,element['VNFC'])]
+ ifaceItem["vm_id"] = vmDict[element['VNFC']]
+ ifaceItem["net_id"] = net_id
+ ifaceItem["type"] = net['type']
+ if ifaceItem ["type"] == "data":
+ dataiface = dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ]
+ ifaceItem["vpci"] = dataiface['vpci']
+ ifaceItem["bw"] = dataiface['bw']
+ ifaceItem["model"] = dataiface['model']
+ created_time_iface = dataiface['created_time']
+ else:
+ bridgeiface = bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]
+ ifaceItem["vpci"] = bridgeiface['vpci']
+ ifaceItem["mac"] = bridgeiface['mac']
+ ifaceItem["bw"] = bridgeiface['bw']
+ ifaceItem["model"] = bridgeiface['model']
+ ifaceItem["port_security"] = bridgeiface['port_security']
+ ifaceItem["floating_ip"] = bridgeiface['floating_ip']
+ created_time_iface = bridgeiface['created_time']
+ internalconnList.append(ifaceItem)
+ #print "Internal net id in NFVO DB: %s" % net_id
+
+ #print "Adding internal interfaces to the NFVO database (if any)"
+ for iface in internalconnList:
+ #print "Iface name: %s" % iface['internal_name']
+ iface_id = self._new_row_internal('interfaces', iface, add_uuid=True, root_uuid=vnf_id, created_time = created_time_iface)
+ #print "Iface id in NFVO DB: %s" % iface_id
+
+ #print "Adding external interfaces to the NFVO database"
+ for iface in vnf_descriptor['vnf']['external-connections']:
+ myIfaceDict = {}
+ #myIfaceDict["internal_name"] = "%s-%s-%s" % (vnf_name,iface['VNFC'], iface['local_iface_name'])
+ myIfaceDict["internal_name"] = iface['local_iface_name']
+ #myIfaceDict["vm_id"] = vmDict["%s-%s" % (vnf_name,iface['VNFC'])]
+ myIfaceDict["vm_id"] = vmDict[iface['VNFC']]
+ myIfaceDict["external_name"] = iface['name']
+ myIfaceDict["type"] = iface['type']
+ if iface["type"] == "data":
+ dataiface = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]
+ myIfaceDict["vpci"] = dataiface['vpci']
+ myIfaceDict["bw"] = dataiface['bw']
+ myIfaceDict["model"] = dataiface['model']
+ created_time_iface = dataiface['created_time']
+ else:
+ bridgeiface = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]
+ myIfaceDict["vpci"] = bridgeiface['vpci']
+ myIfaceDict["bw"] = bridgeiface['bw']
+ myIfaceDict["model"] = bridgeiface['model']
+ myIfaceDict["mac"] = bridgeiface['mac']
+ myIfaceDict["port_security"]= bridgeiface['port_security']
+ myIfaceDict["floating_ip"] = bridgeiface['floating_ip']
+ created_time_iface = bridgeiface['created_time']
+ #print "Iface name: %s" % iface['name']
+ iface_id = self._new_row_internal('interfaces', myIfaceDict, add_uuid=True, root_uuid=vnf_id, created_time = created_time_iface)
+ #print "Iface id in NFVO DB: %s" % iface_id
+
+ return vnf_id
+
+ except (mdb.Error, AttributeError) as e:
+ self._format_error(e, tries)
+ tries -= 1
+
+ def new_vnf_as_a_whole2(self,nfvo_tenant,vnf_name,vnf_descriptor,VNFCDict):
+ self.logger.debug("Adding new vnf to the NFVO database")
+ tries = 2
+ while tries:
+ created_time = time.time()
+ try:
+ with self.con:
+
+ myVNFDict = {}
+ myVNFDict["name"] = vnf_name
+ myVNFDict["descriptor"] = vnf_descriptor['vnf'].get('descriptor')
+ myVNFDict["public"] = vnf_descriptor['vnf'].get('public', "false")
+ myVNFDict["description"] = vnf_descriptor['vnf']['description']
+ myVNFDict["class"] = vnf_descriptor['vnf'].get('class',"MISC")
+ myVNFDict["tenant_id"] = vnf_descriptor['vnf'].get("tenant_id")
+
+ vnf_id = self._new_row_internal('vnfs', myVNFDict, add_uuid=True, root_uuid=None, created_time=created_time)
+ #print "Adding new vms to the NFVO database"
+ #For each vm, we must create the appropriate vm in the NFVO database.
+ vmDict = {}
+ for _,vm in VNFCDict.iteritems():
+ #This code could make the name of the vms grow and grow.
+ #If we agree to follow this convention, we should check with a regex that the vnfc name is not including yet the vnf name
+ #vm['name'] = "%s-%s" % (vnf_name,vm['name'])
+ #print "VM name: %s. Description: %s" % (vm['name'], vm['description'])
+ vm["vnf_id"] = vnf_id
+ created_time += 0.00001
+ vm_id = self._new_row_internal('vms', vm, add_uuid=True, root_uuid=vnf_id, created_time=created_time)
+ #print "Internal vm id in NFVO DB: %s" % vm_id
+ vmDict[vm['name']] = vm_id
+
+ #Collect the bridge interfaces of each VM/VNFC under the 'bridge-ifaces' field
+ bridgeInterfacesDict = {}
+ for vm in vnf_descriptor['vnf']['VNFC']:
+ if 'bridge-ifaces' in vm:
+ bridgeInterfacesDict[vm['name']] = {}
+ for bridgeiface in vm['bridge-ifaces']:
+ created_time += 0.00001
+ db_base._convert_bandwidth(bridgeiface, logger=self.logger)
+ if 'port-security' in bridgeiface:
+ bridgeiface['port_security'] = bridgeiface.pop('port-security')
+ if 'floating-ip' in bridgeiface:
+ bridgeiface['floating_ip'] = bridgeiface.pop('floating-ip')
+ ifaceDict = {}
+ ifaceDict['vpci'] = bridgeiface.get('vpci',None)
+ ifaceDict['mac'] = bridgeiface.get('mac_address',None)
+ ifaceDict['bw'] = bridgeiface.get('bandwidth', None)
+ ifaceDict['model'] = bridgeiface.get('model', None)
+ ifaceDict['port_security'] = int(bridgeiface.get('port_security', True))
+ ifaceDict['floating_ip'] = int(bridgeiface.get('floating_ip', False))
+ ifaceDict['created_time'] = created_time
+ bridgeInterfacesDict[vm['name']][bridgeiface['name']] = ifaceDict
+
+ # Collect the data interfaces of each VM/VNFC under the 'numas' field
+ dataifacesDict = {}
+ for vm in vnf_descriptor['vnf']['VNFC']:
+ dataifacesDict[vm['name']] = {}
+ for numa in vm.get('numas', []):
+ for dataiface in numa.get('interfaces', []):
+ created_time += 0.00001
+ db_base._convert_bandwidth(dataiface, logger=self.logger)
+ ifaceDict = {}
+ ifaceDict['vpci'] = dataiface['vpci']
+ ifaceDict['bw'] = dataiface['bandwidth']
+ ifaceDict['model'] = "PF" if dataiface['dedicated'] == "yes" else \
+ ("VF" if dataiface['dedicated'] == "no" else "VFnotShared")
+ ifaceDict['created_time'] = created_time
+ dataifacesDict[vm['name']][dataiface['name']] = ifaceDict
+
+ #For each internal connection, we add it to the interfaceDict and we create the appropriate net in the NFVO database.
+ #print "Adding new nets (VNF internal nets) to the NFVO database (if any)"
+ if 'internal-connections' in vnf_descriptor['vnf']:
+ for net in vnf_descriptor['vnf']['internal-connections']:
+ #print "Net name: %s. Description: %s" % (net['name'], net['description'])
+
+ myNetDict = {}
+ myNetDict["name"] = net['name']
+ myNetDict["description"] = net['description']
+ if (net["implementation"] == "overlay"):
+ net["type"] = "bridge"
+ #It should give an error if the type is e-line. For the moment, we consider it as a bridge
+ elif (net["implementation"] == "underlay"):
+ if (net["type"] == "e-line"):
+ net["type"] = "ptp"
+ elif (net["type"] == "e-lan"):
+ net["type"] = "data"
+ net.pop("implementation")
+ myNetDict["type"] = net['type']
+ myNetDict["vnf_id"] = vnf_id
+
+ created_time += 0.00001
+ net_id = self._new_row_internal('nets', myNetDict, add_uuid=True, root_uuid=vnf_id, created_time=created_time)
+
+ if "ip-profile" in net:
+ ip_profile = net["ip-profile"]
+ myIPProfileDict = {}
+ myIPProfileDict["net_id"] = net_id
+ myIPProfileDict["ip_version"] = ip_profile.get('ip-version',"IPv4")
+ myIPProfileDict["subnet_address"] = ip_profile.get('subnet-address',None)
+ myIPProfileDict["gateway_address"] = ip_profile.get('gateway-address',None)
+ myIPProfileDict["dns_address"] = ip_profile.get('dns-address',None)
+ if ("dhcp" in ip_profile):
+ myIPProfileDict["dhcp_enabled"] = ip_profile["dhcp"].get('enabled',"true")
+ myIPProfileDict["dhcp_start_address"] = ip_profile["dhcp"].get('start-address',None)
+ myIPProfileDict["dhcp_count"] = ip_profile["dhcp"].get('count',None)
+
+ created_time += 0.00001
+ ip_profile_id = self._new_row_internal('ip_profiles', myIPProfileDict)
+
+ for element in net['elements']:
+ ifaceItem = {}
+ #ifaceItem["internal_name"] = "%s-%s-%s" % (net['name'],element['VNFC'], element['local_iface_name'])
+ ifaceItem["internal_name"] = element['local_iface_name']
+ #ifaceItem["vm_id"] = vmDict["%s-%s" % (vnf_name,element['VNFC'])]
+ ifaceItem["vm_id"] = vmDict[element['VNFC']]
+ ifaceItem["net_id"] = net_id
+ ifaceItem["type"] = net['type']
+ ifaceItem["ip_address"] = element.get('ip_address',None)
+ if ifaceItem ["type"] == "data":
+ ifaceDict = dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ]
+ ifaceItem["vpci"] = ifaceDict['vpci']
+ ifaceItem["bw"] = ifaceDict['bw']
+ ifaceItem["model"] = ifaceDict['model']
+ else:
+ ifaceDict = bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]
+ ifaceItem["vpci"] = ifaceDict['vpci']
+ ifaceItem["mac"] = ifaceDict['mac']
+ ifaceItem["bw"] = ifaceDict['bw']
+ ifaceItem["model"] = ifaceDict['model']
+ ifaceItem["port_security"] = ifaceDict['port_security']
+ ifaceItem["floating_ip"] = ifaceDict['floating_ip']
+ created_time_iface = ifaceDict["created_time"]
+ #print "Iface name: %s" % iface['internal_name']
+ iface_id = self._new_row_internal('interfaces', ifaceItem, add_uuid=True, root_uuid=vnf_id, created_time=created_time_iface)
+ #print "Iface id in NFVO DB: %s" % iface_id
+
+ #print "Adding external interfaces to the NFVO database"
+ for iface in vnf_descriptor['vnf']['external-connections']:
+ myIfaceDict = {}
+ #myIfaceDict["internal_name"] = "%s-%s-%s" % (vnf_name,iface['VNFC'], iface['local_iface_name'])
+ myIfaceDict["internal_name"] = iface['local_iface_name']
+ #myIfaceDict["vm_id"] = vmDict["%s-%s" % (vnf_name,iface['VNFC'])]
+ myIfaceDict["vm_id"] = vmDict[iface['VNFC']]
+ myIfaceDict["external_name"] = iface['name']
+ myIfaceDict["type"] = iface['type']
+ if iface["type"] == "data":
+ myIfaceDict["vpci"] = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['vpci']
+ myIfaceDict["bw"] = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['bw']
+ myIfaceDict["model"] = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['model']
+ created_time_iface = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['created_time']
+ else:
+ myIfaceDict["vpci"] = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['vpci']
+ myIfaceDict["bw"] = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['bw']
+ myIfaceDict["model"] = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['model']
+ myIfaceDict["mac"] = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['mac']
+ myIfaceDict["port_security"] = \
+ bridgeInterfacesDict[iface['VNFC']][iface['local_iface_name']]['port_security']
+ myIfaceDict["floating_ip"] = \
+ bridgeInterfacesDict[iface['VNFC']][iface['local_iface_name']]['floating_ip']
+ created_time_iface = bridgeInterfacesDict[iface['VNFC']][iface['local_iface_name']]['created_time']
+ #print "Iface name: %s" % iface['name']
+ iface_id = self._new_row_internal('interfaces', myIfaceDict, add_uuid=True, root_uuid=vnf_id, created_time=created_time_iface)
+ #print "Iface id in NFVO DB: %s" % iface_id
+
+ return vnf_id
+
+ except (mdb.Error, AttributeError) as e:
+ self._format_error(e, tries)
+# except KeyError as e2:
+# exc_type, exc_obj, exc_tb = sys.exc_info()
+# fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
+# self.logger.debug("Exception type: %s; Filename: %s; Line number: %s", exc_type, fname, exc_tb.tb_lineno)
+# raise KeyError
+ tries -= 1
+
+ def new_scenario(self, scenario_dict):
+ tries = 2
+ while tries:
+ created_time = time.time()
+ try:
+ with self.con:
+ self.cur = self.con.cursor()
+ tenant_id = scenario_dict.get('tenant_id')
+ #scenario
+ INSERT_={'tenant_id': tenant_id,
+ 'name': scenario_dict['name'],
+ 'description': scenario_dict['description'],
+ 'public': scenario_dict.get('public', "false")}
+
+ scenario_uuid = self._new_row_internal('scenarios', INSERT_, add_uuid=True, root_uuid=None, created_time=created_time)
+ #sce_nets
+ for net in scenario_dict['nets'].values():
+ net_dict={'scenario_id': scenario_uuid}
+ net_dict["name"] = net["name"]
+ net_dict["type"] = net["type"]
+ net_dict["description"] = net.get("description")
+ net_dict["external"] = net.get("external", False)
+ if "graph" in net:
+ #net["graph"]=yaml.safe_dump(net["graph"],default_flow_style=True,width=256)
+ #TODO, must be json because of the GUI, change to yaml
+ net_dict["graph"]=json.dumps(net["graph"])
+ created_time += 0.00001
+ net_uuid = self._new_row_internal('sce_nets', net_dict, add_uuid=True, root_uuid=scenario_uuid, created_time=created_time)
+ net['uuid']=net_uuid
+
+ if net.get("ip-profile"):
+ ip_profile = net["ip-profile"]
+ myIPProfileDict = {
+ "sce_net_id": net_uuid,
+ "ip_version": ip_profile.get('ip-version', "IPv4"),
+ "subnet_address": ip_profile.get('subnet-address'),
+ "gateway_address": ip_profile.get('gateway-address'),
+ "dns_address": ip_profile.get('dns-address')}
+ if "dhcp" in ip_profile:
+ myIPProfileDict["dhcp_enabled"] = ip_profile["dhcp"].get('enabled', "true")
+ myIPProfileDict["dhcp_start_address"] = ip_profile["dhcp"].get('start-address')
+ myIPProfileDict["dhcp_count"] = ip_profile["dhcp"].get('count')
+ self._new_row_internal('ip_profiles', myIPProfileDict)
+
+ # sce_vnfs
+ for k, vnf in scenario_dict['vnfs'].items():
+ INSERT_ = {'scenario_id': scenario_uuid,
+ 'name': k,
+ 'vnf_id': vnf['uuid'],
+ # 'description': scenario_dict['name']
+ 'description': vnf['description']}
+ if "graph" in vnf:
+ #I NSERT_["graph"]=yaml.safe_dump(vnf["graph"],default_flow_style=True,width=256)
+ # TODO, must be json because of the GUI, change to yaml
+ INSERT_["graph"] = json.dumps(vnf["graph"])
+ created_time += 0.00001
+ scn_vnf_uuid = self._new_row_internal('sce_vnfs', INSERT_, add_uuid=True,
+ root_uuid=scenario_uuid, created_time=created_time)
+ vnf['scn_vnf_uuid']=scn_vnf_uuid
+ # sce_interfaces
+ for iface in vnf['ifaces'].values():
+ # print 'iface', iface
+ if 'net_key' not in iface:
+ continue
+ iface['net_id'] = scenario_dict['nets'][ iface['net_key'] ]['uuid']
+ INSERT_={'sce_vnf_id': scn_vnf_uuid,
+ 'sce_net_id': iface['net_id'],
+ 'interface_id': iface['uuid'],
+ 'ip_address': iface.get('ip_address')}
+ created_time += 0.00001
+ iface_uuid = self._new_row_internal('sce_interfaces', INSERT_, add_uuid=True,
+ root_uuid=scenario_uuid, created_time=created_time)
+
+ return scenario_uuid
+
+ except (mdb.Error, AttributeError) as e:
+ self._format_error(e, tries)
+ tries -= 1
+
+ def edit_scenario(self, scenario_dict):
+ tries = 2
+ while tries:
+ modified_time = time.time()
+ item_changed=0
+ try:
+ with self.con:
+ self.cur = self.con.cursor()
+ #check that scenario exist
+ tenant_id = scenario_dict.get('tenant_id')
+ scenario_uuid = scenario_dict['uuid']
+
+ where_text = "uuid='{}'".format(scenario_uuid)
+ if not tenant_id and tenant_id != "any":
+ where_text += " AND (tenant_id='{}' OR public='True')".format(tenant_id)
+ cmd = "SELECT * FROM scenarios WHERE "+ where_text
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ self.cur.fetchall()
+ if self.cur.rowcount==0:
+ raise db_base.db_base_Exception("No scenario found with this criteria " + where_text, db_base.HTTP_Bad_Request)
+ elif self.cur.rowcount>1:
+ raise db_base.db_base_Exception("More than one scenario found with this criteria " + where_text, db_base.HTTP_Bad_Request)
+
+ #scenario
+ nodes = {}
+ topology = scenario_dict.pop("topology", None)
+ if topology != None and "nodes" in topology:
+ nodes = topology.get("nodes",{})
+ UPDATE_ = {}
+ if "name" in scenario_dict: UPDATE_["name"] = scenario_dict["name"]
+ if "description" in scenario_dict: UPDATE_["description"] = scenario_dict["description"]
+ if len(UPDATE_)>0:
+ WHERE_={'tenant_id': tenant_id, 'uuid': scenario_uuid}
+ item_changed += self._update_rows('scenarios', UPDATE_, WHERE_, modified_time=modified_time)
+ #sce_nets
+ for node_id, node in nodes.items():
+ if "graph" in node:
+ #node["graph"] = yaml.safe_dump(node["graph"],default_flow_style=True,width=256)
+ #TODO, must be json because of the GUI, change to yaml
+ node["graph"] = json.dumps(node["graph"])
+ WHERE_={'scenario_id': scenario_uuid, 'uuid': node_id}
+ #Try to change at sce_nets(version 0 API backward compatibility and sce_vnfs)
+ item_changed += self._update_rows('sce_nets', node, WHERE_)
+ item_changed += self._update_rows('sce_vnfs', node, WHERE_, modified_time=modified_time)
+ return item_changed
+
+ except (mdb.Error, AttributeError) as e:
+ self._format_error(e, tries)
+ tries -= 1
+
+# def get_instance_scenario(self, instance_scenario_id, tenant_id=None):
+# '''Obtain the scenario instance information, filtering by one or serveral of the tenant, uuid or name
+# instance_scenario_id is the uuid or the name if it is not a valid uuid format
+# Only one scenario isntance must mutch the filtering or an error is returned
+# '''
+# print "1******************************************************************"
+# try:
+# with self.con:
+# self.cur = self.con.cursor(mdb.cursors.DictCursor)
+# #scenario table
+# where_list=[]
+# if tenant_id is not None: where_list.append( "tenant_id='" + tenant_id +"'" )
+# if db_base._check_valid_uuid(instance_scenario_id):
+# where_list.append( "uuid='" + instance_scenario_id +"'" )
+# else:
+# where_list.append( "name='" + instance_scenario_id +"'" )
+# where_text = " AND ".join(where_list)
+# self.cur.execute("SELECT * FROM instance_scenarios WHERE "+ where_text)
+# rows = self.cur.fetchall()
+# if self.cur.rowcount==0:
+# return -HTTP_Bad_Request, "No scenario instance found with this criteria " + where_text
+# elif self.cur.rowcount>1:
+# return -HTTP_Bad_Request, "More than one scenario instance found with this criteria " + where_text
+# instance_scenario_dict = rows[0]
+#
+# #instance_vnfs
+# self.cur.execute("SELECT uuid,vnf_id FROM instance_vnfs WHERE instance_scenario_id='"+ instance_scenario_dict['uuid'] + "'")
+# instance_scenario_dict['instance_vnfs'] = self.cur.fetchall()
+# for vnf in instance_scenario_dict['instance_vnfs']:
+# #instance_vms
+# self.cur.execute("SELECT uuid, vim_vm_id "+
+# "FROM instance_vms "+
+# "WHERE instance_vnf_id='" + vnf['uuid'] +"'"
+# )
+# vnf['instance_vms'] = self.cur.fetchall()
+# #instance_nets
+# self.cur.execute("SELECT uuid, vim_net_id FROM instance_nets WHERE instance_scenario_id='"+ instance_scenario_dict['uuid'] + "'")
+# instance_scenario_dict['instance_nets'] = self.cur.fetchall()
+#
+# #instance_interfaces
+# self.cur.execute("SELECT uuid, vim_interface_id, instance_vm_id, instance_net_id FROM instance_interfaces WHERE instance_scenario_id='"+ instance_scenario_dict['uuid'] + "'")
+# instance_scenario_dict['instance_interfaces'] = self.cur.fetchall()
+#
+# db_base._convert_datetime2str(instance_scenario_dict)
+# db_base._convert_str2boolean(instance_scenario_dict, ('public','shared','external') )
+# print "2******************************************************************"
+# return 1, instance_scenario_dict
+# except (mdb.Error, AttributeError) as e:
+# print "nfvo_db.get_instance_scenario DB Exception %d: %s" % (e.args[0], e.args[1])
+# return self._format_error(e)
+
+ def get_scenario(self, scenario_id, tenant_id=None, datacenter_id=None):
+ '''Obtain the scenario information, filtering by one or serveral of the tenant, uuid or name
+ scenario_id is the uuid or the name if it is not a valid uuid format
+ if datacenter_id is provided, it supply aditional vim_id fields with the matching vim uuid
+ Only one scenario must mutch the filtering or an error is returned
+ '''
+ tries = 2
+ while tries:
+ try:
+ with self.con:
+ self.cur = self.con.cursor(mdb.cursors.DictCursor)
+ where_text = "uuid='{}'".format(scenario_id)
+ if not tenant_id and tenant_id != "any":
+ where_text += " AND (tenant_id='{}' OR public='True')".format(tenant_id)
+ cmd = "SELECT * FROM scenarios WHERE " + where_text
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ rows = self.cur.fetchall()
+ if self.cur.rowcount==0:
+ raise db_base.db_base_Exception("No scenario found with this criteria " + where_text, db_base.HTTP_Bad_Request)
+ elif self.cur.rowcount>1:
+ raise db_base.db_base_Exception("More than one scenario found with this criteria " + where_text, db_base.HTTP_Bad_Request)
+ scenario_dict = rows[0]
+ if scenario_dict["cloud_config"]:
+ scenario_dict["cloud-config"] = yaml.load(scenario_dict["cloud_config"])
+ del scenario_dict["cloud_config"]
+ #sce_vnfs
+ cmd = "SELECT uuid,name,vnf_id,description FROM sce_vnfs WHERE scenario_id='{}' ORDER BY created_at".format(scenario_dict['uuid'])
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ scenario_dict['vnfs'] = self.cur.fetchall()
+ for vnf in scenario_dict['vnfs']:
+ #sce_interfaces
+ cmd = "SELECT scei.uuid,scei.sce_net_id,scei.interface_id,i.external_name,scei.ip_address FROM sce_interfaces as scei join interfaces as i on scei.interface_id=i.uuid WHERE scei.sce_vnf_id='{}' ORDER BY scei.created_at".format(vnf['uuid'])
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ vnf['interfaces'] = self.cur.fetchall()
+ #vms
+ cmd = "SELECT vms.uuid as uuid, flavor_id, image_id, vms.name as name, vms.description as description, vms.boot_data as boot_data " \
+ " FROM vnfs join vms on vnfs.uuid=vms.vnf_id " \
+ " WHERE vnfs.uuid='" + vnf['vnf_id'] +"'" \
+ " ORDER BY vms.created_at"
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ vnf['vms'] = self.cur.fetchall()
+ for vm in vnf['vms']:
+ if vm["boot_data"]:
+ vm["boot_data"] = yaml.safe_load(vm["boot_data"])
+ else:
+ del vm["boot_data"]
+ if datacenter_id!=None:
+ cmd = "SELECT vim_id FROM datacenters_images WHERE image_id='{}' AND datacenter_id='{}'".format(vm['image_id'],datacenter_id)
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ if self.cur.rowcount==1:
+ vim_image_dict = self.cur.fetchone()
+ vm['vim_image_id']=vim_image_dict['vim_id']
+ cmd = "SELECT vim_id FROM datacenters_flavors WHERE flavor_id='{}' AND datacenter_id='{}'".format(vm['flavor_id'],datacenter_id)
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ if self.cur.rowcount==1:
+ vim_flavor_dict = self.cur.fetchone()
+ vm['vim_flavor_id']=vim_flavor_dict['vim_id']
+
+ #interfaces
+ cmd = "SELECT uuid,internal_name,external_name,net_id,type,vpci,mac,bw,model,ip_address," \
+ "floating_ip, port_security" \
+ " FROM interfaces" \
+ " WHERE vm_id='{}'" \
+ " ORDER BY created_at".format(vm['uuid'])
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ vm['interfaces'] = self.cur.fetchall()
+ for index in range(0,len(vm['interfaces'])):
+ vm['interfaces'][index]['port-security'] = vm['interfaces'][index].pop("port_security")
+ vm['interfaces'][index]['floating-ip'] = vm['interfaces'][index].pop("floating_ip")
+ #nets every net of a vms
+ cmd = "SELECT uuid,name,type,description FROM nets WHERE vnf_id='{}'".format(vnf['vnf_id'])
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ vnf['nets'] = self.cur.fetchall()
+ for vnf_net in vnf['nets']:
+ SELECT_ = "ip_version,subnet_address,gateway_address,dns_address,dhcp_enabled,dhcp_start_address,dhcp_count"
+ cmd = "SELECT {} FROM ip_profiles WHERE net_id='{}'".format(SELECT_,vnf_net['uuid'])
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ ipprofiles = self.cur.fetchall()
+ if self.cur.rowcount==1:
+ vnf_net["ip_profile"] = ipprofiles[0]
+ elif self.cur.rowcount>1:
+ raise db_base.db_base_Exception("More than one ip-profile found with this criteria: net_id='{}'".format(vnf_net['uuid']), db_base.HTTP_Bad_Request)
+
+ #sce_nets
+ cmd = "SELECT uuid,name,type,external,description" \
+ " FROM sce_nets WHERE scenario_id='{}'" \
+ " ORDER BY created_at ".format(scenario_dict['uuid'])
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ scenario_dict['nets'] = self.cur.fetchall()
+ #datacenter_nets
+ for net in scenario_dict['nets']:
+ if str(net['external']) == 'false':
+ SELECT_ = "ip_version,subnet_address,gateway_address,dns_address,dhcp_enabled,dhcp_start_address,dhcp_count"
+ cmd = "SELECT {} FROM ip_profiles WHERE sce_net_id='{}'".format(SELECT_,net['uuid'])
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ ipprofiles = self.cur.fetchall()
+ if self.cur.rowcount==1:
+ net["ip_profile"] = ipprofiles[0]
+ elif self.cur.rowcount>1:
+ raise db_base.db_base_Exception("More than one ip-profile found with this criteria: sce_net_id='{}'".format(net['uuid']), db_base.HTTP_Bad_Request)
+ continue
+ WHERE_=" WHERE name='{}'".format(net['name'])
+ if datacenter_id!=None:
+ WHERE_ += " AND datacenter_id='{}'".format(datacenter_id)
+ cmd = "SELECT vim_net_id FROM datacenter_nets" + WHERE_
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ d_net = self.cur.fetchone()
+ if d_net==None or datacenter_id==None:
+ #print "nfvo_db.get_scenario() WARNING external net %s not found" % net['name']
+ net['vim_id']=None
+ else:
+ net['vim_id']=d_net['vim_net_id']
+
+ db_base._convert_datetime2str(scenario_dict)
+ db_base._convert_str2boolean(scenario_dict, ('public','shared','external','port-security','floating-ip') )
+ return scenario_dict
+ except (mdb.Error, AttributeError) as e:
+ self._format_error(e, tries)
+ tries -= 1
+
+ def delete_scenario(self, scenario_id, tenant_id=None):
+ '''Deletes a scenario, filtering by one or several of the tenant, uuid or name
+ scenario_id is the uuid or the name if it is not a valid uuid format
+ Only one scenario must mutch the filtering or an error is returned
+ '''
+ tries = 2
+ while tries:
+ try:
+ with self.con:
+ self.cur = self.con.cursor(mdb.cursors.DictCursor)
+
+ #scenario table
+ where_text = "uuid='{}'".format(scenario_id)
+ if not tenant_id and tenant_id != "any":
+ where_text += " AND (tenant_id='{}' OR public='True')".format(tenant_id)
+ cmd = "SELECT * FROM scenarios WHERE "+ where_text
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ rows = self.cur.fetchall()
+ if self.cur.rowcount==0:
+ raise db_base.db_base_Exception("No scenario found where " + where_text, db_base.HTTP_Bad_Request)
+ elif self.cur.rowcount>1:
+ raise db_base.db_base_Exception("More than one scenario found where " + where_text, db_base.HTTP_Bad_Request)
+ scenario_uuid = rows[0]["uuid"]
+ scenario_name = rows[0]["name"]
+
+ #sce_vnfs
+ cmd = "DELETE FROM scenarios WHERE uuid='{}'".format(scenario_uuid)
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+
+ return scenario_uuid + " " + scenario_name
+ except (mdb.Error, AttributeError) as e:
+ self._format_error(e, tries, "delete", "instances running")
+ tries -= 1
+
+ def new_instance_scenario_as_a_whole(self,tenant_id,instance_scenario_name,instance_scenario_description,scenarioDict):
+ tries = 2
+ while tries:
+ created_time = time.time()
+ try:
+ with self.con:
+ self.cur = self.con.cursor()
+ #instance_scenarios
+ datacenter_id = scenarioDict['datacenter_id']
+ INSERT_={'tenant_id': tenant_id,
+ 'datacenter_tenant_id': scenarioDict["datacenter2tenant"][datacenter_id],
+ 'name': instance_scenario_name,
+ 'description': instance_scenario_description,
+ 'scenario_id' : scenarioDict['uuid'],
+ 'datacenter_id': datacenter_id
+ }
+ if scenarioDict.get("cloud-config"):
+ INSERT_["cloud_config"] = yaml.safe_dump(scenarioDict["cloud-config"], default_flow_style=True, width=256)
+
+ instance_uuid = self._new_row_internal('instance_scenarios', INSERT_, add_uuid=True, root_uuid=None, created_time=created_time)
+
+ net_scene2instance={}
+ #instance_nets #nets interVNF
+ for net in scenarioDict['nets']:
+ net_scene2instance[ net['uuid'] ] ={}
+ datacenter_site_id = net.get('datacenter_id', datacenter_id)
+ if not "vim_id_sites" in net:
+ net["vim_id_sites"] ={datacenter_site_id: net['vim_id']}
+ net["vim_id_sites"]["datacenter_site_id"] = {datacenter_site_id: net['vim_id']}
+ sce_net_id = net.get("uuid")
+
+ for datacenter_site_id,vim_id in net["vim_id_sites"].iteritems():
+ INSERT_={'vim_net_id': vim_id, 'created': net.get('created', False), 'instance_scenario_id':instance_uuid } #, 'type': net['type']
+ INSERT_['datacenter_id'] = datacenter_site_id
+ INSERT_['datacenter_tenant_id'] = scenarioDict["datacenter2tenant"][datacenter_site_id]
+ if sce_net_id:
+ INSERT_['sce_net_id'] = sce_net_id
+ created_time += 0.00001
+ instance_net_uuid = self._new_row_internal('instance_nets', INSERT_, True, instance_uuid, created_time)
+ net_scene2instance[ sce_net_id ][datacenter_site_id] = instance_net_uuid
+ net['uuid'] = instance_net_uuid #overwrite scnario uuid by instance uuid
+
+ if 'ip_profile' in net:
+ net['ip_profile']['net_id'] = None
+ net['ip_profile']['sce_net_id'] = None
+ net['ip_profile']['instance_net_id'] = instance_net_uuid
+ created_time += 0.00001
+ ip_profile_id = self._new_row_internal('ip_profiles', net['ip_profile'])
+
+ #instance_vnfs
+ for vnf in scenarioDict['vnfs']:
+ datacenter_site_id = vnf.get('datacenter_id', datacenter_id)
+ INSERT_={'instance_scenario_id': instance_uuid, 'vnf_id': vnf['vnf_id'] }
+ INSERT_['datacenter_id'] = datacenter_site_id
+ INSERT_['datacenter_tenant_id'] = scenarioDict["datacenter2tenant"][datacenter_site_id]
+ if vnf.get("uuid"):
+ INSERT_['sce_vnf_id'] = vnf['uuid']
+ created_time += 0.00001
+ instance_vnf_uuid = self._new_row_internal('instance_vnfs', INSERT_, True, instance_uuid, created_time)
+ vnf['uuid'] = instance_vnf_uuid #overwrite scnario uuid by instance uuid
+
+ #instance_nets #nets intraVNF
+ for net in vnf['nets']:
+ net_scene2instance[ net['uuid'] ] = {}
+ INSERT_={'vim_net_id': net['vim_id'], 'created': net.get('created', False), 'instance_scenario_id':instance_uuid } #, 'type': net['type']
+ INSERT_['datacenter_id'] = net.get('datacenter_id', datacenter_site_id)
+ INSERT_['datacenter_tenant_id'] = scenarioDict["datacenter2tenant"][datacenter_id]
+ if net.get("uuid"):
+ INSERT_['net_id'] = net['uuid']
+ created_time += 0.00001
+ instance_net_uuid = self._new_row_internal('instance_nets', INSERT_, True, instance_uuid, created_time)
+ net_scene2instance[ net['uuid'] ][datacenter_site_id] = instance_net_uuid
+ net['uuid'] = instance_net_uuid #overwrite scnario uuid by instance uuid
+
+ if 'ip_profile' in net:
+ net['ip_profile']['net_id'] = None
+ net['ip_profile']['sce_net_id'] = None
+ net['ip_profile']['instance_net_id'] = instance_net_uuid
+ created_time += 0.00001
+ ip_profile_id = self._new_row_internal('ip_profiles', net['ip_profile'])
+
+ #instance_vms
+ for vm in vnf['vms']:
+ INSERT_={'instance_vnf_id': instance_vnf_uuid, 'vm_id': vm['uuid'], 'vim_vm_id': vm['vim_id'] }
+ created_time += 0.00001
+ instance_vm_uuid = self._new_row_internal('instance_vms', INSERT_, True, instance_uuid, created_time)
+ vm['uuid'] = instance_vm_uuid #overwrite scnario uuid by instance uuid
+
+ #instance_interfaces
+ for interface in vm['interfaces']:
+ net_id = interface.get('net_id', None)
+ if net_id is None:
+ #check if is connected to a inter VNFs net
+ for iface in vnf['interfaces']:
+ if iface['interface_id'] == interface['uuid']:
+ if 'ip_address' in iface:
+ interface['ip_address'] = iface['ip_address']
+ net_id = iface.get('sce_net_id', None)
+ break
+ if net_id is None:
+ continue
+ interface_type='external' if interface['external_name'] is not None else 'internal'
+ INSERT_={'instance_vm_id': instance_vm_uuid, 'instance_net_id': net_scene2instance[net_id][datacenter_site_id],
+ 'interface_id': interface['uuid'], 'vim_interface_id': interface.get('vim_id'), 'type': interface_type,
+ 'ip_address': interface.get('ip_address'), 'floating_ip': int(interface.get('floating-ip',False)),
+ 'port_security': int(interface.get('port-security',True))}
+ #created_time += 0.00001
+ interface_uuid = self._new_row_internal('instance_interfaces', INSERT_, True, instance_uuid) #, created_time)
+ interface['uuid'] = interface_uuid #overwrite scnario uuid by instance uuid
+ return instance_uuid
+ except (mdb.Error, AttributeError) as e:
+ self._format_error(e, tries)
+ tries -= 1
+
+ def get_instance_scenario(self, instance_id, tenant_id=None, verbose=False):
+ '''Obtain the instance information, filtering by one or several of the tenant, uuid or name
+ instance_id is the uuid or the name if it is not a valid uuid format
+ Only one instance must mutch the filtering or an error is returned
+ '''
+ tries = 2
+ while tries:
+ try:
+ with self.con:
+ self.cur = self.con.cursor(mdb.cursors.DictCursor)
+ #instance table
+ where_list=[]
+ if tenant_id is not None: where_list.append( "inst.tenant_id='" + tenant_id +"'" )
+ if db_base._check_valid_uuid(instance_id):
+ where_list.append( "inst.uuid='" + instance_id +"'" )
+ else:
+ where_list.append( "inst.name='" + instance_id +"'" )
+ where_text = " AND ".join(where_list)
+ cmd = "SELECT inst.uuid as uuid,inst.name as name,inst.scenario_id as scenario_id, datacenter_id" +\
+ " ,datacenter_tenant_id, s.name as scenario_name,inst.tenant_id as tenant_id" + \
+ " ,inst.description as description,inst.created_at as created_at" +\
+ " ,inst.cloud_config as 'cloud_config'" +\
+ " FROM instance_scenarios as inst join scenarios as s on inst.scenario_id=s.uuid"+\
+ " WHERE " + where_text
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ rows = self.cur.fetchall()
+
+ if self.cur.rowcount==0:
+ raise db_base.db_base_Exception("No instance found where " + where_text, db_base.HTTP_Not_Found)
+ elif self.cur.rowcount>1:
+ raise db_base.db_base_Exception("More than one instance found where " + where_text, db_base.HTTP_Bad_Request)
+ instance_dict = rows[0]
+ if instance_dict["cloud_config"]:
+ instance_dict["cloud-config"] = yaml.load(instance_dict["cloud_config"])
+ del instance_dict["cloud_config"]
+
+ #instance_vnfs
+ cmd = "SELECT iv.uuid as uuid,sv.vnf_id as vnf_id,sv.name as vnf_name, sce_vnf_id, datacenter_id, datacenter_tenant_id"\
+ " FROM instance_vnfs as iv join sce_vnfs as sv on iv.sce_vnf_id=sv.uuid" \
+ " WHERE iv.instance_scenario_id='{}'" \
+ " ORDER BY iv.created_at ".format(instance_dict['uuid'])
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ instance_dict['vnfs'] = self.cur.fetchall()
+ for vnf in instance_dict['vnfs']:
+ vnf_manage_iface_list=[]
+ #instance vms
+ cmd = "SELECT iv.uuid as uuid, vim_vm_id, status, error_msg, vim_info, iv.created_at as created_at, name "\
+ " FROM instance_vms as iv join vms on iv.vm_id=vms.uuid "\
+ " WHERE instance_vnf_id='{}' ORDER BY iv.created_at".format(vnf['uuid'])
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ vnf['vms'] = self.cur.fetchall()
+ for vm in vnf['vms']:
+ vm_manage_iface_list=[]
+ #instance_interfaces
+ cmd = "SELECT vim_interface_id, instance_net_id, internal_name,external_name, mac_address,"\
+ " ii.ip_address as ip_address, vim_info, i.type as type"\
+ " FROM instance_interfaces as ii join interfaces as i on ii.interface_id=i.uuid"\
+ " WHERE instance_vm_id='{}' ORDER BY created_at".format(vm['uuid'])
+ self.logger.debug(cmd)
+ self.cur.execute(cmd )
+ vm['interfaces'] = self.cur.fetchall()
+ for iface in vm['interfaces']:
+ if iface["type"] == "mgmt" and iface["ip_address"]:
+ vnf_manage_iface_list.append(iface["ip_address"])
+ vm_manage_iface_list.append(iface["ip_address"])
+ if not verbose:
+ del iface["type"]
+ if vm_manage_iface_list: vm["ip_address"] = ",".join(vm_manage_iface_list)
+ if vnf_manage_iface_list: vnf["ip_address"] = ",".join(vnf_manage_iface_list)
+
+ #instance_nets
+ #select_text = "instance_nets.uuid as uuid,sce_nets.name as net_name,instance_nets.vim_net_id as net_id,instance_nets.status as status,instance_nets.external as external"
+ #from_text = "instance_nets join instance_scenarios on instance_nets.instance_scenario_id=instance_scenarios.uuid " + \
+ # "join sce_nets on instance_scenarios.scenario_id=sce_nets.scenario_id"
+ #where_text = "instance_nets.instance_scenario_id='"+ instance_dict['uuid'] + "'"
+ cmd = "SELECT uuid,vim_net_id,status,error_msg,vim_info,created, sce_net_id, net_id as vnf_net_id, datacenter_id, datacenter_tenant_id"\
+ " FROM instance_nets" \
+ " WHERE instance_scenario_id='{}' ORDER BY created_at".format(instance_dict['uuid'])
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ instance_dict['nets'] = self.cur.fetchall()
+
+ db_base._convert_datetime2str(instance_dict)
+ db_base._convert_str2boolean(instance_dict, ('public','shared','created') )
+ return instance_dict
+ except (mdb.Error, AttributeError) as e:
+ self._format_error(e, tries)
+ tries -= 1
+
+ def delete_instance_scenario(self, instance_id, tenant_id=None):
+ '''Deletes a instance_Scenario, filtering by one or serveral of the tenant, uuid or name
+ instance_id is the uuid or the name if it is not a valid uuid format
+ Only one instance_scenario must mutch the filtering or an error is returned
+ '''
+ tries = 2
+ while tries:
+ try:
+ with self.con:
+ self.cur = self.con.cursor(mdb.cursors.DictCursor)
+
+ #instance table
+ where_list=[]
+ if tenant_id is not None: where_list.append( "tenant_id='" + tenant_id +"'" )
+ if db_base._check_valid_uuid(instance_id):
+ where_list.append( "uuid='" + instance_id +"'" )
+ else:
+ where_list.append( "name='" + instance_id +"'" )
+ where_text = " AND ".join(where_list)
+ cmd = "SELECT * FROM instance_scenarios WHERE "+ where_text
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ rows = self.cur.fetchall()
+
+ if self.cur.rowcount==0:
+ raise db_base.db_base_Exception("No instance found where " + where_text, db_base.HTTP_Bad_Request)
+ elif self.cur.rowcount>1:
+ raise db_base.db_base_Exception("More than one instance found where " + where_text, db_base.HTTP_Bad_Request)
+ instance_uuid = rows[0]["uuid"]
+ instance_name = rows[0]["name"]
+
+ #sce_vnfs
+ cmd = "DELETE FROM instance_scenarios WHERE uuid='{}'".format(instance_uuid)
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+
+ return instance_uuid + " " + instance_name
+ except (mdb.Error, AttributeError) as e:
+ self._format_error(e, tries, "delete", "No dependences can avoid deleting!!!!")
+ tries -= 1
+
+ def new_instance_scenario(self, instance_scenario_dict, tenant_id):
+ #return self.new_row('vnfs', vnf_dict, None, tenant_id, True, True)
+ return self._new_row_internal('instance_scenarios', instance_scenario_dict, tenant_id, add_uuid=True, root_uuid=None, log=True)
+
+ def update_instance_scenario(self, instance_scenario_dict):
+ #TODO:
+ return
+
+ def new_instance_vnf(self, instance_vnf_dict, tenant_id, instance_scenario_id = None):
+ #return self.new_row('vms', vm_dict, tenant_id, True, True)
+ return self._new_row_internal('instance_vnfs', instance_vnf_dict, tenant_id, add_uuid=True, root_uuid=instance_scenario_id, log=True)
+
+ def update_instance_vnf(self, instance_vnf_dict):
+ #TODO:
+ return
+
+ def delete_instance_vnf(self, instance_vnf_id):
+ #TODO:
+ return
+
+ def new_instance_vm(self, instance_vm_dict, tenant_id, instance_scenario_id = None):
+ #return self.new_row('vms', vm_dict, tenant_id, True, True)
+ return self._new_row_internal('instance_vms', instance_vm_dict, tenant_id, add_uuid=True, root_uuid=instance_scenario_id, log=True)
+
+ def update_instance_vm(self, instance_vm_dict):
+ #TODO:
+ return
+
+ def delete_instance_vm(self, instance_vm_id):
+ #TODO:
+ return
+
+ def new_instance_net(self, instance_net_dict, tenant_id, instance_scenario_id = None):
+ return self._new_row_internal('instance_nets', instance_net_dict, tenant_id, add_uuid=True, root_uuid=instance_scenario_id, log=True)
+
+ def update_instance_net(self, instance_net_dict):
+ #TODO:
+ return
+
+ def delete_instance_net(self, instance_net_id):
+ #TODO:
+ return
+
+ def new_instance_interface(self, instance_interface_dict, tenant_id, instance_scenario_id = None):
+ return self._new_row_internal('instance_interfaces', instance_interface_dict, tenant_id, add_uuid=True, root_uuid=instance_scenario_id, log=True)
+
+ def update_instance_interface(self, instance_interface_dict):
+ #TODO:
+ return
+
+ def delete_instance_interface(self, instance_interface_dict):
+ #TODO:
+ return
+
+ def update_datacenter_nets(self, datacenter_id, new_net_list=[]):
+ ''' Removes the old and adds the new net list at datacenter list for one datacenter.
+ Attribute
+ datacenter_id: uuid of the datacenter to act upon
+ table: table where to insert
+ new_net_list: the new values to be inserted. If empty it only deletes the existing nets
+ Return: (Inserted items, Deleted items) if OK, (-Error, text) if error
+ '''
+ tries = 2
+ while tries:
+ created_time = time.time()
+ try:
+ with self.con:
+ self.cur = self.con.cursor()
+ cmd="DELETE FROM datacenter_nets WHERE datacenter_id='{}'".format(datacenter_id)
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ deleted = self.cur.rowcount
+ inserted = 0
+ for new_net in new_net_list:
+ created_time += 0.00001
+ self._new_row_internal('datacenter_nets', new_net, add_uuid=True, created_time=created_time)
+ inserted += 1
+ return inserted, deleted
+ except (mdb.Error, AttributeError) as e:
+ self._format_error(e, tries)
+ tries -= 1
+
+
--- /dev/null
+# -*- 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
+##
+
+'''
+JSON schemas used by openmano httpserver.py module to parse the different files and messages sent through the API
+'''
+__author__="Alfonso Tierno, Gerardo Garcia, Pablo Montes"
+__date__ ="$09-oct-2014 09:09:48$"
+
+#Basis schemas
+patern_name="^[ -~]+$"
+passwd_schema={"type" : "string", "minLength":1, "maxLength":60}
+nameshort_schema={"type" : "string", "minLength":1, "maxLength":60, "pattern" : "^[^,;()'\"]+$"}
+name_schema={"type" : "string", "minLength":1, "maxLength":255, "pattern" : "^[^,;()'\"]+$"}
+xml_text_schema={"type" : "string", "minLength":1, "maxLength":1000, "pattern" : "^[^']+$"}
+description_schema={"type" : ["string","null"], "maxLength":255, "pattern" : "^[^'\"]+$"}
+id_schema_fake = {"type" : "string", "minLength":2, "maxLength":36 } #"pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
+id_schema = {"type" : "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"}
+pci_schema={"type":"string", "pattern":"^[0-9a-fA-F]{4}(:[0-9a-fA-F]{2}){2}\.[0-9a-fA-F]$"}
+http_schema={"type":"string", "pattern":"^https?://[^'\"=]+$"}
+bandwidth_schema={"type":"string", "pattern" : "^[0-9]+ *([MG]bps)?$"}
+memory_schema={"type":"string", "pattern" : "^[0-9]+ *([MG]i?[Bb])?$"}
+integer0_schema={"type":"integer","minimum":0}
+integer1_schema={"type":"integer","minimum":1}
+path_schema={"type":"string", "pattern":"^(\.){0,2}(/[^/\"':{}\(\)]+)+$"}
+vlan_schema={"type":"integer","minimum":1,"maximum":4095}
+vlan1000_schema={"type":"integer","minimum":1000,"maximum":4095}
+mac_schema={"type":"string", "pattern":"^[0-9a-fA-F][02468aceACE](:[0-9a-fA-F]{2}){5}$"} #must be unicast LSB bit of MSB byte ==0
+#mac_schema={"type":"string", "pattern":"^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$"}
+ip_schema={"type":"string","pattern":"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"}
+ip_prefix_schema={"type":"string","pattern":"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/(30|[12]?[0-9])$"}
+port_schema={"type":"integer","minimum":1,"maximum":65534}
+object_schema={"type":"object"}
+schema_version_2={"type":"integer","minimum":2,"maximum":2}
+#schema_version_string={"type":"string","enum": ["0.1", "2", "0.2", "3", "0.3"]}
+log_level_schema={"type":"string", "enum":["DEBUG", "INFO", "WARNING","ERROR","CRITICAL"]}
+checksum_schema={"type":"string", "pattern":"^[0-9a-fA-F]{32}$"}
+size_schema={"type":"integer","minimum":1,"maximum":100}
+
+metadata_schema={
+ "type":"object",
+ "properties":{
+ "architecture": {"type":"string"},
+ "use_incremental": {"type":"string","enum":["yes","no"]},
+ "vpci": pci_schema,
+ "os_distro": {"type":"string"},
+ "os_type": {"type":"string"},
+ "os_version": {"type":"string"},
+ "bus": {"type":"string"},
+ "topology": {"type":"string", "enum": ["oneSocket"]}
+ }
+}
+
+#Schema for the configuration file
+config_schema = {
+ "title":"configuration response information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "http_port": port_schema,
+ "http_admin_port": port_schema,
+ "http_host": nameshort_schema,
+ "auto_push_VNF_to_VIMs": {"type":"boolean"},
+ "vnf_repository": path_schema,
+ "db_host": nameshort_schema,
+ "db_user": nameshort_schema,
+ "db_passwd": {"type":"string"},
+ "db_name": nameshort_schema,
+ # Next fields will disappear once the MANO API includes appropriate primitives
+ "vim_url": http_schema,
+ "vim_url_admin": http_schema,
+ "vim_name": nameshort_schema,
+ "vim_tenant_name": nameshort_schema,
+ "mano_tenant_name": nameshort_schema,
+ "mano_tenant_id": id_schema,
+ "http_console_proxy": {"type":"boolean"},
+ "http_console_host": nameshort_schema,
+ "http_console_ports": {
+ "type": "array",
+ "items": {"OneOf" : [
+ port_schema,
+ {"type":"object", "properties":{"from": port_schema, "to": port_schema}, "required": ["from","to"]}
+ ]}
+ },
+ "log_level": log_level_schema,
+ "log_socket_level": log_level_schema,
+ "log_level_db": log_level_schema,
+ "log_level_vim": log_level_schema,
+ "log_level_nfvo": log_level_schema,
+ "log_level_http": log_level_schema,
+ "log_level_console": log_level_schema,
+ "log_file_db": path_schema,
+ "log_file_vim": path_schema,
+ "log_file_nfvo": path_schema,
+ "log_file_http": path_schema,
+ "log_file_console": path_schema,
+ "log_socket_host": nameshort_schema,
+ "log_socket_port": port_schema,
+ "log_file": path_schema,
+ },
+ "required": ['db_host', 'db_user', 'db_passwd', 'db_name'],
+ "additionalProperties": False
+}
+
+tenant_schema = {
+ "title":"tenant information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "tenant":{
+ "type":"object",
+ "properties":{
+ "name": nameshort_schema,
+ "description": description_schema,
+ },
+ "required": ["name"],
+ "additionalProperties": True
+ }
+ },
+ "required": ["tenant"],
+ "additionalProperties": False
+}
+
+tenant_edit_schema = {
+ "title":"tenant edit information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "tenant":{
+ "type":"object",
+ "properties":{
+ "name": name_schema,
+ "description": description_schema,
+ },
+ "additionalProperties": False
+ }
+ },
+ "required": ["tenant"],
+ "additionalProperties": False
+}
+
+datacenter_schema_properties={
+ "name": name_schema,
+ "description": description_schema,
+ "type": nameshort_schema, #currently "openvim" or "openstack", can be enlarged with plugins
+ "vim_url": description_schema,
+ "vim_url_admin": description_schema,
+ "config": { "type":"object" }
+}
+
+datacenter_schema = {
+ "title":"datacenter information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "datacenter":{
+ "type":"object",
+ "properties":datacenter_schema_properties,
+ "required": ["name", "vim_url"],
+ "additionalProperties": True
+ }
+ },
+ "required": ["datacenter"],
+ "additionalProperties": False
+}
+
+
+datacenter_edit_schema = {
+ "title":"datacenter edit nformation schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "datacenter":{
+ "type":"object",
+ "properties":datacenter_schema_properties,
+ "additionalProperties": False
+ }
+ },
+ "required": ["datacenter"],
+ "additionalProperties": False
+}
+
+
+netmap_new_schema = {
+ "title":"netmap new information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "netmap":{ #delete from datacenter
+ "type":"object",
+ "properties":{
+ "name": name_schema, #name or uuid of net to change
+ "vim_id": id_schema,
+ "vim_name": name_schema
+ },
+ "minProperties": 1,
+ "additionalProperties": False
+ },
+ },
+ "required": ["netmap"],
+ "additionalProperties": False
+}
+
+netmap_edit_schema = {
+ "title":"netmap edit information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "netmap":{ #delete from datacenter
+ "type":"object",
+ "properties":{
+ "name": name_schema, #name or uuid of net to change
+ },
+ "minProperties": 1,
+ "additionalProperties": False
+ },
+ },
+ "required": ["netmap"],
+ "additionalProperties": False
+}
+
+datacenter_action_schema = {
+ "title":"datacenter action information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "net-update":{"type":"null",},
+ "net-edit":{
+ "type":"object",
+ "properties":{
+ "net": name_schema, #name or uuid of net to change
+ "name": name_schema,
+ "description": description_schema,
+ "shared": {"type": "boolean"}
+ },
+ "minProperties": 1,
+ "additionalProperties": False
+ },
+ "net-delete":{
+ "type":"object",
+ "properties":{
+ "net": name_schema, #name or uuid of net to change
+ },
+ "required": ["net"],
+ "additionalProperties": False
+ },
+ },
+ "minProperties": 1,
+ "maxProperties": 1,
+ "additionalProperties": False
+}
+
+
+datacenter_associate_schema={
+ "title":"datacenter associate information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "datacenter":{
+ "type":"object",
+ "properties":{
+ "vim_tenant": name_schema,
+ "vim_tenant_name": name_schema,
+ "vim_username": nameshort_schema,
+ "vim_password": nameshort_schema,
+ "config": {"type": "object"}
+ },
+# "required": ["vim_tenant"],
+ "additionalProperties": True
+ }
+ },
+ "required": ["datacenter"],
+ "additionalProperties": False
+}
+
+dhcp_schema = {
+ "title":"DHCP schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "enabled": {"type": "boolean"},
+ "start-address": ip_schema,
+ "count": integer1_schema
+ },
+ "required": ["enabled", "start-address", "count"],
+}
+
+ip_profile_schema = {
+ "title":"IP profile schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "ip-version": {"type":"string", "enum":["IPv4","IPv6"]},
+ "subnet-address": ip_prefix_schema,
+ "gateway-address": ip_schema,
+ "dns-address": ip_schema,
+ "dhcp": dhcp_schema
+ },
+}
+
+key_pair_schema = {
+ "title": "Key-pair schema for cloud-init configuration schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "name": name_schema,
+ "key": {"type":"string"}
+ },
+ "required": ["key"],
+ "additionalProperties": False
+}
+
+cloud_config_user_schema = {
+ "title": "User schema for cloud-init configuration schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "name": nameshort_schema,
+ "user-info": {"type":"string"},
+ #"key-pairs": {"type" : "array", "items": key_pair_schema}
+ "key-pairs": {"type" : "array", "items": {"type":"string"}}
+ },
+ "required": ["name"],
+ "additionalProperties": False
+}
+
+cloud_config_schema = {
+ "title": "Cloud-init configuration schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ #"key-pairs": {"type" : "array", "items": key_pair_schema},
+ "key-pairs": {"type" : "array", "items": {"type":"string"}},
+ "users": {"type" : "array", "items": cloud_config_user_schema}
+ },
+ "additionalProperties": False
+}
+
+internal_connection_element_schema = {
+ "type":"object",
+ "properties":{
+ "VNFC": name_schema,
+ "local_iface_name": name_schema
+ }
+}
+
+internal_connection_element_schema_v02 = {
+ "type":"object",
+ "properties":{
+ "VNFC": name_schema,
+ "local_iface_name": name_schema,
+ "ip_address": ip_schema
+ }
+}
+
+internal_connection_schema = {
+ "type":"object",
+ "properties":{
+ "name": name_schema,
+ "description":description_schema,
+ "type":{"type":"string", "enum":["bridge","data","ptp"]},
+ "elements": {"type" : "array", "items": internal_connection_element_schema, "minItems":2}
+ },
+ "required": ["name", "type", "elements"],
+ "additionalProperties": False
+}
+
+internal_connection_schema_v02 = {
+ "type":"object",
+ "properties":{
+ "name": name_schema,
+ "description":description_schema,
+ "type": {"type": "string", "enum":["e-line", "e-lan"]},
+ "implementation": {"type": "string", "enum":["overlay", "underlay"]},
+ "ip-profile": ip_profile_schema,
+ "elements": {"type" : "array", "items": internal_connection_element_schema_v02, "minItems":2}
+ },
+ "required": ["name", "type", "implementation", "elements"],
+ "additionalProperties": False
+}
+
+external_connection_schema = {
+ "type":"object",
+ "properties":{
+ "name": name_schema,
+ "type":{"type":"string", "enum":["mgmt","bridge","data"]},
+ "VNFC": name_schema,
+ "local_iface_name": name_schema ,
+ "description":description_schema
+ },
+ "required": ["name", "type", "VNFC", "local_iface_name"],
+ "additionalProperties": False
+}
+
+#Not yet used
+external_connection_schema_v02 = {
+ "type":"object",
+ "properties":{
+ "name": name_schema,
+ "mgmt": {"type":"boolean"},
+ "type": {"type": "string", "enum":["e-line", "e-lan"]},
+ "implementation": {"type": "string", "enum":["overlay", "underlay"]},
+ "VNFC": name_schema,
+ "local_iface_name": name_schema ,
+ "description":description_schema
+ },
+ "required": ["name", "type", "VNFC", "local_iface_name"],
+ "additionalProperties": False
+}
+
+interfaces_schema={
+ "type":"array",
+ "items":{
+ "type":"object",
+ "properties":{
+ "name":name_schema,
+ "dedicated":{"type":"string","enum":["yes","no","yes:sriov"]},
+ "bandwidth":bandwidth_schema,
+ "vpci":pci_schema,
+ "mac_address": mac_schema
+ },
+ "additionalProperties": False,
+ "required": ["name","dedicated", "bandwidth"]
+ }
+}
+
+bridge_interfaces_schema={
+ "type":"array",
+ "items":{
+ "type":"object",
+ "properties":{
+ "name": name_schema,
+ "bandwidth":bandwidth_schema,
+ "vpci":pci_schema,
+ "mac_address": mac_schema,
+ "model": {"type":"string", "enum":["virtio","e1000","ne2k_pci","pcnet","rtl8139"]},
+ "port-security": {"type" : "boolean"},
+ "floating-ip": {"type" : "boolean"}
+ },
+ "additionalProperties": False,
+ "required": ["name"]
+ }
+}
+
+devices_schema={
+ "type":"array",
+ "items":{
+ "type":"object",
+ "properties":{
+ "type":{"type":"string", "enum":["disk","cdrom","xml"] },
+ "image": path_schema,
+ "image name": name_schema,
+ "image checksum": checksum_schema,
+ "image metadata": metadata_schema,
+ "size": size_schema,
+ "vpci":pci_schema,
+ "xml":xml_text_schema,
+ },
+ "additionalProperties": False,
+ "required": ["type"]
+ }
+}
+
+
+numa_schema = {
+ "type": "object",
+ "properties": {
+ "memory":integer1_schema,
+ "cores":integer1_schema,
+ "paired-threads":integer1_schema,
+ "threads":integer1_schema,
+ "cores-id":{"type":"array","items":integer0_schema},
+ "paired-threads-id":{"type":"array","items":{"type":"array","minItems":2,"maxItems":2,"items":integer0_schema}},
+ "threads-id":{"type":"array","items":integer0_schema},
+ "interfaces":interfaces_schema
+ },
+ "additionalProperties": False,
+ #"required": ["memory"]
+}
+
+config_files_schema = {
+ "title": "Config files for cloud init schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type": "object",
+ "properties": {
+ "dest": path_schema,
+ "encoding": {"type": "string", "enum": ["b64", "base64", "gz", "gz+b64", "gz+base64", "gzip+b64", "gzip+base64"]}, #by default text
+ "content": {"type": "string"},
+ "permissions": {"type": "string"}, # tiypically octal notation '0644'
+ "owner": {"type": "string"}, # format: owner:group
+
+ },
+ "additionalProperties": False,
+ "required": ["dest", "content"],
+}
+
+boot_data_vdu_schema = {
+ "title": "Boot data (Cloud-init) configuration schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type": "object",
+ "properties":{
+ "key-pairs": {"type" : "array", "items": {"type":"string"}},
+ "users": {"type" : "array", "items": cloud_config_user_schema},
+ "user-data": {"type" : "string"}, # scrip to run
+ "config-files": {"type": "array", "items": config_files_schema},
+ # NOTE: “user-data” are mutually exclusive with users and config-files because user/files are injected using user-data
+ "boot-data-drive": {"type": "boolean"},
+ },
+ "additionalProperties": False,
+}
+
+vnfc_schema = {
+ "type":"object",
+ "properties":{
+ "name": name_schema,
+ "description": description_schema,
+ "VNFC image": {"oneOf": [path_schema, http_schema]},
+ "image name": name_schema,
+ "image checksum": checksum_schema,
+ "image metadata": metadata_schema,
+ #"cloud-config": cloud_config_schema, #common for all vnfs in the scenario
+ "processor": {
+ "type":"object",
+ "properties":{
+ "model":description_schema,
+ "features":{"type":"array","items":nameshort_schema}
+ },
+ "required": ["model"],
+ "additionalProperties": False
+ },
+ "hypervisor": {
+ "type":"object",
+ "properties":{
+ "type":nameshort_schema,
+ "version":description_schema
+ },
+ },
+ "ram":integer0_schema,
+ "vcpus":integer0_schema,
+ "disk": integer1_schema,
+ "numas": {
+ "type": "array",
+ "items": numa_schema
+ },
+ "bridge-ifaces": bridge_interfaces_schema,
+ "devices": devices_schema,
+ "boot-data" : boot_data_vdu_schema
+
+ },
+ "required": ["name"],
+ "oneOf": [
+ {"required": ["VNFC image"]},
+ {"required": ["image name"]}
+ ],
+ "additionalProperties": False
+}
+
+vnfd_schema_v01 = {
+ "title":"vnfd information schema v0.1",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "vnf":{
+ "type":"object",
+ "properties":{
+ "name": name_schema,
+ "description": description_schema,
+ "class": nameshort_schema,
+ "public": {"type" : "boolean"},
+ "physical": {"type" : "boolean"},
+ "tenant_id": id_schema, #only valid for admin
+ "external-connections": {"type" : "array", "items": external_connection_schema, "minItems":1},
+ "internal-connections": {"type" : "array", "items": internal_connection_schema, "minItems":1},
+ "VNFC":{"type" : "array", "items": vnfc_schema, "minItems":1}
+ },
+ "required": ["name","external-connections"],
+ "additionalProperties": True
+ }
+ },
+ "required": ["vnf"],
+ "additionalProperties": False
+}
+
+#VNFD schema for OSM R1
+vnfd_schema_v02 = {
+ "title":"vnfd information schema v0.2",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "schema_version": {"type": "string", "enum": ["0.2"]},
+ "vnf":{
+ "type":"object",
+ "properties":{
+ "name": name_schema,
+ "description": description_schema,
+ "class": nameshort_schema,
+ "public": {"type" : "boolean"},
+ "physical": {"type" : "boolean"},
+ "tenant_id": id_schema, #only valid for admin
+ "external-connections": {"type" : "array", "items": external_connection_schema, "minItems":1},
+ "internal-connections": {"type" : "array", "items": internal_connection_schema_v02, "minItems":1},
+ # "cloud-config": cloud_config_schema, #common for all vnfcs
+ "VNFC":{"type" : "array", "items": vnfc_schema, "minItems":1}
+ },
+ "required": ["name"],
+ "additionalProperties": True
+ }
+ },
+ "required": ["vnf", "schema_version"],
+ "additionalProperties": False
+}
+
+#vnfd_schema = vnfd_schema_v01
+#{
+# "title":"vnfd information schema v0.2",
+# "$schema": "http://json-schema.org/draft-04/schema#",
+# "oneOf": [vnfd_schema_v01, vnfd_schema_v02]
+#}
+
+graph_schema = {
+ "title":"graphical scenario descriptor information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "x": integer0_schema,
+ "y": integer0_schema,
+ "ifaces": {
+ "type":"object",
+ "properties":{
+ "left": {"type":"array"},
+ "right": {"type":"array"},
+ "bottom": {"type":"array"},
+ }
+ }
+ },
+ "required": ["x","y"]
+}
+
+nsd_schema_v01 = {
+ "title":"network scenario descriptor information schema v0.1",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "name":name_schema,
+ "description": description_schema,
+ "tenant_id": id_schema, #only valid for admin
+ "public": {"type": "boolean"},
+ "topology":{
+ "type":"object",
+ "properties":{
+ "nodes": {
+ "type":"object",
+ "patternProperties":{
+ ".": {
+ "type": "object",
+ "properties":{
+ "type":{"type":"string", "enum":["VNF", "other_network", "network", "external_network"]},
+ "vnf_id": id_schema,
+ "graph": graph_schema,
+ },
+ "patternProperties":{
+ "^(VNF )?model$": {"type": "string"}
+ },
+ "required": ["type"]
+ }
+ }
+ },
+ "connections": {
+ "type":"object",
+ "patternProperties":{
+ ".": {
+ "type": "object",
+ "properties":{
+ "nodes":{"oneOf":[{"type":"object", "minProperties":2}, {"type":"array", "minLength":1}]},
+ "type": {"type": "string", "enum":["link", "external_network", "dataplane_net", "bridge_net"]},
+ "graph": graph_schema
+ },
+ "required": ["nodes"]
+ },
+ }
+ }
+ },
+ "required": ["nodes"],
+ "additionalProperties": False
+ }
+ },
+ "required": ["name","topology"],
+ "additionalProperties": False
+}
+
+nsd_schema_v02 = {
+ "title":"network scenario descriptor information schema v0.2",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "schema_version": schema_version_2,
+ "scenario":{
+ "type":"object",
+ "properties":{
+ "name": name_schema,
+ "description": description_schema,
+ "tenant_id": id_schema, #only valid for admin
+ "public": {"type": "boolean"},
+ "vnfs": {
+ "type":"object",
+ "patternProperties":{
+ ".": {
+ "type": "object",
+ "properties":{
+ "vnf_id": id_schema,
+ "graph": graph_schema,
+ "vnf_name": name_schema,
+ },
+ }
+ },
+ "minProperties": 1
+ },
+ "networks": {
+ "type":"object",
+ "patternProperties":{
+ ".": {
+ "type": "object",
+ "properties":{
+ "interfaces":{"type":"array", "minLength":1},
+ "type": {"type": "string", "enum":["dataplane", "bridge"]},
+ "external" : {"type": "boolean"},
+ "graph": graph_schema
+ },
+ "required": ["interfaces"]
+ },
+ }
+ },
+
+ },
+ "required": ["vnfs", "name"],
+ "additionalProperties": False
+ }
+ },
+ "required": ["scenario","schema_version"],
+ "additionalProperties": False
+}
+
+#NSD schema for OSM R1
+nsd_schema_v03 = {
+ "title":"network scenario descriptor information schema v0.3",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "schema_version": {"type": "string", "enum": ["0.3"]},
+ "scenario":{
+ "type":"object",
+ "properties":{
+ "name": name_schema,
+ "description": description_schema,
+ "tenant_id": id_schema, #only valid for admin
+ "public": {"type": "boolean"},
+ "cloud-config": cloud_config_schema, #common for all vnfs in the scenario
+ #"datacenter": name_schema,
+ "vnfs": {
+ "type":"object",
+ "patternProperties":{
+ ".": {
+ "type": "object",
+ "properties":{
+ "vnf_id": id_schema,
+ "graph": graph_schema,
+ "vnf_name": name_schema,
+ #"cloud-config": cloud_config_schema, #particular for a vnf
+ #"datacenter": name_schema,
+ "internal-connections": {
+ "type": "object",
+ "patternProperties": {
+ ".": {
+ "type": "object",
+ "properties": {
+ "ip-profile": ip_profile_schema,
+ "elements": {
+ "type" : "array",
+ "items":{
+ "type":"object",
+ "properties":{
+ "VNFC": name_schema,
+ "local_iface_name": name_schema,
+ "ip_address": ip_schema
+ },
+ "required": ["VNFC", "local_iface_name"],
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ },
+ "minProperties": 1
+ },
+ "networks": {
+ "type":"object",
+ "patternProperties":{
+ ".": {
+ "type": "object",
+ "properties":{
+ "interfaces":{
+ "type":"array",
+ "minLength":1,
+ "items":{
+ "type":"object",
+ "properties":{
+ "vnf": name_schema,
+ "vnf_interface": name_schema,
+ "ip_address": ip_schema
+ },
+ "required": ["vnf", "vnf_interface"],
+ }
+ },
+ "type": {"type": "string", "enum":["e-line", "e-lan"]},
+ "implementation": {"type": "string", "enum":["overlay", "underlay"]},
+ "external" : {"type": "boolean"},
+ "graph": graph_schema,
+ "ip-profile": ip_profile_schema
+ },
+ "required": ["interfaces"]
+ },
+ }
+ },
+
+ },
+ "required": ["vnfs", "networks","name"],
+ "additionalProperties": False
+ }
+ },
+ "required": ["scenario","schema_version"],
+ "additionalProperties": False
+}
+
+#scenario_new_schema = {
+# "title":"new scenario information schema",
+# "$schema": "http://json-schema.org/draft-04/schema#",
+# #"oneOf": [nsd_schema_v01, nsd_schema_v02]
+# "oneOf": [nsd_schema_v01]
+#}
+
+scenario_edit_schema = {
+ "title":"edit scenario information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "name":name_schema,
+ "description": description_schema,
+ "topology":{
+ "type":"object",
+ "properties":{
+ "nodes": {
+ "type":"object",
+ "patternProperties":{
+ "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$": {
+ "type":"object",
+ "properties":{
+ "graph":{
+ "type": "object",
+ "properties":{
+ "x": integer0_schema,
+ "y": integer0_schema,
+ "ifaces":{ "type": "object"}
+ }
+ },
+ "description": description_schema,
+ "name": name_schema
+ }
+ }
+ }
+ }
+ },
+ "required": ["nodes"],
+ "additionalProperties": False
+ }
+ },
+ "additionalProperties": False
+}
+
+scenario_action_schema = {
+ "title":"scenario action information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "start":{
+ "type": "object",
+ "properties": {
+ "instance_name":name_schema,
+ "description":description_schema,
+ "datacenter": {"type": "string"}
+ },
+ "required": ["instance_name"]
+ },
+ "deploy":{
+ "type": "object",
+ "properties": {
+ "instance_name":name_schema,
+ "description":description_schema,
+ "datacenter": {"type": "string"}
+ },
+ "required": ["instance_name"]
+ },
+ "reserve":{
+ "type": "object",
+ "properties": {
+ "instance_name":name_schema,
+ "description":description_schema,
+ "datacenter": {"type": "string"}
+ },
+ "required": ["instance_name"]
+ },
+ "verify":{
+ "type": "object",
+ "properties": {
+ "instance_name":name_schema,
+ "description":description_schema,
+ "datacenter": {"type": "string"}
+ },
+ "required": ["instance_name"]
+ }
+ },
+ "minProperties": 1,
+ "maxProperties": 1,
+ "additionalProperties": False
+}
+
+instance_scenario_create_schema_v01 = {
+ "title":"instance scenario create information schema v0.1",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "schema_version": {"type": "string", "enum": ["0.1"]},
+ "instance":{
+ "type":"object",
+ "properties":{
+ "name":name_schema,
+ "description":description_schema,
+ "datacenter": name_schema,
+ "scenario" : name_schema, #can be an UUID or name
+ "action":{"enum": ["deploy","reserve","verify" ]},
+ "connect_mgmt_interfaces": {"oneOf": [{"type":"boolean"}, {"type":"object"}]},# can be true or a dict with datacenter: net_name
+ "cloud-config": cloud_config_schema, #common to all vnfs in the instance scenario
+ "vnfs":{ #mapping from scenario to datacenter
+ "type": "object",
+ "patternProperties":{
+ ".": {
+ "type": "object",
+ "properties":{
+ "name": name_schema, #override vnf name
+ "datacenter": name_schema,
+ #"metadata": {"type": "object"},
+ #"user_data": {"type": "string"}
+ #"cloud-config": cloud_config_schema, #particular for a vnf
+ "external-connections": {
+ "type": "object",
+ "patternProperties": {
+ ".": {
+ "type": "object",
+ "properties": {
+ "vim-network-name": name_schema,
+ "ip_address": ip_schema
+ }
+ }
+ }
+ },
+ "internal-connections": {
+ "type": "object",
+ "patternProperties": {
+ ".": {
+ "type": "object",
+ "properties": {
+ "ip-profile": ip_profile_schema,
+ "elements": {
+ "type" : "array",
+ "items":{
+ "type":"object",
+ "properties":{
+ "VNFC": name_schema,
+ "local_iface_name": name_schema,
+ "ip_address": ip_schema
+ },
+ "required": ["VNFC", "local_iface_name"],
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ "networks":{ #mapping from scenario to datacenter
+ "type": "object",
+ "patternProperties":{
+ ".": {
+ "type": "object",
+ "properties":{
+ "interfaces":{
+ "type":"array",
+ "minLength":1,
+ "items":{
+ "type":"object",
+ "properties":{
+ "ip_address": ip_schema,
+ "datacenter": name_schema,
+ "vim-network-name": name_schema
+ },
+ "patternProperties":{
+ ".": {"type": "string"}
+ }
+ }
+ },
+ "ip-profile": ip_profile_schema,
+ #if the network connects VNFs deployed at different sites, you must specify one entry per site that this network connect to
+ "sites": {
+ "type":"array",
+ "minLength":1,
+ "items":{
+ "type":"object",
+ "properties":{
+ # By default for an scenario 'external' network openmano looks for an existing VIM network to map this external scenario network,
+ # for other networks openamno creates at VIM
+ # Use netmap-create to force to create an external scenario network
+ "netmap-create": {"oneOf":[name_schema,{"type": "null"}]}, #datacenter network to use. Null if must be created as an internal net
+ #netmap-use: Indicates an existing VIM network that must be used for this scenario network.
+ #Can use both the VIM network name (if it is not ambiguous) or the VIM net UUID
+ #If both 'netmap-create' and 'netmap-use'are supplied, netmap-use precedes, but if fails openmano follows the netmap-create
+ #In oder words, it is the same as 'try to map to the VIM network (netmap-use) if exist, and if not create the network (netmap-create)
+ "netmap-use": name_schema, #
+ "vim-network-name": name_schema, #override network name
+ #"ip-profile": ip_profile_schema,
+ "datacenter": name_schema,
+ }
+ }
+ },
+
+
+
+ }
+ }
+ },
+ },
+ },
+ "additionalProperties": False,
+ "required": ["name"]
+ },
+ },
+ "required": ["instance"],
+ "additionalProperties": False
+}
+
+instance_scenario_action_schema = {
+ "title":"instance scenario action information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "start":{"type": "null"},
+ "pause":{"type": "null"},
+ "resume":{"type": "null"},
+ "shutoff":{"type": "null"},
+ "shutdown":{"type": "null"},
+ "forceOff":{"type": "null"},
+ "rebuild":{"type": "null"},
+ "reboot":{
+ "type": ["object","null"],
+ },
+ "console": {"type": ["string", "null"], "enum": ["novnc", "xvpvnc", "rdp-html5", "spice-html5", None]},
+ "vnfs":{"type": "array", "items":{"type":"string"}},
+ "vms":{"type": "array", "items":{"type":"string"}}
+ },
+ "minProperties": 1,
+ #"maxProperties": 1,
+ "additionalProperties": False
+}
--- /dev/null
+#!/usr/bin/env python3
+# -*- 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
+##
+
+'''
+openmano python client used to interact with openmano-server
+'''
+__author__="Alfonso Tierno"
+__date__ ="$09-Mar-2016 09:09:48$"
+__version__="0.0.1-r467"
+version_date="Mar 2016"
+
+import requests
+import json
+import yaml
+import logging
+import sys
+if sys.version_info.major == 3:
+ from urllib.parse import quote
+elif sys.version_info.major == 2:
+ from urllib import quote
+
+class OpenmanoException(Exception):
+ '''Common Exception for all openmano client exceptions'''
+
+class OpenmanoBadParamsException(OpenmanoException):
+ '''Bad or missing input parameters'''
+
+class OpenmanoResponseException(OpenmanoException):
+ '''Unexpected response from openmano server'''
+
+class OpenmanoNotFoundException(OpenmanoException):
+ '''Not found at server'''
+
+# class vnf():
+# def __init__(self, message):
+# print "Error: %s" %message
+# print
+# self.print_usage()
+# #self.print_help()
+# print
+# print "Type 'openmano -h' for help"
+
+class openmanoclient():
+ headers_req = {'Accept': 'application/yaml', 'content-type': 'application/yaml'}
+
+ def __init__(self, **kwargs):
+ self.username = kwargs.get("username")
+ self.password = kwargs.get("password")
+ self.endpoint_url = kwargs.get("endpoint_url")
+ self.tenant_id = kwargs.get("tenant_id")
+ self.tenant_name = kwargs.get("tenant_name")
+ self.tenant = None
+ self.datacenter_id = kwargs.get("datacenter_id")
+ self.datacenter_name = kwargs.get("datacenter_name")
+ self.datacenter = None
+ self.logger = logging.getLogger(kwargs.get('logger','manoclient'))
+ if kwargs.get("debug"):
+ self.logger.setLevel(logging.DEBUG)
+
+ def __getitem__(self, index):
+ if index=='tenant_name':
+ return self.tenant_name
+ elif index=='tenant_id':
+ return self.tenant_id
+ elif index=='datacenter_name':
+ return self.datacenter_name
+ elif index=='datacenter_id':
+ return self.datacenter_id
+ elif index=='username':
+ return self.username
+ elif index=='password':
+ return self.password
+ elif index=='endpoint_url':
+ return self.endpoint_url
+ else:
+ raise KeyError("Invalid key '%s'" %str(index))
+
+ def __setitem__(self,index, value):
+ if index=='tenant_name':
+ self.tenant_name = value
+ elif index=='tenant_id':
+ self.tenant_id = value
+ elif index=='datacenter_name':
+ self.datacenter_name = value
+ elif index=='datacenter_id':
+ self.datacenter_id = value
+ elif index=='username':
+ self.username = value
+ elif index=='password':
+ self.password = value
+ elif index=='endpoint_url':
+ self.endpoint_url = value
+ else:
+ raise KeyError("Invalid key '%s'" %str(index))
+ self.tenant = None # force to reload tenant with different credentials
+ self.datacenter = None # force to reload datacenter with different credentials
+
+ def _parse(self, descriptor, descriptor_format, response=False):
+ #try yaml
+ if descriptor_format and descriptor_format != "json" and descriptor_format != "yaml":
+ raise OpenmanoBadParamsException("'descriptor_format' must be a 'json' or 'yaml' text")
+ if descriptor_format != "json":
+ try:
+ return yaml.load(descriptor)
+ except yaml.YAMLError as exc:
+ error_pos = ""
+ if hasattr(exc, 'problem_mark'):
+ mark = exc.problem_mark
+ error_pos = " at line:{} column:{}s".format(mark.line+1, mark.column+1)
+ error_text = "yaml format error" + error_pos
+ elif descriptor_format != "yaml":
+ try:
+ return json.loads(descriptor)
+ except Exception as e:
+ if response:
+ error_text = "json format error" + str(e)
+
+ if response:
+ raise OpenmanoResponseException(error_text)
+ raise OpenmanoBadParamsException(error_text)
+
+ def _parse_yaml(self, descriptor, response=False):
+ try:
+ return yaml.load(descriptor)
+ except yaml.YAMLError as exc:
+ error_pos = ""
+ if hasattr(exc, 'problem_mark'):
+ mark = exc.problem_mark
+ error_pos = " at line:{} column:{}s".format(mark.line+1, mark.column+1)
+ error_text = "yaml format error" + error_pos
+ if response:
+ raise OpenmanoResponseException(error_text)
+ raise OpenmanoBadParamsException(error_text)
+
+
+ def _get_item_uuid(self, item, item_id=None, item_name=None, all_tenants=False):
+ if all_tenants == None:
+ tenant_text = ""
+ elif all_tenants == False:
+ tenant_text = "/" + self.tenant
+ else:
+ tenant_text = "/any"
+ URLrequest = "{}{}/{}".format(self.endpoint_url, tenant_text, item)
+ self.logger.debug("GET %s", URLrequest )
+ mano_response = requests.get(URLrequest, headers=self.headers_req)
+ self.logger.debug("openmano response: %s", mano_response.text )
+ content = self._parse_yaml(mano_response.text, response=True)
+ #print content
+ found = 0
+ if not item_id and not item_name:
+ raise OpenmanoResponseException("Missing either {0}_name or {0}_id".format(item[:-1]))
+ for i in content[item]:
+ if item_id and i["uuid"] == item_id:
+ return item_id
+ elif item_name and i["name"] == item_name:
+ uuid = i["uuid"]
+ found += 1
+
+ if found == 0:
+ if item_id:
+ raise OpenmanoNotFoundException("No {} found with id '{}'".format(item[:-1], item_id))
+ else:
+ #print(item, item_name)
+ raise OpenmanoNotFoundException("No {} found with name '{}'".format(item[:-1], item_name) )
+ elif found > 1:
+ raise OpenmanoNotFoundException("{} {} found with name '{}'. uuid must be used".format(found, item, item_name))
+ return uuid
+
+ def _get_item(self, item, uuid=None, name=None, all_tenants=False):
+ if all_tenants:
+ tenant_text = "/any"
+ elif all_tenants==None:
+ tenant_text = ""
+ else:
+ tenant_text = "/"+self._get_tenant()
+ if not uuid:
+ #check that exist
+ uuid = self._get_item_uuid(item, uuid, name, all_tenants)
+
+ URLrequest = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, uuid)
+ self.logger.debug("GET %s", URLrequest )
+ mano_response = requests.get(URLrequest, headers=self.headers_req)
+ self.logger.debug("openmano response: %s", mano_response.text )
+
+ content = self._parse_yaml(mano_response.text, response=True)
+ if mano_response.status_code==200:
+ return content
+ else:
+ raise OpenmanoResponseException(str(content))
+
+ def _get_tenant(self):
+ if not self.tenant:
+ self.tenant = self._get_item_uuid("tenants", self.tenant_id, self.tenant_name, None)
+ return self.tenant
+
+ def _get_datacenter(self):
+ if not self.tenant:
+ self._get_tenant()
+ if not self.datacenter:
+ self.datacenter = self._get_item_uuid("datacenters", self.datacenter_id, self.datacenter_name, False)
+ return self.datacenter
+
+ def _create_item(self, item, descriptor, all_tenants=False):
+ if all_tenants:
+ tenant_text = "/any"
+ elif all_tenants==None:
+ tenant_text = ""
+ else:
+ tenant_text = "/"+self._get_tenant()
+ payload_req = yaml.safe_dump(descriptor)
+
+ #print payload_req
+
+ URLrequest = "{}{}/{}".format(self.endpoint_url, tenant_text, item)
+ self.logger.debug("openmano POST %s %s", URLrequest, payload_req)
+ mano_response = requests.post(URLrequest, headers = self.headers_req, data=payload_req)
+ self.logger.debug("openmano response: %s", mano_response.text )
+
+ content = self._parse_yaml(mano_response.text, response=True)
+ if mano_response.status_code==200:
+ return content
+ else:
+ raise OpenmanoResponseException(str(content))
+
+ def _del_item(self, item, uuid=None, name=None, all_tenants=False):
+ if all_tenants:
+ tenant_text = "/any"
+ elif all_tenants==None:
+ tenant_text = ""
+ else:
+ tenant_text = "/"+self._get_tenant()
+ if not uuid:
+ #check that exist
+ uuid = self._get_item_uuid(item, uuid, name, all_tenants)
+
+ URLrequest = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, uuid)
+ self.logger.debug("DELETE %s", URLrequest )
+ mano_response = requests.delete(URLrequest, headers = self.headers_req)
+ self.logger.debug("openmano response: %s", mano_response.text )
+
+ content = self._parse_yaml(mano_response.text, response=True)
+ if mano_response.status_code==200:
+ return content
+ else:
+ raise OpenmanoResponseException(str(content))
+
+ def _list_item(self, item, all_tenants=False, filter_dict=None):
+ if all_tenants:
+ tenant_text = "/any"
+ elif all_tenants==None:
+ tenant_text = ""
+ else:
+ tenant_text = "/"+self._get_tenant()
+
+ URLrequest = "{}{}/{}".format(self.endpoint_url, tenant_text, item)
+ separator="?"
+ if filter_dict:
+ for k in filter_dict:
+ URLrequest += separator + quote(str(k)) + "=" + quote(str(filter_dict[k]))
+ separator = "&"
+ self.logger.debug("openmano GET %s", URLrequest)
+ mano_response = requests.get(URLrequest, headers=self.headers_req)
+ self.logger.debug("openmano response: %s", mano_response.text )
+
+ content = self._parse_yaml(mano_response.text, response=True)
+ if mano_response.status_code==200:
+ return content
+ else:
+ raise OpenmanoResponseException(str(content))
+
+ def _edit_item(self, item, descriptor, uuid=None, name=None, all_tenants=False):
+ if all_tenants:
+ tenant_text = "/any"
+ elif all_tenants==None:
+ tenant_text = ""
+ else:
+ tenant_text = "/"+self._get_tenant()
+
+ if not uuid:
+ #check that exist
+ uuid = self._get_item_uuid("tenants", uuid, name, all_tenants)
+
+ payload_req = yaml.safe_dump(descriptor)
+
+ #print payload_req
+
+ URLrequest = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, uuid)
+ self.logger.debug("openmano PUT %s %s", URLrequest, payload_req)
+ mano_response = requests.put(URLrequest, headers = self.headers_req, data=payload_req)
+ self.logger.debug("openmano response: %s", mano_response.text )
+
+ content = self._parse_yaml(mano_response.text, response=True)
+ if mano_response.status_code==200:
+ return content
+ else:
+ raise OpenmanoResponseException(str(content))
+
+ #TENANTS
+ def list_tenants(self, **kwargs):
+ '''Obtain a list of tenants
+ Params: can be filtered by 'uuid','name','description'
+ Return: Raises an exception on error
+ Obtain a dictionary with format {'tenants':[{tenant1_info},{tenant2_info},...]}}
+ '''
+ return self._list_item("tenants", all_tenants=None, filter_dict=kwargs)
+
+ def get_tenant(self, uuid=None, name=None):
+ '''Obtain the information of a tenant
+ Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
+ Return: Raises an exception on error, not found, found several
+ Obtain a dictionary with format {'tenant':{tenant_info}}
+ '''
+ return self._get_item("tenants", uuid, name, all_tenants=None)
+
+ def delete_tenant(self, uuid=None, name=None):
+ '''Delete a tenant
+ Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
+ Return: Raises an exception on error, not found, found several
+ Obtain a dictionary with format {'result': text indicating deleted}
+ '''
+ return self._del_item("tenants", uuid, name, all_tenants=None)
+
+ def create_tenant(self, descriptor=None, descriptor_format=None, name=None, description=None):
+ '''Creates a tenant
+ Params: must supply a descriptor or/and just a name
+ descriptor: with format {'tenant':{new_tenant_info}}
+ newtenant_info must contain 'name', and optionally 'description'
+ must be a dictionary or a json/yaml text.
+ name: the tenant name. Overwrite descriptor name if any
+ description: tenant descriptor.. Overwrite descriptor description if any
+ Return: Raises an exception on error
+ Obtain a dictionary with format {'tenant':{new_tenant_info}}
+ '''
+ if isinstance(descriptor, str):
+ descriptor = self._parse(descriptor, descriptor_format)
+ elif descriptor:
+ pass
+ elif name:
+ descriptor={"tenant": {"name": name}}
+ else:
+ raise OpenmanoBadParamsException("Missing descriptor")
+
+ if 'tenant' not in descriptor or len(descriptor)!=1:
+ raise OpenmanoBadParamsException("Descriptor must contain only one 'tenant' field")
+ if name:
+ descriptor['tenant']['name'] = name
+ if description:
+ descriptor['tenant']['description'] = description
+
+ return self._create_item("tenants", descriptor, all_tenants=None)
+
+ def edit_tenant(self, uuid=None, name=None, descriptor=None, descriptor_format=None, new_name=None, new_description=None):
+ '''Edit the parameters of a tenant
+ Params: must supply a descriptor or/and a new_name or new_description
+ uuid or/and name. If only name is supplied, there must be only one or an exception is raised
+ descriptor: with format {'tenant':{params to change info}}
+ must be a dictionary or a json/yaml text.
+ name: the tenant name. Overwrite descriptor name if any
+ description: tenant descriptor.. Overwrite descriptor description if any
+ Return: Raises an exception on error, not found or found several
+ Obtain a dictionary with format {'tenant':{newtenant_info}}
+ '''
+
+ if isinstance(descriptor, str):
+ descriptor = self.parse(descriptor, descriptor_format)
+ elif descriptor:
+ pass
+ elif new_name or new_description:
+ descriptor={"tenant": {}}
+ else:
+ raise OpenmanoBadParamsException("Missing descriptor")
+
+ if 'tenant' not in descriptor or len(descriptor)!=1:
+ raise OpenmanoBadParamsException("Descriptor must contain only one 'tenant' field")
+ if new_name:
+ descriptor['tenant']['name'] = new_name
+ if new_description:
+ descriptor['tenant']['description'] = new_description
+
+ return self._edit_item("tenants", descriptor, uuid, name, all_tenants=None)
+
+ #DATACENTERS
+
+ def list_datacenters(self, all_tenants=False, **kwargs):
+ '''Obtain a list of datacenters, that are the VIM information at openmano
+ Params: can be filtered by 'uuid','name','vim_url','type'
+ Return: Raises an exception on error
+ Obtain a dictionary with format {'datacenters':[{datacenter1_info},{datacenter2_info},...]}}
+ '''
+ return self._list_item("datacenters", all_tenants, filter_dict=kwargs)
+
+ def get_datacenter(self, uuid=None, name=None, all_tenants=False):
+ '''Obtain the information of a datacenter
+ Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
+ Return: Raises an exception on error, not found, found several
+ Obtain a dictionary with format {'datacenter':{datacenter_info}}
+ '''
+ return self._get_item("datacenters", uuid, name, all_tenants)
+
+ def delete_datacenter(self, uuid=None, name=None):
+ '''Delete a datacenter
+ Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
+ Return: Raises an exception on error, not found, found several, not free
+ Obtain a dictionary with format {'result': text indicating deleted}
+ '''
+ if not uuid:
+ # check that exist
+ uuid = self._get_item_uuid("datacenters", uuid, name, all_tenants=True)
+ return self._del_item("datacenters", uuid, name, all_tenants=None)
+
+ def create_datacenter(self, descriptor=None, descriptor_format=None, name=None, vim_url=None, **kwargs):
+#, type="openvim", public=False, description=None):
+ '''Creates a datacenter
+ Params: must supply a descriptor or/and just a name and vim_url
+ descriptor: with format {'datacenter':{new_datacenter_info}}
+ newdatacenter_info must contain 'name', 'vim_url', and optionally 'description'
+ must be a dictionary or a json/yaml text.
+ name: the datacenter name. Overwrite descriptor name if any
+ vim_url: the datacenter URL. Overwrite descriptor vim_url if any
+ vim_url_admin: the datacenter URL for administrative issues. Overwrite descriptor vim_url if any
+ vim_type: the datacenter type, can be openstack or openvim. Overwrite descriptor type if any
+ public: boolean, by default not public
+ description: datacenter description. Overwrite descriptor description if any
+ config: dictionary with extra configuration for the concrete datacenter
+ Return: Raises an exception on error
+ Obtain a dictionary with format {'datacenter':{new_datacenter_info}}
+ '''
+ if isinstance(descriptor, str):
+ descriptor = self.parse(descriptor, descriptor_format)
+ elif descriptor:
+ pass
+ elif name and vim_url:
+ descriptor={"datacenter": {"name": name, "vim_url": vim_url}}
+ else:
+ raise OpenmanoBadParamsException("Missing descriptor, or name and vim_url")
+
+ if 'datacenter' not in descriptor or len(descriptor)!=1:
+ raise OpenmanoBadParamsException("Descriptor must contain only one 'datacenter' field")
+ if name:
+ descriptor['datacenter']['name'] = name
+ if vim_url:
+ descriptor['datacenter']['vim_url'] = vim_url
+ for param in kwargs:
+ descriptor['datacenter'][param] = kwargs[param]
+
+ return self._create_item("datacenters", descriptor, all_tenants=None)
+
+ def edit_datacenter(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False, **kwargs):
+ '''Edit the parameters of a datacenter
+ Params: must supply a descriptor or/and a parameter to change
+ uuid or/and name. If only name is supplied, there must be only one or an exception is raised
+ descriptor: with format {'datacenter':{params to change info}}
+ must be a dictionary or a json/yaml text.
+ parameters to change can be supplyied by the descriptor or as parameters:
+ new_name: the datacenter name
+ vim_url: the datacenter URL
+ vim_url_admin: the datacenter URL for administrative issues
+ vim_type: the datacenter type, can be openstack or openvim.
+ public: boolean, available to other tenants
+ description: datacenter description
+ Return: Raises an exception on error, not found or found several
+ Obtain a dictionary with format {'datacenter':{new_datacenter_info}}
+ '''
+
+ if isinstance(descriptor, str):
+ descriptor = self.parse(descriptor, descriptor_format)
+ elif descriptor:
+ pass
+ elif kwargs:
+ descriptor={"datacenter": {}}
+ else:
+ raise OpenmanoBadParamsException("Missing descriptor")
+
+ if 'datacenter' not in descriptor or len(descriptor)!=1:
+ raise OpenmanoBadParamsException("Descriptor must contain only one 'datacenter' field")
+ for param in kwargs:
+ if param=='new_name':
+ descriptor['datacenter']['name'] = kwargs[param]
+ else:
+ descriptor['datacenter'][param] = kwargs[param]
+ return self._edit_item("datacenters", descriptor, uuid, name, all_tenants=None)
+
+ def attach_datacenter(self, uuid=None, name=None, descriptor=None, descriptor_format=None, vim_user=None, vim_password=None, vim_tenant_name=None, vim_tenant_id=None):
+ #check that exist
+ uuid = self._get_item_uuid("datacenters", uuid, name, all_tenants=True)
+ tenant_text = "/"+self._get_tenant()
+
+ if isinstance(descriptor, str):
+ descriptor = self.parse(descriptor, descriptor_format)
+ elif descriptor:
+ pass
+ elif vim_user or vim_password or vim_tenant_name or vim_tenant_id:
+ descriptor={"datacenter": {}}
+ else:
+ raise OpenmanoBadParamsException("Missing descriptor or params")
+
+ if vim_user or vim_password or vim_tenant_name or vim_tenant_id:
+ #print args.name
+ try:
+ if vim_user:
+ descriptor['datacenter']['vim_user'] = vim_user
+ if vim_password:
+ descriptor['datacenter']['vim_password'] = vim_password
+ if vim_tenant_name:
+ descriptor['datacenter']['vim_tenant_name'] = vim_tenant_name
+ if vim_tenant_id:
+ descriptor['datacenter']['vim_tenant'] = vim_tenant_id
+ except (KeyError, TypeError) as e:
+ if str(e)=='datacenter': error_pos= "missing field 'datacenter'"
+ else: error_pos="wrong format"
+ raise OpenmanoBadParamsException("Wrong datacenter descriptor: " + error_pos)
+
+ payload_req = yaml.safe_dump(descriptor)
+ #print payload_req
+ URLrequest = "{}{}/datacenters/{}".format(self.endpoint_url, tenant_text, uuid)
+ self.logger.debug("openmano POST %s %s", URLrequest, payload_req)
+ mano_response = requests.post(URLrequest, headers = self.headers_req, data=payload_req)
+ self.logger.debug("openmano response: %s", mano_response.text )
+
+ content = self._parse_yaml(mano_response.text, response=True)
+ if mano_response.status_code==200:
+ return content
+ else:
+ raise OpenmanoResponseException(str(content))
+
+ def detach_datacenter(self, uuid=None, name=None):
+ if not uuid:
+ #check that exist
+ uuid = self._get_item_uuid("datacenters", uuid, name, all_tenants=False)
+ tenant_text = "/"+self._get_tenant()
+ URLrequest = "{}{}/datacenters/{}".format(self.endpoint_url, tenant_text, uuid)
+ self.logger.debug("openmano DELETE %s", URLrequest)
+ mano_response = requests.delete(URLrequest, headers = self.headers_req)
+ self.logger.debug("openmano response: %s", mano_response.text )
+
+ content = self._parse_yaml(mano_response.text, response=True)
+ if mano_response.status_code==200:
+ return content
+ else:
+ raise OpenmanoResponseException(str(content))
+
+ #VNFS
+ def list_vnfs(self, all_tenants=False, **kwargs):
+ '''Obtain a list of vnfs
+ Params: can be filtered by 'uuid','name','description','public', "tenant_id"
+ Return: Raises an exception on error
+ Obtain a dictionary with format {'vnfs':[{vnf1_info},{vnf2_info},...]}}
+ '''
+ return self._list_item("vnfs", all_tenants, kwargs)
+
+ def get_vnf(self, uuid=None, name=None, all_tenants=False):
+ '''Obtain the information of a vnf
+ Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
+ Return: Raises an exception on error, not found, found several
+ Obtain a dictionary with format {'vnf':{vnf_info}}
+ '''
+ return self._get_item("vnfs", uuid, name, all_tenants)
+
+ def delete_vnf(self, uuid=None, name=None, all_tenants=False):
+ '''Delete a vnf
+ Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
+ Return: Raises an exception on error, not found, found several, not free
+ Obtain a dictionary with format {'result': text indicating deleted}
+ '''
+ return self._del_item("vnfs", uuid, name, all_tenants)
+
+ def create_vnf(self, descriptor=None, descriptor_format=None, **kwargs):
+ '''Creates a vnf
+ Params: must supply a descriptor
+ descriptor: with format {'vnf':{new_vnf_info}}
+ must be a dictionary or a json/yaml text.
+ must be a dictionary or a json/yaml text.
+ Other parameters can be:
+ name: the vnf name. Overwrite descriptor name if any
+ image_path: Can be a string or a string list. Overwrite the image_path at descriptor
+ description: vnf descriptor.. Overwrite descriptor description if any
+ public: boolean, available to other tenants
+ class: user text for vnf classification
+ tenant_id: Propietary tenant
+ ...
+ Return: Raises an exception on error
+ Obtain a dictionary with format {'vnf':{new_vnf_info}}
+ '''
+ if isinstance(descriptor, str):
+ descriptor = self.parse(descriptor, descriptor_format)
+ elif descriptor:
+ pass
+ else:
+ raise OpenmanoBadParamsException("Missing descriptor")
+
+ if 'vnf' not in descriptor or len(descriptor)>2:
+ raise OpenmanoBadParamsException("Descriptor must contain only one 'vnf' field, and an optional version")
+ for param in kwargs:
+ if param == 'image_path':
+ #print args.name
+ try:
+ if isinstance(kwargs[param], str):
+ descriptor['vnf']['VNFC'][0]['VNFC image']=kwargs[param]
+ elif isinstance(kwargs[param], tuple) or isinstance(kwargs[param], list):
+ index=0
+ for image_path_ in kwargs[param]:
+ #print "image-path", image_path_
+ descriptor['vnf']['VNFC'][index]['VNFC image']=image_path_
+ index=index+1
+ else:
+ raise OpenmanoBadParamsException("Wrong image_path type. Expected text or a text list")
+ except (KeyError, TypeError) as e:
+ if str(e)=='vnf': error_pos= "missing field 'vnf'"
+ elif str(e)=='VNFC': error_pos= "missing field 'vnf':'VNFC'"
+ elif str(e)==str(index): error_pos= "field 'vnf':'VNFC' must be an array"
+ elif str(e)=='VNFC image': error_pos= "missing field 'vnf':'VNFC'['VNFC image']"
+ else: error_pos="wrong format"
+ raise OpenmanoBadParamsException("Wrong VNF descriptor: " + error_pos)
+ else:
+ descriptor['vnf'][param] = kwargs[param]
+ return self._create_item("vnfs", descriptor)
+
+# def edit_vnf(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False, **kwargs):
+# '''Edit the parameters of a vnf
+# Params: must supply a descriptor or/and a parameters to change
+# uuid or/and name. If only name is supplied, there must be only one or an exception is raised
+# descriptor: with format {'vnf':{params to change info}}
+# parameters to change can be supplyied by the descriptor or as parameters:
+# new_name: the vnf name
+# vim_url: the vnf URL
+# vim_url_admin: the vnf URL for administrative issues
+# vim_type: the vnf type, can be openstack or openvim.
+# public: boolean, available to other tenants
+# description: vnf description
+# Return: Raises an exception on error, not found or found several
+# Obtain a dictionary with format {'vnf':{new_vnf_info}}
+# '''
+#
+# if isinstance(descriptor, str):
+# descriptor = self.parse(descriptor, descriptor_format)
+# elif descriptor:
+# pass
+# elif kwargs:
+# descriptor={"vnf": {}}
+# else:
+# raise OpenmanoBadParamsException("Missing descriptor")
+#
+# if 'vnf' not in descriptor or len(descriptor)>2:
+# raise OpenmanoBadParamsException("Descriptor must contain only one 'vnf' field")
+# for param in kwargs:
+# if param=='new_name':
+# descriptor['vnf']['name'] = kwargs[param]
+# else:
+# descriptor['vnf'][param] = kwargs[param]
+# return self._edit_item("vnfs", descriptor, uuid, name, all_tenants=None)
+
+ #SCENARIOS
+ def list_scenarios(self, all_tenants=False, **kwargs):
+ '''Obtain a list of scenarios
+ Params: can be filtered by 'uuid','name','description','public', "tenant_id"
+ Return: Raises an exception on error
+ Obtain a dictionary with format {'scenarios':[{scenario1_info},{scenario2_info},...]}}
+ '''
+ return self._list_item("scenarios", all_tenants, kwargs)
+
+ def get_scenario(self, uuid=None, name=None, all_tenants=False):
+ '''Obtain the information of a scenario
+ Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
+ Return: Raises an exception on error, not found, found several
+ Obtain a dictionary with format {'scenario':{scenario_info}}
+ '''
+ return self._get_item("scenarios", uuid, name, all_tenants)
+
+ def delete_scenario(self, uuid=None, name=None, all_tenants=False):
+ '''Delete a scenario
+ Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
+ Return: Raises an exception on error, not found, found several, not free
+ Obtain a dictionary with format {'result': text indicating deleted}
+ '''
+ return self._del_item("scenarios", uuid, name, all_tenants)
+
+ def create_scenario(self, descriptor=None, descriptor_format=None, **kwargs):
+ '''Creates a scenario
+ Params: must supply a descriptor
+ descriptor: with format {'scenario':{new_scenario_info}}
+ must be a dictionary or a json/yaml text.
+ Other parameters can be:
+ name: the scenario name. Overwrite descriptor name if any
+ description: scenario descriptor.. Overwrite descriptor description if any
+ public: boolean, available to other tenants
+ tenant_id. Propietary tenant
+ Return: Raises an exception on error
+ Obtain a dictionary with format {'scenario':{new_scenario_info}}
+ '''
+ if isinstance(descriptor, str):
+ descriptor = self.parse(descriptor, descriptor_format)
+ elif descriptor:
+ pass
+ else:
+ raise OpenmanoBadParamsException("Missing descriptor")
+
+ if 'scenario' not in descriptor or len(descriptor)>2:
+ raise OpenmanoBadParamsException("Descriptor must contain only one 'scenario' field, and an optional version")
+ for param in kwargs:
+ descriptor['scenario'][param] = kwargs[param]
+ return self._create_item("scenarios", descriptor)
+
+ def edit_scenario(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False, **kwargs):
+ '''Edit the parameters of a scenario
+ Params: must supply a descriptor or/and a parameters to change
+ uuid or/and name. If only name is supplied, there must be only one or an exception is raised
+ descriptor: with format {'scenario':{params to change info}}
+ must be a dictionary or a json/yaml text.
+ parameters to change can be supplyied by the descriptor or as parameters:
+ new_name: the scenario name
+ public: boolean, available to other tenants
+ description: scenario description
+ tenant_id. Propietary tenant
+ Return: Raises an exception on error, not found or found several
+ Obtain a dictionary with format {'scenario':{new_scenario_info}}
+ '''
+
+ if isinstance(descriptor, str):
+ descriptor = self.parse(descriptor, descriptor_format)
+ elif descriptor:
+ pass
+ elif kwargs:
+ descriptor={"scenario": {}}
+ else:
+ raise OpenmanoBadParamsException("Missing descriptor")
+
+ if 'scenario' not in descriptor or len(descriptor)>2:
+ raise OpenmanoBadParamsException("Descriptor must contain only one 'scenario' field")
+ for param in kwargs:
+ if param=='new_name':
+ descriptor['scenario']['name'] = kwargs[param]
+ else:
+ descriptor['scenario'][param] = kwargs[param]
+ return self._edit_item("scenarios", descriptor, uuid, name, all_tenants=None)
+
+
+ #INSTANCE-SCENARIOS
+ def list_instances(self, all_tenants=False, **kwargs):
+ '''Obtain a list of instances
+ Params: can be filtered by 'uuid','name','description','scenario_id', "tenant_id"
+ Return: Raises an exception on error
+ Obtain a dictionary with format {'instances':[{instance1_info},{instance2_info},...]}}
+ '''
+ return self._list_item("instances", all_tenants, kwargs)
+
+ def get_instance(self, uuid=None, name=None, all_tenants=False):
+ '''Obtain the information of a instance
+ Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
+ Return: Raises an exception on error, not found, found several
+ Obtain a dictionary with format {'instance':{instance_info}}
+ '''
+ return self._get_item("instances", uuid, name, all_tenants)
+
+ def delete_instance(self, uuid=None, name=None, all_tenants=False):
+ '''Delete a instance
+ Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
+ Return: Raises an exception on error, not found, found several, not free
+ Obtain a dictionary with format {'result': text indicating deleted}
+ '''
+ return self._del_item("instances", uuid, name, all_tenants)
+
+ def create_instance(self, descriptor=None, descriptor_format=None, name=None, **kwargs):
+ '''Creates a instance
+ Params: must supply a descriptor or/and a name and scenario
+ descriptor: with format {'instance':{new_instance_info}}
+ must be a dictionary or a json/yaml text.
+ name: the instance name. Overwrite descriptor name if any
+ Other parameters can be:
+ description: instance descriptor.. Overwrite descriptor description if any
+ datacenter_name, datacenter_id: datacenter where to be deployed
+ scenario_name, scenario_id: Scenario this instance is based on
+ Return: Raises an exception on error
+ Obtain a dictionary with format {'instance':{new_instance_info}}
+ '''
+ if isinstance(descriptor, str):
+ descriptor = self.parse(descriptor, descriptor_format)
+ elif descriptor:
+ pass
+ elif name and ("scenario_name" in kwargs or "scenario_id" in kwargs):
+ descriptor = {"instance":{"name": name}}
+ else:
+ raise OpenmanoBadParamsException("Missing descriptor")
+
+ if 'instance' not in descriptor or len(descriptor)>2:
+ raise OpenmanoBadParamsException("Descriptor must contain only one 'instance' field, and an optional version")
+ if name:
+ descriptor['instance']["name"] = name
+ if "scenario_name" in kwargs or "scenario_id" in kwargs:
+ descriptor['instance']["scenario"] = self._get_item_uuid("scenarios", kwargs.get("scenario_id"), kwargs.get("scenario_name"))
+ if "datacenter_name" in kwargs or "datacenter_id" in kwargs:
+ descriptor['instance']["datacenter"] = self._get_item_uuid("datacenters", kwargs.get("datacenter_id"), kwargs.get("datacenter_name"))
+ if "description" in kwargs:
+ descriptor['instance']["description"] = kwargs.get("description")
+ #for param in kwargs:
+ # descriptor['instance'][param] = kwargs[param]
+ if "datacenter" not in descriptor['instance']:
+ descriptor['instance']["datacenter"] = self._get_datacenter()
+ return self._create_item("instances", descriptor)
+
+ #VIM ACTIONS
+ def vim_action(self, action, item, uuid=None, all_tenants=False, **kwargs):
+ '''Perform an action over a vim
+ Params:
+ action: can be 'list', 'get'/'show', 'delete' or 'create'
+ item: can be 'tenants' or 'networks'
+ uuid: uuid of the tenant/net to show or to delete. Ignore otherwise
+ other parameters:
+ datacenter_name, datacenter_id: datacenters to act on, if missing uses classes store datacenter
+ descriptor, descriptor_format: descriptor needed on creation, can be a dict or a yaml/json str
+ must be a dictionary or a json/yaml text.
+ name: for created tenant/net Overwrite descriptor name if any
+ description: tenant descriptor. Overwrite descriptor description if any
+
+ Return: Raises an exception on error
+ Obtain a dictionary with format {'tenant':{new_tenant_info}}
+ '''
+ if item not in ("tenants", "networks", "images"):
+ raise OpenmanoBadParamsException("Unknown value for item '{}', must be 'tenants', 'nets' or "
+ "images".format(str(item)))
+
+ image_actions = ['list','get','show','delete']
+ if item == "images" and action not in image_actions:
+ raise OpenmanoBadParamsException("Only available actions for item '{}' are {}\n"
+ "Requested action was '{}'".format(item, ', '.join(image_actions), action))
+ if all_tenants:
+ tenant_text = "/any"
+ else:
+ tenant_text = "/"+self._get_tenant()
+
+ if "datacenter_id" in kwargs or "datacenter_name" in kwargs:
+ datacenter = self._get_item_uuid("datacenters", kwargs.get("datacenter_id"), kwargs.get("datacenter_name"), all_tenants=all_tenants)
+ else:
+ datacenter = self._get_datacenter()
+
+ if action=="list":
+ URLrequest = "{}{}/vim/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item)
+ self.logger.debug("GET %s", URLrequest )
+ mano_response = requests.get(URLrequest, headers=self.headers_req)
+ self.logger.debug("openmano response: %s", mano_response.text )
+ content = self._parse_yaml(mano_response.text, response=True)
+ if mano_response.status_code==200:
+ return content
+ else:
+ raise OpenmanoResponseException(str(content))
+ elif action=="get" or action=="show":
+ URLrequest = "{}{}/vim/{}/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item, uuid)
+ self.logger.debug("GET %s", URLrequest )
+ mano_response = requests.get(URLrequest, headers=self.headers_req)
+ self.logger.debug("openmano response: %s", mano_response.text )
+ content = self._parse_yaml(mano_response.text, response=True)
+ if mano_response.status_code==200:
+ return content
+ else:
+ raise OpenmanoResponseException(str(content))
+ elif action=="delete":
+ URLrequest = "{}{}/vim/{}/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item, uuid)
+ self.logger.debug("DELETE %s", URLrequest )
+ mano_response = requests.delete(URLrequest, headers=self.headers_req)
+ self.logger.debug("openmano response: %s", mano_response.text )
+ content = self._parse_yaml(mano_response.text, response=True)
+ if mano_response.status_code==200:
+ return content
+ else:
+ raise OpenmanoResponseException(str(content))
+ elif action=="create":
+ if "descriptor" in kwargs:
+ if isinstance(kwargs["descriptor"], str):
+ descriptor = self._parse(kwargs["descriptor"], kwargs.get("descriptor_format") )
+ else:
+ descriptor = kwargs["descriptor"]
+ elif "name" in kwargs:
+ descriptor={item[:-1]: {"name": kwargs["name"]}}
+ else:
+ raise OpenmanoResponseException("Missing descriptor")
+
+ if item[:-1] not in descriptor or len(descriptor)!=1:
+ raise OpenmanoBadParamsException("Descriptor must contain only one 'tenant' field")
+ if "name" in kwargs:
+ descriptor[ item[:-1] ]['name'] = kwargs["name"]
+ if "description" in kwargs:
+ descriptor[ item[:-1] ]['description'] = kwargs["description"]
+ payload_req = yaml.safe_dump(descriptor)
+ #print payload_req
+ URLrequest = "{}{}/vim/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item)
+ self.logger.debug("openmano POST %s %s", URLrequest, payload_req)
+ mano_response = requests.post(URLrequest, headers = self.headers_req, data=payload_req)
+ self.logger.debug("openmano response: %s", mano_response.text )
+ content = self._parse_yaml(mano_response.text, response=True)
+ if mano_response.status_code==200:
+ return content
+ else:
+ raise OpenmanoResponseException(str(content))
+ else:
+ raise OpenmanoBadParamsException("Unknown value for action '{}".format(str(action)))
+
--- /dev/null
+# -*- 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
+##
+
+'''
+utils is a module that implements functions that are used by all openmano modules,
+dealing with aspects such as reading/writing files, formatting inputs/outputs for quick translation
+from dictionaries to appropriate database dictionaries, etc.
+'''
+__author__="Alfonso Tierno, Gerardo Garcia"
+__date__ ="$08-sep-2014 12:21:22$"
+
+import datetime
+from jsonschema import validate as js_v, exceptions as js_e
+#from bs4 import BeautifulSoup
+
+def read_file(file_to_read):
+ """Reads a file specified by 'file_to_read' and returns (True,<its content as a string>) in case of success or (False, <error message>) in case of failure"""
+ try:
+ f = open(file_to_read, 'r')
+ read_data = f.read()
+ f.close()
+ except Exception as e:
+ return (False, str(e))
+
+ return (True, read_data)
+
+def write_file(file_to_write, text):
+ """Write a file specified by 'file_to_write' and returns (True,NOne) in case of success or (False, <error message>) in case of failure"""
+ try:
+ f = open(file_to_write, 'w')
+ f.write(text)
+ f.close()
+ except Exception as e:
+ return (False, str(e))
+
+ return (True, None)
+
+def format_in(http_response, schema):
+ try:
+ client_data = http_response.json()
+ js_v(client_data, schema)
+ #print "Input data: ", str(client_data)
+ return True, client_data
+ except js_e.ValidationError as exc:
+ print "validate_in error, jsonschema exception ", exc.message, "at", exc.path
+ return False, ("validate_in error, jsonschema exception ", exc.message, "at", exc.path)
+
+def remove_extra_items(data, schema):
+ deleted=[]
+ if type(data) is tuple or type(data) is list:
+ for d in data:
+ a= remove_extra_items(d, schema['items'])
+ if a is not None: deleted.append(a)
+ elif type(data) is dict:
+ #TODO deal with patternProperties
+ if 'properties' not in schema:
+ return None
+ for k in data.keys():
+ if k not in schema['properties'].keys():
+ del data[k]
+ deleted.append(k)
+ else:
+ a = remove_extra_items(data[k], schema['properties'][k])
+ if a is not None: deleted.append({k:a})
+ if len(deleted) == 0: return None
+ elif len(deleted) == 1: return deleted[0]
+ else: return deleted
+
+#def format_html2text(http_content):
+# soup=BeautifulSoup(http_content)
+# text = soup.p.get_text() + " " + soup.pre.get_text()
+# return text
+
+
+def convert_bandwidth(data, reverse=False):
+ '''Check the field bandwidth recursivelly and when found, it removes units and convert to number
+ It assumes that bandwidth is well formed
+ Attributes:
+ 'data': dictionary bottle.FormsDict variable to be checked. None or empty is consideted valid
+ 'reverse': by default convert form str to int (Mbps), if True it convert from number to units
+ Return:
+ None
+ '''
+ 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:
+ convert_bandwidth(data[k], reverse)
+ if "bandwidth" in data:
+ try:
+ value=str(data["bandwidth"])
+ if not reverse:
+ pos = value.find("bps")
+ if pos>0:
+ if value[pos-1]=="G": data["bandwidth"] = int(data["bandwidth"][:pos-1]) * 1000
+ elif value[pos-1]=="k": data["bandwidth"]= int(data["bandwidth"][:pos-1]) / 1000
+ else: data["bandwidth"]= int(data["bandwidth"][:pos-1])
+ else:
+ value = int(data["bandwidth"])
+ if value % 1000 == 0: data["bandwidth"]=str(value/1000) + " Gbps"
+ else: data["bandwidth"]=str(value) + " Mbps"
+ except:
+ print "convert_bandwidth exception for type", type(data["bandwidth"]), " data", data["bandwidth"]
+ return
+ if type(data) is tuple or type(data) is list:
+ for k in data:
+ if type(k) is dict or type(k) is tuple or type(k) is list:
+ convert_bandwidth(k, reverse)
+
+
+
+def convert_datetime2str(var):
+ '''Converts a datetime variable to a string with the format '%Y-%m-%dT%H:%i:%s'
+ It enters recursively in the dict var finding this kind of variables
+ '''
+ if type(var) is dict:
+ for k,v in var.items():
+ if type(v) is datetime.datetime:
+ var[k]= v.strftime('%Y-%m-%dT%H:%M:%S')
+ elif type(v) is dict or type(v) is list or type(v) is tuple:
+ convert_datetime2str(v)
+ if len(var) == 0: return True
+ elif type(var) is list or type(var) is tuple:
+ for v in var:
+ convert_datetime2str(v)
+
+def convert_str2boolean(data, items):
+ '''Check recursively the content of data, and if there is an key contained in items, convert value from string to boolean
+ Done recursively
+ Attributes:
+ 'data': dictionary variable to be checked. None or empty is considered valid
+ 'items': tuple of keys to convert
+ Return:
+ None
+ '''
+ 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:
+ convert_str2boolean(data[k], items)
+ if k in items:
+ if type(data[k]) is str:
+ if data[k]=="false" or data[k]=="False": data[k]=False
+ elif data[k]=="true" or data[k]=="True": data[k]=True
+ if type(data) is tuple or type(data) is list:
+ for k in data:
+ if type(k) is dict or type(k) is tuple or type(k) is list:
+ convert_str2boolean(k, items)
+
+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}$"}
+ id_schema2 = {"type" : "string", "pattern": "^[a-fA-F0-9]{32}$"}
+ try:
+ js_v(uuid, id_schema)
+ return True
+ except js_e.ValidationError:
+ try:
+ js_v(uuid, id_schema2)
+ return True
+ except js_e.ValidationError:
+ return False
+ return False
+
--- /dev/null
+# -*- coding: utf-8 -*-
+
+##
+# 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 thread that interact with the host and the libvirt to manage VM
+One thread will be launched per host
+'''
+__author__ = "Alfonso Tierno"
+__date__ = "$10-feb-2017 12:07:15$"
+
+import threading
+import time
+import Queue
+import logging
+import vimconn
+from db_base import db_base_Exception
+
+
+# from logging import Logger
+# import auxiliary_functions as af
+
+
+def is_task_id(id):
+ return True if id[:5] == "TASK." else False
+
+
+class vim_thread(threading.Thread):
+
+ def __init__(self, vimconn, task_lock, name=None, datacenter_name=None, datacenter_tenant_id=None, db=None, db_lock=None):
+ """Init a thread.
+ Arguments:
+ 'id' number of thead
+ 'name' name of thread
+ 'host','user': host ip or name to manage and user
+ 'db', 'db_lock': database class and lock to use it in exclusion
+ """
+ self.tasksResult = {}
+ """ It will contain a dictionary with
+ task_id:
+ status: enqueued,done,error,deleted,processing
+ result: VIM result,
+ """
+ threading.Thread.__init__(self)
+ self.vim = vimconn
+ self.datacenter_name = datacenter_name
+ self.datacenter_tenant_id = datacenter_tenant_id
+ if not name:
+ self.name = vimconn["id"] + "." + vimconn["config"]["datacenter_tenant_id"]
+ else:
+ self.name = name
+
+ self.logger = logging.getLogger('openmano.vim.'+self.name)
+ self.db = db
+ self.db_lock = db_lock
+
+ self.task_lock = task_lock
+ self.task_queue = Queue.Queue(2000)
+
+ def insert_task(self, task):
+ try:
+ self.task_queue.put(task, False)
+ return task["id"]
+ except Queue.Full:
+ raise vimconn.vimconnException(self.name + ": timeout inserting a task")
+
+ def del_task(self, task):
+ with self.task_lock:
+ if task["status"] == "enqueued":
+ task["status"] == "deleted"
+ return True
+ else: # task["status"] == "processing"
+ self.task_lock.release()
+ return False
+
+ def run(self):
+ self.logger.debug("Starting")
+ while True:
+ #TODO reload service
+ while True:
+ if not self.task_queue.empty():
+ task = self.task_queue.get()
+ self.task_lock.acquire()
+ if task["status"] == "deleted":
+ self.task_lock.release()
+ continue
+ task["status"] == "processing"
+ self.task_lock.release()
+ else:
+ now=time.time()
+ time.sleep(1)
+ continue
+ self.logger.debug("processing task id={} name={} params={}".format(task["id"], task["name"],
+ str(task["params"])))
+ if task["name"] == 'exit' or task["name"] == 'reload':
+ result, content = self.terminate(task)
+ elif task["name"] == 'new-vm':
+ result, content = self.new_vm(task)
+ elif task["name"] == 'del-vm':
+ result, content = self.del_vm(task)
+ elif task["name"] == 'new-net':
+ result, content = self.new_net(task)
+ elif task["name"] == 'del-net':
+ result, content = self.del_net(task)
+ else:
+ error_text = "unknown task {}".format(task["name"])
+ self.logger.error(error_text)
+ result = False
+ content = error_text
+
+ with self.task_lock:
+ task["status"] = "done" if result else "error"
+ task["result"] = content
+ self.task_queue.task_done()
+
+ if task["name"] == 'exit':
+ return 0
+ elif task["name"] == 'reload':
+ break
+
+ self.logger.debug("Finishing")
+
+ def terminate(self, task):
+ return True, None
+
+ def new_net(self, task):
+ try:
+ task_id = task["id"]
+ params = task["params"]
+ net_id = self.vim.new_network(*params)
+ with self.db_lock:
+ self.db.update_rows("instance_nets", UPDATE={"vim_net_id": net_id}, WHERE={"vim_net_id": task_id})
+ return True, net_id
+ except db_base_Exception as e:
+ self.logger.error("Error updating database %s", str(e))
+ return True, net_id
+ except vimconn.vimconnException as e:
+ return False, str(e)
+
+ def new_vm(self, task):
+ try:
+ params = task["params"]
+ task_id = task["id"]
+ depends = task.get("depends")
+ net_list = params[5]
+ for net in net_list:
+ if is_task_id(net["net_id"]): # change task_id into network_id
+ try:
+ task_net = depends[net["net_id"]]
+ with self.task_lock:
+ if task_net["status"] == "error":
+ return False, "Cannot create VM because depends on a network that cannot be created: " + \
+ str(task_net["result"])
+ elif task_net["status"] == "enqueued" or task_net["status"] == "processing":
+ return False, "Cannot create VM because depends on a network still not created"
+ network_id = task_net["result"]
+ net["net_id"] = network_id
+ except Exception as e:
+ return False, "Error trying to map from task_id={} to task result: {}".format(net["net_id"],
+ str(e))
+ vm_id = self.vim.new_vminstance(*params)
+ with self.db_lock:
+ self.db.update_rows("instance_vms", UPDATE={"vim_vm_id": vm_id}, WHERE={"vim_vm_id": task_id})
+ return True, vm_id
+ except db_base_Exception as e:
+ self.logger.error("Error updtaing database %s", str(e))
+ return True, vm_id
+ except vimconn.vimconnException as e:
+ return False, str(e)
+
+ def del_vm(self, task):
+ vm_id = task["params"]
+ if is_task_id(vm_id):
+ try:
+ task_create = task["depends"][vm_id]
+ with self.task_lock:
+ if task_create["status"] == "error":
+ return True, "VM was not created. It has error: " + str(task_create["result"])
+ elif task_create["status"] == "enqueued" or task_create["status"] == "processing":
+ return False, "Cannot delete VM because still creating"
+ vm_id = task_create["result"]
+ except Exception as e:
+ return False, "Error trying to get task_id='{}':".format(vm_id, str(e))
+ try:
+ return True, self.vim.delete_vminstance(vm_id)
+ except vimconn.vimconnException as e:
+ return False, str(e)
+
+ def del_net(self, task):
+ net_id = task["params"]
+ if is_task_id(net_id):
+ try:
+ task_create = task["depends"][net_id]
+ with self.task_lock:
+ if task_create["status"] == "error":
+ return True, "net was not created. It has error: " + str(task_create["result"])
+ elif task_create["status"] == "enqueued" or task_create["status"] == "processing":
+ return False, "Cannot delete net because still creating"
+ net_id = task_create["result"]
+ except Exception as e:
+ return False, "Error trying to get task_id='{}':".format(net_id, str(e))
+ try:
+ return True, self.vim.delete_network(net_id)
+ except vimconn.vimconnException as e:
+ return False, str(e)
+
+
--- /dev/null
+# -*- 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
+##
+
+"""
+vimconn implement an Abstract class for the vim connector plugins
+ with the definition of the method to be implemented.
+"""
+__author__="Alfonso Tierno"
+__date__ ="$16-oct-2015 11:09:29$"
+
+import logging
+
+#Error variables
+HTTP_Bad_Request = 400
+HTTP_Unauthorized = 401
+HTTP_Not_Found = 404
+HTTP_Method_Not_Allowed = 405
+HTTP_Request_Timeout = 408
+HTTP_Conflict = 409
+HTTP_Not_Implemented = 501
+HTTP_Service_Unavailable = 503
+HTTP_Internal_Server_Error = 500
+
+class vimconnException(Exception):
+ """Common and base class Exception for all vimconnector exceptions"""
+ def __init__(self, message, http_code=HTTP_Bad_Request):
+ Exception.__init__(self, message)
+ self.http_code = http_code
+
+class vimconnConnectionException(vimconnException):
+ """Connectivity error with the VIM"""
+ def __init__(self, message, http_code=HTTP_Service_Unavailable):
+ vimconnException.__init__(self, message, http_code)
+
+class vimconnUnexpectedResponse(vimconnException):
+ """Get an wrong response from VIM"""
+ def __init__(self, message, http_code=HTTP_Service_Unavailable):
+ vimconnException.__init__(self, message, http_code)
+
+class vimconnAuthException(vimconnException):
+ """Invalid credentials or authorization to perform this action over the VIM"""
+ def __init__(self, message, http_code=HTTP_Unauthorized):
+ vimconnException.__init__(self, message, http_code)
+
+class vimconnNotFoundException(vimconnException):
+ """The item is not found at VIM"""
+ def __init__(self, message, http_code=HTTP_Not_Found):
+ vimconnException.__init__(self, message, http_code)
+
+class vimconnConflictException(vimconnException):
+ """There is a conflict, e.g. more item found than one"""
+ def __init__(self, message, http_code=HTTP_Conflict):
+ vimconnException.__init__(self, message, http_code)
+
+class vimconnNotSupportedException(vimconnException):
+ """The request is not supported by connector"""
+ def __init__(self, message, http_code=HTTP_Service_Unavailable):
+ vimconnException.__init__(self, message, http_code)
+
+class vimconnNotImplemented(vimconnException):
+ """The method is not implemented by the connected"""
+ def __init__(self, message, http_code=HTTP_Not_Implemented):
+ vimconnException.__init__(self, message, http_code)
+
+class vimconnector():
+ """Abstract base class for all the VIM connector plugins
+ These plugins must implement a vimconnector class derived from this
+ and all these privated methods
+ """
+ def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level=None,
+ config={}, persitent_info={}):
+ """Constructor of VIM
+ Params:
+ 'uuid': id asigned to this VIM
+ 'name': name assigned to this VIM, can be used for logging
+ 'tenant_id', 'tenant_name': (only one of them is mandatory) VIM tenant to be used
+ 'url_admin': (optional), url used for administrative tasks
+ 'user', 'passwd': credentials of the VIM user
+ 'log_level': provider if it should use a different log_level than the general one
+ 'config': dictionary with extra VIM information. This contains a consolidate version of general VIM config
+ at creation and particular VIM config at teh attachment
+ 'persistent_info': dict where the class can store information that will be available among class
+ destroy/creation cycles. This info is unique per VIM/credential. At first call it will contain an
+ empty dict. Useful to store login/tokens information for speed up communication
+
+ Returns: Raise an exception is some needed parameter is missing, but it must not do any connectivity
+ check against the VIM
+ """
+ self.id = uuid
+ self.name = name
+ self.url = url
+ self.url_admin = url_admin
+ self.tenant_id = tenant_id
+ self.tenant_name = tenant_name
+ self.user = user
+ self.passwd = passwd
+ self.config = config
+ self.logger = logging.getLogger('openmano.vim')
+ if log_level:
+ self.logger.setLevel( getattr(logging, log_level) )
+ if not self.url_admin: #try to use normal url
+ self.url_admin = self.url
+
+ def __getitem__(self,index):
+ if index=='tenant_id':
+ return self.tenant_id
+ if index=='tenant_name':
+ return self.tenant_name
+ elif index=='id':
+ return self.id
+ elif index=='name':
+ return self.name
+ elif index=='user':
+ return self.user
+ elif index=='passwd':
+ return self.passwd
+ elif index=='url':
+ return self.url
+ elif index=='url_admin':
+ return self.url_admin
+ elif index=="config":
+ return self.config
+ else:
+ raise KeyError("Invalid key '%s'" %str(index))
+
+ def __setitem__(self,index, value):
+ if index=='tenant_id':
+ self.tenant_id = value
+ if index=='tenant_name':
+ self.tenant_name = value
+ elif index=='id':
+ self.id = value
+ elif index=='name':
+ self.name = value
+ elif index=='user':
+ self.user = value
+ elif index=='passwd':
+ self.passwd = value
+ elif index=='url':
+ self.url = value
+ elif index=='url_admin':
+ self.url_admin = value
+ else:
+ raise KeyError("Invalid key '%s'" %str(index))
+
+ def check_vim_connectivity(self):
+ """Checks VIM can be reached and user credentials are ok.
+ Returns None if success or raised vimconnConnectionException, vimconnAuthException, ...
+ """
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def new_tenant(self,tenant_name,tenant_description):
+ """Adds a new tenant to VIM with this name and description, this is done using admin_url if provided
+ "tenant_name": string max lenght 64
+ "tenant_description": string max length 256
+ returns the tenant identifier or raise exception
+ """
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def delete_tenant(self,tenant_id,):
+ """Delete a tenant from VIM
+ tenant_id: returned VIM tenant_id on "new_tenant"
+ Returns None on success. Raises and exception of failure. If tenant is not found raises vimconnNotFoundException
+ """
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def get_tenant_list(self, filter_dict={}):
+ """Obtain tenants of VIM
+ filter_dict dictionary that can contain the following keys:
+ name: filter by tenant name
+ id: filter by tenant uuid/id
+ <other VIM specific>
+ Returns the tenant list of dictionaries, and empty list if no tenant match all the filers:
+ [{'name':'<name>, 'id':'<id>, ...}, ...]
+ """
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def new_network(self, net_name, net_type, ip_profile=None, shared=False, vlan=None):
+ """Adds a tenant network to VIM
+ Params:
+ 'net_name': name of the network
+ 'net_type': one of:
+ 'bridge': overlay isolated network
+ 'data': underlay E-LAN network for Passthrough and SRIOV interfaces
+ 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces.
+ 'ip_profile': is a dict containing the IP parameters of the network (Currently only IPv4 is implemented)
+ 'ip-version': can be one of ["IPv4","IPv6"]
+ 'subnet-address': ip_prefix_schema, that is X.X.X.X/Y
+ 'gateway-address': (Optional) ip_schema, that is X.X.X.X
+ 'dns-address': (Optional) ip_schema,
+ 'dhcp': (Optional) dict containing
+ 'enabled': {"type": "boolean"},
+ 'start-address': ip_schema, first IP to grant
+ 'count': number of IPs to grant.
+ 'shared': if this network can be seen/use by other tenants/organization
+ 'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network
+ Returns the network identifier on success or raises and exception on failure
+ """
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def get_network_list(self, filter_dict={}):
+ """Obtain tenant networks of VIM
+ Params:
+ 'filter_dict' (optional) contains entries to return only networks that matches ALL entries:
+ name: string => returns only networks with this name
+ id: string => returns networks with this VIM id, this imply returns one network at most
+ shared: boolean >= returns only networks that are (or are not) shared
+ tenant_id: sting => returns only networks that belong to this tenant/project
+ ,#(not used yet) admin_state_up: boolean => returns only networks that are (or are not) in admin state active
+ #(not used yet) status: 'ACTIVE','ERROR',... => filter networks that are on this status
+ Returns the network list of dictionaries. each dictionary contains:
+ 'id': (mandatory) VIM network id
+ 'name': (mandatory) VIM network name
+ 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
+ 'error_msg': (optional) text that explains the ERROR status
+ other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
+ List can be empty if no network map the filter_dict. Raise an exception only upon VIM connectivity,
+ authorization, or some other unspecific error
+ """
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def get_network(self, net_id):
+ """Obtain network details from the 'net_id' VIM network
+ Return a dict that contains:
+ 'id': (mandatory) VIM network id, that is, net_id
+ 'name': (mandatory) VIM network name
+ 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
+ 'error_msg': (optional) text that explains the ERROR status
+ other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
+ Raises an exception upon error or when network is not found
+ """
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def delete_network(self, net_id):
+ """Deletes a tenant network from VIM
+ Returns the network identifier or raises an exception upon error or when network is not found
+ """
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def refresh_nets_status(self, net_list):
+ """Get the status of the networks
+ Params:
+ 'net_list': a list with the VIM network id to be get the status
+ Returns a dictionary with:
+ 'net_id': #VIM id of this network
+ status: #Mandatory. Text with one of:
+ # DELETED (not found at vim)
+ # VIM_ERROR (Cannot connect to VIM, authentication problems, VIM response error, ...)
+ # OTHER (Vim reported other status not understood)
+ # ERROR (VIM indicates an ERROR status)
+ # ACTIVE, INACTIVE, DOWN (admin down),
+ # BUILD (on building process)
+ error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
+ vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
+ 'net_id2': ...
+ """
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def get_flavor(self, flavor_id):
+ """Obtain flavor details from the VIM
+ Returns the flavor dict details {'id':<>, 'name':<>, other vim specific }
+ Raises an exception upon error or if not found
+ """
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def get_flavor_id_from_data(self, flavor_dict):
+ """Obtain flavor id that match the flavor description
+ Params:
+ 'flavor_dict': dictionary that contains:
+ 'disk': main hard disk in GB
+ 'ram': meomry in MB
+ 'vcpus': number of virtual cpus
+ #TODO: complete parameters for EPA
+ Returns the flavor_id or raises a vimconnNotFoundException
+ """
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def new_flavor(self, flavor_data):
+ """Adds a tenant flavor to VIM
+ flavor_data contains a dictionary with information, keys:
+ name: flavor name
+ ram: memory (cloud type) in MBytes
+ vpcus: cpus (cloud type)
+ extended: EPA parameters
+ - numas: #items requested in same NUMA
+ memory: number of 1G huge pages memory
+ paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
+ interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
+ - name: interface name
+ dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
+ bandwidth: X Gbps; requested guarantee bandwidth
+ vpci: requested virtual PCI address
+ disk: disk size
+ is_public:
+ #TODO to concrete
+ Returns the flavor identifier"""
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def delete_flavor(self, flavor_id):
+ """Deletes a tenant flavor from VIM identify by its id
+ Returns the used id or raise an exception"""
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def new_image(self, image_dict):
+ """ Adds a tenant image to VIM
+ Returns the image id or raises an exception if failed
+ """
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def delete_image(self, image_id):
+ """Deletes a tenant image from VIM
+ Returns the image_id if image is deleted or raises an exception on error"""
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def get_image_id_from_path(self, path):
+ """Get the image id from image path in the VIM database.
+ Returns the image_id or raises a vimconnNotFoundException
+ """
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def get_image_list(self, filter_dict={}):
+ """Obtain tenant images from VIM
+ Filter_dict can be:
+ name: image name
+ id: image uuid
+ checksum: image checksum
+ location: image path
+ Returns the image list of dictionaries:
+ [{<the fields at Filter_dict plus some VIM specific>}, ...]
+ List can be empty
+ """
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def new_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None,
+ disk_list=None):
+ """Adds a VM instance to VIM
+ Params:
+ 'start': (boolean) indicates if VM must start or created in pause mode.
+ 'image_id','flavor_id': image and flavor VIM id to use for the VM
+ 'net_list': list of interfaces, each one is a dictionary with:
+ 'name': (optional) name for the interface.
+ 'net_id': VIM network id where this interface must be connect to. Mandatory for type==virtual
+ 'vpci': (optional) virtual vPCI address to assign at the VM. Can be ignored depending on VIM capabilities
+ 'model': (optional and only have sense for type==virtual) interface model: virtio, e2000, ...
+ 'mac_address': (optional) mac address to assign to this interface
+ #TODO: CHECK if an optional 'vlan' parameter is needed for VIMs when type if VF and net_id is not provided,
+ the VLAN tag to be used. In case net_id is provided, the internal network vlan is used for tagging VF
+ 'type': (mandatory) can be one of:
+ 'virtual', in this case always connected to a network of type 'net_type=bridge'
+ 'PF' (passthrough): depending on VIM capabilities it can be connected to a data/ptp network ot it
+ can created unconnected
+ 'VF' (SRIOV with VLAN tag): same as PF for network connectivity.
+ 'VFnotShared'(SRIOV without VLAN tag) same as PF for network connectivity. VF where no other VFs
+ are allocated on the same physical NIC
+ 'bw': (optional) only for PF/VF/VFnotShared. Minimal Bandwidth required for the interface in GBPS
+ 'port_security': (optional) If False it must avoid any traffic filtering at this interface. If missing
+ or True, it must apply the default VIM behaviour
+ After execution the method will add the key:
+ 'vim_id': must be filled/added by this method with the VIM identifier generated by the VIM for this
+ interface. 'net_list' is modified
+ 'cloud_config': (optional) dictionary with:
+ 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
+ 'users': (optional) list of users to be inserted, each item is a dict with:
+ 'name': (mandatory) user name,
+ 'key-pairs': (optional) list of strings with the public key to be inserted to the user
+ 'user-data': (optional) string is a text script to be passed directly to cloud-init
+ 'config-files': (optional). List of files to be transferred. Each item is a dict with:
+ 'dest': (mandatory) string with the destination absolute path
+ 'encoding': (optional, by default text). Can be one of:
+ 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
+ 'content' (mandatory): string with the content of the file
+ 'permissions': (optional) string with file permissions, typically octal notation '0644'
+ 'owner': (optional) file owner, string with the format 'owner:group'
+ 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk)
+ 'disk_list': (optional) list with additional disks to the VM. Each item is a dict with:
+ 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
+ 'size': (mandatory) string with the size of the disk in GB
+ Returns the instance identifier or raises an exception on error
+ """
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def get_vminstance(self,vm_id):
+ """Returns the VM instance information from VIM"""
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def delete_vminstance(self, vm_id):
+ """Removes a VM instance from VIM
+ Returns the instance identifier"""
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def refresh_vms_status(self, vm_list):
+ """Get the status of the virtual machines and their interfaces/ports
+ Params: the list of VM identifiers
+ Returns a dictionary with:
+ vm_id: #VIM id of this Virtual Machine
+ status: #Mandatory. Text with one of:
+ # DELETED (not found at vim)
+ # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
+ # OTHER (Vim reported other status not understood)
+ # ERROR (VIM indicates an ERROR status)
+ # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
+ # BUILD (on building process), ERROR
+ # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
+ #
+ error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
+ vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
+ interfaces: list with interface info. Each item a dictionary with:
+ vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
+ mac_address: #Text format XX:XX:XX:XX:XX:XX
+ vim_net_id: #network id where this interface is connected, if provided at creation
+ vim_interface_id: #interface/port VIM id
+ ip_address: #null, or text with IPv4, IPv6 address
+ physical_compute: #identification of compute node where PF,VF interface is allocated
+ physical_pci: #PCI address of the NIC that hosts the PF,VF
+ physical_vlan: #physical VLAN used for VF
+ """
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def action_vminstance(self, vm_id, action_dict):
+ """Send and action over a VM instance from VIM
+ Returns the vm_id if the action was successfully sent to the VIM"""
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def get_vminstance_console(self, vm_id, console_type="vnc"):
+ """
+ Get a console for the virtual machine
+ Params:
+ vm_id: uuid of the VM
+ console_type, can be:
+ "novnc" (by default), "xvpvnc" for VNC types,
+ "rdp-html5" for RDP types, "spice-html5" for SPICE types
+ Returns dict with the console parameters:
+ protocol: ssh, ftp, http, https, ...
+ server: usually ip address
+ port: the http, ssh, ... port
+ suffix: extra text, e.g. the http path and query string
+ """
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+#NOT USED METHODS in current version
+
+ def host_vim2gui(self, host, server_dict):
+ """Transform host dictionary from VIM format to GUI format,
+ and append to the server_dict
+ """
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def get_hosts_info(self):
+ """Get the information of deployed hosts
+ Returns the hosts content"""
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def get_hosts(self, vim_tenant):
+ """Get the hosts and deployed instances
+ Returns the hosts content"""
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def get_processor_rankings(self):
+ """Get the processor rankings in the VIM database"""
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def new_host(self, host_data):
+ """Adds a new host to VIM"""
+ """Returns status code of the VIM response"""
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def new_external_port(self, port_data):
+ """Adds a external port to VIM"""
+ """Returns the port identifier"""
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def new_external_network(self,net_name,net_type):
+ """Adds a external network to VIM (shared)"""
+ """Returns the network identifier"""
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def connect_port_network(self, port_id, network_id, admin=False):
+ """Connects a external port to a network"""
+ """Returns status code of the VIM response"""
+ raise vimconnNotImplemented( "Should have implemented this" )
+
+ def new_vminstancefromJSON(self, vm_data):
+ """Adds a VM instance to VIM"""
+ """Returns the instance identifier"""
+ raise vimconnNotImplemented( "Should have implemented this" )
+
--- /dev/null
+# -*- 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
+##
+
+'''
+osconnector implements all the methods to interact with openstack using the python-client.
+'''
+__author__="Alfonso Tierno, Gerardo Garcia, Pablo Montes, xFlow Research"
+__date__ ="$22-jun-2014 11:19:29$"
+
+import vimconn
+import json
+import yaml
+import logging
+import netaddr
+import time
+import yaml
+import random
+
+from novaclient import client as nClient_v2, exceptions as nvExceptions
+from novaclient import api_versions
+import keystoneclient.v2_0.client as ksClient_v2
+from novaclient.v2.client import Client as nClient
+import keystoneclient.v3.client as ksClient
+import keystoneclient.exceptions as ksExceptions
+import glanceclient.v2.client as glClient
+import glanceclient.client as gl1Client
+import glanceclient.exc as gl1Exceptions
+import cinderclient.v2.client as cClient_v2
+from httplib import HTTPException
+from neutronclient.neutron import client as neClient_v2
+from neutronclient.v2_0 import client as neClient
+from neutronclient.common import exceptions as neExceptions
+from requests.exceptions import ConnectionError
+
+'''contain the openstack virtual machine status to openmano status'''
+vmStatus2manoFormat={'ACTIVE':'ACTIVE',
+ 'PAUSED':'PAUSED',
+ 'SUSPENDED': 'SUSPENDED',
+ 'SHUTOFF':'INACTIVE',
+ 'BUILD':'BUILD',
+ 'ERROR':'ERROR','DELETED':'DELETED'
+ }
+netStatus2manoFormat={'ACTIVE':'ACTIVE','PAUSED':'PAUSED','INACTIVE':'INACTIVE','BUILD':'BUILD','ERROR':'ERROR','DELETED':'DELETED'
+ }
+
+#global var to have a timeout creating and deleting volumes
+volume_timeout = 60
+server_timeout = 60
+
+class vimconnector(vimconn.vimconnector):
+ def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None,
+ log_level=None, config={}, persistent_info={}):
+ '''using common constructor parameters. In this case
+ 'url' is the keystone authorization url,
+ 'url_admin' is not use
+ '''
+ self.osc_api_version = 'v2.0'
+ if config.get('APIversion') == 'v3.3':
+ self.osc_api_version = 'v3.3'
+ vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level, config)
+
+ self.persistent_info = persistent_info
+ self.k_creds={}
+ self.n_creds={}
+ if self.config.get("insecure"):
+ self.k_creds["insecure"] = True
+ self.n_creds["insecure"] = True
+ if not url:
+ raise TypeError, 'url param can not be NoneType'
+ self.k_creds['auth_url'] = url
+ self.n_creds['auth_url'] = url
+ if tenant_name:
+ self.k_creds['tenant_name'] = tenant_name
+ self.n_creds['project_id'] = tenant_name
+ if tenant_id:
+ self.k_creds['tenant_id'] = tenant_id
+ self.n_creds['tenant_id'] = tenant_id
+ if user:
+ self.k_creds['username'] = user
+ self.n_creds['username'] = user
+ if passwd:
+ self.k_creds['password'] = passwd
+ self.n_creds['api_key'] = passwd
+ if self.osc_api_version == 'v3.3':
+ self.k_creds['project_name'] = tenant_name
+ self.k_creds['project_id'] = tenant_id
+ if config.get('region_name'):
+ self.k_creds['region_name'] = config.get('region_name')
+ self.n_creds['region_name'] = config.get('region_name')
+
+ self.reload_client = True
+ self.logger = logging.getLogger('openmano.vim.openstack')
+ if log_level:
+ self.logger.setLevel( getattr(logging, log_level) )
+
+ def __setitem__(self,index, value):
+ '''Set individuals parameters
+ Throw TypeError, KeyError
+ '''
+ if index=='tenant_id':
+ self.reload_client=True
+ self.tenant_id = value
+ if self.osc_api_version == 'v3.3':
+ if value:
+ self.k_creds['project_id'] = value
+ self.n_creds['project_id'] = value
+ else:
+ del self.k_creds['project_id']
+ del self.n_creds['project_id']
+ else:
+ if value:
+ self.k_creds['tenant_id'] = value
+ self.n_creds['tenant_id'] = value
+ else:
+ del self.k_creds['tenant_id']
+ del self.n_creds['tenant_id']
+ elif index=='tenant_name':
+ self.reload_client=True
+ self.tenant_name = value
+ if self.osc_api_version == 'v3.3':
+ if value:
+ self.k_creds['project_name'] = value
+ self.n_creds['project_name'] = value
+ else:
+ del self.k_creds['project_name']
+ del self.n_creds['project_name']
+ else:
+ if value:
+ self.k_creds['tenant_name'] = value
+ self.n_creds['project_id'] = value
+ else:
+ del self.k_creds['tenant_name']
+ del self.n_creds['project_id']
+ elif index=='user':
+ self.reload_client=True
+ self.user = value
+ if value:
+ self.k_creds['username'] = value
+ self.n_creds['username'] = value
+ else:
+ del self.k_creds['username']
+ del self.n_creds['username']
+ elif index=='passwd':
+ self.reload_client=True
+ self.passwd = value
+ if value:
+ self.k_creds['password'] = value
+ self.n_creds['api_key'] = value
+ else:
+ del self.k_creds['password']
+ del self.n_creds['api_key']
+ elif index=='url':
+ self.reload_client=True
+ self.url = value
+ if value:
+ self.k_creds['auth_url'] = value
+ self.n_creds['auth_url'] = value
+ else:
+ raise TypeError, 'url param can not be NoneType'
+ else:
+ vimconn.vimconnector.__setitem__(self,index, value)
+
+ def _reload_connection(self):
+ '''Called before any operation, it check if credentials has changed
+ Throw keystoneclient.apiclient.exceptions.AuthorizationFailure
+ '''
+ #TODO control the timing and possible token timeout, but it seams that python client does this task for us :-)
+ if self.reload_client:
+ #test valid params
+ if len(self.n_creds) <4:
+ raise ksExceptions.ClientException("Not enough parameters to connect to openstack")
+ if self.osc_api_version == 'v3.3':
+ self.nova = nClient(api_version=api_versions.APIVersion(version_str='2.0'), **self.n_creds)
+ #TODO To be updated for v3
+ #self.cinder = cClient.Client(**self.n_creds)
+ self.keystone = ksClient.Client(**self.k_creds)
+ self.ne_endpoint=self.keystone.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
+ self.neutron = neClient.Client(api_version=api_versions.APIVersion(version_str='2.0'), endpoint_url=self.ne_endpoint, token=self.keystone.auth_token, **self.k_creds)
+ else:
+ self.nova = nClient_v2.Client(version='2', **self.n_creds)
+ self.cinder = cClient_v2.Client(**self.n_creds)
+ self.keystone = ksClient_v2.Client(**self.k_creds)
+ self.ne_endpoint=self.keystone.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
+ self.neutron = neClient_v2.Client('2.0', endpoint_url=self.ne_endpoint, token=self.keystone.auth_token, **self.k_creds)
+ self.glance_endpoint = self.keystone.service_catalog.url_for(service_type='image', endpoint_type='publicURL')
+ self.glance = glClient.Client(self.glance_endpoint, token=self.keystone.auth_token, **self.k_creds) #TODO check k_creds vs n_creds
+ self.reload_client = False
+
+ def __net_os2mano(self, net_list_dict):
+ '''Transform the net openstack format to mano format
+ net_list_dict can be a list of dict or a single dict'''
+ if type(net_list_dict) is dict:
+ net_list_=(net_list_dict,)
+ elif type(net_list_dict) is list:
+ net_list_=net_list_dict
+ else:
+ raise TypeError("param net_list_dict must be a list or a dictionary")
+ for net in net_list_:
+ if net.get('provider:network_type') == "vlan":
+ net['type']='data'
+ else:
+ net['type']='bridge'
+
+
+
+ def _format_exception(self, exception):
+ '''Transform a keystone, nova, neutron exception into a vimconn exception'''
+ if isinstance(exception, (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError,
+ ConnectionError, ksExceptions.ConnectionError, neExceptions.ConnectionFailed
+ )):
+ raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception))
+ elif isinstance(exception, (nvExceptions.ClientException, ksExceptions.ClientException,
+ neExceptions.NeutronException, nvExceptions.BadRequest)):
+ raise vimconn.vimconnUnexpectedResponse(type(exception).__name__ + ": " + str(exception))
+ elif isinstance(exception, (neExceptions.NetworkNotFoundClient, nvExceptions.NotFound)):
+ raise vimconn.vimconnNotFoundException(type(exception).__name__ + ": " + str(exception))
+ elif isinstance(exception, nvExceptions.Conflict):
+ raise vimconn.vimconnConflictException(type(exception).__name__ + ": " + str(exception))
+ else: # ()
+ raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception))
+
+ def get_tenant_list(self, filter_dict={}):
+ '''Obtain tenants of VIM
+ filter_dict can contain the following keys:
+ name: filter by tenant name
+ id: filter by tenant uuid/id
+ <other VIM specific>
+ Returns the tenant list of dictionaries: [{'name':'<name>, 'id':'<id>, ...}, ...]
+ '''
+ self.logger.debug("Getting tenants from VIM filter: '%s'", str(filter_dict))
+ try:
+ self._reload_connection()
+ if self.osc_api_version == 'v3.3':
+ project_class_list=self.keystone.projects.findall(**filter_dict)
+ else:
+ project_class_list=self.keystone.tenants.findall(**filter_dict)
+ project_list=[]
+ for project in project_class_list:
+ project_list.append(project.to_dict())
+ return project_list
+ except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e:
+ self._format_exception(e)
+
+ def new_tenant(self, tenant_name, tenant_description):
+ '''Adds a new tenant to openstack VIM. Returns the tenant identifier'''
+ self.logger.debug("Adding a new tenant name: %s", tenant_name)
+ try:
+ self._reload_connection()
+ if self.osc_api_version == 'v3.3':
+ project=self.keystone.projects.create(tenant_name, tenant_description)
+ else:
+ project=self.keystone.tenants.create(tenant_name, tenant_description)
+ return project.id
+ except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e:
+ self._format_exception(e)
+
+ def delete_tenant(self, tenant_id):
+ '''Delete a tenant from openstack VIM. Returns the old tenant identifier'''
+ self.logger.debug("Deleting tenant %s from VIM", tenant_id)
+ try:
+ self._reload_connection()
+ if self.osc_api_version == 'v3.3':
+ self.keystone.projects.delete(tenant_id)
+ else:
+ self.keystone.tenants.delete(tenant_id)
+ return tenant_id
+ except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e:
+ self._format_exception(e)
+
+ def new_network(self,net_name, net_type, ip_profile=None, shared=False, vlan=None):
+ '''Adds a tenant network to VIM. Returns the network identifier'''
+ self.logger.debug("Adding a new network to VIM name '%s', type '%s'", net_name, net_type)
+ #self.logger.debug(">>>>>>>>>>>>>>>>>> IP profile %s", str(ip_profile))
+ try:
+ new_net = None
+ self._reload_connection()
+ network_dict = {'name': net_name, 'admin_state_up': True}
+ if net_type=="data" or net_type=="ptp":
+ if self.config.get('dataplane_physical_net') == None:
+ raise vimconn.vimconnConflictException("You must provide a 'dataplane_physical_net' at config value before creating sriov network")
+ network_dict["provider:physical_network"] = self.config['dataplane_physical_net'] #"physnet_sriov" #TODO physical
+ network_dict["provider:network_type"] = "vlan"
+ if vlan!=None:
+ network_dict["provider:network_type"] = vlan
+ network_dict["shared"]=shared
+ new_net=self.neutron.create_network({'network':network_dict})
+ #print new_net
+ #create subnetwork, even if there is no profile
+ if not ip_profile:
+ ip_profile = {}
+ if 'subnet_address' not in ip_profile:
+ #Fake subnet is required
+ subnet_rand = random.randint(0, 255)
+ ip_profile['subnet_address'] = "192.168.{}.0/24".format(subnet_rand)
+ if 'ip_version' not in ip_profile:
+ ip_profile['ip_version'] = "IPv4"
+ subnet={"name":net_name+"-subnet",
+ "network_id": new_net["network"]["id"],
+ "ip_version": 4 if ip_profile['ip_version']=="IPv4" else 6,
+ "cidr": ip_profile['subnet_address']
+ }
+ if 'gateway_address' in ip_profile:
+ subnet['gateway_ip'] = ip_profile['gateway_address']
+ if ip_profile.get('dns_address'):
+ #TODO: manage dns_address as a list of addresses separated by commas
+ subnet['dns_nameservers'] = []
+ subnet['dns_nameservers'].append(ip_profile['dns_address'])
+ if 'dhcp_enabled' in ip_profile:
+ subnet['enable_dhcp'] = False if ip_profile['dhcp_enabled']=="false" else True
+ if 'dhcp_start_address' in ip_profile:
+ subnet['allocation_pools']=[]
+ subnet['allocation_pools'].append(dict())
+ subnet['allocation_pools'][0]['start'] = ip_profile['dhcp_start_address']
+ if 'dhcp_count' in ip_profile:
+ #parts = ip_profile['dhcp_start_address'].split('.')
+ #ip_int = (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])
+ ip_int = int(netaddr.IPAddress(ip_profile['dhcp_start_address']))
+ ip_int += ip_profile['dhcp_count'] - 1
+ ip_str = str(netaddr.IPAddress(ip_int))
+ subnet['allocation_pools'][0]['end'] = ip_str
+ #self.logger.debug(">>>>>>>>>>>>>>>>>> Subnet: %s", str(subnet))
+ self.neutron.create_subnet({"subnet": subnet} )
+ return new_net["network"]["id"]
+ except (neExceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
+ if new_net:
+ self.neutron.delete_network(new_net['network']['id'])
+ self._format_exception(e)
+
+ def get_network_list(self, filter_dict={}):
+ '''Obtain tenant networks of VIM
+ Filter_dict can be:
+ name: network name
+ id: network uuid
+ shared: boolean
+ tenant_id: tenant
+ admin_state_up: boolean
+ status: 'ACTIVE'
+ Returns the network list of dictionaries
+ '''
+ self.logger.debug("Getting network from VIM filter: '%s'", str(filter_dict))
+ try:
+ self._reload_connection()
+ if self.osc_api_version == 'v3.3' and "tenant_id" in filter_dict:
+ filter_dict['project_id'] = filter_dict.pop('tenant_id')
+ net_dict=self.neutron.list_networks(**filter_dict)
+ net_list=net_dict["networks"]
+ self.__net_os2mano(net_list)
+ return net_list
+ except (neExceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
+ self._format_exception(e)
+
+ def get_network(self, net_id):
+ '''Obtain details of network from VIM
+ Returns the network information from a network id'''
+ self.logger.debug(" Getting tenant network %s from VIM", net_id)
+ filter_dict={"id": net_id}
+ net_list = self.get_network_list(filter_dict)
+ if len(net_list)==0:
+ raise vimconn.vimconnNotFoundException("Network '{}' not found".format(net_id))
+ elif len(net_list)>1:
+ raise vimconn.vimconnConflictException("Found more than one network with this criteria")
+ net = net_list[0]
+ subnets=[]
+ for subnet_id in net.get("subnets", () ):
+ try:
+ subnet = self.neutron.show_subnet(subnet_id)
+ except Exception as e:
+ self.logger.error("osconnector.get_network(): Error getting subnet %s %s" % (net_id, str(e)))
+ subnet = {"id": subnet_id, "fault": str(e)}
+ subnets.append(subnet)
+ net["subnets"] = subnets
+ return net
+
+ def delete_network(self, net_id):
+ '''Deletes a tenant network from VIM. Returns the old network identifier'''
+ self.logger.debug("Deleting network '%s' from VIM", net_id)
+ try:
+ self._reload_connection()
+ #delete VM ports attached to this networks before the network
+ ports = self.neutron.list_ports(network_id=net_id)
+ for p in ports['ports']:
+ try:
+ self.neutron.delete_port(p["id"])
+ except Exception as e:
+ self.logger.error("Error deleting port %s: %s", p["id"], str(e))
+ self.neutron.delete_network(net_id)
+ return net_id
+ except (neExceptions.ConnectionFailed, neExceptions.NetworkNotFoundClient, neExceptions.NeutronException,
+ ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
+ self._format_exception(e)
+
+ def refresh_nets_status(self, net_list):
+ '''Get the status of the networks
+ Params: the list of network identifiers
+ Returns a dictionary with:
+ net_id: #VIM id of this network
+ status: #Mandatory. Text with one of:
+ # DELETED (not found at vim)
+ # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
+ # OTHER (Vim reported other status not understood)
+ # ERROR (VIM indicates an ERROR status)
+ # ACTIVE, INACTIVE, DOWN (admin down),
+ # BUILD (on building process)
+ #
+ error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
+ vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
+
+ '''
+ net_dict={}
+ for net_id in net_list:
+ net = {}
+ try:
+ net_vim = self.get_network(net_id)
+ if net_vim['status'] in netStatus2manoFormat:
+ net["status"] = netStatus2manoFormat[ net_vim['status'] ]
+ else:
+ net["status"] = "OTHER"
+ net["error_msg"] = "VIM status reported " + net_vim['status']
+
+ if net['status'] == "ACTIVE" and not net_vim['admin_state_up']:
+ net['status'] = 'DOWN'
+ try:
+ net['vim_info'] = yaml.safe_dump(net_vim, default_flow_style=True, width=256)
+ except yaml.representer.RepresenterError:
+ net['vim_info'] = str(net_vim)
+ if net_vim.get('fault'): #TODO
+ net['error_msg'] = str(net_vim['fault'])
+ except vimconn.vimconnNotFoundException as e:
+ self.logger.error("Exception getting net status: %s", str(e))
+ net['status'] = "DELETED"
+ net['error_msg'] = str(e)
+ except vimconn.vimconnException as e:
+ self.logger.error("Exception getting net status: %s", str(e))
+ net['status'] = "VIM_ERROR"
+ net['error_msg'] = str(e)
+ net_dict[net_id] = net
+ return net_dict
+
+ def get_flavor(self, flavor_id):
+ '''Obtain flavor details from the VIM. Returns the flavor dict details'''
+ self.logger.debug("Getting flavor '%s'", flavor_id)
+ try:
+ self._reload_connection()
+ flavor = self.nova.flavors.find(id=flavor_id)
+ #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
+ return flavor.to_dict()
+ except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException, ConnectionError) as e:
+ self._format_exception(e)
+
+ def get_flavor_id_from_data(self, flavor_dict):
+ """Obtain flavor id that match the flavor description
+ Returns the flavor_id or raises a vimconnNotFoundException
+ """
+ try:
+ self._reload_connection()
+ numa=None
+ numas = flavor_dict.get("extended",{}).get("numas")
+ if numas:
+ #TODO
+ raise vimconn.vimconnNotFoundException("Flavor with EPA still not implemted")
+ # if len(numas) > 1:
+ # raise vimconn.vimconnNotFoundException("Cannot find any flavor with more than one numa")
+ # numa=numas[0]
+ # numas = extended.get("numas")
+ for flavor in self.nova.flavors.list():
+ epa = flavor.get_keys()
+ if epa:
+ continue
+ #TODO
+ if flavor.ram != flavor_dict["ram"]:
+ continue
+ if flavor.vcpus != flavor_dict["vcpus"]:
+ continue
+ if flavor.disk != flavor_dict["disk"]:
+ continue
+ return flavor.id
+ raise vimconn.vimconnNotFoundException("Cannot find any flavor matching '{}'".format(str(flavor_dict)))
+ except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException, ConnectionError) as e:
+ self._format_exception(e)
+
+
+ def new_flavor(self, flavor_data, change_name_if_used=True):
+ '''Adds a tenant flavor to openstack VIM
+ if change_name_if_used is True, it will change name in case of conflict, because it is not supported name repetition
+ Returns the flavor identifier
+ '''
+ self.logger.debug("Adding flavor '%s'", str(flavor_data))
+ retry=0
+ max_retries=3
+ name_suffix = 0
+ name=flavor_data['name']
+ while retry<max_retries:
+ retry+=1
+ try:
+ self._reload_connection()
+ if change_name_if_used:
+ #get used names
+ fl_names=[]
+ fl=self.nova.flavors.list()
+ for f in fl:
+ fl_names.append(f.name)
+ while name in fl_names:
+ name_suffix += 1
+ name = flavor_data['name']+"-" + str(name_suffix)
+
+ ram = flavor_data.get('ram',64)
+ vcpus = flavor_data.get('vcpus',1)
+ numa_properties=None
+
+ extended = flavor_data.get("extended")
+ if extended:
+ numas=extended.get("numas")
+ if numas:
+ numa_nodes = len(numas)
+ if numa_nodes > 1:
+ return -1, "Can not add flavor with more than one numa"
+ numa_properties = {"hw:numa_nodes":str(numa_nodes)}
+ numa_properties["hw:mem_page_size"] = "large"
+ numa_properties["hw:cpu_policy"] = "dedicated"
+ numa_properties["hw:numa_mempolicy"] = "strict"
+ for numa in numas:
+ #overwrite ram and vcpus
+ ram = numa['memory']*1024
+ if 'paired-threads' in numa:
+ vcpus = numa['paired-threads']*2
+ numa_properties["hw:cpu_threads_policy"] = "prefer"
+ elif 'cores' in numa:
+ vcpus = numa['cores']
+ #numa_properties["hw:cpu_threads_policy"] = "prefer"
+ elif 'threads' in numa:
+ vcpus = numa['threads']
+ numa_properties["hw:cpu_policy"] = "isolated"
+ for interface in numa.get("interfaces",() ):
+ if interface["dedicated"]=="yes":
+ raise vimconn.vimconnException("Passthrough interfaces are not supported for the openstack connector", http_code=vimconn.HTTP_Service_Unavailable)
+ #TODO, add the key 'pci_passthrough:alias"="<label at config>:<number ifaces>"' when a way to connect it is available
+
+ #create flavor
+ new_flavor=self.nova.flavors.create(name,
+ ram,
+ vcpus,
+ flavor_data.get('disk',1),
+ is_public=flavor_data.get('is_public', True)
+ )
+ #add metadata
+ if numa_properties:
+ new_flavor.set_keys(numa_properties)
+ return new_flavor.id
+ except nvExceptions.Conflict as e:
+ if change_name_if_used and retry < max_retries:
+ continue
+ self._format_exception(e)
+ #except nvExceptions.BadRequest as e:
+ except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
+ self._format_exception(e)
+
+ def delete_flavor(self,flavor_id):
+ '''Deletes a tenant flavor from openstack VIM. Returns the old flavor_id
+ '''
+ try:
+ self._reload_connection()
+ self.nova.flavors.delete(flavor_id)
+ return flavor_id
+ #except nvExceptions.BadRequest as e:
+ except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
+ self._format_exception(e)
+
+ def new_image(self,image_dict):
+ '''
+ Adds a tenant image to VIM. imge_dict is a dictionary with:
+ name: name
+ disk_format: qcow2, vhd, vmdk, raw (by default), ...
+ location: path or URI
+ public: "yes" or "no"
+ metadata: metadata of the image
+ Returns the image_id
+ '''
+ #using version 1 of glance client
+ glancev1 = gl1Client.Client('1',self.glance_endpoint, token=self.keystone.auth_token, **self.k_creds) #TODO check k_creds vs n_creds
+ retry=0
+ max_retries=3
+ while retry<max_retries:
+ retry+=1
+ try:
+ self._reload_connection()
+ #determine format http://docs.openstack.org/developer/glance/formats.html
+ if "disk_format" in image_dict:
+ disk_format=image_dict["disk_format"]
+ else: #autodiscover based on extension
+ if image_dict['location'][-6:]==".qcow2":
+ disk_format="qcow2"
+ elif image_dict['location'][-4:]==".vhd":
+ disk_format="vhd"
+ elif image_dict['location'][-5:]==".vmdk":
+ disk_format="vmdk"
+ elif image_dict['location'][-4:]==".vdi":
+ disk_format="vdi"
+ elif image_dict['location'][-4:]==".iso":
+ disk_format="iso"
+ elif image_dict['location'][-4:]==".aki":
+ disk_format="aki"
+ elif image_dict['location'][-4:]==".ari":
+ disk_format="ari"
+ elif image_dict['location'][-4:]==".ami":
+ disk_format="ami"
+ else:
+ disk_format="raw"
+ self.logger.debug("new_image: '%s' loading from '%s'", image_dict['name'], image_dict['location'])
+ if image_dict['location'][0:4]=="http":
+ new_image = glancev1.images.create(name=image_dict['name'], is_public=image_dict.get('public',"yes")=="yes",
+ container_format="bare", location=image_dict['location'], disk_format=disk_format)
+ else: #local path
+ with open(image_dict['location']) as fimage:
+ new_image = glancev1.images.create(name=image_dict['name'], is_public=image_dict.get('public',"yes")=="yes",
+ container_format="bare", data=fimage, disk_format=disk_format)
+ #insert metadata. We cannot use 'new_image.properties.setdefault'
+ #because nova and glance are "INDEPENDENT" and we are using nova for reading metadata
+ new_image_nova=self.nova.images.find(id=new_image.id)
+ new_image_nova.metadata.setdefault('location',image_dict['location'])
+ metadata_to_load = image_dict.get('metadata')
+ if metadata_to_load:
+ for k,v in yaml.load(metadata_to_load).iteritems():
+ new_image_nova.metadata.setdefault(k,v)
+ return new_image.id
+ except (nvExceptions.Conflict, ksExceptions.ClientException, nvExceptions.ClientException) as e:
+ self._format_exception(e)
+ except (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError, ConnectionError) as e:
+ if retry==max_retries:
+ continue
+ self._format_exception(e)
+ except IOError as e: #can not open the file
+ raise vimconn.vimconnConnectionException(type(e).__name__ + ": " + str(e)+ " for " + image_dict['location'],
+ http_code=vimconn.HTTP_Bad_Request)
+
+ def delete_image(self, image_id):
+ '''Deletes a tenant image from openstack VIM. Returns the old id
+ '''
+ try:
+ self._reload_connection()
+ self.nova.images.delete(image_id)
+ return image_id
+ except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e: #TODO remove
+ self._format_exception(e)
+
+ def get_image_id_from_path(self, path):
+ '''Get the image id from image path in the VIM database. Returns the image_id'''
+ try:
+ self._reload_connection()
+ images = self.nova.images.list()
+ for image in images:
+ if image.metadata.get("location")==path:
+ return image.id
+ raise vimconn.vimconnNotFoundException("image with location '{}' not found".format( path))
+ except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e:
+ self._format_exception(e)
+
+ def get_image_list(self, filter_dict={}):
+ '''Obtain tenant images from VIM
+ Filter_dict can be:
+ id: image id
+ name: image name
+ checksum: image checksum
+ Returns the image list of dictionaries:
+ [{<the fields at Filter_dict plus some VIM specific>}, ...]
+ List can be empty
+ '''
+ self.logger.debug("Getting image list from VIM filter: '%s'", str(filter_dict))
+ try:
+ self._reload_connection()
+ filter_dict_os=filter_dict.copy()
+ #First we filter by the available filter fields: name, id. The others are removed.
+ filter_dict_os.pop('checksum',None)
+ image_list=self.nova.images.findall(**filter_dict_os)
+ if len(image_list)==0:
+ return []
+ #Then we filter by the rest of filter fields: checksum
+ filtered_list = []
+ for image in image_list:
+ image_class=self.glance.images.get(image.id)
+ if 'checksum' not in filter_dict or image_class['checksum']==filter_dict.get('checksum'):
+ filtered_list.append(image_class.copy())
+ return filtered_list
+ except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e:
+ self._format_exception(e)
+
+ def new_vminstance(self,name,description,start,image_id,flavor_id,net_list,cloud_config=None,disk_list=None):
+ '''Adds a VM instance to VIM
+ Params:
+ start: indicates if VM must start or boot in pause mode. Ignored
+ image_id,flavor_id: iamge and flavor uuid
+ net_list: list of interfaces, each one is a dictionary with:
+ name:
+ net_id: network uuid to connect
+ vpci: virtual vcpi to assign, ignored because openstack lack #TODO
+ model: interface model, ignored #TODO
+ mac_address: used for SR-IOV ifaces #TODO for other types
+ use: 'data', 'bridge', 'mgmt'
+ type: 'virtual', 'PF', 'VF', 'VFnotShared'
+ vim_id: filled/added by this function
+ floating_ip: True/False (or it can be None)
+ #TODO ip, security groups
+ Returns the instance identifier
+ '''
+ self.logger.debug("new_vminstance input: image='%s' flavor='%s' nics='%s'",image_id, flavor_id,str(net_list))
+ try:
+ metadata={}
+ net_list_vim=[]
+ external_network=[] #list of external networks to be connected to instance, later on used to create floating_ip
+ self._reload_connection()
+ metadata_vpci={} #For a specific neutron plugin
+ for net in net_list:
+ if not net.get("net_id"): #skip non connected iface
+ continue
+ if net["type"]=="virtual" or net["type"]=="VF":
+ port_dict={
+ "network_id": net["net_id"],
+ "name": net.get("name"),
+ "admin_state_up": True
+ }
+ if net["type"]=="virtual":
+ if "vpci" in net:
+ metadata_vpci[ net["net_id"] ] = [[ net["vpci"], "" ]]
+ else: # for VF
+ if "vpci" in net:
+ if "VF" not in metadata_vpci:
+ metadata_vpci["VF"]=[]
+ metadata_vpci["VF"].append([ net["vpci"], "" ])
+ port_dict["binding:vnic_type"]="direct"
+ if not port_dict["name"]:
+ port_dict["name"]=name
+ if net.get("mac_address"):
+ port_dict["mac_address"]=net["mac_address"]
+ if net.get("port_security") == False:
+ port_dict["port_security_enabled"]=net["port_security"]
+ new_port = self.neutron.create_port({"port": port_dict })
+ net["mac_adress"] = new_port["port"]["mac_address"]
+ net["vim_id"] = new_port["port"]["id"]
+ net["ip"] = new_port["port"].get("fixed_ips", [{}])[0].get("ip_address")
+ net_list_vim.append({"port-id": new_port["port"]["id"]})
+ else: # for PF
+ self.logger.warn("new_vminstance: Warning, can not connect a passthrough interface ")
+ #TODO insert this when openstack consider passthrough ports as openstack neutron ports
+ if net.get('floating_ip', False):
+ net['exit_on_floating_ip_error'] = True
+ external_network.append(net)
+ elif net['use'] == 'mgmt' and self.config.get('use_floating_ip'):
+ net['exit_on_floating_ip_error'] = False
+ external_network.append(net)
+
+ if metadata_vpci:
+ metadata = {"pci_assignement": json.dumps(metadata_vpci)}
+ if len(metadata["pci_assignement"]) >255:
+ #limit the metadata size
+ #metadata["pci_assignement"] = metadata["pci_assignement"][0:255]
+ self.logger.warn("Metadata deleted since it exceeds the expected length (255) ")
+ metadata = {}
+
+ self.logger.debug("name '%s' image_id '%s'flavor_id '%s' net_list_vim '%s' description '%s' metadata %s",
+ name, image_id, flavor_id, str(net_list_vim), description, str(metadata))
+
+ security_groups = self.config.get('security_groups')
+ if type(security_groups) is str:
+ security_groups = ( security_groups, )
+ #cloud config
+ userdata=None
+ config_drive = None
+ if isinstance(cloud_config, dict):
+ if cloud_config.get("user-data"):
+ userdata=cloud_config["user-data"]
+ if cloud_config.get("boot-data-drive") != None:
+ config_drive = cloud_config["boot-data-drive"]
+ if cloud_config.get("config-files") or cloud_config.get("users") or cloud_config.get("key-pairs"):
+ if userdata:
+ raise vimconn.vimconnConflictException("Cloud-config cannot contain both 'userdata' and 'config-files'/'users'/'key-pairs'")
+ userdata_dict={}
+ #default user
+ if cloud_config.get("key-pairs"):
+ userdata_dict["ssh-authorized-keys"] = cloud_config["key-pairs"]
+ userdata_dict["users"] = [{"default": None, "ssh-authorized-keys": cloud_config["key-pairs"] }]
+ if cloud_config.get("users"):
+ if "users" not in userdata_dict:
+ userdata_dict["users"] = [ "default" ]
+ for user in cloud_config["users"]:
+ user_info = {
+ "name" : user["name"],
+ "sudo": "ALL = (ALL)NOPASSWD:ALL"
+ }
+ if "user-info" in user:
+ user_info["gecos"] = user["user-info"]
+ if user.get("key-pairs"):
+ user_info["ssh-authorized-keys"] = user["key-pairs"]
+ userdata_dict["users"].append(user_info)
+
+ if cloud_config.get("config-files"):
+ userdata_dict["write_files"] = []
+ for file in cloud_config["config-files"]:
+ file_info = {
+ "path" : file["dest"],
+ "content": file["content"]
+ }
+ if file.get("encoding"):
+ file_info["encoding"] = file["encoding"]
+ if file.get("permissions"):
+ file_info["permissions"] = file["permissions"]
+ if file.get("owner"):
+ file_info["owner"] = file["owner"]
+ userdata_dict["write_files"].append(file_info)
+ userdata = "#cloud-config\n"
+ userdata += yaml.safe_dump(userdata_dict, indent=4, default_flow_style=False)
+ self.logger.debug("userdata: %s", userdata)
+ elif isinstance(cloud_config, str):
+ userdata = cloud_config
+
+ #Create additional volumes in case these are present in disk_list
+ block_device_mapping = None
+ base_disk_index = ord('b')
+ if disk_list != None:
+ block_device_mapping = dict()
+ for disk in disk_list:
+ if 'image_id' in disk:
+ volume = self.cinder.volumes.create(size = disk['size'],name = name + '_vd' +
+ chr(base_disk_index), imageRef = disk['image_id'])
+ else:
+ volume = self.cinder.volumes.create(size=disk['size'], name=name + '_vd' +
+ chr(base_disk_index))
+ block_device_mapping['_vd' + chr(base_disk_index)] = volume.id
+ base_disk_index += 1
+
+ #wait until volumes are with status available
+ keep_waiting = True
+ elapsed_time = 0
+ while keep_waiting and elapsed_time < volume_timeout:
+ keep_waiting = False
+ for volume_id in block_device_mapping.itervalues():
+ if self.cinder.volumes.get(volume_id).status != 'available':
+ keep_waiting = True
+ if keep_waiting:
+ time.sleep(1)
+ elapsed_time += 1
+
+ #if we exceeded the timeout rollback
+ if elapsed_time >= volume_timeout:
+ #delete the volumes we just created
+ for volume_id in block_device_mapping.itervalues():
+ self.cinder.volumes.delete(volume_id)
+
+ #delete ports we just created
+ for net_item in net_list_vim:
+ if 'port-id' in net_item:
+ self.neutron.delete_port(net_item['port-id'])
+
+ raise vimconn.vimconnException('Timeout creating volumes for instance ' + name,
+ http_code=vimconn.HTTP_Request_Timeout)
+
+ server = self.nova.servers.create(name, image_id, flavor_id, nics=net_list_vim, meta=metadata,
+ security_groups=security_groups,
+ availability_zone=self.config.get('availability_zone'),
+ key_name=self.config.get('keypair'),
+ userdata=userdata,
+ config_drive = config_drive,
+ block_device_mapping = block_device_mapping
+ ) # , description=description)
+ #print "DONE :-)", server
+ pool_id = None
+ floating_ips = self.neutron.list_floatingips().get("floatingips", ())
+ for floating_network in external_network:
+ try:
+ # wait until vm is active
+ elapsed_time = 0
+ while elapsed_time < server_timeout:
+ status = self.nova.servers.get(server.id).status
+ if status == 'ACTIVE':
+ break
+ time.sleep(1)
+ elapsed_time += 1
+
+ #if we exceeded the timeout rollback
+ if elapsed_time >= server_timeout:
+ raise vimconn.vimconnException('Timeout creating instance ' + name,
+ http_code=vimconn.HTTP_Request_Timeout)
+
+ assigned = False
+ while(assigned == False):
+ if floating_ips:
+ ip = floating_ips.pop(0)
+ if not ip.get("port_id", False) and ip.get('tenant_id') == server.tenant_id:
+ free_floating_ip = ip.get("floating_ip_address")
+ try:
+ fix_ip = floating_network.get('ip')
+ server.add_floating_ip(free_floating_ip, fix_ip)
+ assigned = True
+ except Exception as e:
+ raise vimconn.vimconnException(type(e).__name__ + ": Cannot create floating_ip "+ str(e), http_code=vimconn.HTTP_Conflict)
+ else:
+ #Find the external network
+ external_nets = list()
+ for net in self.neutron.list_networks()['networks']:
+ if net['router:external']:
+ external_nets.append(net)
+
+ if len(external_nets) == 0:
+ raise vimconn.vimconnException("Cannot create floating_ip automatically since no external "
+ "network is present",
+ http_code=vimconn.HTTP_Conflict)
+ if len(external_nets) > 1:
+ raise vimconn.vimconnException("Cannot create floating_ip automatically since multiple "
+ "external networks are present",
+ http_code=vimconn.HTTP_Conflict)
+
+ pool_id = external_nets[0].get('id')
+ param = {'floatingip': {'floating_network_id': pool_id, 'tenant_id': server.tenant_id}}
+ try:
+ #self.logger.debug("Creating floating IP")
+ new_floating_ip = self.neutron.create_floatingip(param)
+ free_floating_ip = new_floating_ip['floatingip']['floating_ip_address']
+ fix_ip = floating_network.get('ip')
+ server.add_floating_ip(free_floating_ip, fix_ip)
+ assigned=True
+ except Exception as e:
+ raise vimconn.vimconnException(type(e).__name__ + ": Cannot assign floating_ip "+ str(e), http_code=vimconn.HTTP_Conflict)
+ except Exception as e:
+ if not floating_network['exit_on_floating_ip_error']:
+ self.logger.warn("Cannot create floating_ip. %s", str(e))
+ continue
+ self.delete_vminstance(server.id)
+ raise
+
+ return server.id
+# except nvExceptions.NotFound as e:
+# error_value=-vimconn.HTTP_Not_Found
+# error_text= "vm instance %s not found" % vm_id
+ except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
+ # delete the volumes we just created
+ if block_device_mapping != None:
+ for volume_id in block_device_mapping.itervalues():
+ self.cinder.volumes.delete(volume_id)
+
+ # delete ports we just created
+ for net_item in net_list_vim:
+ if 'port-id' in net_item:
+ self.neutron.delete_port(net_item['port-id'])
+ self._format_exception(e)
+ except TypeError as e:
+ raise vimconn.vimconnException(type(e).__name__ + ": "+ str(e), http_code=vimconn.HTTP_Bad_Request)
+
+ def get_vminstance(self,vm_id):
+ '''Returns the VM instance information from VIM'''
+ #self.logger.debug("Getting VM from VIM")
+ try:
+ self._reload_connection()
+ server = self.nova.servers.find(id=vm_id)
+ #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
+ return server.to_dict()
+ except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound, ConnectionError) as e:
+ self._format_exception(e)
+
+ def get_vminstance_console(self,vm_id, console_type="vnc"):
+ '''
+ Get a console for the virtual machine
+ Params:
+ vm_id: uuid of the VM
+ console_type, can be:
+ "novnc" (by default), "xvpvnc" for VNC types,
+ "rdp-html5" for RDP types, "spice-html5" for SPICE types
+ Returns dict with the console parameters:
+ protocol: ssh, ftp, http, https, ...
+ server: usually ip address
+ port: the http, ssh, ... port
+ suffix: extra text, e.g. the http path and query string
+ '''
+ self.logger.debug("Getting VM CONSOLE from VIM")
+ try:
+ self._reload_connection()
+ server = self.nova.servers.find(id=vm_id)
+ if console_type == None or console_type == "novnc":
+ console_dict = server.get_vnc_console("novnc")
+ elif console_type == "xvpvnc":
+ console_dict = server.get_vnc_console(console_type)
+ elif console_type == "rdp-html5":
+ console_dict = server.get_rdp_console(console_type)
+ elif console_type == "spice-html5":
+ console_dict = server.get_spice_console(console_type)
+ else:
+ raise vimconn.vimconnException("console type '{}' not allowed".format(console_type), http_code=vimconn.HTTP_Bad_Request)
+
+ console_dict1 = console_dict.get("console")
+ if console_dict1:
+ console_url = console_dict1.get("url")
+ if console_url:
+ #parse console_url
+ protocol_index = console_url.find("//")
+ suffix_index = console_url[protocol_index+2:].find("/") + protocol_index+2
+ port_index = console_url[protocol_index+2:suffix_index].find(":") + protocol_index+2
+ if protocol_index < 0 or port_index<0 or suffix_index<0:
+ return -vimconn.HTTP_Internal_Server_Error, "Unexpected response from VIM"
+ console_dict={"protocol": console_url[0:protocol_index],
+ "server": console_url[protocol_index+2:port_index],
+ "port": console_url[port_index:suffix_index],
+ "suffix": console_url[suffix_index+1:]
+ }
+ protocol_index += 2
+ return console_dict
+ raise vimconn.vimconnUnexpectedResponse("Unexpected response from VIM")
+
+ except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.BadRequest, ConnectionError) as e:
+ self._format_exception(e)
+
+ def delete_vminstance(self, vm_id):
+ '''Removes a VM instance from VIM. Returns the old identifier
+ '''
+ #print "osconnector: Getting VM from VIM"
+ try:
+ self._reload_connection()
+ #delete VM ports attached to this networks before the virtual machine
+ ports = self.neutron.list_ports(device_id=vm_id)
+ for p in ports['ports']:
+ try:
+ self.neutron.delete_port(p["id"])
+ except Exception as e:
+ self.logger.error("Error deleting port: " + type(e).__name__ + ": "+ str(e))
+
+ #commented because detaching the volumes makes the servers.delete not work properly ?!?
+ #dettach volumes attached
+ server = self.nova.servers.get(vm_id)
+ volumes_attached_dict = server._info['os-extended-volumes:volumes_attached']
+ #for volume in volumes_attached_dict:
+ # self.cinder.volumes.detach(volume['id'])
+
+ self.nova.servers.delete(vm_id)
+
+ #delete volumes.
+ #Although having detached them should have them in active status
+ #we ensure in this loop
+ keep_waiting = True
+ elapsed_time = 0
+ while keep_waiting and elapsed_time < volume_timeout:
+ keep_waiting = False
+ for volume in volumes_attached_dict:
+ if self.cinder.volumes.get(volume['id']).status != 'available':
+ keep_waiting = True
+ else:
+ self.cinder.volumes.delete(volume['id'])
+ if keep_waiting:
+ time.sleep(1)
+ elapsed_time += 1
+
+ return vm_id
+ except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
+ self._format_exception(e)
+ #TODO insert exception vimconn.HTTP_Unauthorized
+ #if reaching here is because an exception
+
+ def refresh_vms_status(self, vm_list):
+ '''Get the status of the virtual machines and their interfaces/ports
+ Params: the list of VM identifiers
+ Returns a dictionary with:
+ vm_id: #VIM id of this Virtual Machine
+ status: #Mandatory. Text with one of:
+ # DELETED (not found at vim)
+ # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
+ # OTHER (Vim reported other status not understood)
+ # ERROR (VIM indicates an ERROR status)
+ # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
+ # CREATING (on building process), ERROR
+ # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
+ #
+ error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
+ vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
+ interfaces:
+ - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
+ mac_address: #Text format XX:XX:XX:XX:XX:XX
+ vim_net_id: #network id where this interface is connected
+ vim_interface_id: #interface/port VIM id
+ ip_address: #null, or text with IPv4, IPv6 address
+ '''
+ vm_dict={}
+ self.logger.debug("refresh_vms status: Getting tenant VM instance information from VIM")
+ for vm_id in vm_list:
+ vm={}
+ try:
+ vm_vim = self.get_vminstance(vm_id)
+ if vm_vim['status'] in vmStatus2manoFormat:
+ vm['status'] = vmStatus2manoFormat[ vm_vim['status'] ]
+ else:
+ vm['status'] = "OTHER"
+ vm['error_msg'] = "VIM status reported " + vm_vim['status']
+ try:
+ vm['vim_info'] = yaml.safe_dump(vm_vim, default_flow_style=True, width=256)
+ except yaml.representer.RepresenterError:
+ vm['vim_info'] = str(vm_vim)
+ vm["interfaces"] = []
+ if vm_vim.get('fault'):
+ vm['error_msg'] = str(vm_vim['fault'])
+ #get interfaces
+ try:
+ self._reload_connection()
+ port_dict=self.neutron.list_ports(device_id=vm_id)
+ for port in port_dict["ports"]:
+ interface={}
+ try:
+ interface['vim_info'] = yaml.safe_dump(port, default_flow_style=True, width=256)
+ except yaml.representer.RepresenterError:
+ interface['vim_info'] = str(port)
+ interface["mac_address"] = port.get("mac_address")
+ interface["vim_net_id"] = port["network_id"]
+ interface["vim_interface_id"] = port["id"]
+ ips=[]
+ #look for floating ip address
+ floating_ip_dict = self.neutron.list_floatingips(port_id=port["id"])
+ if floating_ip_dict.get("floatingips"):
+ ips.append(floating_ip_dict["floatingips"][0].get("floating_ip_address") )
+
+ for subnet in port["fixed_ips"]:
+ ips.append(subnet["ip_address"])
+ interface["ip_address"] = ";".join(ips)
+ vm["interfaces"].append(interface)
+ except Exception as e:
+ self.logger.error("Error getting vm interface information " + type(e).__name__ + ": "+ str(e))
+ except vimconn.vimconnNotFoundException as e:
+ self.logger.error("Exception getting vm status: %s", str(e))
+ vm['status'] = "DELETED"
+ vm['error_msg'] = str(e)
+ except vimconn.vimconnException as e:
+ self.logger.error("Exception getting vm status: %s", str(e))
+ vm['status'] = "VIM_ERROR"
+ vm['error_msg'] = str(e)
+ vm_dict[vm_id] = vm
+ return vm_dict
+
+ def action_vminstance(self, vm_id, action_dict):
+ '''Send and action over a VM instance from VIM
+ Returns the vm_id if the action was successfully sent to the VIM'''
+ self.logger.debug("Action over VM '%s': %s", vm_id, str(action_dict))
+ try:
+ self._reload_connection()
+ server = self.nova.servers.find(id=vm_id)
+ if "start" in action_dict:
+ if action_dict["start"]=="rebuild":
+ server.rebuild()
+ else:
+ if server.status=="PAUSED":
+ server.unpause()
+ elif server.status=="SUSPENDED":
+ server.resume()
+ elif server.status=="SHUTOFF":
+ server.start()
+ elif "pause" in action_dict:
+ server.pause()
+ elif "resume" in action_dict:
+ server.resume()
+ elif "shutoff" in action_dict or "shutdown" in action_dict:
+ server.stop()
+ elif "forceOff" in action_dict:
+ server.stop() #TODO
+ elif "terminate" in action_dict:
+ server.delete()
+ elif "createImage" in action_dict:
+ server.create_image()
+ #"path":path_schema,
+ #"description":description_schema,
+ #"name":name_schema,
+ #"metadata":metadata_schema,
+ #"imageRef": id_schema,
+ #"disk": {"oneOf":[{"type": "null"}, {"type":"string"}] },
+ elif "rebuild" in action_dict:
+ server.rebuild(server.image['id'])
+ elif "reboot" in action_dict:
+ server.reboot() #reboot_type='SOFT'
+ elif "console" in action_dict:
+ console_type = action_dict["console"]
+ if console_type == None or console_type == "novnc":
+ console_dict = server.get_vnc_console("novnc")
+ elif console_type == "xvpvnc":
+ console_dict = server.get_vnc_console(console_type)
+ elif console_type == "rdp-html5":
+ console_dict = server.get_rdp_console(console_type)
+ elif console_type == "spice-html5":
+ console_dict = server.get_spice_console(console_type)
+ else:
+ raise vimconn.vimconnException("console type '{}' not allowed".format(console_type),
+ http_code=vimconn.HTTP_Bad_Request)
+ try:
+ console_url = console_dict["console"]["url"]
+ #parse console_url
+ protocol_index = console_url.find("//")
+ suffix_index = console_url[protocol_index+2:].find("/") + protocol_index+2
+ port_index = console_url[protocol_index+2:suffix_index].find(":") + protocol_index+2
+ if protocol_index < 0 or port_index<0 or suffix_index<0:
+ raise vimconn.vimconnException("Unexpected response from VIM " + str(console_dict))
+ console_dict2={"protocol": console_url[0:protocol_index],
+ "server": console_url[protocol_index+2 : port_index],
+ "port": int(console_url[port_index+1 : suffix_index]),
+ "suffix": console_url[suffix_index+1:]
+ }
+ return console_dict2
+ except Exception as e:
+ raise vimconn.vimconnException("Unexpected response from VIM " + str(console_dict))
+
+ return vm_id
+ except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound, ConnectionError) as e:
+ self._format_exception(e)
+ #TODO insert exception vimconn.HTTP_Unauthorized
+
+#NOT USED FUNCTIONS
+
+ def new_external_port(self, port_data):
+ #TODO openstack if needed
+ '''Adds a external port to VIM'''
+ '''Returns the port identifier'''
+ return -vimconn.HTTP_Internal_Server_Error, "osconnector.new_external_port() not implemented"
+
+ def connect_port_network(self, port_id, network_id, admin=False):
+ #TODO openstack if needed
+ '''Connects a external port to a network'''
+ '''Returns status code of the VIM response'''
+ return -vimconn.HTTP_Internal_Server_Error, "osconnector.connect_port_network() not implemented"
+
+ def new_user(self, user_name, user_passwd, tenant_id=None):
+ '''Adds a new user to openstack VIM'''
+ '''Returns the user identifier'''
+ self.logger.debug("osconnector: Adding a new user to VIM")
+ try:
+ self._reload_connection()
+ user=self.keystone.users.create(user_name, user_passwd, tenant_id=tenant_id)
+ #self.keystone.tenants.add_user(self.k_creds["username"], #role)
+ return user.id
+ except ksExceptions.ConnectionError as e:
+ error_value=-vimconn.HTTP_Bad_Request
+ error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
+ except ksExceptions.ClientException as e: #TODO remove
+ error_value=-vimconn.HTTP_Bad_Request
+ error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
+ #TODO insert exception vimconn.HTTP_Unauthorized
+ #if reaching here is because an exception
+ if self.debug:
+ self.logger.debug("new_user " + error_text)
+ return error_value, error_text
+
+ def delete_user(self, user_id):
+ '''Delete a user from openstack VIM'''
+ '''Returns the user identifier'''
+ if self.debug:
+ print "osconnector: Deleting a user from VIM"
+ try:
+ self._reload_connection()
+ self.keystone.users.delete(user_id)
+ return 1, user_id
+ except ksExceptions.ConnectionError as e:
+ error_value=-vimconn.HTTP_Bad_Request
+ error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
+ except ksExceptions.NotFound as e:
+ error_value=-vimconn.HTTP_Not_Found
+ error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
+ except ksExceptions.ClientException as e: #TODO remove
+ error_value=-vimconn.HTTP_Bad_Request
+ error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
+ #TODO insert exception vimconn.HTTP_Unauthorized
+ #if reaching here is because an exception
+ if self.debug:
+ print "delete_tenant " + error_text
+ return error_value, error_text
+
+ def get_hosts_info(self):
+ '''Get the information of deployed hosts
+ Returns the hosts content'''
+ if self.debug:
+ print "osconnector: Getting Host info from VIM"
+ try:
+ h_list=[]
+ self._reload_connection()
+ hypervisors = self.nova.hypervisors.list()
+ for hype in hypervisors:
+ h_list.append( hype.to_dict() )
+ return 1, {"hosts":h_list}
+ except nvExceptions.NotFound as e:
+ error_value=-vimconn.HTTP_Not_Found
+ error_text= (str(e) if len(e.args)==0 else str(e.args[0]))
+ except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
+ error_value=-vimconn.HTTP_Bad_Request
+ error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
+ #TODO insert exception vimconn.HTTP_Unauthorized
+ #if reaching here is because an exception
+ if self.debug:
+ print "get_hosts_info " + error_text
+ return error_value, error_text
+
+ def get_hosts(self, vim_tenant):
+ '''Get the hosts and deployed instances
+ Returns the hosts content'''
+ r, hype_dict = self.get_hosts_info()
+ if r<0:
+ return r, hype_dict
+ hypervisors = hype_dict["hosts"]
+ try:
+ servers = self.nova.servers.list()
+ for hype in hypervisors:
+ for server in servers:
+ if server.to_dict()['OS-EXT-SRV-ATTR:hypervisor_hostname']==hype['hypervisor_hostname']:
+ if 'vm' in hype:
+ hype['vm'].append(server.id)
+ else:
+ hype['vm'] = [server.id]
+ return 1, hype_dict
+ except nvExceptions.NotFound as e:
+ error_value=-vimconn.HTTP_Not_Found
+ error_text= (str(e) if len(e.args)==0 else str(e.args[0]))
+ except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
+ error_value=-vimconn.HTTP_Bad_Request
+ error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
+ #TODO insert exception vimconn.HTTP_Unauthorized
+ #if reaching here is because an exception
+ if self.debug:
+ print "get_hosts " + error_text
+ return error_value, error_text
+
+
--- /dev/null
+# -*- 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
+##
+
+'''
+vimconnector implements all the methods to interact with openvim using the openvim API.
+'''
+__author__="Alfonso Tierno, Gerardo Garcia"
+__date__ ="$26-aug-2014 11:09:29$"
+
+import vimconn
+import requests
+import json
+import yaml
+import logging
+from openmano_schemas import id_schema, name_schema, nameshort_schema, description_schema, \
+ vlan1000_schema, integer0_schema
+from jsonschema import validate as js_v, exceptions as js_e
+from urllib import quote
+
+'''contain the openvim virtual machine status to openmano status'''
+vmStatus2manoFormat={'ACTIVE':'ACTIVE',
+ 'PAUSED':'PAUSED',
+ 'SUSPENDED': 'SUSPENDED',
+ 'INACTIVE':'INACTIVE',
+ 'CREATING':'BUILD',
+ 'ERROR':'ERROR','DELETED':'DELETED'
+ }
+netStatus2manoFormat={'ACTIVE':'ACTIVE','INACTIVE':'INACTIVE','BUILD':'BUILD','ERROR':'ERROR','DELETED':'DELETED', 'DOWN':'DOWN'
+ }
+
+
+host_schema = {
+ "type":"object",
+ "properties":{
+ "id": id_schema,
+ "name": name_schema,
+ },
+ "required": ["id"]
+}
+image_schema = {
+ "type":"object",
+ "properties":{
+ "id": id_schema,
+ "name": name_schema,
+ },
+ "required": ["id","name"]
+}
+server_schema = {
+ "type":"object",
+ "properties":{
+ "id":id_schema,
+ "name": name_schema,
+ },
+ "required": ["id","name"]
+}
+new_host_response_schema = {
+ "title":"host response information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "host": host_schema
+ },
+ "required": ["host"],
+ "additionalProperties": False
+}
+
+get_images_response_schema = {
+ "title":"openvim images response information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "images":{
+ "type":"array",
+ "items": image_schema,
+ }
+ },
+ "required": ["images"],
+ "additionalProperties": False
+}
+
+get_hosts_response_schema = {
+ "title":"openvim hosts response information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "hosts":{
+ "type":"array",
+ "items": host_schema,
+ }
+ },
+ "required": ["hosts"],
+ "additionalProperties": False
+}
+
+get_host_detail_response_schema = new_host_response_schema # TODO: Content is not parsed yet
+
+get_server_response_schema = {
+ "title":"openvim server response information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "servers":{
+ "type":"array",
+ "items": server_schema,
+ }
+ },
+ "required": ["servers"],
+ "additionalProperties": False
+}
+
+new_tenant_response_schema = {
+ "title":"tenant response information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "tenant":{
+ "type":"object",
+ "properties":{
+ "id": id_schema,
+ "name": nameshort_schema,
+ "description":description_schema,
+ "enabled":{"type" : "boolean"}
+ },
+ "required": ["id"]
+ }
+ },
+ "required": ["tenant"],
+ "additionalProperties": False
+}
+
+new_network_response_schema = {
+ "title":"network response information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "network":{
+ "type":"object",
+ "properties":{
+ "id":id_schema,
+ "name":name_schema,
+ "type":{"type":"string", "enum":["bridge_man","bridge_data","data", "ptp"]},
+ "shared":{"type":"boolean"},
+ "tenant_id":id_schema,
+ "admin_state_up":{"type":"boolean"},
+ "vlan":vlan1000_schema
+ },
+ "required": ["id"]
+ }
+ },
+ "required": ["network"],
+ "additionalProperties": False
+}
+
+
+# get_network_response_schema = {
+# "title":"get network response information schema",
+# "$schema": "http://json-schema.org/draft-04/schema#",
+# "type":"object",
+# "properties":{
+# "network":{
+# "type":"object",
+# "properties":{
+# "id":id_schema,
+# "name":name_schema,
+# "type":{"type":"string", "enum":["bridge_man","bridge_data","data", "ptp"]},
+# "shared":{"type":"boolean"},
+# "tenant_id":id_schema,
+# "admin_state_up":{"type":"boolean"},
+# "vlan":vlan1000_schema
+# },
+# "required": ["id"]
+# }
+# },
+# "required": ["network"],
+# "additionalProperties": False
+# }
+
+
+new_port_response_schema = {
+ "title":"port response information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "port":{
+ "type":"object",
+ "properties":{
+ "id":id_schema,
+ },
+ "required": ["id"]
+ }
+ },
+ "required": ["port"],
+ "additionalProperties": False
+}
+
+get_flavor_response_schema = {
+ "title":"openvim flavors response information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "flavor":{
+ "type":"object",
+ "properties":{
+ "id": id_schema,
+ "name": name_schema,
+ "extended": {"type":"object"},
+ },
+ "required": ["id", "name"],
+ }
+ },
+ "required": ["flavor"],
+ "additionalProperties": False
+}
+
+new_flavor_response_schema = {
+ "title":"flavor response information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "flavor":{
+ "type":"object",
+ "properties":{
+ "id":id_schema,
+ },
+ "required": ["id"]
+ }
+ },
+ "required": ["flavor"],
+ "additionalProperties": False
+}
+
+get_image_response_schema = {
+ "title":"openvim images response information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "image":{
+ "type":"object",
+ "properties":{
+ "id": id_schema,
+ "name": name_schema,
+ },
+ "required": ["id", "name"],
+ }
+ },
+ "required": ["flavor"],
+ "additionalProperties": False
+}
+new_image_response_schema = {
+ "title":"image response information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "image":{
+ "type":"object",
+ "properties":{
+ "id":id_schema,
+ },
+ "required": ["id"]
+ }
+ },
+ "required": ["image"],
+ "additionalProperties": False
+}
+
+new_vminstance_response_schema = {
+ "title":"server response information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "server":{
+ "type":"object",
+ "properties":{
+ "id":id_schema,
+ },
+ "required": ["id"]
+ }
+ },
+ "required": ["server"],
+ "additionalProperties": False
+}
+
+get_processor_rankings_response_schema = {
+ "title":"processor rankings information schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type":"object",
+ "properties":{
+ "rankings":{
+ "type":"array",
+ "items":{
+ "type":"object",
+ "properties":{
+ "model": description_schema,
+ "value": integer0_schema
+ },
+ "additionalProperties": False,
+ "required": ["model","value"]
+ }
+ },
+ "additionalProperties": False,
+ "required": ["rankings"]
+ }
+}
+
+class vimconnector(vimconn.vimconnector):
+ def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None,
+ log_level="DEBUG", config={}, persistent_info={}):
+ vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level, config)
+ self.tenant = None
+ self.headers_req = {'content-type': 'application/json'}
+ self.logger = logging.getLogger('openmano.vim.openvim')
+ self.persistent_info = persistent_info
+ if tenant_id:
+ self.tenant = tenant_id
+
+ def __setitem__(self,index, value):
+ '''Set individuals parameters
+ Throw TypeError, KeyError
+ '''
+ if index=='tenant_id':
+ self.tenant = value
+ elif index=='tenant_name':
+ self.tenant = None
+ vimconn.vimconnector.__setitem__(self,index, value)
+
+ def _get_my_tenant(self):
+ '''Obtain uuid of my tenant from name
+ '''
+ if self.tenant:
+ return self.tenant
+
+ url = self.url+'/tenants?name='+ quote(self.tenant_name)
+ self.logger.info("Getting VIM tenant_id GET %s", url)
+ vim_response = requests.get(url, headers = self.headers_req)
+ self._check_http_request_response(vim_response)
+ try:
+ tenant_list = vim_response.json()["tenants"]
+ if len(tenant_list) == 0:
+ raise vimconn.vimconnNotFoundException("No tenant found for name '%s'" % str(self.tenant_name))
+ elif len(tenant_list) > 1:
+ raise vimconn.vimconnConflictException ("More that one tenant found for name '%s'" % str(self.tenant_name))
+ self.tenant = tenant_list[0]["id"]
+ return self.tenant
+ except Exception as e:
+ raise vimconn.vimconnUnexpectedResponse("Get VIM tenant {} '{}'".format(type(e).__name__, str(e)))
+
+ def _format_jsonerror(self,http_response):
+ #DEPRECATED, to delete in the future
+ try:
+ data = http_response.json()
+ return data["error"]["description"]
+ except:
+ return http_response.text
+
+ def _format_in(self, http_response, schema):
+ #DEPRECATED, to delete in the future
+ try:
+ client_data = http_response.json()
+ js_v(client_data, schema)
+ #print "Input data: ", str(client_data)
+ return True, client_data
+ except js_e.ValidationError as exc:
+ print "validate_in error, jsonschema exception ", exc.message, "at", exc.path
+ return False, ("validate_in error, jsonschema exception ", exc.message, "at", exc.path)
+
+ def _remove_extra_items(self, data, schema):
+ deleted=[]
+ if type(data) is tuple or type(data) is list:
+ for d in data:
+ a= self._remove_extra_items(d, schema['items'])
+ if a is not None: deleted.append(a)
+ elif type(data) is dict:
+ for k in data.keys():
+ if 'properties' not in schema or k not in schema['properties'].keys():
+ del data[k]
+ deleted.append(k)
+ else:
+ a = self._remove_extra_items(data[k], schema['properties'][k])
+ if a is not None: deleted.append({k:a})
+ if len(deleted) == 0: return None
+ elif len(deleted) == 1: return deleted[0]
+ else: return deleted
+
+ def _format_request_exception(self, request_exception):
+ '''Transform a request exception into a vimconn exception'''
+ if isinstance(request_exception, js_e.ValidationError):
+ raise vimconn.vimconnUnexpectedResponse("jsonschema exception '{}' at '{}'".format(request_exception.message, request_exception.path))
+ elif isinstance(request_exception, requests.exceptions.HTTPError):
+ raise vimconn.vimconnUnexpectedResponse(type(request_exception).__name__ + ": " + str(request_exception))
+ else:
+ raise vimconn.vimconnConnectionException(type(request_exception).__name__ + ": " + str(request_exception))
+
+ def _check_http_request_response(self, request_response):
+ '''Raise a vimconn exception if the response is not Ok'''
+ if request_response.status_code >= 200 and request_response.status_code < 300:
+ return
+ if request_response.status_code == vimconn.HTTP_Unauthorized:
+ raise vimconn.vimconnAuthException(request_response.text)
+ elif request_response.status_code == vimconn.HTTP_Not_Found:
+ raise vimconn.vimconnNotFoundException(request_response.text)
+ elif request_response.status_code == vimconn.HTTP_Conflict:
+ raise vimconn.vimconnConflictException(request_response.text)
+ else:
+ raise vimconn.vimconnUnexpectedResponse("VIM HTTP_response {}, {}".format(request_response.status_code, str(request_response.text)))
+
+ def new_tenant(self,tenant_name,tenant_description):
+ '''Adds a new tenant to VIM with this name and description, returns the tenant identifier'''
+ #print "VIMConnector: Adding a new tenant to VIM"
+ payload_dict = {"tenant": {"name":tenant_name,"description": tenant_description, "enabled": True}}
+ payload_req = json.dumps(payload_dict)
+ try:
+ url = self.url_admin+'/tenants'
+ self.logger.info("Adding a new tenant %s", url)
+ vim_response = requests.post(url, headers = self.headers_req, data=payload_req)
+ self._check_http_request_response(vim_response)
+ self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ response = vim_response.json()
+ js_v(response, new_tenant_response_schema)
+ #r = self._remove_extra_items(response, new_tenant_response_schema)
+ #if r is not None:
+ # self.logger.warn("Warning: remove extra items %s", str(r))
+ tenant_id = response['tenant']['id']
+ return tenant_id
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+ def delete_tenant(self,tenant_id):
+ '''Delete a tenant from VIM. Returns the old tenant identifier'''
+ try:
+ url = self.url_admin+'/tenants/'+tenant_id
+ self.logger.info("Delete a tenant DELETE %s", url)
+ vim_response = requests.delete(url, headers = self.headers_req)
+ self._check_http_request_response(vim_response)
+ self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ return tenant_id
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+ def get_tenant_list(self, filter_dict={}):
+ '''Obtain tenants of VIM
+ filter_dict can contain the following keys:
+ name: filter by tenant name
+ id: filter by tenant uuid/id
+ <other VIM specific>
+ Returns the tenant list of dictionaries: [{'name':'<name>, 'id':'<id>, ...}, ...]
+ '''
+ filterquery=[]
+ filterquery_text=''
+ for k,v in filter_dict.iteritems():
+ filterquery.append(str(k)+'='+str(v))
+ if len(filterquery)>0:
+ filterquery_text='?'+ '&'.join(filterquery)
+ try:
+ url = self.url+'/tenants'+filterquery_text
+ self.logger.info("get_tenant_list GET %s", url)
+ vim_response = requests.get(url, headers = self.headers_req)
+ self._check_http_request_response(vim_response)
+ self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ return vim_response.json()["tenants"]
+ except requests.exceptions.RequestException as e:
+ self._format_request_exception(e)
+
+ def new_network(self,net_name, net_type, ip_profile=None, shared=False, vlan=None): #, **vim_specific):
+ '''Adds a tenant network to VIM'''
+ '''Returns the network identifier'''
+ try:
+ self._get_my_tenant()
+ if net_type=="bridge":
+ net_type="bridge_data"
+ payload_req = {"name": net_name, "type": net_type, "tenant_id": self.tenant, "shared": shared}
+ if vlan:
+ payload_req["provider:vlan"] = vlan
+ # payload_req.update(vim_specific)
+ url = self.url+'/networks'
+ self.logger.info("Adding a new network POST: %s DATA: %s", url, str(payload_req))
+ vim_response = requests.post(url, headers = self.headers_req, data=json.dumps({"network": payload_req}) )
+ self._check_http_request_response(vim_response)
+ self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ response = vim_response.json()
+ js_v(response, new_network_response_schema)
+ #r = self._remove_extra_items(response, new_network_response_schema)
+ #if r is not None:
+ # self.logger.warn("Warning: remove extra items %s", str(r))
+ network_id = response['network']['id']
+ return network_id
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+ def get_network_list(self, filter_dict={}):
+ '''Obtain tenant networks of VIM
+ Filter_dict can be:
+ name: network name
+ id: network uuid
+ public: boolean
+ tenant_id: tenant
+ admin_state_up: boolean
+ status: 'ACTIVE'
+ Returns the network list of dictionaries
+ '''
+ try:
+ if 'tenant_id' not in filter_dict:
+ filter_dict["tenant_id"] = self._get_my_tenant()
+ elif not filter_dict["tenant_id"]:
+ del filter_dict["tenant_id"]
+ filterquery=[]
+ filterquery_text=''
+ for k,v in filter_dict.iteritems():
+ filterquery.append(str(k)+'='+str(v))
+ if len(filterquery)>0:
+ filterquery_text='?'+ '&'.join(filterquery)
+ url = self.url+'/networks'+filterquery_text
+ self.logger.info("Getting network list GET %s", url)
+ vim_response = requests.get(url, headers = self.headers_req)
+ self._check_http_request_response(vim_response)
+ self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ response = vim_response.json()
+ return response['networks']
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+ def get_network(self, net_id):
+ '''Obtain network details of network id'''
+ try:
+ url = self.url+'/networks/'+net_id
+ self.logger.info("Getting network GET %s", url)
+ vim_response = requests.get(url, headers = self.headers_req)
+ self._check_http_request_response(vim_response)
+ self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ response = vim_response.json()
+ return response['network']
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+ def delete_network(self, net_id):
+ '''Deletes a tenant network from VIM'''
+ '''Returns the network identifier'''
+ try:
+ self._get_my_tenant()
+ url = self.url+'/networks/'+net_id
+ self.logger.info("Deleting VIM network DELETE %s", url)
+ vim_response = requests.delete(url, headers=self.headers_req)
+ self._check_http_request_response(vim_response)
+ #self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ return net_id
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+ def get_flavor(self, flavor_id):
+ '''Obtain flavor details from the VIM'''
+ try:
+ self._get_my_tenant()
+ url = self.url+'/'+self.tenant+'/flavors/'+flavor_id
+ self.logger.info("Getting flavor GET %s", url)
+ vim_response = requests.get(url, headers = self.headers_req)
+ self._check_http_request_response(vim_response)
+ self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ response = vim_response.json()
+ js_v(response, get_flavor_response_schema)
+ r = self._remove_extra_items(response, get_flavor_response_schema)
+ if r is not None:
+ self.logger.warn("Warning: remove extra items %s", str(r))
+ return response['flavor']
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+ def new_flavor(self, flavor_data):
+ '''Adds a tenant flavor to VIM'''
+ '''Returns the flavor identifier'''
+ try:
+ new_flavor_dict = flavor_data.copy()
+ new_flavor_dict["name"] = flavor_data["name"][:64]
+ self._get_my_tenant()
+ payload_req = json.dumps({'flavor': new_flavor_dict})
+ url = self.url+'/'+self.tenant+'/flavors'
+ self.logger.info("Adding a new VIM flavor POST %s", url)
+ vim_response = requests.post(url, headers = self.headers_req, data=payload_req)
+ self._check_http_request_response(vim_response)
+ self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ response = vim_response.json()
+ js_v(response, new_flavor_response_schema)
+ r = self._remove_extra_items(response, new_flavor_response_schema)
+ if r is not None:
+ self.logger.warn("Warning: remove extra items %s", str(r))
+ flavor_id = response['flavor']['id']
+ return flavor_id
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+ def delete_flavor(self,flavor_id):
+ '''Deletes a tenant flavor from VIM'''
+ '''Returns the old flavor_id'''
+ try:
+ self._get_my_tenant()
+ url = self.url+'/'+self.tenant+'/flavors/'+flavor_id
+ self.logger.info("Deleting VIM flavor DELETE %s", url)
+ vim_response = requests.delete(url, headers=self.headers_req)
+ self._check_http_request_response(vim_response)
+ #self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ return flavor_id
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+ def get_image(self, image_id):
+ '''Obtain image details from the VIM'''
+ try:
+ self._get_my_tenant()
+ url = self.url+'/'+self.tenant+'/images/'+image_id
+ self.logger.info("Getting image GET %s", url)
+ vim_response = requests.get(url, headers = self.headers_req)
+ self._check_http_request_response(vim_response)
+ self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ response = vim_response.json()
+ js_v(response, get_image_response_schema)
+ r = self._remove_extra_items(response, get_image_response_schema)
+ if r is not None:
+ self.logger.warn("Warning: remove extra items %s", str(r))
+ return response['image']
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+ def new_image(self,image_dict):
+ ''' Adds a tenant image to VIM, returns image_id'''
+ try:
+ self._get_my_tenant()
+ new_image_dict={'name': image_dict['name'][:64]}
+ if image_dict.get('description'):
+ new_image_dict['description'] = image_dict['description']
+ if image_dict.get('metadata'):
+ new_image_dict['metadata'] = yaml.load(image_dict['metadata'])
+ if image_dict.get('location'):
+ new_image_dict['path'] = image_dict['location']
+ payload_req = json.dumps({"image":new_image_dict})
+ url=self.url + '/' + self.tenant + '/images'
+ self.logger.info("Adding a new VIM image POST %s", url)
+ vim_response = requests.post(url, headers = self.headers_req, data=payload_req)
+ self._check_http_request_response(vim_response)
+ self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ response = vim_response.json()
+ js_v(response, new_image_response_schema)
+ r = self._remove_extra_items(response, new_image_response_schema)
+ if r is not None:
+ self.logger.warn("Warning: remove extra items %s", str(r))
+ image_id = response['image']['id']
+ return image_id
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+ def delete_image(self, image_id):
+ '''Deletes a tenant image from VIM'''
+ '''Returns the deleted image_id'''
+ try:
+ self._get_my_tenant()
+ url = self.url + '/'+ self.tenant +'/images/'+image_id
+ self.logger.info("Deleting VIM image DELETE %s", url)
+ vim_response = requests.delete(url, headers=self.headers_req)
+ self._check_http_request_response(vim_response)
+ #self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ return image_id
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+
+ def get_image_id_from_path(self, path):
+ '''Get the image id from image path in the VIM database. Returns the image_id'''
+ try:
+ self._get_my_tenant()
+ url=self.url + '/' + self.tenant + '/images?path='+quote(path)
+ self.logger.info("Getting images GET %s", url)
+ vim_response = requests.get(url)
+ self._check_http_request_response(vim_response)
+ self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ response = vim_response.json()
+ js_v(response, get_images_response_schema)
+ #r = self._remove_extra_items(response, get_images_response_schema)
+ #if r is not None:
+ # self.logger.warn("Warning: remove extra items %s", str(r))
+ if len(response['images'])==0:
+ raise vimconn.vimconnNotFoundException("Image not found at VIM with path '%s'", path)
+ elif len(response['images'])>1:
+ raise vimconn.vimconnConflictException("More than one image found at VIM with path '%s'", path)
+ return response['images'][0]['id']
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+ def get_image_list(self, filter_dict={}):
+ '''Obtain tenant images from VIM
+ Filter_dict can be:
+ name: image name
+ id: image uuid
+ checksum: image checksum
+ location: image path
+ Returns the image list of dictionaries:
+ [{<the fields at Filter_dict plus some VIM specific>}, ...]
+ List can be empty
+ '''
+ try:
+ self._get_my_tenant()
+ filterquery=[]
+ filterquery_text=''
+ for k,v in filter_dict.iteritems():
+ filterquery.append(str(k)+'='+str(v))
+ if len(filterquery)>0:
+ filterquery_text='?'+ '&'.join(filterquery)
+ url = self.url+'/'+self.tenant+'/images'+filterquery_text
+ self.logger.info("Getting image list GET %s", url)
+ vim_response = requests.get(url, headers = self.headers_req)
+ self._check_http_request_response(vim_response)
+ self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ response = vim_response.json()
+ return response['images']
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+ def new_vminstancefromJSON(self, vm_data):
+ '''Adds a VM instance to VIM'''
+ '''Returns the instance identifier'''
+ try:
+ self._get_my_tenant()
+ except Exception as e:
+ return -vimconn.HTTP_Not_Found, str(e)
+ print "VIMConnector: Adding a new VM instance from JSON to VIM"
+ payload_req = vm_data
+ try:
+ vim_response = requests.post(self.url+'/'+self.tenant+'/servers', headers = self.headers_req, data=payload_req)
+ except requests.exceptions.RequestException as e:
+ print "new_vminstancefromJSON Exception: ", e.args
+ return -vimconn.HTTP_Not_Found, str(e.args[0])
+ 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)
+ res,http_content = self._format_in(vim_response, new_image_response_schema)
+ #print http_content
+ if res:
+ r = self._remove_extra_items(http_content, new_image_response_schema)
+ if r is not None: print "Warning: remove extra items ", r
+ #print http_content
+ vminstance_id = http_content['server']['id']
+ print "Tenant image id: ",vminstance_id
+ return vim_response.status_code,vminstance_id
+ else: return -vimconn.HTTP_Bad_Request,http_content
+ else:
+ #print vim_response.text
+ jsonerror = self._format_jsonerror(vim_response)
+ text = 'Error in VIM "%s": not possible to add new vm instance. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror)
+ #print text
+ return -vim_response.status_code,text
+
+ def new_vminstance(self,name,description,start,image_id,flavor_id,net_list, cloud_config=None, disk_list=None):
+ '''Adds a VM instance to VIM
+ Params:
+ start: indicates if VM must start or boot in pause mode. Ignored
+ image_id,flavor_id: image and flavor uuid
+ net_list: list of interfaces, each one is a dictionary with:
+ name:
+ net_id: network uuid to connect
+ vpci: virtual vcpi to assign
+ model: interface model, virtio, e2000, ...
+ mac_address:
+ use: 'data', 'bridge', 'mgmt'
+ type: 'virtual', 'PF', 'VF', 'VFnotShared'
+ vim_id: filled/added by this function
+ #TODO ip, security groups
+ Returns the instance identifier
+ '''
+ self.logger.debug("new_vminstance input: image='%s' flavor='%s' nics='%s'", image_id, flavor_id, str(net_list))
+ try:
+ self._get_my_tenant()
+# net_list = []
+# for k,v in net_dict.items():
+# print k,v
+# net_list.append('{"name":"' + k + '", "uuid":"' + v + '"}')
+# net_list_string = ', '.join(net_list)
+ virtio_net_list=[]
+ for net in net_list:
+ if not net.get("net_id"):
+ continue
+ net_dict={'uuid': net["net_id"]}
+ if net.get("type"): net_dict["type"] = net["type"]
+ if net.get("name"): net_dict["name"] = net["name"]
+ if net.get("vpci"): net_dict["vpci"] = net["vpci"]
+ if net.get("model"): net_dict["model"] = net["model"]
+ if net.get("mac_address"): net_dict["mac_address"] = net["mac_address"]
+ virtio_net_list.append(net_dict)
+ payload_dict={ "name": name[:64],
+ "description": description,
+ "imageRef": image_id,
+ "flavorRef": flavor_id,
+ "networks": virtio_net_list
+ }
+ if start != None:
+ payload_dict["start"] = start
+ payload_req = json.dumps({"server": payload_dict})
+ url = self.url+'/'+self.tenant+'/servers'
+ self.logger.info("Adding a new vm POST %s DATA %s", url, payload_req)
+ vim_response = requests.post(url, headers = self.headers_req, data=payload_req)
+ self._check_http_request_response(vim_response)
+ self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ response = vim_response.json()
+ js_v(response, new_vminstance_response_schema)
+ #r = self._remove_extra_items(response, new_vminstance_response_schema)
+ #if r is not None:
+ # self.logger.warn("Warning: remove extra items %s", str(r))
+ vminstance_id = response['server']['id']
+
+ #connect data plane interfaces to network
+ for net in net_list:
+ if net["type"]=="virtual":
+ if not net.get("net_id"):
+ continue
+ for iface in response['server']['networks']:
+ if "name" in net:
+ if net["name"]==iface["name"]:
+ net["vim_id"] = iface['iface_id']
+ break
+ elif "net_id" in net:
+ if net["net_id"]==iface["net_id"]:
+ net["vim_id"] = iface['iface_id']
+ break
+ else: #dataplane
+ for numa in response['server'].get('extended',{}).get('numas',() ):
+ for iface in numa.get('interfaces',() ):
+ if net['name'] == iface['name']:
+ net['vim_id'] = iface['iface_id']
+ #Code bellow is not needed, current openvim connect dataplane interfaces
+ #if net.get("net_id"):
+ ##connect dataplane interface
+ # result, port_id = self.connect_port_network(iface['iface_id'], net["net_id"])
+ # if result < 0:
+ # error_text = "Error attaching port %s to network %s: %s." % (iface['iface_id'], net["net_id"], port_id)
+ # print "new_vminstance: " + error_text
+ # self.delete_vminstance(vminstance_id)
+ # return result, error_text
+ break
+
+ return vminstance_id
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+ def get_vminstance(self, vm_id):
+ '''Returns the VM instance information from VIM'''
+ try:
+ self._get_my_tenant()
+ url = self.url+'/'+self.tenant+'/servers/'+vm_id
+ self.logger.info("Getting vm GET %s", url)
+ vim_response = requests.get(url, headers = self.headers_req)
+ vim_response = requests.get(url, headers = self.headers_req)
+ self._check_http_request_response(vim_response)
+ self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ response = vim_response.json()
+ js_v(response, new_vminstance_response_schema)
+ #r = self._remove_extra_items(response, new_vminstance_response_schema)
+ #if r is not None:
+ # self.logger.warn("Warning: remove extra items %s", str(r))
+ return response['server']
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+ def delete_vminstance(self, vm_id):
+ '''Removes a VM instance from VIM, returns the deleted vm_id'''
+ try:
+ self._get_my_tenant()
+ url = self.url+'/'+self.tenant+'/servers/'+vm_id
+ self.logger.info("Deleting VIM vm DELETE %s", url)
+ vim_response = requests.delete(url, headers=self.headers_req)
+ self._check_http_request_response(vim_response)
+ #self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ return vm_id
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+ def refresh_vms_status(self, vm_list):
+ '''Refreshes the status of the virtual machines'''
+ try:
+ self._get_my_tenant()
+ except requests.exceptions.RequestException as e:
+ self._format_request_exception(e)
+ vm_dict={}
+ for vm_id in vm_list:
+ vm={}
+ #print "VIMConnector refresh_tenant_vms and nets: Getting tenant VM instance information from VIM"
+ try:
+ url = self.url+'/'+self.tenant+'/servers/'+ vm_id
+ self.logger.info("Getting vm GET %s", url)
+ vim_response = requests.get(url, headers = self.headers_req)
+ self._check_http_request_response(vim_response)
+ response = vim_response.json()
+ js_v(response, new_vminstance_response_schema)
+ if response['server']['status'] in vmStatus2manoFormat:
+ vm['status'] = vmStatus2manoFormat[ response['server']['status'] ]
+ else:
+ vm['status'] = "OTHER"
+ vm['error_msg'] = "VIM status reported " + response['server']['status']
+ if response['server'].get('last_error'):
+ vm['error_msg'] = response['server']['last_error']
+ vm["vim_info"] = yaml.safe_dump(response['server'])
+ #get interfaces info
+ try:
+ management_ip = False
+ url2 = self.url+'/ports?device_id='+ quote(vm_id)
+ self.logger.info("Getting PORTS GET %s", url2)
+ vim_response2 = requests.get(url2, headers = self.headers_req)
+ self._check_http_request_response(vim_response2)
+ client_data = vim_response2.json()
+ if isinstance(client_data.get("ports"), list):
+ vm["interfaces"]=[]
+ for port in client_data.get("ports"):
+ interface={}
+ interface['vim_info'] = yaml.safe_dump(port)
+ interface["mac_address"] = port.get("mac_address")
+ interface["vim_net_id"] = port.get("network_id")
+ interface["vim_interface_id"] = port["id"]
+ interface["ip_address"] = port.get("ip_address")
+ if interface["ip_address"]:
+ management_ip = True
+ if interface["ip_address"] == "0.0.0.0":
+ interface["ip_address"] = None
+ vm["interfaces"].append(interface)
+
+ except Exception as e:
+ self.logger.error("refresh_vms_and_nets. Port get %s: %s", type(e).__name__, str(e))
+
+ if vm['status'] == "ACTIVE" and not management_ip:
+ vm['status'] = "ACTIVE:NoMgmtIP"
+
+ except vimconn.vimconnNotFoundException as e:
+ self.logger.error("Exception getting vm status: %s", str(e))
+ vm['status'] = "DELETED"
+ vm['error_msg'] = str(e)
+ except (requests.exceptions.RequestException, js_e.ValidationError, vimconn.vimconnException) as e:
+ self.logger.error("Exception getting vm status: %s", str(e))
+ vm['status'] = "VIM_ERROR"
+ vm['error_msg'] = str(e)
+ vm_dict[vm_id] = vm
+ return vm_dict
+
+ def refresh_nets_status(self, net_list):
+ '''Get the status of the networks
+ Params: the list of network identifiers
+ Returns a dictionary with:
+ net_id: #VIM id of this network
+ status: #Mandatory. Text with one of:
+ # DELETED (not found at vim)
+ # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
+ # OTHER (Vim reported other status not understood)
+ # ERROR (VIM indicates an ERROR status)
+ # ACTIVE, INACTIVE, DOWN (admin down),
+ # BUILD (on building process)
+ #
+ error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
+ vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
+
+ '''
+ try:
+ self._get_my_tenant()
+ except requests.exceptions.RequestException as e:
+ self._format_request_exception(e)
+
+ net_dict={}
+ for net_id in net_list:
+ net = {}
+ #print "VIMConnector refresh_tenant_vms_and_nets: Getting tenant network from VIM (tenant: " + str(self.tenant) + "): "
+ try:
+ net_vim = self.get_network(net_id)
+ if net_vim['status'] in netStatus2manoFormat:
+ net["status"] = netStatus2manoFormat[ net_vim['status'] ]
+ else:
+ net["status"] = "OTHER"
+ net["error_msg"] = "VIM status reported " + net_vim['status']
+
+ if net["status"] == "ACTIVE" and not net_vim['admin_state_up']:
+ net["status"] = "DOWN"
+ if net_vim.get('last_error'):
+ net['error_msg'] = net_vim['last_error']
+ net["vim_info"] = yaml.safe_dump(net_vim)
+ except vimconn.vimconnNotFoundException as e:
+ self.logger.error("Exception getting net status: %s", str(e))
+ net['status'] = "DELETED"
+ net['error_msg'] = str(e)
+ except (requests.exceptions.RequestException, js_e.ValidationError, vimconn.vimconnException) as e:
+ self.logger.error("Exception getting net status: %s", str(e))
+ net['status'] = "VIM_ERROR"
+ net['error_msg'] = str(e)
+ net_dict[net_id] = net
+ return net_dict
+
+ def action_vminstance(self, vm_id, action_dict):
+ '''Send and action over a VM instance from VIM'''
+ '''Returns the status'''
+ try:
+ self._get_my_tenant()
+ if "console" in action_dict:
+ raise vimconn.vimconnException("getting console is not available at openvim", http_code=vimconn.HTTP_Service_Unavailable)
+ url = self.url+'/'+self.tenant+'/servers/'+vm_id+"/action"
+ self.logger.info("Action over VM instance POST %s", url)
+ vim_response = requests.post(url, headers = self.headers_req, data=json.dumps(action_dict) )
+ self._check_http_request_response(vim_response)
+ return vm_id
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+#NOT USED METHODS in current version
+
+ def host_vim2gui(self, host, server_dict):
+ '''Transform host dictionary from VIM format to GUI format,
+ and append to the server_dict
+ '''
+ if type(server_dict) is not dict:
+ print 'vimconnector.host_vim2gui() ERROR, param server_dict must be a dictionary'
+ return
+ RAD={}
+ occupation={}
+ for numa in host['host']['numas']:
+ RAD_item={}
+ occupation_item={}
+ #memory
+ RAD_item['memory']={'size': str(numa['memory'])+'GB', 'eligible': str(numa['hugepages'])+'GB'}
+ occupation_item['memory']= str(numa['hugepages_consumed'])+'GB'
+ #cpus
+ RAD_item['cpus']={}
+ RAD_item['cpus']['cores'] = []
+ RAD_item['cpus']['eligible_cores'] = []
+ occupation_item['cores']=[]
+ for _ in range(0, len(numa['cores']) / 2):
+ RAD_item['cpus']['cores'].append( [] )
+ for core in numa['cores']:
+ RAD_item['cpus']['cores'][core['core_id']].append(core['thread_id'])
+ if not 'status' in core: RAD_item['cpus']['eligible_cores'].append(core['thread_id'])
+ if 'instance_id' in core: occupation_item['cores'].append(core['thread_id'])
+ #ports
+ RAD_item['ports']={}
+ occupation_item['ports']={}
+ for iface in numa['interfaces']:
+ RAD_item['ports'][ iface['pci'] ] = 'speed:'+str(iface['Mbps'])+'M'
+ occupation_item['ports'][ iface['pci'] ] = { 'occupied': str(100*iface['Mbps_consumed'] / iface['Mbps']) + "%" }
+
+ RAD[ numa['numa_socket'] ] = RAD_item
+ occupation[ numa['numa_socket'] ] = occupation_item
+ server_dict[ host['host']['name'] ] = {'RAD':RAD, 'occupation':occupation}
+
+ def get_hosts_info(self):
+ '''Get the information of deployed hosts
+ Returns the hosts content'''
+ #obtain hosts list
+ url=self.url+'/hosts'
+ try:
+ vim_response = requests.get(url)
+ except requests.exceptions.RequestException as e:
+ print "get_hosts_info Exception: ", e.args
+ return -vimconn.HTTP_Not_Found, str(e.args[0])
+ print "vim get", url, "response:", vim_response.status_code, vim_response.json()
+ #print vim_response.status_code
+ #print json.dumps(vim_response.json(), indent=4)
+ if vim_response.status_code != 200:
+ #TODO: get error
+ print 'vimconnector.get_hosts_info error getting host list %d %s' %(vim_response.status_code, vim_response.json())
+ return -vim_response.status_code, "Error getting host list"
+
+ res,hosts = self._format_in(vim_response, get_hosts_response_schema)
+
+ if res==False:
+ print "vimconnector.get_hosts_info error parsing GET HOSTS vim response", hosts
+ return vimconn.HTTP_Internal_Server_Error, hosts
+ #obtain hosts details
+ hosts_dict={}
+ for host in hosts['hosts']:
+ url=self.url+'/hosts/'+host['id']
+ try:
+ vim_response = requests.get(url)
+ except requests.exceptions.RequestException as e:
+ print "get_hosts_info Exception: ", e.args
+ return -vimconn.HTTP_Not_Found, str(e.args[0])
+ print "vim get", url, "response:", vim_response.status_code, vim_response.json()
+ if vim_response.status_code != 200:
+ print 'vimconnector.get_hosts_info error getting detailed host %d %s' %(vim_response.status_code, vim_response.json())
+ continue
+ res,host_detail = self._format_in(vim_response, get_host_detail_response_schema)
+ if res==False:
+ print "vimconnector.get_hosts_info error parsing GET HOSTS/%s vim response" % host['id'], host_detail
+ continue
+ #print 'host id '+host['id'], json.dumps(host_detail, indent=4)
+ self.host_vim2gui(host_detail, hosts_dict)
+ return 200, hosts_dict
+
+ def get_hosts(self, vim_tenant):
+ '''Get the hosts and deployed instances
+ Returns the hosts content'''
+ #obtain hosts list
+ url=self.url+'/hosts'
+ try:
+ vim_response = requests.get(url)
+ except requests.exceptions.RequestException as e:
+ print "get_hosts Exception: ", e.args
+ return -vimconn.HTTP_Not_Found, str(e.args[0])
+ print "vim get", url, "response:", vim_response.status_code, vim_response.json()
+ #print vim_response.status_code
+ #print json.dumps(vim_response.json(), indent=4)
+ if vim_response.status_code != 200:
+ #TODO: get error
+ print 'vimconnector.get_hosts error getting host list %d %s' %(vim_response.status_code, vim_response.json())
+ return -vim_response.status_code, "Error getting host list"
+
+ res,hosts = self._format_in(vim_response, get_hosts_response_schema)
+
+ if res==False:
+ print "vimconnector.get_host error parsing GET HOSTS vim response", hosts
+ return vimconn.HTTP_Internal_Server_Error, hosts
+ #obtain instances from hosts
+ for host in hosts['hosts']:
+ url=self.url+'/' + vim_tenant + '/servers?hostId='+host['id']
+ try:
+ vim_response = requests.get(url)
+ except requests.exceptions.RequestException as e:
+ print "get_hosts Exception: ", e.args
+ return -vimconn.HTTP_Not_Found, str(e.args[0])
+ print "vim get", url, "response:", vim_response.status_code, vim_response.json()
+ if vim_response.status_code != 200:
+ print 'vimconnector.get_hosts error getting instances at host %d %s' %(vim_response.status_code, vim_response.json())
+ continue
+ res,servers = self._format_in(vim_response, get_server_response_schema)
+ if res==False:
+ print "vimconnector.get_host error parsing GET SERVERS/%s vim response" % host['id'], servers
+ continue
+ #print 'host id '+host['id'], json.dumps(host_detail, indent=4)
+ host['instances'] = servers['servers']
+ return 200, hosts['hosts']
+
+ def get_processor_rankings(self):
+ '''Get the processor rankings in the VIM database'''
+ url=self.url+'/processor_ranking'
+ try:
+ vim_response = requests.get(url)
+ except requests.exceptions.RequestException as e:
+ print "get_processor_rankings Exception: ", e.args
+ return -vimconn.HTTP_Not_Found, str(e.args[0])
+ print "vim get", url, "response:", vim_response.status_code, vim_response.json()
+ #print vim_response.status_code
+ #print json.dumps(vim_response.json(), indent=4)
+ if vim_response.status_code != 200:
+ #TODO: get error
+ print 'vimconnector.get_processor_rankings error getting processor rankings %d %s' %(vim_response.status_code, vim_response.json())
+ return -vim_response.status_code, "Error getting processor rankings"
+
+ res,rankings = self._format_in(vim_response, get_processor_rankings_response_schema)
+ return res, rankings['rankings']
+
+ def new_host(self, host_data):
+ '''Adds a new host to VIM'''
+ '''Returns status code of the VIM response'''
+ payload_req = host_data
+ try:
+ url = self.url_admin+'/hosts'
+ self.logger.info("Adding a new host POST %s", url)
+ vim_response = requests.post(url, headers = self.headers_req, data=payload_req)
+ self._check_http_request_response(vim_response)
+ self.logger.debug(vim_response.text)
+ #print json.dumps(vim_response.json(), indent=4)
+ response = vim_response.json()
+ js_v(response, new_host_response_schema)
+ r = self._remove_extra_items(response, new_host_response_schema)
+ if r is not None:
+ self.logger.warn("Warning: remove extra items %s", str(r))
+ host_id = response['host']['id']
+ return host_id
+ except (requests.exceptions.RequestException, js_e.ValidationError) as e:
+ self._format_request_exception(e)
+
+ def new_external_port(self, port_data):
+ '''Adds a external port to VIM'''
+ '''Returns the port identifier'''
+ #TODO change to logging exception code policies
+ print "VIMConnector: Adding a new external port"
+ payload_req = port_data
+ try:
+ vim_response = requests.post(self.url_admin+'/ports', headers = self.headers_req, data=payload_req)
+ except requests.exceptions.RequestException as e:
+ self.logger.error("new_external_port Exception: ", str(e))
+ return -vimconn.HTTP_Not_Found, str(e.args[0])
+ 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)
+ res, http_content = self._format_in(vim_response, new_port_response_schema)
+ #print http_content
+ if res:
+ r = self._remove_extra_items(http_content, new_port_response_schema)
+ if r is not None: print "Warning: remove extra items ", r
+ #print http_content
+ port_id = http_content['port']['id']
+ print "Port id: ",port_id
+ return vim_response.status_code,port_id
+ else: return -vimconn.HTTP_Bad_Request,http_content
+ else:
+ #print vim_response.text
+ jsonerror = self._format_jsonerror(vim_response)
+ text = 'Error in VIM "%s": not possible to add new external port. HTTP Response: %d. Error: %s' % (self.url_admin, vim_response.status_code, jsonerror)
+ #print text
+ return -vim_response.status_code,text
+
+ def new_external_network(self,net_name,net_type):
+ '''Adds a external network to VIM (shared)'''
+ '''Returns the network identifier'''
+ #TODO change to logging exception code policies
+ print "VIMConnector: Adding external shared network to VIM (type " + net_type + "): "+ net_name
+
+ payload_req = '{"network":{"name": "' + net_name + '","shared":true,"type": "' + net_type + '"}}'
+ try:
+ vim_response = requests.post(self.url+'/networks', headers = self.headers_req, data=payload_req)
+ except requests.exceptions.RequestException as e:
+ self.logger.error( "new_external_network Exception: ", e.args)
+ return -vimconn.HTTP_Not_Found, str(e.args[0])
+ 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)
+ res,http_content = self._format_in(vim_response, new_network_response_schema)
+ #print http_content
+ if res:
+ r = self._remove_extra_items(http_content, new_network_response_schema)
+ if r is not None: print "Warning: remove extra items ", r
+ #print http_content
+ network_id = http_content['network']['id']
+ print "Network id: ",network_id
+ return vim_response.status_code,network_id
+ else: return -vimconn.HTTP_Bad_Request,http_content
+ else:
+ #print vim_response.text
+ jsonerror = self._format_jsonerror(vim_response)
+ text = 'Error in VIM "%s": not possible to add new external network. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror)
+ #print text
+ return -vim_response.status_code,text
+
+ def connect_port_network(self, port_id, network_id, admin=False):
+ '''Connects a external port to a network'''
+ '''Returns status code of the VIM response'''
+ #TODO change to logging exception code policies
+ print "VIMConnector: Connecting external port to network"
+
+ payload_req = '{"port":{"network_id":"' + network_id + '"}}'
+ if admin:
+ if self.url_admin==None:
+ return -vimconn.HTTP_Unauthorized, "datacenter cannot contain admin URL"
+ url= self.url_admin
+ else:
+ url= self.url
+ try:
+ vim_response = requests.put(url +'/ports/'+port_id, headers = self.headers_req, data=payload_req)
+ except requests.exceptions.RequestException as e:
+ print "connect_port_network Exception: ", e.args
+ return -vimconn.HTTP_Not_Found, str(e.args[0])
+ 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)
+ res,http_content = self._format_in(vim_response, new_port_response_schema)
+ #print http_content
+ if res:
+ r = self._remove_extra_items(http_content, new_port_response_schema)
+ if r is not None: print "Warning: remove extra items ", r
+ #print http_content
+ port_id = http_content['port']['id']
+ print "Port id: ",port_id
+ return vim_response.status_code,port_id
+ else: return -vimconn.HTTP_Bad_Request,http_content
+ else:
+ print vim_response.text
+ jsonerror = self._format_jsonerror(vim_response)
+ text = 'Error in VIM "%s": not possible to connect external port to network. HTTP Response: %d. Error: %s' % (self.url_admin, vim_response.status_code, jsonerror)
+ print text
+ return -vim_response.status_code,text
+
+
--- /dev/null
+# -*- 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
+##
+
+"""
+vimconn_vmware implementation an Abstract class in order to interact with VMware vCloud Director.
+mbayramov@vmware.com
+"""
+from progressbar import Percentage, Bar, ETA, FileTransferSpeed, ProgressBar
+
+import vimconn
+import os
+import traceback
+import itertools
+import requests
+import ssl
+import atexit
+
+from pyVmomi import vim, vmodl
+from pyVim.connect import SmartConnect, Disconnect
+
+from xml.etree import ElementTree as XmlElementTree
+from lxml import etree as lxmlElementTree
+
+import yaml
+from pyvcloud import Http
+from pyvcloud.vcloudair import VCA
+from pyvcloud.schema.vcd.v1_5.schemas.vcloud import sessionType, organizationType, \
+ vAppType, organizationListType, vdcType, catalogType, queryRecordViewType, \
+ networkType, vcloudType, taskType, diskType, vmsType, vdcTemplateListType, mediaType
+from xml.sax.saxutils import escape
+
+from pyvcloud.schema.vcd.v1_5.schemas.admin.vCloudEntities import TaskType
+from pyvcloud.schema.vcd.v1_5.schemas.vcloud.taskType import TaskType as GenericTask
+from pyvcloud.schema.vcd.v1_5.schemas.vcloud.vAppType import TaskType as VappTask
+from pyvcloud.schema.vcd.v1_5.schemas.admin.vCloudEntities import TasksInProgressType
+
+import logging
+import json
+import time
+import uuid
+import httplib
+import hashlib
+import socket
+import struct
+import netaddr
+
+# global variable for vcd connector type
+STANDALONE = 'standalone'
+
+# key for flavor dicts
+FLAVOR_RAM_KEY = 'ram'
+FLAVOR_VCPUS_KEY = 'vcpus'
+FLAVOR_DISK_KEY = 'disk'
+DEFAULT_IP_PROFILE = {'gateway_address':"192.168.1.1",
+ 'dhcp_count':50,
+ 'subnet_address':"192.168.1.0/24",
+ 'dhcp_enabled':True,
+ 'dhcp_start_address':"192.168.1.3",
+ 'ip_version':"IPv4",
+ 'dns_address':"192.168.1.2"
+ }
+# global variable for wait time
+INTERVAL_TIME = 5
+MAX_WAIT_TIME = 1800
+
+VCAVERSION = '5.9'
+
+__author__ = "Mustafa Bayramov, Arpita Kate, Sachin Bhangare"
+__date__ = "$12-Jan-2017 11:09:29$"
+__version__ = '0.1'
+
+# -1: "Could not be created",
+# 0: "Unresolved",
+# 1: "Resolved",
+# 2: "Deployed",
+# 3: "Suspended",
+# 4: "Powered on",
+# 5: "Waiting for user input",
+# 6: "Unknown state",
+# 7: "Unrecognized state",
+# 8: "Powered off",
+# 9: "Inconsistent state",
+# 10: "Children do not all have the same status",
+# 11: "Upload initiated, OVF descriptor pending",
+# 12: "Upload initiated, copying contents",
+# 13: "Upload initiated , disk contents pending",
+# 14: "Upload has been quarantined",
+# 15: "Upload quarantine period has expired"
+
+# mapping vCD status to MANO
+vcdStatusCode2manoFormat = {4: 'ACTIVE',
+ 7: 'PAUSED',
+ 3: 'SUSPENDED',
+ 8: 'INACTIVE',
+ 12: 'BUILD',
+ -1: 'ERROR',
+ 14: 'DELETED'}
+
+#
+netStatus2manoFormat = {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
+ 'ERROR': 'ERROR', 'DELETED': 'DELETED'
+ }
+
+class vimconnector(vimconn.vimconnector):
+ # dict used to store flavor in memory
+ flavorlist = {}
+
+ def __init__(self, uuid=None, name=None, tenant_id=None, tenant_name=None,
+ url=None, url_admin=None, user=None, passwd=None, log_level=None, config={}, persistent_info={}):
+ """
+ Constructor create vmware connector to vCloud director.
+
+ By default construct doesn't validate connection state. So client can create object with None arguments.
+ If client specified username , password and host and VDC name. Connector initialize other missing attributes.
+
+ a) It initialize organization UUID
+ b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
+
+ Args:
+ uuid - is organization uuid.
+ name - is organization name that must be presented in vCloud director.
+ tenant_id - is VDC uuid it must be presented in vCloud director
+ tenant_name - is VDC name.
+ url - is hostname or ip address of vCloud director
+ url_admin - same as above.
+ user - is user that administrator for organization. Caller must make sure that
+ username has right privileges.
+
+ password - is password for a user.
+
+ VMware connector also requires PVDC administrative privileges and separate account.
+ This variables must be passed via config argument dict contains keys
+
+ dict['admin_username']
+ dict['admin_password']
+ config - Provide NSX and vCenter information
+
+ Returns:
+ Nothing.
+ """
+
+ vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url,
+ url_admin, user, passwd, log_level, config)
+
+ self.logger = logging.getLogger('openmano.vim.vmware')
+ self.logger.setLevel(10)
+ self.persistent_info = persistent_info
+
+ self.name = name
+ self.id = uuid
+ self.url = url
+ self.url_admin = url_admin
+ self.tenant_id = tenant_id
+ self.tenant_name = tenant_name
+ self.user = user
+ self.passwd = passwd
+ self.config = config
+ self.admin_password = None
+ self.admin_user = None
+ self.org_name = ""
+ self.nsx_manager = None
+ self.nsx_user = None
+ self.nsx_password = None
+ self.vcenter_ip = None
+ self.vcenter_port = None
+ self.vcenter_user = None
+ self.vcenter_password = None
+
+ if tenant_name is not None:
+ orgnameandtenant = tenant_name.split(":")
+ if len(orgnameandtenant) == 2:
+ self.tenant_name = orgnameandtenant[1]
+ self.org_name = orgnameandtenant[0]
+ else:
+ self.tenant_name = tenant_name
+ if "orgname" in config:
+ self.org_name = config['orgname']
+
+ if log_level:
+ self.logger.setLevel(getattr(logging, log_level))
+
+ try:
+ self.admin_user = config['admin_username']
+ self.admin_password = config['admin_password']
+ except KeyError:
+ raise vimconn.vimconnException(message="Error admin username or admin password is empty.")
+
+ try:
+ self.nsx_manager = config['nsx_manager']
+ self.nsx_user = config['nsx_user']
+ self.nsx_password = config['nsx_password']
+ except KeyError:
+ raise vimconn.vimconnException(message="Error: nsx manager or nsx user or nsx password is empty in Config")
+
+ self.vcenter_ip = config.get("vcenter_ip", None)
+ self.vcenter_port = config.get("vcenter_port", None)
+ self.vcenter_user = config.get("vcenter_user", None)
+ self.vcenter_password = config.get("vcenter_password", None)
+
+ self.org_uuid = None
+ self.vca = None
+
+ if not url:
+ raise vimconn.vimconnException('url param can not be NoneType')
+
+ if not self.url_admin: # try to use normal url
+ self.url_admin = self.url
+
+ logging.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self.id, self.org_name,
+ self.tenant_id, self.tenant_name))
+ logging.debug("vcd url {} vcd username: {} vcd password: {}".format(self.url, self.user, self.passwd))
+ logging.debug("vcd admin username {} vcd admin passowrd {}".format(self.admin_user, self.admin_password))
+
+ # initialize organization
+ if self.user is not None and self.passwd is not None and self.url:
+ self.init_organization()
+
+ def __getitem__(self, index):
+ if index == 'name':
+ return self.name
+ if index == 'tenant_id':
+ return self.tenant_id
+ if index == 'tenant_name':
+ return self.tenant_name
+ elif index == 'id':
+ return self.id
+ elif index == 'org_name':
+ return self.org_name
+ elif index == 'org_uuid':
+ return self.org_uuid
+ elif index == 'user':
+ return self.user
+ elif index == 'passwd':
+ return self.passwd
+ elif index == 'url':
+ return self.url
+ elif index == 'url_admin':
+ return self.url_admin
+ elif index == "config":
+ return self.config
+ else:
+ raise KeyError("Invalid key '%s'" % str(index))
+
+ def __setitem__(self, index, value):
+ if index == 'name':
+ self.name = value
+ if index == 'tenant_id':
+ self.tenant_id = value
+ if index == 'tenant_name':
+ self.tenant_name = value
+ elif index == 'id':
+ self.id = value
+ elif index == 'org_name':
+ self.org_name = value
+ elif index == 'org_uuid':
+ self.org_uuid = value
+ elif index == 'user':
+ self.user = value
+ elif index == 'passwd':
+ self.passwd = value
+ elif index == 'url':
+ self.url = value
+ elif index == 'url_admin':
+ self.url_admin = value
+ else:
+ raise KeyError("Invalid key '%s'" % str(index))
+
+ def connect_as_admin(self):
+ """ Method connect as pvdc admin user to vCloud director.
+ There are certain action that can be done only by provider vdc admin user.
+ Organization creation / provider network creation etc.
+
+ Returns:
+ The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
+ """
+
+ self.logger.debug("Logging in to a vca {} as admin.".format(self.org_name))
+
+ vca_admin = VCA(host=self.url,
+ username=self.admin_user,
+ service_type=STANDALONE,
+ version=VCAVERSION,
+ verify=False,
+ log=False)
+ result = vca_admin.login(password=self.admin_password, org='System')
+ if not result:
+ raise vimconn.vimconnConnectionException(
+ "Can't connect to a vCloud director as: {}".format(self.admin_user))
+ result = vca_admin.login(token=vca_admin.token, org='System', org_url=vca_admin.vcloud_session.org_url)
+ if result is True:
+ self.logger.info(
+ "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self.admin_user))
+
+ return vca_admin
+
+ def connect(self):
+ """ Method connect as normal user to vCloud director.
+
+ Returns:
+ The return vca object that letter can be used to connect to vCloud director as admin for VDC
+ """
+
+ try:
+ self.logger.debug("Logging in to a vca {} as {} to datacenter {}.".format(self.org_name,
+ self.user,
+ self.org_name))
+ vca = VCA(host=self.url,
+ username=self.user,
+ service_type=STANDALONE,
+ version=VCAVERSION,
+ verify=False,
+ log=False)
+
+ result = vca.login(password=self.passwd, org=self.org_name)
+ if not result:
+ raise vimconn.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self.user))
+ result = vca.login(token=vca.token, org=self.org_name, org_url=vca.vcloud_session.org_url)
+ if result is True:
+ self.logger.info(
+ "Successfully logged to a vcloud direct org: {} as user: {}".format(self.org_name, self.user))
+
+ except:
+ raise vimconn.vimconnConnectionException("Can't connect to a vCloud director org: "
+ "{} as user: {}".format(self.org_name, self.user))
+
+ return vca
+
+ def init_organization(self):
+ """ Method initialize organization UUID and VDC parameters.
+
+ At bare minimum client must provide organization name that present in vCloud director and VDC.
+
+ The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
+ The Org - UUID will be initialized at the run time if data center present in vCloud director.
+
+ Returns:
+ The return vca object that letter can be used to connect to vcloud direct as admin
+ """
+ try:
+ if self.org_uuid is None:
+ org_dict = self.get_org_list()
+ for org in org_dict:
+ # we set org UUID at the init phase but we can do it only when we have valid credential.
+ if org_dict[org] == self.org_name:
+ self.org_uuid = org
+ self.logger.debug("Setting organization UUID {}".format(self.org_uuid))
+ break
+ else:
+ raise vimconn.vimconnException("Vcloud director organization {} not found".format(self.org_name))
+
+ # if well good we require for org details
+ org_details_dict = self.get_org(org_uuid=self.org_uuid)
+
+ # we have two case if we want to initialize VDC ID or VDC name at run time
+ # tenant_name provided but no tenant id
+ if self.tenant_id is None and self.tenant_name is not None and 'vdcs' in org_details_dict:
+ vdcs_dict = org_details_dict['vdcs']
+ for vdc in vdcs_dict:
+ if vdcs_dict[vdc] == self.tenant_name:
+ self.tenant_id = vdc
+ self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id,
+ self.org_name))
+ break
+ else:
+ raise vimconn.vimconnException("Tenant name indicated but not present in vcloud director.")
+ # case two we have tenant_id but we don't have tenant name so we find and set it.
+ if self.tenant_id is not None and self.tenant_name is None and 'vdcs' in org_details_dict:
+ vdcs_dict = org_details_dict['vdcs']
+ for vdc in vdcs_dict:
+ if vdc == self.tenant_id:
+ self.tenant_name = vdcs_dict[vdc]
+ self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id,
+ self.org_name))
+ break
+ else:
+ raise vimconn.vimconnException("Tenant id indicated but not present in vcloud director")
+ self.logger.debug("Setting organization uuid {}".format(self.org_uuid))
+ except:
+ self.logger.debug("Failed initialize organization UUID for org {}".format(self.org_name))
+ self.logger.debug(traceback.format_exc())
+ self.org_uuid = None
+
+ def new_tenant(self, tenant_name=None, tenant_description=None):
+ """ Method adds a new tenant to VIM with this name.
+ This action requires access to create VDC action in vCloud director.
+
+ Args:
+ tenant_name is tenant_name to be created.
+ tenant_description not used for this call
+
+ Return:
+ returns the tenant identifier in UUID format.
+ If action is failed method will throw vimconn.vimconnException method
+ """
+ vdc_task = self.create_vdc(vdc_name=tenant_name)
+ if vdc_task is not None:
+ vdc_uuid, value = vdc_task.popitem()
+ self.logger.info("Crated new vdc {} and uuid: {}".format(tenant_name, vdc_uuid))
+ return vdc_uuid
+ else:
+ raise vimconn.vimconnException("Failed create tenant {}".format(tenant_name))
+
+ def delete_tenant(self, tenant_id=None):
+ """Delete a tenant from VIM"""
+ 'Returns the tenant identifier'
+ raise vimconn.vimconnNotImplemented("Should have implemented this")
+
+ def get_tenant_list(self, filter_dict={}):
+ """Obtain tenants of VIM
+ filter_dict can contain the following keys:
+ name: filter by tenant name
+ id: filter by tenant uuid/id
+ <other VIM specific>
+ Returns the tenant list of dictionaries:
+ [{'name':'<name>, 'id':'<id>, ...}, ...]
+
+ """
+ org_dict = self.get_org(self.org_uuid)
+ vdcs_dict = org_dict['vdcs']
+
+ vdclist = []
+ try:
+ for k in vdcs_dict:
+ entry = {'name': vdcs_dict[k], 'id': k}
+ # if caller didn't specify dictionary we return all tenants.
+ if filter_dict is not None and filter_dict:
+ filtered_entry = entry.copy()
+ filtered_dict = set(entry.keys()) - set(filter_dict)
+ for unwanted_key in filtered_dict: del entry[unwanted_key]
+ if filter_dict == entry:
+ vdclist.append(filtered_entry)
+ else:
+ vdclist.append(entry)
+ except:
+ self.logger.debug("Error in get_tenant_list()")
+ self.logger.debug(traceback.format_exc())
+ raise vimconn.vimconnException("Incorrect state. {}")
+
+ return vdclist
+
+ def new_network(self, net_name, net_type, ip_profile=None, shared=False):
+ """Adds a tenant network to VIM
+ net_name is the name
+ net_type can be 'bridge','data'.'ptp'.
+ ip_profile is a dict containing the IP parameters of the network
+ shared is a boolean
+ Returns the network identifier"""
+
+ self.logger.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
+ .format(net_name, net_type, ip_profile, shared))
+
+ isshared = 'false'
+ if shared:
+ isshared = 'true'
+
+ network_uuid = self.create_network(network_name=net_name, net_type=net_type,
+ ip_profile=ip_profile, isshared=isshared)
+ if network_uuid is not None:
+ return network_uuid
+ else:
+ raise vimconn.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name))
+
+ def get_vcd_network_list(self):
+ """ Method available organization for a logged in tenant
+
+ Returns:
+ The return vca object that letter can be used to connect to vcloud direct as admin
+ """
+
+ self.logger.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self.tenant_name))
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed.")
+
+ if not self.tenant_name:
+ raise vimconn.vimconnConnectionException("Tenant name is empty.")
+
+ vdc = vca.get_vdc(self.tenant_name)
+ if vdc is None:
+ raise vimconn.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self.tenant_name))
+
+ vdc_uuid = vdc.get_id().split(":")[3]
+ networks = vca.get_networks(vdc.get_name())
+ network_list = []
+ try:
+ for network in networks:
+ filter_dict = {}
+ netid = network.get_id().split(":")
+ if len(netid) != 4:
+ continue
+
+ filter_dict["name"] = network.get_name()
+ filter_dict["id"] = netid[3]
+ filter_dict["shared"] = network.get_IsShared()
+ filter_dict["tenant_id"] = vdc_uuid
+ if network.get_status() == 1:
+ filter_dict["admin_state_up"] = True
+ else:
+ filter_dict["admin_state_up"] = False
+ filter_dict["status"] = "ACTIVE"
+ filter_dict["type"] = "bridge"
+ network_list.append(filter_dict)
+ self.logger.debug("get_vcd_network_list adding entry {}".format(filter_dict))
+ except:
+ self.logger.debug("Error in get_vcd_network_list")
+ self.logger.debug(traceback.format_exc())
+ pass
+
+ self.logger.debug("get_vcd_network_list returning {}".format(network_list))
+ return network_list
+
+ def get_network_list(self, filter_dict={}):
+ """Obtain tenant networks of VIM
+ Filter_dict can be:
+ name: network name OR/AND
+ id: network uuid OR/AND
+ shared: boolean OR/AND
+ tenant_id: tenant OR/AND
+ admin_state_up: boolean
+ status: 'ACTIVE'
+
+ [{key : value , key : value}]
+
+ Returns the network list of dictionaries:
+ [{<the fields at Filter_dict plus some VIM specific>}, ...]
+ List can be empty
+ """
+
+ self.logger.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self.tenant_name))
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed.")
+
+ if not self.tenant_name:
+ raise vimconn.vimconnConnectionException("Tenant name is empty.")
+
+ vdc = vca.get_vdc(self.tenant_name)
+ if vdc is None:
+ raise vimconn.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self.tenant_name))
+
+ vdcid = vdc.get_id().split(":")[3]
+ networks = vca.get_networks(vdc.get_name())
+ network_list = []
+
+ try:
+ for network in networks:
+ filter_entry = {}
+ net_uuid = network.get_id().split(":")
+ if len(net_uuid) != 4:
+ continue
+ else:
+ net_uuid = net_uuid[3]
+ # create dict entry
+ self.logger.debug("Adding {} to a list vcd id {} network {}".format(net_uuid,
+ vdcid,
+ network.get_name()))
+ filter_entry["name"] = network.get_name()
+ filter_entry["id"] = net_uuid
+ filter_entry["shared"] = network.get_IsShared()
+ filter_entry["tenant_id"] = vdcid
+ if network.get_status() == 1:
+ filter_entry["admin_state_up"] = True
+ else:
+ filter_entry["admin_state_up"] = False
+ filter_entry["status"] = "ACTIVE"
+ filter_entry["type"] = "bridge"
+ filtered_entry = filter_entry.copy()
+
+ if filter_dict is not None and filter_dict:
+ # we remove all the key : value we don't care and match only
+ # respected field
+ filtered_dict = set(filter_entry.keys()) - set(filter_dict)
+ for unwanted_key in filtered_dict: del filter_entry[unwanted_key]
+ if filter_dict == filter_entry:
+ network_list.append(filtered_entry)
+ else:
+ network_list.append(filtered_entry)
+ except:
+ self.logger.debug("Error in get_vcd_network_list")
+ self.logger.debug(traceback.format_exc())
+
+ self.logger.debug("Returning {}".format(network_list))
+ return network_list
+
+ def get_network(self, net_id):
+ """Method obtains network details of net_id VIM network
+ Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
+
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed")
+
+ vdc = vca.get_vdc(self.tenant_name)
+ vdc_id = vdc.get_id().split(":")[3]
+
+ networks = vca.get_networks(vdc.get_name())
+ filter_dict = {}
+
+ try:
+ for network in networks:
+ vdc_network_id = network.get_id().split(":")
+ if len(vdc_network_id) == 4 and vdc_network_id[3] == net_id:
+ filter_dict["name"] = network.get_name()
+ filter_dict["id"] = vdc_network_id[3]
+ filter_dict["shared"] = network.get_IsShared()
+ filter_dict["tenant_id"] = vdc_id
+ if network.get_status() == 1:
+ filter_dict["admin_state_up"] = True
+ else:
+ filter_dict["admin_state_up"] = False
+ filter_dict["status"] = "ACTIVE"
+ filter_dict["type"] = "bridge"
+ self.logger.debug("Returning {}".format(filter_dict))
+ return filter_dict
+ except:
+ self.logger.debug("Error in get_network")
+ self.logger.debug(traceback.format_exc())
+
+ return filter_dict
+
+ def delete_network(self, net_id):
+ """
+ Method Deletes a tenant network from VIM, provide the network id.
+
+ Returns the network identifier or raise an exception
+ """
+
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self.tenant_name))
+
+ vcd_network = self.get_vcd_network(network_uuid=net_id)
+ if vcd_network is not None and vcd_network:
+ if self.delete_network_action(network_uuid=net_id):
+ return net_id
+ else:
+ raise vimconn.vimconnNotFoundException("Network {} not found".format(net_id))
+
+ def refresh_nets_status(self, net_list):
+ """Get the status of the networks
+ Params: the list of network identifiers
+ Returns a dictionary with:
+ net_id: #VIM id of this network
+ status: #Mandatory. Text with one of:
+ # DELETED (not found at vim)
+ # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
+ # OTHER (Vim reported other status not understood)
+ # ERROR (VIM indicates an ERROR status)
+ # ACTIVE, INACTIVE, DOWN (admin down),
+ # BUILD (on building process)
+ #
+ error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
+ vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
+
+ """
+
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed")
+
+ dict_entry = {}
+ try:
+ for net in net_list:
+ errormsg = ''
+ vcd_network = self.get_vcd_network(network_uuid=net)
+ if vcd_network is not None and vcd_network:
+ if vcd_network['status'] == '1':
+ status = 'ACTIVE'
+ else:
+ status = 'DOWN'
+ else:
+ status = 'DELETED'
+ errormsg = 'Network not found.'
+
+ dict_entry[net] = {'status': status, 'error_msg': errormsg,
+ 'vim_info': yaml.safe_dump(vcd_network)}
+ except:
+ self.logger.debug("Error in refresh_nets_status")
+ self.logger.debug(traceback.format_exc())
+
+ return dict_entry
+
+ def get_flavor(self, flavor_id):
+ """Obtain flavor details from the VIM
+ Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
+ """
+ if flavor_id not in vimconnector.flavorlist:
+ raise vimconn.vimconnNotFoundException("Flavor not found.")
+ return vimconnector.flavorlist[flavor_id]
+
+ def new_flavor(self, flavor_data):
+ """Adds a tenant flavor to VIM
+ flavor_data contains a dictionary with information, keys:
+ name: flavor name
+ ram: memory (cloud type) in MBytes
+ vpcus: cpus (cloud type)
+ extended: EPA parameters
+ - numas: #items requested in same NUMA
+ memory: number of 1G huge pages memory
+ paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
+ interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
+ - name: interface name
+ dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
+ bandwidth: X Gbps; requested guarantee bandwidth
+ vpci: requested virtual PCI address
+ disk: disk size
+ is_public:
+ #TODO to concrete
+ Returns the flavor identifier"""
+
+ # generate a new uuid put to internal dict and return it.
+ self.logger.debug("Creating new flavor - flavor_data: {}".format(flavor_data))
+ new_flavor=flavor_data
+ ram = flavor_data.get(FLAVOR_RAM_KEY, 1024)
+ cpu = flavor_data.get(FLAVOR_VCPUS_KEY, 1)
+ disk = flavor_data.get(FLAVOR_DISK_KEY, 1)
+
+ extended_flv = flavor_data.get("extended")
+ if extended_flv:
+ numas=extended_flv.get("numas")
+ if numas:
+ for numa in numas:
+ #overwrite ram and vcpus
+ ram = numa['memory']*1024
+ if 'paired-threads' in numa:
+ cpu = numa['paired-threads']*2
+ elif 'cores' in numa:
+ cpu = numa['cores']
+ elif 'threads' in numa:
+ cpu = numa['threads']
+
+ new_flavor[FLAVOR_RAM_KEY] = ram
+ new_flavor[FLAVOR_VCPUS_KEY] = cpu
+ new_flavor[FLAVOR_DISK_KEY] = disk
+ # generate a new uuid put to internal dict and return it.
+ flavor_id = uuid.uuid4()
+ vimconnector.flavorlist[str(flavor_id)] = new_flavor
+ self.logger.debug("Created flavor - {} : {}".format(flavor_id, new_flavor))
+
+ return str(flavor_id)
+
+ def delete_flavor(self, flavor_id):
+ """Deletes a tenant flavor from VIM identify by its id
+
+ Returns the used id or raise an exception
+ """
+ if flavor_id not in vimconnector.flavorlist:
+ raise vimconn.vimconnNotFoundException("Flavor not found.")
+
+ vimconnector.flavorlist.pop(flavor_id, None)
+ return flavor_id
+
+ def new_image(self, image_dict):
+ """
+ Adds a tenant image to VIM
+ Returns:
+ 200, image-id if the image is created
+ <0, message if there is an error
+ """
+
+ return self.get_image_id_from_path(image_dict['location'])
+
+ def delete_image(self, image_id):
+ """
+
+ :param image_id:
+ :return:
+ """
+
+ raise vimconn.vimconnNotImplemented("Should have implemented this")
+
+ def catalog_exists(self, catalog_name, catalogs):
+ """
+
+ :param catalog_name:
+ :param catalogs:
+ :return:
+ """
+ for catalog in catalogs:
+ if catalog.name == catalog_name:
+ return True
+ return False
+
+ def create_vimcatalog(self, vca=None, catalog_name=None):
+ """ Create new catalog entry in vCloud director.
+
+ Args
+ vca: vCloud director.
+ catalog_name catalog that client wish to create. Note no validation done for a name.
+ Client must make sure that provide valid string representation.
+
+ Return (bool) True if catalog created.
+
+ """
+ try:
+ task = vca.create_catalog(catalog_name, catalog_name)
+ result = vca.block_until_completed(task)
+ if not result:
+ return False
+ catalogs = vca.get_catalogs()
+ except:
+ return False
+ return self.catalog_exists(catalog_name, catalogs)
+
+ # noinspection PyIncorrectDocstring
+ def upload_ovf(self, vca=None, catalog_name=None, image_name=None, media_file_name=None,
+ description='', progress=False, chunk_bytes=128 * 1024):
+ """
+ Uploads a OVF file to a vCloud catalog
+
+ :param chunk_bytes:
+ :param progress:
+ :param description:
+ :param image_name:
+ :param vca:
+ :param catalog_name: (str): The name of the catalog to upload the media.
+ :param media_file_name: (str): The name of the local media file to upload.
+ :return: (bool) True if the media file was successfully uploaded, false otherwise.
+ """
+ os.path.isfile(media_file_name)
+ statinfo = os.stat(media_file_name)
+
+ # find a catalog entry where we upload OVF.
+ # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
+ # status change.
+ # if VCD can parse OVF we upload VMDK file
+ for catalog in vca.get_catalogs():
+ if catalog_name != catalog.name:
+ continue
+ link = filter(lambda link: link.get_type() == "application/vnd.vmware.vcloud.media+xml" and
+ link.get_rel() == 'add', catalog.get_Link())
+ assert len(link) == 1
+ data = """
+ <UploadVAppTemplateParams name="%s" xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"><Description>%s vApp Template</Description></UploadVAppTemplateParams>
+ """ % (escape(catalog_name), escape(description))
+ headers = vca.vcloud_session.get_vcloud_headers()
+ headers['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
+ response = Http.post(link[0].get_href(), headers=headers, data=data, verify=vca.verify, logger=self.logger)
+ if response.status_code == requests.codes.created:
+ catalogItem = XmlElementTree.fromstring(response.content)
+ entity = [child for child in catalogItem if
+ child.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
+ href = entity.get('href')
+ template = href
+ response = Http.get(href, headers=vca.vcloud_session.get_vcloud_headers(),
+ verify=vca.verify, logger=self.logger)
+
+ if response.status_code == requests.codes.ok:
+ media = mediaType.parseString(response.content, True)
+ link = filter(lambda link: link.get_rel() == 'upload:default',
+ media.get_Files().get_File()[0].get_Link())[0]
+ headers = vca.vcloud_session.get_vcloud_headers()
+ headers['Content-Type'] = 'Content-Type text/xml'
+ response = Http.put(link.get_href(),
+ data=open(media_file_name, 'rb'),
+ headers=headers,
+ verify=vca.verify, logger=self.logger)
+ if response.status_code != requests.codes.ok:
+ self.logger.debug(
+ "Failed create vApp template for catalog name {} and image {}".format(catalog_name,
+ media_file_name))
+ return False
+
+ # TODO fix this with aync block
+ time.sleep(5)
+
+ self.logger.debug("vApp template for catalog name {} and image {}".format(catalog_name, media_file_name))
+
+ # uploading VMDK file
+ # check status of OVF upload and upload remaining files.
+ response = Http.get(template,
+ headers=vca.vcloud_session.get_vcloud_headers(),
+ verify=vca.verify,
+ logger=self.logger)
+
+ if response.status_code == requests.codes.ok:
+ media = mediaType.parseString(response.content, True)
+ number_of_files = len(media.get_Files().get_File())
+ for index in xrange(0, number_of_files):
+ links_list = filter(lambda link: link.get_rel() == 'upload:default',
+ media.get_Files().get_File()[index].get_Link())
+ for link in links_list:
+ # we skip ovf since it already uploaded.
+ if 'ovf' in link.get_href():
+ continue
+ # The OVF file and VMDK must be in a same directory
+ head, tail = os.path.split(media_file_name)
+ file_vmdk = head + '/' + link.get_href().split("/")[-1]
+ if not os.path.isfile(file_vmdk):
+ return False
+ statinfo = os.stat(file_vmdk)
+ if statinfo.st_size == 0:
+ return False
+ hrefvmdk = link.get_href()
+
+ if progress:
+ print("Uploading file: {}".format(file_vmdk))
+ if progress:
+ widgets = ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
+ FileTransferSpeed()]
+ progress_bar = ProgressBar(widgets=widgets, maxval=statinfo.st_size).start()
+
+ bytes_transferred = 0
+ f = open(file_vmdk, 'rb')
+ while bytes_transferred < statinfo.st_size:
+ my_bytes = f.read(chunk_bytes)
+ if len(my_bytes) <= chunk_bytes:
+ headers = vca.vcloud_session.get_vcloud_headers()
+ headers['Content-Range'] = 'bytes %s-%s/%s' % (
+ bytes_transferred, len(my_bytes) - 1, statinfo.st_size)
+ headers['Content-Length'] = str(len(my_bytes))
+ response = Http.put(hrefvmdk,
+ headers=headers,
+ data=my_bytes,
+ verify=vca.verify,
+ logger=None)
+
+ if response.status_code == requests.codes.ok:
+ bytes_transferred += len(my_bytes)
+ if progress:
+ progress_bar.update(bytes_transferred)
+ else:
+ self.logger.debug(
+ 'file upload failed with error: [%s] %s' % (response.status_code,
+ response.content))
+
+ f.close()
+ return False
+ f.close()
+ if progress:
+ progress_bar.finish()
+ time.sleep(10)
+ return True
+ else:
+ self.logger.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
+ format(catalog_name, media_file_name))
+ return False
+
+ self.logger.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name, media_file_name))
+ return False
+
+ def upload_vimimage(self, vca=None, catalog_name=None, media_name=None, medial_file_name=None, progress=False):
+ """Upload media file"""
+ # TODO add named parameters for readability
+
+ return self.upload_ovf(vca=vca, catalog_name=catalog_name, image_name=media_name.split(".")[0],
+ media_file_name=medial_file_name, description='medial_file_name', progress=progress)
+
+ def validate_uuid4(self, uuid_string=None):
+ """ Method validate correct format of UUID.
+
+ Return: true if string represent valid uuid
+ """
+ try:
+ val = uuid.UUID(uuid_string, version=4)
+ except ValueError:
+ return False
+ return True
+
+ def get_catalogid(self, catalog_name=None, catalogs=None):
+ """ Method check catalog and return catalog ID in UUID format.
+
+ Args
+ catalog_name: catalog name as string
+ catalogs: list of catalogs.
+
+ Return: catalogs uuid
+ """
+
+ for catalog in catalogs:
+ if catalog.name == catalog_name:
+ catalog_id = catalog.get_id().split(":")
+ return catalog_id[3]
+ return None
+
+ def get_catalogbyid(self, catalog_uuid=None, catalogs=None):
+ """ Method check catalog and return catalog name lookup done by catalog UUID.
+
+ Args
+ catalog_name: catalog name as string
+ catalogs: list of catalogs.
+
+ Return: catalogs name or None
+ """
+
+ if not self.validate_uuid4(uuid_string=catalog_uuid):
+ return None
+
+ for catalog in catalogs:
+ catalog_id = catalog.get_id().split(":")[3]
+ if catalog_id == catalog_uuid:
+ return catalog.name
+ return None
+
+ def get_image_id_from_path(self, path=None, progress=False):
+ """ Method upload OVF image to vCloud director.
+
+ Each OVF image represented as single catalog entry in vcloud director.
+ The method check for existing catalog entry. The check done by file name without file extension.
+
+ if given catalog name already present method will respond with existing catalog uuid otherwise
+ it will create new catalog entry and upload OVF file to newly created catalog.
+
+ If method can't create catalog entry or upload a file it will throw exception.
+
+ Method accept boolean flag progress that will output progress bar. It useful method
+ for standalone upload use case. In case to test large file upload.
+
+ Args
+ path: - valid path to OVF file.
+ progress - boolean progress bar show progress bar.
+
+ Return: if image uploaded correct method will provide image catalog UUID.
+ """
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed.")
+
+ if not path:
+ raise vimconn.vimconnException("Image path can't be None.")
+
+ if not os.path.isfile(path):
+ raise vimconn.vimconnException("Can't read file. File not found.")
+
+ if not os.access(path, os.R_OK):
+ raise vimconn.vimconnException("Can't read file. Check file permission to read.")
+
+ self.logger.debug("get_image_id_from_path() client requesting {} ".format(path))
+
+ dirpath, filename = os.path.split(path)
+ flname, file_extension = os.path.splitext(path)
+ if file_extension != '.ovf':
+ self.logger.debug("Wrong file extension {} connector support only OVF container.".format(file_extension))
+ raise vimconn.vimconnException("Wrong container. vCloud director supports only OVF.")
+
+ catalog_name = os.path.splitext(filename)[0]
+ catalog_md5_name = hashlib.md5(path).hexdigest()
+ self.logger.debug("File name {} Catalog Name {} file path {} "
+ "vdc catalog name {}".format(filename, catalog_name, path, catalog_md5_name))
+
+ catalogs = vca.get_catalogs()
+ if len(catalogs) == 0:
+ self.logger.info("Creating a new catalog entry {} in vcloud director".format(catalog_name))
+ result = self.create_vimcatalog(vca, catalog_md5_name)
+ if not result:
+ raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name))
+ result = self.upload_vimimage(vca=vca, catalog_name=catalog_md5_name,
+ media_name=filename, medial_file_name=path, progress=progress)
+ if not result:
+ raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name))
+ return self.get_catalogid(catalog_name, vca.get_catalogs())
+ else:
+ for catalog in catalogs:
+ # search for existing catalog if we find same name we return ID
+ # TODO optimize this
+ if catalog.name == catalog_md5_name:
+ self.logger.debug("Found existing catalog entry for {} "
+ "catalog id {}".format(catalog_name,
+ self.get_catalogid(catalog_md5_name, catalogs)))
+ return self.get_catalogid(catalog_md5_name, vca.get_catalogs())
+
+ # if we didn't find existing catalog we create a new one and upload image.
+ self.logger.debug("Creating new catalog entry {} - {}".format(catalog_name, catalog_md5_name))
+ result = self.create_vimcatalog(vca, catalog_md5_name)
+ if not result:
+ raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name))
+
+ result = self.upload_vimimage(vca=vca, catalog_name=catalog_md5_name,
+ media_name=filename, medial_file_name=path, progress=progress)
+ if not result:
+ raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name))
+
+ return self.get_catalogid(catalog_md5_name, vca.get_catalogs())
+
+ def get_image_list(self, filter_dict={}):
+ '''Obtain tenant images from VIM
+ Filter_dict can be:
+ name: image name
+ id: image uuid
+ checksum: image checksum
+ location: image path
+ Returns the image list of dictionaries:
+ [{<the fields at Filter_dict plus some VIM specific>}, ...]
+ List can be empty
+ '''
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed.")
+ try:
+ image_list = []
+ catalogs = vca.get_catalogs()
+ if len(catalogs) == 0:
+ return image_list
+ else:
+ for catalog in catalogs:
+ catalog_uuid = catalog.get_id().split(":")[3]
+ name = catalog.name
+ filtered_dict = {}
+ if filter_dict.get("name") and filter_dict["name"] != name:
+ continue
+ if filter_dict.get("id") and filter_dict["id"] != catalog_uuid:
+ continue
+ filtered_dict ["name"] = name
+ filtered_dict ["id"] = catalog_uuid
+ image_list.append(filtered_dict)
+
+ self.logger.debug("List of already created catalog items: {}".format(image_list))
+ return image_list
+ except Exception as exp:
+ raise vimconn.vimconnException("Exception occured while retriving catalog items {}".format(exp))
+
+ def get_vappid(self, vdc=None, vapp_name=None):
+ """ Method takes vdc object and vApp name and returns vapp uuid or None
+
+ Args:
+ vdc: The VDC object.
+ vapp_name: is application vappp name identifier
+
+ Returns:
+ The return vApp name otherwise None
+ """
+ if vdc is None or vapp_name is None:
+ return None
+ # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
+ try:
+ refs = filter(lambda ref: ref.name == vapp_name and ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
+ vdc.ResourceEntities.ResourceEntity)
+ if len(refs) == 1:
+ return refs[0].href.split("vapp")[1][1:]
+ except Exception as e:
+ self.logger.exception(e)
+ return False
+ return None
+
+ def check_vapp(self, vdc=None, vapp_uuid=None):
+ """ Method Method returns True or False if vapp deployed in vCloud director
+
+ Args:
+ vca: Connector to VCA
+ vdc: The VDC object.
+ vappid: vappid is application identifier
+
+ Returns:
+ The return True if vApp deployed
+ :param vdc:
+ :param vapp_uuid:
+ """
+ try:
+ refs = filter(lambda ref:
+ ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
+ vdc.ResourceEntities.ResourceEntity)
+ for ref in refs:
+ vappid = ref.href.split("vapp")[1][1:]
+ # find vapp with respected vapp uuid
+ if vappid == vapp_uuid:
+ return True
+ except Exception as e:
+ self.logger.exception(e)
+ return False
+ return False
+
+ def get_namebyvappid(self, vca=None, vdc=None, vapp_uuid=None):
+ """Method returns vApp name from vCD and lookup done by vapp_id.
+
+ Args:
+ vca: Connector to VCA
+ vdc: The VDC object.
+ vapp_uuid: vappid is application identifier
+
+ Returns:
+ The return vApp name otherwise None
+ """
+
+ try:
+ refs = filter(lambda ref: ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
+ vdc.ResourceEntities.ResourceEntity)
+ for ref in refs:
+ # we care only about UUID the rest doesn't matter
+ vappid = ref.href.split("vapp")[1][1:]
+ if vappid == vapp_uuid:
+ response = Http.get(ref.href, headers=vca.vcloud_session.get_vcloud_headers(), verify=vca.verify,
+ logger=self.logger)
+ tree = XmlElementTree.fromstring(response.content)
+ return tree.attrib['name']
+ except Exception as e:
+ self.logger.exception(e)
+ return None
+ return None
+
+ def new_vminstance(self, name=None, description="", start=False, image_id=None, flavor_id=None, net_list={},
+ cloud_config=None, disk_list=None):
+ """Adds a VM instance to VIM
+ Params:
+ start: indicates if VM must start or boot in pause mode. Ignored
+ image_id,flavor_id: image and flavor uuid
+ net_list: list of interfaces, each one is a dictionary with:
+ name:
+ net_id: network uuid to connect
+ vpci: virtual vcpi to assign
+ model: interface model, virtio, e2000, ...
+ mac_address:
+ use: 'data', 'bridge', 'mgmt'
+ type: 'virtual', 'PF', 'VF', 'VFnotShared'
+ vim_id: filled/added by this function
+ cloud_config: can be a text script to be passed directly to cloud-init,
+ or an object to inject users and ssh keys with format:
+ key-pairs: [] list of keys to install to the default user
+ users: [{ name, key-pairs: []}] list of users to add with their key-pair
+ #TODO ip, security groups
+ Returns >=0, the instance identifier
+ <0, error_text
+ """
+
+ self.logger.info("Creating new instance for entry {}".format(name))
+ self.logger.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".format(
+ description, start, image_id, flavor_id, net_list, cloud_config))
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed.")
+
+ #new vm name = vmname + tenant_id + uuid
+ new_vm_name = [name, '-', str(uuid.uuid4())]
+ vmname_andid = ''.join(new_vm_name)
+
+ # if vm already deployed we return existing uuid
+ # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
+ # if vapp_uuid is not None:
+ # return vapp_uuid
+
+ # we check for presence of VDC, Catalog entry and Flavor.
+ vdc = vca.get_vdc(self.tenant_name)
+ if vdc is None:
+ raise vimconn.vimconnNotFoundException(
+ "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name))
+ catalogs = vca.get_catalogs()
+ if catalogs is None:
+ raise vimconn.vimconnNotFoundException(
+ "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name))
+
+ catalog_hash_name = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
+ if catalog_hash_name:
+ self.logger.info("Found catalog entry {} for image id {}".format(catalog_hash_name, image_id))
+ else:
+ raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
+ "(Failed retrieve catalog information {})".format(name, image_id))
+
+
+ # Set vCPU and Memory based on flavor.
+ #
+ vm_cpus = None
+ vm_memory = None
+ vm_disk = None
+ pci_devices_info = []
+ if flavor_id is not None:
+ if flavor_id not in vimconnector.flavorlist:
+ raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
+ "Failed retrieve flavor information "
+ "flavor id {}".format(name, flavor_id))
+ else:
+ try:
+ flavor = vimconnector.flavorlist[flavor_id]
+ vm_cpus = flavor[FLAVOR_VCPUS_KEY]
+ vm_memory = flavor[FLAVOR_RAM_KEY]
+ vm_disk = flavor[FLAVOR_DISK_KEY]
+ extended = flavor.get("extended", None)
+ if extended:
+ numas=extended.get("numas", None)
+ if numas:
+ for numa in numas:
+ for interface in numa.get("interfaces",() ):
+ if interface["dedicated"].strip()=="yes":
+ pci_devices_info.append(interface)
+ except Exception as exp:
+ raise vimconn.vimconnException("Corrupted flavor. {}.Exception: {}".format(flavor_id, exp))
+
+ # image upload creates template name as catalog name space Template.
+ templateName = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
+ power_on = 'false'
+ if start:
+ power_on = 'true'
+
+ # client must provide at least one entry in net_list if not we report error
+ #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
+ #If no mgmt, then the 1st NN in netlist is considered as primary net.
+ primary_net = None
+ primary_netname = None
+ network_mode = 'bridged'
+ if net_list is not None and len(net_list) > 0:
+ for net in net_list:
+ if 'use' in net and net['use'] == 'mgmt':
+ primary_net = net
+ if primary_net is None:
+ primary_net = net_list[0]
+
+ try:
+ primary_net_id = primary_net['net_id']
+ network_dict = self.get_vcd_network(network_uuid=primary_net_id)
+ if 'name' in network_dict:
+ primary_netname = network_dict['name']
+
+ except KeyError:
+ raise vimconn.vimconnException("Corrupted flavor. {}".format(primary_net))
+ else:
+ raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name))
+
+ # use: 'data', 'bridge', 'mgmt'
+ # create vApp. Set vcpu and ram based on flavor id.
+ vapptask = vca.create_vapp(self.tenant_name, vmname_andid, templateName,
+ self.get_catalogbyid(image_id, catalogs),
+ network_name=None, # None while creating vapp
+ network_mode=network_mode,
+ vm_name=vmname_andid,
+ vm_cpus=vm_cpus, # can be None if flavor is None
+ vm_memory=vm_memory) # can be None if flavor is None
+
+ if vapptask is None or vapptask is False:
+ raise vimconn.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(vmname_andid))
+ if type(vapptask) is VappTask:
+ vca.block_until_completed(vapptask)
+
+ # we should have now vapp in undeployed state.
+ vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid)
+ vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), vmname_andid)
+ if vapp is None:
+ raise vimconn.vimconnUnexpectedResponse(
+ "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(
+ vmname_andid))
+
+ #Add PCI passthrough configrations
+ PCI_devices_status = False
+ vm_obj = None
+ si = None
+ if len(pci_devices_info) > 0:
+ self.logger.info("Need to add PCI devices {} into VM {}".format(pci_devices_info,
+ vmname_andid ))
+ PCI_devices_status, vm_obj, vcenter_conect = self.add_pci_devices(vapp_uuid,
+ pci_devices_info,
+ vmname_andid)
+ if PCI_devices_status:
+ self.logger.info("Added PCI devives {} to VM {}".format(
+ pci_devices_info,
+ vmname_andid)
+ )
+ else:
+ self.logger.info("Fail to add PCI devives {} to VM {}".format(
+ pci_devices_info,
+ vmname_andid)
+ )
+ # add vm disk
+ if vm_disk:
+ #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
+ result = self.modify_vm_disk(vapp_uuid, vm_disk)
+ if result :
+ self.logger.debug("Modified Disk size of VM {} ".format(vmname_andid))
+
+ # add NICs & connect to networks in netlist
+ try:
+ self.logger.info("Request to connect VM to a network: {}".format(net_list))
+ nicIndex = 0
+ primary_nic_index = 0
+ for net in net_list:
+ # openmano uses network id in UUID format.
+ # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
+ # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
+ # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
+
+ if 'net_id' not in net:
+ continue
+
+ interface_net_id = net['net_id']
+ interface_net_name = self.get_network_name_by_id(network_uuid=interface_net_id)
+ interface_network_mode = net['use']
+
+ if interface_network_mode == 'mgmt':
+ primary_nic_index = nicIndex
+
+ """- POOL (A static IP address is allocated automatically from a pool of addresses.)
+ - DHCP (The IP address is obtained from a DHCP service.)
+ - MANUAL (The IP address is assigned manually in the IpAddress element.)
+ - NONE (No IP addressing mode specified.)"""
+
+ if primary_netname is not None:
+ nets = filter(lambda n: n.name == interface_net_name, vca.get_networks(self.tenant_name))
+ if len(nets) == 1:
+ self.logger.info("new_vminstance(): Found requested network: {}".format(nets[0].name))
+ task = vapp.connect_to_network(nets[0].name, nets[0].href)
+ if type(task) is GenericTask:
+ vca.block_until_completed(task)
+ # connect network to VM - with all DHCP by default
+ self.logger.info("new_vminstance(): Connecting VM to a network {}".format(nets[0].name))
+ task = vapp.connect_vms(nets[0].name,
+ connection_index=nicIndex,
+ connections_primary_index=primary_nic_index,
+ ip_allocation_mode='DHCP')
+ if type(task) is GenericTask:
+ vca.block_until_completed(task)
+ nicIndex += 1
+ except KeyError:
+ # it might be a case if specific mandatory entry in dict is empty
+ self.logger.debug("Key error {}".format(KeyError.message))
+ raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
+
+ # deploy and power on vm
+ self.logger.debug("new_vminstance(): Deploying vApp {} ".format(name))
+ deploytask = vapp.deploy(powerOn=False)
+ if type(deploytask) is GenericTask:
+ vca.block_until_completed(deploytask)
+
+ # If VM has PCI devices reserve memory for VM
+ if PCI_devices_status and vm_obj and vcenter_conect:
+ memReserve = vm_obj.config.hardware.memoryMB
+ spec = vim.vm.ConfigSpec()
+ spec.memoryAllocation = vim.ResourceAllocationInfo(reservation=memReserve)
+ task = vm_obj.ReconfigVM_Task(spec=spec)
+ if task:
+ result = self.wait_for_vcenter_task(task, vcenter_conect)
+ self.logger.info("Reserved memmoery {} MB for "\
+ "VM VM status: {}".format(str(memReserve),result))
+ else:
+ self.logger.info("Fail to reserved memmoery {} to VM {}".format(
+ str(memReserve),str(vm_obj)))
+
+ self.logger.debug("new_vminstance(): power on vApp {} ".format(name))
+ poweron_task = vapp.poweron()
+ if type(poweron_task) is GenericTask:
+ vca.block_until_completed(poweron_task)
+
+ # check if vApp deployed and if that the case return vApp UUID otherwise -1
+ wait_time = 0
+ vapp_uuid = None
+ while wait_time <= MAX_WAIT_TIME:
+ vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid)
+ if vapp and vapp.me.deployed:
+ vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), vmname_andid)
+ break
+ else:
+ self.logger.debug("new_vminstance(): Wait for vApp {} to deploy".format(name))
+ time.sleep(INTERVAL_TIME)
+
+ wait_time +=INTERVAL_TIME
+
+ if vapp_uuid is not None:
+ return vapp_uuid
+ else:
+ raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
+
+ ##
+ ##
+ ## based on current discussion
+ ##
+ ##
+ ## server:
+ # created: '2016-09-08T11:51:58'
+ # description: simple-instance.linux1.1
+ # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
+ # hostId: e836c036-74e7-11e6-b249-0800273e724c
+ # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
+ # status: ACTIVE
+ # error_msg:
+ # interfaces: …
+ #
+ def get_vminstance(self, vim_vm_uuid=None):
+ """Returns the VM instance information from VIM"""
+
+ self.logger.debug("Client requesting vm instance {} ".format(vim_vm_uuid))
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed.")
+
+ vdc = vca.get_vdc(self.tenant_name)
+ if vdc is None:
+ raise vimconn.vimconnConnectionException(
+ "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
+
+ vm_info_dict = self.get_vapp_details_rest(vapp_uuid=vim_vm_uuid)
+ if not vm_info_dict:
+ self.logger.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid))
+ raise vimconn.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid))
+
+ status_key = vm_info_dict['status']
+ error = ''
+ try:
+ vm_dict = {'created': vm_info_dict['created'],
+ 'description': vm_info_dict['name'],
+ 'status': vcdStatusCode2manoFormat[int(status_key)],
+ 'hostId': vm_info_dict['vmuuid'],
+ 'error_msg': error,
+ 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
+
+ if 'interfaces' in vm_info_dict:
+ vm_dict['interfaces'] = vm_info_dict['interfaces']
+ else:
+ vm_dict['interfaces'] = []
+ except KeyError:
+ vm_dict = {'created': '',
+ 'description': '',
+ 'status': vcdStatusCode2manoFormat[int(-1)],
+ 'hostId': vm_info_dict['vmuuid'],
+ 'error_msg': "Inconsistency state",
+ 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
+
+ return vm_dict
+
+ def delete_vminstance(self, vm__vim_uuid):
+ """Method poweroff and remove VM instance from vcloud director network.
+
+ Args:
+ vm__vim_uuid: VM UUID
+
+ Returns:
+ Returns the instance identifier
+ """
+
+ self.logger.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid))
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed.")
+
+ vdc = vca.get_vdc(self.tenant_name)
+ if vdc is None:
+ self.logger.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
+ self.tenant_name))
+ raise vimconn.vimconnException(
+ "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
+
+ try:
+ vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
+ if vapp_name is None:
+ self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
+ return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
+ else:
+ self.logger.info("Deleting vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
+
+ # Delete vApp and wait for status change if task executed and vApp is None.
+ vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
+
+ if vapp:
+ if vapp.me.deployed:
+ self.logger.info("Powering off vApp {}".format(vapp_name))
+ #Power off vApp
+ powered_off = False
+ wait_time = 0
+ while wait_time <= MAX_WAIT_TIME:
+ vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
+ if not vapp:
+ self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
+ return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
+
+ power_off_task = vapp.poweroff()
+ if type(power_off_task) is GenericTask:
+ result = vca.block_until_completed(power_off_task)
+ if result:
+ powered_off = True
+ break
+ else:
+ self.logger.info("Wait for vApp {} to power off".format(vapp_name))
+ time.sleep(INTERVAL_TIME)
+
+ wait_time +=INTERVAL_TIME
+ if not powered_off:
+ self.logger.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid))
+ else:
+ self.logger.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid))
+
+ #Undeploy vApp
+ self.logger.info("Undeploy vApp {}".format(vapp_name))
+ wait_time = 0
+ undeployed = False
+ while wait_time <= MAX_WAIT_TIME:
+ vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
+ if not vapp:
+ self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
+ return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
+ undeploy_task = vapp.undeploy(action='powerOff')
+
+ if type(undeploy_task) is GenericTask:
+ result = vca.block_until_completed(undeploy_task)
+ if result:
+ undeployed = True
+ break
+ else:
+ self.logger.debug("Wait for vApp {} to undeploy".format(vapp_name))
+ time.sleep(INTERVAL_TIME)
+
+ wait_time +=INTERVAL_TIME
+
+ if not undeployed:
+ self.logger.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid))
+
+ # delete vapp
+ self.logger.info("Start deletion of vApp {} ".format(vapp_name))
+ vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
+
+ if vapp is not None:
+ wait_time = 0
+ result = False
+
+ while wait_time <= MAX_WAIT_TIME:
+ vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
+ if not vapp:
+ self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
+ return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
+
+ delete_task = vapp.delete()
+
+ if type(delete_task) is GenericTask:
+ vca.block_until_completed(delete_task)
+ result = vca.block_until_completed(delete_task)
+ if result:
+ break
+ else:
+ self.logger.debug("Wait for vApp {} to delete".format(vapp_name))
+ time.sleep(INTERVAL_TIME)
+
+ wait_time +=INTERVAL_TIME
+
+ if not result:
+ self.logger.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid))
+
+ except:
+ self.logger.debug(traceback.format_exc())
+ raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
+
+ if vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name) is None:
+ self.logger.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid))
+ return vm__vim_uuid
+ else:
+ raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
+
+ def refresh_vms_status(self, vm_list):
+ """Get the status of the virtual machines and their interfaces/ports
+ Params: the list of VM identifiers
+ Returns a dictionary with:
+ vm_id: #VIM id of this Virtual Machine
+ status: #Mandatory. Text with one of:
+ # DELETED (not found at vim)
+ # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
+ # OTHER (Vim reported other status not understood)
+ # ERROR (VIM indicates an ERROR status)
+ # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
+ # CREATING (on building process), ERROR
+ # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
+ #
+ error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
+ vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
+ interfaces:
+ - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
+ mac_address: #Text format XX:XX:XX:XX:XX:XX
+ vim_net_id: #network id where this interface is connected
+ vim_interface_id: #interface/port VIM id
+ ip_address: #null, or text with IPv4, IPv6 address
+ """
+
+ self.logger.debug("Client requesting refresh vm status for {} ".format(vm_list))
+
+ mac_ip_addr={}
+ rheaders = {'Content-Type': 'application/xml'}
+ iso_edges = ['edge-2','edge-3','edge-6','edge-7','edge-8','edge-9','edge-10']
+
+ try:
+ for edge in iso_edges:
+ nsx_api_url = '/api/4.0/edges/'+ edge +'/dhcp/leaseInfo'
+ self.logger.debug("refresh_vms_status: NSX Manager url: {}".format(nsx_api_url))
+
+ resp = requests.get(self.nsx_manager + nsx_api_url,
+ auth = (self.nsx_user, self.nsx_password),
+ verify = False, headers = rheaders)
+
+ if resp.status_code == requests.codes.ok:
+ dhcp_leases = XmlElementTree.fromstring(resp.text)
+ for child in dhcp_leases:
+ if child.tag == 'dhcpLeaseInfo':
+ dhcpLeaseInfo = child
+ for leaseInfo in dhcpLeaseInfo:
+ for elem in leaseInfo:
+ if (elem.tag)=='macAddress':
+ mac_addr = elem.text
+ if (elem.tag)=='ipAddress':
+ ip_addr = elem.text
+ if (mac_addr) is not None:
+ mac_ip_addr[mac_addr]= ip_addr
+ self.logger.debug("NSX Manager DHCP Lease info: mac_ip_addr : {}".format(mac_ip_addr))
+ else:
+ self.logger.debug("Error occurred while getting DHCP lease info from NSX Manager: {}".format(resp.content))
+ except KeyError:
+ self.logger.debug("Error in response from NSX Manager {}".format(KeyError.message))
+ self.logger.debug(traceback.format_exc())
+
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed.")
+
+ vdc = vca.get_vdc(self.tenant_name)
+ if vdc is None:
+ raise vimconn.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
+
+ vms_dict = {}
+ for vmuuid in vm_list:
+ vmname = self.get_namebyvappid(vca, vdc, vmuuid)
+ if vmname is not None:
+
+ the_vapp = vca.get_vapp(vdc, vmname)
+ vm_info = the_vapp.get_vms_details()
+ vm_status = vm_info[0]['status']
+ vm_pci_details = self.get_vm_pci_details(vmuuid)
+ vm_info[0].update(vm_pci_details)
+
+ vm_dict = {'status': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
+ 'error_msg': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
+ 'vim_info': yaml.safe_dump(vm_info), 'interfaces': []}
+
+ # get networks
+ try:
+ vm_app_networks = the_vapp.get_vms_network_info()
+ for vapp_network in vm_app_networks:
+ for vm_network in vapp_network:
+ if vm_network['name'] == vmname:
+ #Assign IP Address based on MAC Address in NSX DHCP lease info
+ for mac_adres,ip_adres in mac_ip_addr.iteritems():
+ if mac_adres == vm_network['mac']:
+ vm_network['ip']=ip_adres
+ interface = {"mac_address": vm_network['mac'],
+ "vim_net_id": self.get_network_id_by_name(vm_network['network_name']),
+ "vim_interface_id": self.get_network_id_by_name(vm_network['network_name']),
+ 'ip_address': vm_network['ip']}
+ # interface['vim_info'] = yaml.safe_dump(vm_network)
+ vm_dict["interfaces"].append(interface)
+ # add a vm to vm dict
+ vms_dict.setdefault(vmuuid, vm_dict)
+ except KeyError:
+ self.logger.debug("Error in respond {}".format(KeyError.message))
+ self.logger.debug(traceback.format_exc())
+
+ return vms_dict
+
+ def action_vminstance(self, vm__vim_uuid=None, action_dict=None):
+ """Send and action over a VM instance from VIM
+ Returns the vm_id if the action was successfully sent to the VIM"""
+
+ self.logger.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid, action_dict))
+ if vm__vim_uuid is None or action_dict is None:
+ raise vimconn.vimconnException("Invalid request. VM id or action is None.")
+
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed.")
+
+ vdc = vca.get_vdc(self.tenant_name)
+ if vdc is None:
+ return -1, "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)
+
+ vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
+ if vapp_name is None:
+ self.logger.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
+ raise vimconn.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
+ else:
+ self.logger.info("Action_vminstance vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
+
+ try:
+ the_vapp = vca.get_vapp(vdc, vapp_name)
+ # TODO fix all status
+ if "start" in action_dict:
+ vm_info = the_vapp.get_vms_details()
+ vm_status = vm_info[0]['status']
+ self.logger.info("Power on vApp: vm_status:{} {}".format(type(vm_status),vm_status))
+ if vm_status == "Suspended" or vm_status == "Powered off":
+ power_on_task = the_vapp.poweron()
+ if power_on_task is not None and type(power_on_task) is GenericTask:
+ result = vca.block_until_completed(power_on_task)
+ if result:
+ self.logger.info("action_vminstance: Powered on vApp: {}".format(vapp_name))
+ else:
+ self.logger.info("action_vminstance: Failed to power on vApp: {}".format(vapp_name))
+ else:
+ self.logger.info("action_vminstance: Wait for vApp {} to power on".format(vapp_name))
+ elif "rebuild" in action_dict:
+ self.logger.info("action_vminstance: Rebuilding vApp: {}".format(vapp_name))
+ power_on_task = the_vapp.deploy(powerOn=True)
+ if type(power_on_task) is GenericTask:
+ result = vca.block_until_completed(power_on_task)
+ if result:
+ self.logger.info("action_vminstance: Rebuilt vApp: {}".format(vapp_name))
+ else:
+ self.logger.info("action_vminstance: Failed to rebuild vApp: {}".format(vapp_name))
+ else:
+ self.logger.info("action_vminstance: Wait for vApp rebuild {} to power on".format(vapp_name))
+ elif "pause" in action_dict:
+ pass
+ ## server.pause()
+ elif "resume" in action_dict:
+ pass
+ ## server.resume()
+ elif "shutoff" in action_dict or "shutdown" in action_dict:
+ power_off_task = the_vapp.undeploy(action='powerOff')
+ if type(power_off_task) is GenericTask:
+ result = vca.block_until_completed(power_off_task)
+ if result:
+ self.logger.info("action_vminstance: Powered off vApp: {}".format(vapp_name))
+ else:
+ self.logger.info("action_vminstance: Failed to power off vApp: {}".format(vapp_name))
+ else:
+ self.logger.info("action_vminstance: Wait for vApp {} to power off".format(vapp_name))
+ elif "forceOff" in action_dict:
+ the_vapp.reset()
+ elif "terminate" in action_dict:
+ the_vapp.delete()
+ # elif "createImage" in action_dict:
+ # server.create_image()
+ else:
+ pass
+ except:
+ pass
+
+ def get_vminstance_console(self, vm_id, console_type="vnc"):
+ """
+ Get a console for the virtual machine
+ Params:
+ vm_id: uuid of the VM
+ console_type, can be:
+ "novnc" (by default), "xvpvnc" for VNC types,
+ "rdp-html5" for RDP types, "spice-html5" for SPICE types
+ Returns dict with the console parameters:
+ protocol: ssh, ftp, http, https, ...
+ server: usually ip address
+ port: the http, ssh, ... port
+ suffix: extra text, e.g. the http path and query string
+ """
+ raise vimconn.vimconnNotImplemented("Should have implemented this")
+
+ # NOT USED METHODS in current version
+
+ def host_vim2gui(self, host, server_dict):
+ """Transform host dictionary from VIM format to GUI format,
+ and append to the server_dict
+ """
+ raise vimconn.vimconnNotImplemented("Should have implemented this")
+
+ def get_hosts_info(self):
+ """Get the information of deployed hosts
+ Returns the hosts content"""
+ raise vimconn.vimconnNotImplemented("Should have implemented this")
+
+ def get_hosts(self, vim_tenant):
+ """Get the hosts and deployed instances
+ Returns the hosts content"""
+ raise vimconn.vimconnNotImplemented("Should have implemented this")
+
+ def get_processor_rankings(self):
+ """Get the processor rankings in the VIM database"""
+ raise vimconn.vimconnNotImplemented("Should have implemented this")
+
+ def new_host(self, host_data):
+ """Adds a new host to VIM"""
+ '''Returns status code of the VIM response'''
+ raise vimconn.vimconnNotImplemented("Should have implemented this")
+
+ def new_external_port(self, port_data):
+ """Adds a external port to VIM"""
+ '''Returns the port identifier'''
+ raise vimconn.vimconnNotImplemented("Should have implemented this")
+
+ def new_external_network(self, net_name, net_type):
+ """Adds a external network to VIM (shared)"""
+ '''Returns the network identifier'''
+ raise vimconn.vimconnNotImplemented("Should have implemented this")
+
+ def connect_port_network(self, port_id, network_id, admin=False):
+ """Connects a external port to a network"""
+ '''Returns status code of the VIM response'''
+ raise vimconn.vimconnNotImplemented("Should have implemented this")
+
+ def new_vminstancefromJSON(self, vm_data):
+ """Adds a VM instance to VIM"""
+ '''Returns the instance identifier'''
+ raise vimconn.vimconnNotImplemented("Should have implemented this")
+
+ def get_network_name_by_id(self, network_uuid=None):
+ """Method gets vcloud director network named based on supplied uuid.
+
+ Args:
+ network_uuid: network_id
+
+ Returns:
+ The return network name.
+ """
+
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed.")
+
+ if not network_uuid:
+ return None
+
+ try:
+ org_dict = self.get_org(self.org_uuid)
+ if 'networks' in org_dict:
+ org_network_dict = org_dict['networks']
+ for net_uuid in org_network_dict:
+ if net_uuid == network_uuid:
+ return org_network_dict[net_uuid]
+ except:
+ self.logger.debug("Exception in get_network_name_by_id")
+ self.logger.debug(traceback.format_exc())
+
+ return None
+
+ def get_network_id_by_name(self, network_name=None):
+ """Method gets vcloud director network uuid based on supplied name.
+
+ Args:
+ network_name: network_name
+ Returns:
+ The return network uuid.
+ network_uuid: network_id
+ """
+
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed.")
+
+ if not network_name:
+ self.logger.debug("get_network_id_by_name() : Network name is empty")
+ return None
+
+ try:
+ org_dict = self.get_org(self.org_uuid)
+ if org_dict and 'networks' in org_dict:
+ org_network_dict = org_dict['networks']
+ for net_uuid,net_name in org_network_dict.iteritems():
+ if net_name == network_name:
+ return net_uuid
+
+ except KeyError as exp:
+ self.logger.debug("get_network_id_by_name() : KeyError- {} ".format(exp))
+
+ return None
+
+ def list_org_action(self):
+ """
+ Method leverages vCloud director and query for available organization for particular user
+
+ Args:
+ vca - is active VCA connection.
+ vdc_name - is a vdc name that will be used to query vms action
+
+ Returns:
+ The return XML respond
+ """
+
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed")
+
+ url_list = [vca.host, '/api/org']
+ vm_list_rest_call = ''.join(url_list)
+
+ if not (not vca.vcloud_session or not vca.vcloud_session.organization):
+ response = Http.get(url=vm_list_rest_call,
+ headers=vca.vcloud_session.get_vcloud_headers(),
+ verify=vca.verify,
+ logger=vca.logger)
+ if response.status_code == requests.codes.ok:
+ return response.content
+
+ return None
+
+ def get_org_action(self, org_uuid=None):
+ """
+ Method leverages vCloud director and retrieve available object fdr organization.
+
+ Args:
+ vca - is active VCA connection.
+ vdc_name - is a vdc name that will be used to query vms action
+
+ Returns:
+ The return XML respond
+ """
+
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed")
+
+ if org_uuid is None:
+ return None
+
+ url_list = [vca.host, '/api/org/', org_uuid]
+ vm_list_rest_call = ''.join(url_list)
+
+ if not (not vca.vcloud_session or not vca.vcloud_session.organization):
+ response = Http.get(url=vm_list_rest_call,
+ headers=vca.vcloud_session.get_vcloud_headers(),
+ verify=vca.verify,
+ logger=vca.logger)
+ if response.status_code == requests.codes.ok:
+ return response.content
+
+ return None
+
+ def get_org(self, org_uuid=None):
+ """
+ Method retrieves available organization in vCloud Director
+
+ Args:
+ org_uuid - is a organization uuid.
+
+ Returns:
+ The return dictionary with following key
+ "network" - for network list under the org
+ "catalogs" - for network list under the org
+ "vdcs" - for vdc list under org
+ """
+
+ org_dict = {}
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed")
+
+ if org_uuid is None:
+ return org_dict
+
+ content = self.get_org_action(org_uuid=org_uuid)
+ try:
+ vdc_list = {}
+ network_list = {}
+ catalog_list = {}
+ vm_list_xmlroot = XmlElementTree.fromstring(content)
+ for child in vm_list_xmlroot:
+ if child.attrib['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
+ vdc_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
+ org_dict['vdcs'] = vdc_list
+ if child.attrib['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
+ network_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
+ org_dict['networks'] = network_list
+ if child.attrib['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
+ catalog_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
+ org_dict['catalogs'] = catalog_list
+ except:
+ pass
+
+ return org_dict
+
+ def get_org_list(self):
+ """
+ Method retrieves available organization in vCloud Director
+
+ Args:
+ vca - is active VCA connection.
+
+ Returns:
+ The return dictionary and key for each entry VDC UUID
+ """
+
+ org_dict = {}
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed")
+
+ content = self.list_org_action()
+ try:
+ vm_list_xmlroot = XmlElementTree.fromstring(content)
+ for vm_xml in vm_list_xmlroot:
+ if vm_xml.tag.split("}")[1] == 'Org':
+ org_uuid = vm_xml.attrib['href'].split('/')[-1:]
+ org_dict[org_uuid[0]] = vm_xml.attrib['name']
+ except:
+ pass
+
+ return org_dict
+
+ def vms_view_action(self, vdc_name=None):
+ """ Method leverages vCloud director vms query call
+
+ Args:
+ vca - is active VCA connection.
+ vdc_name - is a vdc name that will be used to query vms action
+
+ Returns:
+ The return XML respond
+ """
+ vca = self.connect()
+ if vdc_name is None:
+ return None
+
+ url_list = [vca.host, '/api/vms/query']
+ vm_list_rest_call = ''.join(url_list)
+
+ if not (not vca.vcloud_session or not vca.vcloud_session.organization):
+ refs = filter(lambda ref: ref.name == vdc_name and ref.type_ == 'application/vnd.vmware.vcloud.vdc+xml',
+ vca.vcloud_session.organization.Link)
+ if len(refs) == 1:
+ response = Http.get(url=vm_list_rest_call,
+ headers=vca.vcloud_session.get_vcloud_headers(),
+ verify=vca.verify,
+ logger=vca.logger)
+ if response.status_code == requests.codes.ok:
+ return response.content
+
+ return None
+
+ def get_vapp_list(self, vdc_name=None):
+ """
+ Method retrieves vApp list deployed vCloud director and returns a dictionary
+ contains a list of all vapp deployed for queried VDC.
+ The key for a dictionary is vApp UUID
+
+
+ Args:
+ vca - is active VCA connection.
+ vdc_name - is a vdc name that will be used to query vms action
+
+ Returns:
+ The return dictionary and key for each entry vapp UUID
+ """
+
+ vapp_dict = {}
+ if vdc_name is None:
+ return vapp_dict
+
+ content = self.vms_view_action(vdc_name=vdc_name)
+ try:
+ vm_list_xmlroot = XmlElementTree.fromstring(content)
+ for vm_xml in vm_list_xmlroot:
+ if vm_xml.tag.split("}")[1] == 'VMRecord':
+ if vm_xml.attrib['isVAppTemplate'] == 'true':
+ rawuuid = vm_xml.attrib['container'].split('/')[-1:]
+ if 'vappTemplate-' in rawuuid[0]:
+ # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
+ # vm and use raw UUID as key
+ vapp_dict[rawuuid[0][13:]] = vm_xml.attrib
+ except:
+ pass
+
+ return vapp_dict
+
+ def get_vm_list(self, vdc_name=None):
+ """
+ Method retrieves VM's list deployed vCloud director. It returns a dictionary
+ contains a list of all VM's deployed for queried VDC.
+ The key for a dictionary is VM UUID
+
+
+ Args:
+ vca - is active VCA connection.
+ vdc_name - is a vdc name that will be used to query vms action
+
+ Returns:
+ The return dictionary and key for each entry vapp UUID
+ """
+ vm_dict = {}
+
+ if vdc_name is None:
+ return vm_dict
+
+ content = self.vms_view_action(vdc_name=vdc_name)
+ try:
+ vm_list_xmlroot = XmlElementTree.fromstring(content)
+ for vm_xml in vm_list_xmlroot:
+ if vm_xml.tag.split("}")[1] == 'VMRecord':
+ if vm_xml.attrib['isVAppTemplate'] == 'false':
+ rawuuid = vm_xml.attrib['href'].split('/')[-1:]
+ if 'vm-' in rawuuid[0]:
+ # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
+ # vm and use raw UUID as key
+ vm_dict[rawuuid[0][3:]] = vm_xml.attrib
+ except:
+ pass
+
+ return vm_dict
+
+ def get_vapp(self, vdc_name=None, vapp_name=None, isuuid=False):
+ """
+ Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
+ contains a list of all VM's deployed for queried VDC.
+ The key for a dictionary is VM UUID
+
+
+ Args:
+ vca - is active VCA connection.
+ vdc_name - is a vdc name that will be used to query vms action
+
+ Returns:
+ The return dictionary and key for each entry vapp UUID
+ """
+ vm_dict = {}
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed")
+
+ if vdc_name is None:
+ return vm_dict
+
+ content = self.vms_view_action(vdc_name=vdc_name)
+ try:
+ vm_list_xmlroot = XmlElementTree.fromstring(content)
+ for vm_xml in vm_list_xmlroot:
+ if vm_xml.tag.split("}")[1] == 'VMRecord' and vm_xml.attrib['isVAppTemplate'] == 'false':
+ # lookup done by UUID
+ if isuuid:
+ if vapp_name in vm_xml.attrib['container']:
+ rawuuid = vm_xml.attrib['href'].split('/')[-1:]
+ if 'vm-' in rawuuid[0]:
+ vm_dict[rawuuid[0][3:]] = vm_xml.attrib
+ break
+ # lookup done by Name
+ else:
+ if vapp_name in vm_xml.attrib['name']:
+ rawuuid = vm_xml.attrib['href'].split('/')[-1:]
+ if 'vm-' in rawuuid[0]:
+ vm_dict[rawuuid[0][3:]] = vm_xml.attrib
+ break
+ except:
+ pass
+
+ return vm_dict
+
+ def get_network_action(self, network_uuid=None):
+ """
+ Method leverages vCloud director and query network based on network uuid
+
+ Args:
+ vca - is active VCA connection.
+ network_uuid - is a network uuid
+
+ Returns:
+ The return XML respond
+ """
+
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed")
+
+ if network_uuid is None:
+ return None
+
+ url_list = [vca.host, '/api/network/', network_uuid]
+ vm_list_rest_call = ''.join(url_list)
+
+ if not (not vca.vcloud_session or not vca.vcloud_session.organization):
+ response = Http.get(url=vm_list_rest_call,
+ headers=vca.vcloud_session.get_vcloud_headers(),
+ verify=vca.verify,
+ logger=vca.logger)
+ if response.status_code == requests.codes.ok:
+ return response.content
+
+ return None
+
+ def get_vcd_network(self, network_uuid=None):
+ """
+ Method retrieves available network from vCloud Director
+
+ Args:
+ network_uuid - is VCD network UUID
+
+ Each element serialized as key : value pair
+
+ Following keys available for access. network_configuration['Gateway'}
+ <Configuration>
+ <IpScopes>
+ <IpScope>
+ <IsInherited>true</IsInherited>
+ <Gateway>172.16.252.100</Gateway>
+ <Netmask>255.255.255.0</Netmask>
+ <Dns1>172.16.254.201</Dns1>
+ <Dns2>172.16.254.202</Dns2>
+ <DnsSuffix>vmwarelab.edu</DnsSuffix>
+ <IsEnabled>true</IsEnabled>
+ <IpRanges>
+ <IpRange>
+ <StartAddress>172.16.252.1</StartAddress>
+ <EndAddress>172.16.252.99</EndAddress>
+ </IpRange>
+ </IpRanges>
+ </IpScope>
+ </IpScopes>
+ <FenceMode>bridged</FenceMode>
+
+ Returns:
+ The return dictionary and key for each entry vapp UUID
+ """
+
+ network_configuration = {}
+ if network_uuid is None:
+ return network_uuid
+
+ content = self.get_network_action(network_uuid=network_uuid)
+ try:
+ vm_list_xmlroot = XmlElementTree.fromstring(content)
+
+ network_configuration['status'] = vm_list_xmlroot.get("status")
+ network_configuration['name'] = vm_list_xmlroot.get("name")
+ network_configuration['uuid'] = vm_list_xmlroot.get("id").split(":")[3]
+
+ for child in vm_list_xmlroot:
+ if child.tag.split("}")[1] == 'IsShared':
+ network_configuration['isShared'] = child.text.strip()
+ if child.tag.split("}")[1] == 'Configuration':
+ for configuration in child.iter():
+ tagKey = configuration.tag.split("}")[1].strip()
+ if tagKey != "":
+ network_configuration[tagKey] = configuration.text.strip()
+ return network_configuration
+ except:
+ pass
+
+ return network_configuration
+
+ def delete_network_action(self, network_uuid=None):
+ """
+ Method delete given network from vCloud director
+
+ Args:
+ network_uuid - is a network uuid that client wish to delete
+
+ Returns:
+ The return None or XML respond or false
+ """
+
+ vca = self.connect_as_admin()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed")
+ if network_uuid is None:
+ return False
+
+ url_list = [vca.host, '/api/admin/network/', network_uuid]
+ vm_list_rest_call = ''.join(url_list)
+
+ if not (not vca.vcloud_session or not vca.vcloud_session.organization):
+ response = Http.delete(url=vm_list_rest_call,
+ headers=vca.vcloud_session.get_vcloud_headers(),
+ verify=vca.verify,
+ logger=vca.logger)
+
+ if response.status_code == 202:
+ return True
+
+ return False
+
+ def create_network(self, network_name=None, net_type='bridge', parent_network_uuid=None,
+ ip_profile=None, isshared='true'):
+ """
+ Method create network in vCloud director
+
+ Args:
+ network_name - is network name to be created.
+ net_type - can be 'bridge','data','ptp','mgmt'.
+ ip_profile is a dict containing the IP parameters of the network
+ isshared - is a boolean
+ parent_network_uuid - is parent provider vdc network that will be used for mapping.
+ It optional attribute. by default if no parent network indicate the first available will be used.
+
+ Returns:
+ The return network uuid or return None
+ """
+
+ new_network_name = [network_name, '-', str(uuid.uuid4())]
+ content = self.create_network_rest(network_name=''.join(new_network_name),
+ ip_profile=ip_profile,
+ net_type=net_type,
+ parent_network_uuid=parent_network_uuid,
+ isshared=isshared)
+ if content is None:
+ self.logger.debug("Failed create network {}.".format(network_name))
+ return None
+
+ try:
+ vm_list_xmlroot = XmlElementTree.fromstring(content)
+ vcd_uuid = vm_list_xmlroot.get('id').split(":")
+ if len(vcd_uuid) == 4:
+ self.logger.info("Create new network name: {} uuid: {}".format(network_name, vcd_uuid[3]))
+ return vcd_uuid[3]
+ except:
+ self.logger.debug("Failed create network {}".format(network_name))
+ return None
+
+ def create_network_rest(self, network_name=None, net_type='bridge', parent_network_uuid=None,
+ ip_profile=None, isshared='true'):
+ """
+ Method create network in vCloud director
+
+ Args:
+ network_name - is network name to be created.
+ net_type - can be 'bridge','data','ptp','mgmt'.
+ ip_profile is a dict containing the IP parameters of the network
+ isshared - is a boolean
+ parent_network_uuid - is parent provider vdc network that will be used for mapping.
+ It optional attribute. by default if no parent network indicate the first available will be used.
+
+ Returns:
+ The return network uuid or return None
+ """
+
+ vca = self.connect_as_admin()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed.")
+ if network_name is None:
+ return None
+
+ url_list = [vca.host, '/api/admin/vdc/', self.tenant_id]
+ vm_list_rest_call = ''.join(url_list)
+ if not (not vca.vcloud_session or not vca.vcloud_session.organization):
+ response = Http.get(url=vm_list_rest_call,
+ headers=vca.vcloud_session.get_vcloud_headers(),
+ verify=vca.verify,
+ logger=vca.logger)
+
+ provider_network = None
+ available_networks = None
+ add_vdc_rest_url = None
+
+ if response.status_code != requests.codes.ok:
+ self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
+ response.status_code))
+ return None
+ else:
+ try:
+ vm_list_xmlroot = XmlElementTree.fromstring(response.content)
+ for child in vm_list_xmlroot:
+ if child.tag.split("}")[1] == 'ProviderVdcReference':
+ provider_network = child.attrib.get('href')
+ # application/vnd.vmware.admin.providervdc+xml
+ if child.tag.split("}")[1] == 'Link':
+ if child.attrib.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
+ and child.attrib.get('rel') == 'add':
+ add_vdc_rest_url = child.attrib.get('href')
+ except:
+ self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
+ self.logger.debug("Respond body {}".format(response.content))
+ return None
+
+ # find pvdc provided available network
+ response = Http.get(url=provider_network,
+ headers=vca.vcloud_session.get_vcloud_headers(),
+ verify=vca.verify,
+ logger=vca.logger)
+ if response.status_code != requests.codes.ok:
+ self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
+ response.status_code))
+ return None
+
+ # available_networks.split("/")[-1]
+
+ if parent_network_uuid is None:
+ try:
+ vm_list_xmlroot = XmlElementTree.fromstring(response.content)
+ for child in vm_list_xmlroot.iter():
+ if child.tag.split("}")[1] == 'AvailableNetworks':
+ for networks in child.iter():
+ # application/vnd.vmware.admin.network+xml
+ if networks.attrib.get('href') is not None:
+ available_networks = networks.attrib.get('href')
+ break
+ except:
+ return None
+
+ #Configure IP profile of the network
+ ip_profile = ip_profile if ip_profile is not None else DEFAULT_IP_PROFILE
+
+ gateway_address=ip_profile['gateway_address']
+ dhcp_count=int(ip_profile['dhcp_count'])
+ subnet_address=self.convert_cidr_to_netmask(ip_profile['subnet_address'])
+
+ if ip_profile['dhcp_enabled']==True:
+ dhcp_enabled='true'
+ else:
+ dhcp_enabled='false'
+ dhcp_start_address=ip_profile['dhcp_start_address']
+
+ #derive dhcp_end_address from dhcp_start_address & dhcp_count
+ end_ip_int = int(netaddr.IPAddress(dhcp_start_address))
+ end_ip_int += dhcp_count - 1
+ dhcp_end_address = str(netaddr.IPAddress(end_ip_int))
+
+ ip_version=ip_profile['ip_version']
+ dns_address=ip_profile['dns_address']
+
+ # either use client provided UUID or search for a first available
+ # if both are not defined we return none
+ if parent_network_uuid is not None:
+ url_list = [vca.host, '/api/admin/network/', parent_network_uuid]
+ add_vdc_rest_url = ''.join(url_list)
+
+ if net_type=='ptp':
+ fence_mode="isolated"
+ isshared='false'
+ is_inherited='false'
+ data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
+ <Description>Openmano created</Description>
+ <Configuration>
+ <IpScopes>
+ <IpScope>
+ <IsInherited>{1:s}</IsInherited>
+ <Gateway>{2:s}</Gateway>
+ <Netmask>{3:s}</Netmask>
+ <Dns1>{4:s}</Dns1>
+ <IsEnabled>{5:s}</IsEnabled>
+ <IpRanges>
+ <IpRange>
+ <StartAddress>{6:s}</StartAddress>
+ <EndAddress>{7:s}</EndAddress>
+ </IpRange>
+ </IpRanges>
+ </IpScope>
+ </IpScopes>
+ <FenceMode>{8:s}</FenceMode>
+ </Configuration>
+ <IsShared>{9:s}</IsShared>
+ </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
+ subnet_address, dns_address, dhcp_enabled,
+ dhcp_start_address, dhcp_end_address, fence_mode, isshared)
+
+ else:
+ fence_mode="bridged"
+ is_inherited='false'
+ data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
+ <Description>Openmano created</Description>
+ <Configuration>
+ <IpScopes>
+ <IpScope>
+ <IsInherited>{1:s}</IsInherited>
+ <Gateway>{2:s}</Gateway>
+ <Netmask>{3:s}</Netmask>
+ <Dns1>{4:s}</Dns1>
+ <IsEnabled>{5:s}</IsEnabled>
+ <IpRanges>
+ <IpRange>
+ <StartAddress>{6:s}</StartAddress>
+ <EndAddress>{7:s}</EndAddress>
+ </IpRange>
+ </IpRanges>
+ </IpScope>
+ </IpScopes>
+ <ParentNetwork href="{8:s}"/>
+ <FenceMode>{9:s}</FenceMode>
+ </Configuration>
+ <IsShared>{10:s}</IsShared>
+ </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
+ subnet_address, dns_address, dhcp_enabled,
+ dhcp_start_address, dhcp_end_address, available_networks,
+ fence_mode, isshared)
+
+ headers = vca.vcloud_session.get_vcloud_headers()
+ headers['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
+ try:
+ response = Http.post(url=add_vdc_rest_url,
+ headers=headers,
+ data=data,
+ verify=vca.verify,
+ logger=vca.logger)
+
+ if response.status_code != 201:
+ self.logger.debug("Create Network POST REST API call failed. Return status code {}"
+ .format(response.status_code))
+ else:
+ network = networkType.parseString(response.content, True)
+ create_nw_task = network.get_Tasks().get_Task()[0]
+
+ # if we all ok we respond with content after network creation completes
+ # otherwise by default return None
+ if create_nw_task is not None:
+ self.logger.debug("Create Network REST : Waiting for Nw creation complete")
+ status = vca.block_until_completed(create_nw_task)
+ if status:
+ return response.content
+ else:
+ self.logger.debug("create_network_rest task failed. Network Create response : {}"
+ .format(response.content))
+ except Exception as exp:
+ self.logger.debug("create_network_rest : Exception : {} ".format(exp))
+
+ return None
+
+ def convert_cidr_to_netmask(self, cidr_ip=None):
+ """
+ Method sets convert CIDR netmask address to normal IP format
+ Args:
+ cidr_ip : CIDR IP address
+ Returns:
+ netmask : Converted netmask
+ """
+ if cidr_ip is not None:
+ if '/' in cidr_ip:
+ network, net_bits = cidr_ip.split('/')
+ netmask = socket.inet_ntoa(struct.pack(">I", (0xffffffff << (32 - int(net_bits))) & 0xffffffff))
+ else:
+ netmask = cidr_ip
+ return netmask
+ return None
+
+ def get_provider_rest(self, vca=None):
+ """
+ Method gets provider vdc view from vcloud director
+
+ Args:
+ network_name - is network name to be created.
+ parent_network_uuid - is parent provider vdc network that will be used for mapping.
+ It optional attribute. by default if no parent network indicate the first available will be used.
+
+ Returns:
+ The return xml content of respond or None
+ """
+
+ url_list = [vca.host, '/api/admin']
+ response = Http.get(url=''.join(url_list),
+ headers=vca.vcloud_session.get_vcloud_headers(),
+ verify=vca.verify,
+ logger=vca.logger)
+
+ if response.status_code == requests.codes.ok:
+ return response.content
+ return None
+
+ def create_vdc(self, vdc_name=None):
+
+ vdc_dict = {}
+
+ xml_content = self.create_vdc_from_tmpl_rest(vdc_name=vdc_name)
+ if xml_content is not None:
+ try:
+ task_resp_xmlroot = XmlElementTree.fromstring(xml_content)
+ for child in task_resp_xmlroot:
+ if child.tag.split("}")[1] == 'Owner':
+ vdc_id = child.attrib.get('href').split("/")[-1]
+ vdc_dict[vdc_id] = task_resp_xmlroot.get('href')
+ return vdc_dict
+ except:
+ self.logger.debug("Respond body {}".format(xml_content))
+
+ return None
+
+ def create_vdc_from_tmpl_rest(self, vdc_name=None):
+ """
+ Method create vdc in vCloud director based on VDC template.
+ it uses pre-defined template that must be named openmano
+
+ Args:
+ vdc_name - name of a new vdc.
+
+ Returns:
+ The return xml content of respond or None
+ """
+
+ self.logger.info("Creating new vdc {}".format(vdc_name))
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed")
+ if vdc_name is None:
+ return None
+
+ url_list = [vca.host, '/api/vdcTemplates']
+ vm_list_rest_call = ''.join(url_list)
+ response = Http.get(url=vm_list_rest_call,
+ headers=vca.vcloud_session.get_vcloud_headers(),
+ verify=vca.verify,
+ logger=vca.logger)
+
+ # container url to a template
+ vdc_template_ref = None
+ try:
+ vm_list_xmlroot = XmlElementTree.fromstring(response.content)
+ for child in vm_list_xmlroot:
+ # application/vnd.vmware.admin.providervdc+xml
+ # we need find a template from witch we instantiate VDC
+ if child.tag.split("}")[1] == 'VdcTemplate':
+ if child.attrib.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child.attrib.get(
+ 'name') == 'openmano':
+ vdc_template_ref = child.attrib.get('href')
+ except:
+ self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
+ self.logger.debug("Respond body {}".format(response.content))
+ return None
+
+ # if we didn't found required pre defined template we return None
+ if vdc_template_ref is None:
+ return None
+
+ try:
+ # instantiate vdc
+ url_list = [vca.host, '/api/org/', self.org_uuid, '/action/instantiate']
+ vm_list_rest_call = ''.join(url_list)
+ data = """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
+ <Source href="{1:s}"></Source>
+ <Description>opnemano</Description>
+ </InstantiateVdcTemplateParams>""".format(vdc_name, vdc_template_ref)
+ headers = vca.vcloud_session.get_vcloud_headers()
+ headers['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
+ response = Http.post(url=vm_list_rest_call, headers=headers, data=data, verify=vca.verify,
+ logger=vca.logger)
+ # if we all ok we respond with content otherwise by default None
+ if response.status_code >= 200 and response.status_code < 300:
+ return response.content
+ return None
+ except:
+ self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
+ self.logger.debug("Respond body {}".format(response.content))
+
+ return None
+
+ def create_vdc_rest(self, vdc_name=None):
+ """
+ Method create network in vCloud director
+
+ Args:
+ network_name - is network name to be created.
+ parent_network_uuid - is parent provider vdc network that will be used for mapping.
+ It optional attribute. by default if no parent network indicate the first available will be used.
+
+ Returns:
+ The return network uuid or return None
+ """
+
+ self.logger.info("Creating new vdc {}".format(vdc_name))
+
+ vca = self.connect_as_admin()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed")
+ if vdc_name is None:
+ return None
+
+ url_list = [vca.host, '/api/admin/org/', self.org_uuid]
+ vm_list_rest_call = ''.join(url_list)
+ if not (not vca.vcloud_session or not vca.vcloud_session.organization):
+ response = Http.get(url=vm_list_rest_call,
+ headers=vca.vcloud_session.get_vcloud_headers(),
+ verify=vca.verify,
+ logger=vca.logger)
+
+ provider_vdc_ref = None
+ add_vdc_rest_url = None
+ available_networks = None
+
+ if response.status_code != requests.codes.ok:
+ self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
+ response.status_code))
+ return None
+ else:
+ try:
+ vm_list_xmlroot = XmlElementTree.fromstring(response.content)
+ for child in vm_list_xmlroot:
+ # application/vnd.vmware.admin.providervdc+xml
+ if child.tag.split("}")[1] == 'Link':
+ if child.attrib.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
+ and child.attrib.get('rel') == 'add':
+ add_vdc_rest_url = child.attrib.get('href')
+ except:
+ self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
+ self.logger.debug("Respond body {}".format(response.content))
+ return None
+
+ response = self.get_provider_rest(vca=vca)
+ try:
+ vm_list_xmlroot = XmlElementTree.fromstring(response)
+ for child in vm_list_xmlroot:
+ if child.tag.split("}")[1] == 'ProviderVdcReferences':
+ for sub_child in child:
+ provider_vdc_ref = sub_child.attrib.get('href')
+ except:
+ self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
+ self.logger.debug("Respond body {}".format(response))
+ return None
+
+ if add_vdc_rest_url is not None and provider_vdc_ref is not None:
+ data = """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
+ <AllocationModel>ReservationPool</AllocationModel>
+ <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
+ <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
+ </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
+ <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
+ <ProviderVdcReference
+ name="Main Provider"
+ href="{2:s}" />
+ <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name),
+ escape(vdc_name),
+ provider_vdc_ref)
+
+ headers = vca.vcloud_session.get_vcloud_headers()
+ headers['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
+ response = Http.post(url=add_vdc_rest_url, headers=headers, data=data, verify=vca.verify,
+ logger=vca.logger)
+
+ # if we all ok we respond with content otherwise by default None
+ if response.status_code == 201:
+ return response.content
+ return None
+
+ def get_vapp_details_rest(self, vapp_uuid=None, need_admin_access=False):
+ """
+ Method retrieve vapp detail from vCloud director
+
+ Args:
+ vapp_uuid - is vapp identifier.
+
+ Returns:
+ The return network uuid or return None
+ """
+
+ parsed_respond = {}
+ vca = None
+
+ if need_admin_access:
+ vca = self.connect_as_admin()
+ else:
+ vca = self.connect()
+
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed")
+ if vapp_uuid is None:
+ return None
+
+ url_list = [vca.host, '/api/vApp/vapp-', vapp_uuid]
+ get_vapp_restcall = ''.join(url_list)
+
+ if vca.vcloud_session and vca.vcloud_session.organization:
+ response = Http.get(url=get_vapp_restcall,
+ headers=vca.vcloud_session.get_vcloud_headers(),
+ verify=vca.verify,
+ logger=vca.logger)
+
+ if response.status_code != requests.codes.ok:
+ self.logger.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall,
+ response.status_code))
+ return parsed_respond
+
+ try:
+ xmlroot_respond = XmlElementTree.fromstring(response.content)
+ parsed_respond['ovfDescriptorUploaded'] = xmlroot_respond.attrib['ovfDescriptorUploaded']
+
+ namespaces = {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
+ 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
+ 'vmw': 'http://www.vmware.com/schema/ovf',
+ 'vm': 'http://www.vmware.com/vcloud/v1.5',
+ 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
+ "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
+ "xmlns":"http://www.vmware.com/vcloud/v1.5"
+ }
+
+ created_section = xmlroot_respond.find('vm:DateCreated', namespaces)
+ if created_section is not None:
+ parsed_respond['created'] = created_section.text
+
+ network_section = xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces)
+ if network_section is not None and 'networkName' in network_section.attrib:
+ parsed_respond['networkname'] = network_section.attrib['networkName']
+
+ ipscopes_section = \
+ xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
+ namespaces)
+ if ipscopes_section is not None:
+ for ipscope in ipscopes_section:
+ for scope in ipscope:
+ tag_key = scope.tag.split("}")[1]
+ if tag_key == 'IpRanges':
+ ip_ranges = scope.getchildren()
+ for ipblock in ip_ranges:
+ for block in ipblock:
+ parsed_respond[block.tag.split("}")[1]] = block.text
+ else:
+ parsed_respond[tag_key] = scope.text
+
+ # parse children section for other attrib
+ children_section = xmlroot_respond.find('vm:Children/', namespaces)
+ if children_section is not None:
+ parsed_respond['name'] = children_section.attrib['name']
+ parsed_respond['nestedHypervisorEnabled'] = children_section.attrib['nestedHypervisorEnabled'] \
+ if "nestedHypervisorEnabled" in children_section.attrib else None
+ parsed_respond['deployed'] = children_section.attrib['deployed']
+ parsed_respond['status'] = children_section.attrib['status']
+ parsed_respond['vmuuid'] = children_section.attrib['id'].split(":")[-1]
+ network_adapter = children_section.find('vm:NetworkConnectionSection', namespaces)
+ nic_list = []
+ for adapters in network_adapter:
+ adapter_key = adapters.tag.split("}")[1]
+ if adapter_key == 'PrimaryNetworkConnectionIndex':
+ parsed_respond['primarynetwork'] = adapters.text
+ if adapter_key == 'NetworkConnection':
+ vnic = {}
+ if 'network' in adapters.attrib:
+ vnic['network'] = adapters.attrib['network']
+ for adapter in adapters:
+ setting_key = adapter.tag.split("}")[1]
+ vnic[setting_key] = adapter.text
+ nic_list.append(vnic)
+
+ for link in children_section:
+ if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
+ if link.attrib['rel'] == 'screen:acquireTicket':
+ parsed_respond['acquireTicket'] = link.attrib
+ if link.attrib['rel'] == 'screen:acquireMksTicket':
+ parsed_respond['acquireMksTicket'] = link.attrib
+
+ parsed_respond['interfaces'] = nic_list
+ vCloud_extension_section = children_section.find('xmlns:VCloudExtension', namespaces)
+ if vCloud_extension_section is not None:
+ vm_vcenter_info = {}
+ vim_info = vCloud_extension_section.find('vmext:VmVimInfo', namespaces)
+ vmext = vim_info.find('vmext:VmVimObjectRef', namespaces)
+ if vmext is not None:
+ vm_vcenter_info["vm_moref_id"] = vmext.find('vmext:MoRef', namespaces).text
+ parsed_respond["vm_vcenter_info"]= vm_vcenter_info
+
+ virtual_hardware_section = children_section.find('ovf:VirtualHardwareSection', namespaces)
+ vm_virtual_hardware_info = {}
+ if virtual_hardware_section is not None:
+ for item in virtual_hardware_section.iterfind('ovf:Item',namespaces):
+ if item.find("rasd:Description",namespaces).text == "Hard disk":
+ disk_size = item.find("rasd:HostResource" ,namespaces
+ ).attrib["{"+namespaces['vm']+"}capacity"]
+
+ vm_virtual_hardware_info["disk_size"]= disk_size
+ break
+
+ for link in virtual_hardware_section:
+ if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
+ if link.attrib['rel'] == 'edit' and link.attrib['href'].endswith("/disks"):
+ vm_virtual_hardware_info["disk_edit_href"] = link.attrib['href']
+ break
+
+ parsed_respond["vm_virtual_hardware"]= vm_virtual_hardware_info
+ except Exception as exp :
+ self.logger.info("Error occurred calling rest api for getting vApp details {}".format(exp))
+ return parsed_respond
+
+ def acuire_console(self, vm_uuid=None):
+
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed")
+ if vm_uuid is None:
+ return None
+
+ if not (not vca.vcloud_session or not vca.vcloud_session.organization):
+ vm_dict = self.get_vapp_details_rest(self, vapp_uuid=vm_uuid)
+ console_dict = vm_dict['acquireTicket']
+ console_rest_call = console_dict['href']
+
+ response = Http.post(url=console_rest_call,
+ headers=vca.vcloud_session.get_vcloud_headers(),
+ verify=vca.verify,
+ logger=vca.logger)
+
+ if response.status_code == requests.codes.ok:
+ return response.content
+
+ return None
+
+ def modify_vm_disk(self, vapp_uuid, flavor_disk):
+ """
+ Method retrieve vm disk details
+
+ Args:
+ vapp_uuid - is vapp identifier.
+ flavor_disk - disk size as specified in VNFD (flavor)
+
+ Returns:
+ The return network uuid or return None
+ """
+ status = None
+ try:
+ #Flavor disk is in GB convert it into MB
+ flavor_disk = int(flavor_disk) * 1024
+ vm_details = self.get_vapp_details_rest(vapp_uuid)
+ if vm_details:
+ vm_name = vm_details["name"]
+ self.logger.info("VM: {} flavor_disk :{}".format(vm_name , flavor_disk))
+
+ if vm_details and "vm_virtual_hardware" in vm_details:
+ vm_disk = int(vm_details["vm_virtual_hardware"]["disk_size"])
+ disk_edit_href = vm_details["vm_virtual_hardware"]["disk_edit_href"]
+
+ self.logger.info("VM: {} VM_disk :{}".format(vm_name , vm_disk))
+
+ if flavor_disk > vm_disk:
+ status = self.modify_vm_disk_rest(disk_edit_href ,flavor_disk)
+ self.logger.info("Modify disk of VM {} from {} to {} MB".format(vm_name,
+ vm_disk, flavor_disk ))
+ else:
+ status = True
+ self.logger.info("No need to modify disk of VM {}".format(vm_name))
+
+ return status
+ except Exception as exp:
+ self.logger.info("Error occurred while modifing disk size {}".format(exp))
+
+
+ def modify_vm_disk_rest(self, disk_href , disk_size):
+ """
+ Method retrieve modify vm disk size
+
+ Args:
+ disk_href - vCD API URL to GET and PUT disk data
+ disk_size - disk size as specified in VNFD (flavor)
+
+ Returns:
+ The return network uuid or return None
+ """
+ vca = self.connect()
+ if not vca:
+ raise vimconn.vimconnConnectionException("self.connect() is failed")
+ if disk_href is None or disk_size is None:
+ return None
+
+ if vca.vcloud_session and vca.vcloud_session.organization:
+ response = Http.get(url=disk_href,
+ headers=vca.vcloud_session.get_vcloud_headers(),
+ verify=vca.verify,
+ logger=vca.logger)
+
+ if response.status_code != requests.codes.ok:
+ self.logger.debug("GET REST API call {} failed. Return status code {}".format(disk_href,
+ response.status_code))
+ return None
+ try:
+ lxmlroot_respond = lxmlElementTree.fromstring(response.content)
+ namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.iteritems() if prefix}
+ namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
+
+ for item in lxmlroot_respond.iterfind('xmlns:Item',namespaces):
+ if item.find("rasd:Description",namespaces).text == "Hard disk":
+ disk_item = item.find("rasd:HostResource" ,namespaces )
+ if disk_item is not None:
+ disk_item.attrib["{"+namespaces['xmlns']+"}capacity"] = str(disk_size)
+ break
+
+ data = lxmlElementTree.tostring(lxmlroot_respond, encoding='utf8', method='xml',
+ xml_declaration=True)
+
+ #Send PUT request to modify disk size
+ headers = vca.vcloud_session.get_vcloud_headers()
+ headers['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
+
+ response = Http.put(url=disk_href,
+ data=data,
+ headers=headers,
+ verify=vca.verify, logger=self.logger)
+
+ if response.status_code != 202:
+ self.logger.debug("PUT REST API call {} failed. Return status code {}".format(disk_href,
+ response.status_code))
+ else:
+ modify_disk_task = taskType.parseString(response.content, True)
+ if type(modify_disk_task) is GenericTask:
+ status = vca.block_until_completed(modify_disk_task)
+ return status
+
+ return None
+
+ except Exception as exp :
+ self.logger.info("Error occurred calling rest api for modifing disk size {}".format(exp))
+ return None
+
+ def add_pci_devices(self, vapp_uuid , pci_devices , vmname_andid):
+ """
+ Method to attach pci devices to VM
+
+ Args:
+ vapp_uuid - uuid of vApp/VM
+ pci_devices - pci devices infromation as specified in VNFD (flavor)
+
+ Returns:
+ The status of add pci device task , vm object and
+ vcenter_conect object
+ """
+ vm_obj = None
+ vcenter_conect = None
+ self.logger.info("Add pci devices {} into vApp {}".format(pci_devices , vapp_uuid))
+ try:
+ vm_vcenter_info = self.get_vm_vcenter_info(vapp_uuid)
+ except Exception as exp:
+ self.logger.error("Error occurred while getting vCenter infromationn"\
+ " for VM : {}".format(exp))
+ raise vimconn.vimconnException(message=exp)
+
+ if vm_vcenter_info["vm_moref_id"]:
+ context = None
+ if hasattr(ssl, '_create_unverified_context'):
+ context = ssl._create_unverified_context()
+ try:
+ no_of_pci_devices = len(pci_devices)
+ if no_of_pci_devices > 0:
+ vcenter_conect = SmartConnect(
+ host=vm_vcenter_info["vm_vcenter_ip"],
+ user=vm_vcenter_info["vm_vcenter_user"],
+ pwd=vm_vcenter_info["vm_vcenter_password"],
+ port=int(vm_vcenter_info["vm_vcenter_port"]),
+ sslContext=context)
+ atexit.register(Disconnect, vcenter_conect)
+ content = vcenter_conect.RetrieveContent()
+
+ #Get VM and its host
+ host_obj, vm_obj = self.get_vm_obj(content ,vm_vcenter_info["vm_moref_id"])
+ self.logger.info("VM {} is currently on host {}".format(vm_obj, host_obj))
+ if host_obj and vm_obj:
+ #get PCI devies from host on which vapp is currently installed
+ avilable_pci_devices = self.get_pci_devices(host_obj, no_of_pci_devices)
+
+ if avilable_pci_devices is None:
+ #find other hosts with active pci devices
+ new_host_obj , avilable_pci_devices = self.get_host_and_PCIdevices(
+ content,
+ no_of_pci_devices
+ )
+
+ if new_host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
+ #Migrate vm to the host where PCI devices are availble
+ self.logger.info("Relocate VM {} on new host {}".format(vm_obj, new_host_obj))
+ task = self.relocate_vm(new_host_obj, vm_obj)
+ if task is not None:
+ result = self.wait_for_vcenter_task(task, vcenter_conect)
+ self.logger.info("Migrate VM status: {}".format(result))
+ host_obj = new_host_obj
+ else:
+ self.logger.info("Fail to migrate VM : {}".format(result))
+ raise vimconn.vimconnNotFoundException(
+ "Fail to migrate VM : {} to host {}".format(
+ vmname_andid,
+ new_host_obj)
+ )
+
+ if host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
+ #Add PCI devices one by one
+ for pci_device in avilable_pci_devices:
+ task = self.add_pci_to_vm(host_obj, vm_obj, pci_device)
+ if task:
+ status= self.wait_for_vcenter_task(task, vcenter_conect)
+ if status:
+ self.logger.info("Added PCI device {} to VM {}".format(pci_device,str(vm_obj)))
+ else:
+ self.logger.error("Fail to add PCI device {} to VM {}".format(pci_device,str(vm_obj)))
+ return True, vm_obj, vcenter_conect
+ else:
+ self.logger.error("Currently there is no host with"\
+ " {} number of avaialble PCI devices required for VM {}".format(
+ no_of_pci_devices,
+ vmname_andid)
+ )
+ raise vimconn.vimconnNotFoundException(
+ "Currently there is no host with {} "\
+ "number of avaialble PCI devices required for VM {}".format(
+ no_of_pci_devices,
+ vmname_andid))
+ else:
+ self.logger.debug("No infromation about PCI devices {} ",pci_devices)
+
+ except vmodl.MethodFault as error:
+ self.logger.error("Error occurred while adding PCI devices {} ",error)
+ return None, vm_obj, vcenter_conect
+
+ def get_vm_obj(self, content, mob_id):
+ """
+ Method to get the vsphere VM object associated with a given morf ID
+ Args:
+ vapp_uuid - uuid of vApp/VM
+ content - vCenter content object
+ mob_id - mob_id of VM
+
+ Returns:
+ VM and host object
+ """
+ vm_obj = None
+ host_obj = None
+ try :
+ container = content.viewManager.CreateContainerView(content.rootFolder,
+ [vim.VirtualMachine], True
+ )
+ for vm in container.view:
+ mobID = vm._GetMoId()
+ if mobID == mob_id:
+ vm_obj = vm
+ host_obj = vm_obj.runtime.host
+ break
+ except Exception as exp:
+ self.logger.error("Error occurred while finding VM object : {}".format(exp))
+ return host_obj, vm_obj
+
+ def get_pci_devices(self, host, need_devices):
+ """
+ Method to get the details of pci devices on given host
+ Args:
+ host - vSphere host object
+ need_devices - number of pci devices needed on host
+
+ Returns:
+ array of pci devices
+ """
+ all_devices = []
+ all_device_ids = []
+ used_devices_ids = []
+
+ try:
+ if host:
+ pciPassthruInfo = host.config.pciPassthruInfo
+ pciDevies = host.hardware.pciDevice
+
+ for pci_status in pciPassthruInfo:
+ if pci_status.passthruActive:
+ for device in pciDevies:
+ if device.id == pci_status.id:
+ all_device_ids.append(device.id)
+ all_devices.append(device)
+
+ #check if devices are in use
+ avalible_devices = all_devices
+ for vm in host.vm:
+ if vm.runtime.powerState == vim.VirtualMachinePowerState.poweredOn:
+ vm_devices = vm.config.hardware.device
+ for device in vm_devices:
+ if type(device) is vim.vm.device.VirtualPCIPassthrough:
+ if device.backing.id in all_device_ids:
+ for use_device in avalible_devices:
+ if use_device.id == device.backing.id:
+ avalible_devices.remove(use_device)
+ used_devices_ids.append(device.backing.id)
+ self.logger.debug("Device {} from devices {}"\
+ "is in use".format(device.backing.id,
+ device)
+ )
+ if len(avalible_devices) < need_devices:
+ self.logger.debug("Host {} don't have {} number of active devices".format(host,
+ need_devices))
+ self.logger.debug("found only {} devives {}".format(len(avalible_devices),
+ avalible_devices))
+ return None
+ else:
+ required_devices = avalible_devices[:need_devices]
+ self.logger.info("Found {} PCI devivces on host {} but required only {}".format(
+ len(avalible_devices),
+ host,
+ need_devices))
+ self.logger.info("Retruning {} devices as {}".format(need_devices,
+ required_devices ))
+ return required_devices
+
+ except Exception as exp:
+ self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host))
+
+ return None
+
+ def get_host_and_PCIdevices(self, content, need_devices):
+ """
+ Method to get the details of pci devices infromation on all hosts
+
+ Args:
+ content - vSphere host object
+ need_devices - number of pci devices needed on host
+
+ Returns:
+ array of pci devices and host object
+ """
+ host_obj = None
+ pci_device_objs = None
+ try:
+ if content:
+ container = content.viewManager.CreateContainerView(content.rootFolder,
+ [vim.HostSystem], True)
+ for host in container.view:
+ devices = self.get_pci_devices(host, need_devices)
+ if devices:
+ host_obj = host
+ pci_device_objs = devices
+ break
+ except Exception as exp:
+ self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host_obj))
+
+ return host_obj,pci_device_objs
+
+ def relocate_vm(self, dest_host, vm) :
+ """
+ Method to get the relocate VM to new host
+
+ Args:
+ dest_host - vSphere host object
+ vm - vSphere VM object
+
+ Returns:
+ task object
+ """
+ task = None
+ try:
+ relocate_spec = vim.vm.RelocateSpec(host=dest_host)
+ task = vm.Relocate(relocate_spec)
+ self.logger.info("Migrating {} to destination host {}".format(vm, dest_host))
+ except Exception as exp:
+ self.logger.error("Error occurred while relocate VM {} to new host {}: {}".format(
+ dest_host, vm, exp))
+ return task
+
+ def wait_for_vcenter_task(self, task, actionName='job', hideResult=False):
+ """
+ Waits and provides updates on a vSphere task
+ """
+ while task.info.state == vim.TaskInfo.State.running:
+ time.sleep(2)
+
+ if task.info.state == vim.TaskInfo.State.success:
+ if task.info.result is not None and not hideResult:
+ self.logger.info('{} completed successfully, result: {}'.format(
+ actionName,
+ task.info.result))
+ else:
+ self.logger.info('Task {} completed successfully.'.format(actionName))
+ else:
+ self.logger.error('{} did not complete successfully: {} '.format(
+ actionName,
+ task.info.error)
+ )
+
+ return task.info.result
+
+ def add_pci_to_vm(self,host_object, vm_object, host_pci_dev):
+ """
+ Method to add pci device in given VM
+
+ Args:
+ host_object - vSphere host object
+ vm_object - vSphere VM object
+ host_pci_dev - host_pci_dev must be one of the devices from the
+ host_object.hardware.pciDevice list
+ which is configured as a PCI passthrough device
+
+ Returns:
+ task object
+ """
+ task = None
+ if vm_object and host_object and host_pci_dev:
+ try :
+ #Add PCI device to VM
+ pci_passthroughs = vm_object.environmentBrowser.QueryConfigTarget(host=None).pciPassthrough
+ systemid_by_pciid = {item.pciDevice.id: item.systemId for item in pci_passthroughs}
+
+ if host_pci_dev.id not in systemid_by_pciid:
+ self.logger.error("Device {} is not a passthrough device ".format(host_pci_dev))
+ return None
+
+ deviceId = hex(host_pci_dev.deviceId % 2**16).lstrip('0x')
+ backing = vim.VirtualPCIPassthroughDeviceBackingInfo(deviceId=deviceId,
+ id=host_pci_dev.id,
+ systemId=systemid_by_pciid[host_pci_dev.id],
+ vendorId=host_pci_dev.vendorId,
+ deviceName=host_pci_dev.deviceName)
+
+ hba_object = vim.VirtualPCIPassthrough(key=-100, backing=backing)
+
+ new_device_config = vim.VirtualDeviceConfigSpec(device=hba_object)
+ new_device_config.operation = "add"
+ vmConfigSpec = vim.vm.ConfigSpec()
+ vmConfigSpec.deviceChange = [new_device_config]
+
+ task = vm_object.ReconfigVM_Task(spec=vmConfigSpec)
+ self.logger.info("Adding PCI device {} into VM {} from host {} ".format(
+ host_pci_dev, vm_object, host_object)
+ )
+ except Exception as exp:
+ self.logger.error("Error occurred while adding pci devive {} to VM {}: {}".format(
+ host_pci_dev,
+ vm_object,
+ exp))
+ return task
+
+ def get_vm_vcenter_info(self , vapp_uuid):
+ """
+ Method to get details of vCenter and vm
+
+ Args:
+ vapp_uuid - uuid of vApp or VM
+
+ Returns:
+ Moref Id of VM and deails of vCenter
+ """
+ vm_vcenter_info = {}
+
+ if self.vcenter_ip is not None:
+ vm_vcenter_info["vm_vcenter_ip"] = self.vcenter_ip
+ else:
+ raise vimconn.vimconnException(message="vCenter IP is not provided."\
+ " Please provide vCenter IP while attaching datacenter to tenant in --config")
+ if self.vcenter_port is not None:
+ vm_vcenter_info["vm_vcenter_port"] = self.vcenter_port
+ else:
+ raise vimconn.vimconnException(message="vCenter port is not provided."\
+ " Please provide vCenter port while attaching datacenter to tenant in --config")
+ if self.vcenter_user is not None:
+ vm_vcenter_info["vm_vcenter_user"] = self.vcenter_user
+ else:
+ raise vimconn.vimconnException(message="vCenter user is not provided."\
+ " Please provide vCenter user while attaching datacenter to tenant in --config")
+
+ if self.vcenter_password is not None:
+ vm_vcenter_info["vm_vcenter_password"] = self.vcenter_password
+ else:
+ raise vimconn.vimconnException(message="vCenter user password is not provided."\
+ " Please provide vCenter user password while attaching datacenter to tenant in --config")
+ try:
+ vm_details = self.get_vapp_details_rest(vapp_uuid, need_admin_access=True)
+ if vm_details and "vm_vcenter_info" in vm_details:
+ vm_vcenter_info["vm_moref_id"] = vm_details["vm_vcenter_info"].get("vm_moref_id", None)
+
+ return vm_vcenter_info
+
+ except Exception as exp:
+ self.logger.error("Error occurred while getting vCenter infromationn"\
+ " for VM : {}".format(exp))
+
+
+ def get_vm_pci_details(self, vmuuid):
+ """
+ Method to get VM PCI device details from vCenter
+
+ Args:
+ vm_obj - vSphere VM object
+
+ Returns:
+ dict of PCI devives attached to VM
+
+ """
+ vm_pci_devices_info = {}
+ try:
+ vm_vcenter_info = self.get_vm_vcenter_info(vmuuid)
+ if vm_vcenter_info["vm_moref_id"]:
+ context = None
+ if hasattr(ssl, '_create_unverified_context'):
+ context = ssl._create_unverified_context()
+ vcenter_conect = SmartConnect(host=vm_vcenter_info["vm_vcenter_ip"],
+ user=vm_vcenter_info["vm_vcenter_user"],
+ pwd=vm_vcenter_info["vm_vcenter_password"],
+ port=int(vm_vcenter_info["vm_vcenter_port"]),
+ sslContext=context
+ )
+ atexit.register(Disconnect, vcenter_conect)
+ content = vcenter_conect.RetrieveContent()
+
+ #Get VM and its host
+ if content:
+ host_obj, vm_obj = self.get_vm_obj(content ,vm_vcenter_info["vm_moref_id"])
+ if host_obj and vm_obj:
+ vm_pci_devices_info["host_name"]= host_obj.name
+ vm_pci_devices_info["host_ip"]= host_obj.config.network.vnic[0].spec.ip.ipAddress
+ for device in vm_obj.config.hardware.device:
+ if type(device) == vim.vm.device.VirtualPCIPassthrough:
+ device_details={'devide_id':device.backing.id,
+ 'pciSlotNumber':device.slotInfo.pciSlotNumber,
+ }
+ vm_pci_devices_info[device.deviceInfo.label] = device_details
+ else:
+ self.logger.error("Can not connect to vCenter while getting "\
+ "PCI devices infromationn")
+ return vm_pci_devices_info
+ except Exception as exp:
+ self.logger.error("Error occurred while getting VM infromationn"\
+ " for VM : {}".format(exp))
+ raise vimconn.vimconnException(message=exp)
+
--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##
+# This file is standalone vmware vcloud director util
+# 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: mbayramov@vmware.com
+##
+
+"""
+
+Standalone application that leverage openmano vmware connector work with vCloud director rest api.
+
+ - Provides capability to create and delete VDC for specific organization.
+ - Create, delete and manage network for specific VDC
+ - List deployed VM's , VAPPs, VDSs, Organization
+ - View detail information about VM / Vapp , Organization etc
+ - Operate with images upload / boot / power on etc
+
+ Usage example.
+
+ List organization created in vCloud director
+ vmwarecli.py -u admin -p qwerty123 -c 172.16.254.206 -U Administrator -P qwerty123 -o test -v TEF list org
+
+ List VDC for particular organization
+ vmwarecli.py -u admin -p qwerty123 -c 172.16.254.206 -U Administrator -P qwerty123 -o test -v TEF list vdc
+
+ Upload image
+ python vmwarerecli.py image upload /Users/spyroot/Developer/Openmano/Ro/vnfs/cirros/cirros.ovf
+
+ Boot Image
+ python vmwarerecli.py -u admin -p qwerty123 -c 172.16.254.206 -o test -v TEF image boot cirros cirros
+
+ View vApp
+ python vmwarerecli.py -u admin -p qwerty123 -c 172.16.254.206 -o test -v TEF view vapp 90bd2b4e-f782-46cf-b5e2-c3817dcf6633 -u
+
+ List VMS
+ python vmwarerecli.py -u admin -p qwerty123 -c 172.16.254.206 -o test -v TEF list vms
+
+ List VDC in OSM format
+ python vmwarerecli.py -u admin -p qwerty123 -c 172.16.254.206 -o test -v TEF list vdc -o
+
+Mustaafa Bayramov
+mbayramov@vmware.com
+"""
+import os
+import argparse
+import traceback
+import uuid
+
+from xml.etree import ElementTree as ET
+
+import sys
+from pyvcloud import Http
+
+import logging
+import vimconn
+import time
+import uuid
+import urllib3
+import requests
+
+from vimconn_vmware import vimconnector
+from requests.packages.urllib3.exceptions import InsecureRequestWarning
+from prettytable import PrettyTable
+
+requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
+
+__author__ = "Mustafa Bayramov"
+__date__ = "$16-Sep-2016 11:09:29$"
+
+
+# TODO move to main vim
+def delete_network_action(vca=None, network_uuid=None):
+ """
+ Method leverages vCloud director and query network based on network uuid
+
+ Args:
+ vca - is active VCA connection.
+ network_uuid - is a network uuid
+
+ Returns:
+ The return XML respond
+ """
+
+ if vca is None or network_uuid is None:
+ return None
+
+ url_list = [vca.host, '/api/admin/network/', network_uuid]
+ vm_list_rest_call = ''.join(url_list)
+
+ if not (not vca.vcloud_session or not vca.vcloud_session.organization):
+ response = Http.get(url=vm_list_rest_call,
+ headers=vca.vcloud_session.get_vcloud_headers(),
+ verify=vca.verify,
+ logger=vca.logger)
+ if response.status_code == requests.codes.ok:
+ print response.content
+ return response.content
+
+ return None
+
+
+def print_vapp(vapp_dict=None):
+ """ Method takes vapp_dict and print in tabular format
+
+ Args:
+ vapp_dict: container vapp object.
+
+ Returns:
+ The return nothing
+ """
+
+ # following key available to print
+ # {'status': 'POWERED_OFF', 'storageProfileName': '*', 'hardwareVersion': '7', 'vmToolsVersion': '0',
+ # 'memoryMB': '384',
+ # 'href': 'https://172.16.254.206/api/vAppTemplate/vm-129e22e8-08dc-4cb6-8358-25f635e65d3b',
+ # 'isBusy': 'false', 'isDeployed': 'false', 'isInMaintenanceMode': 'false', 'isVAppTemplate': 'true',
+ # 'networkName': 'nat', 'isDeleted': 'false', 'catalogName': 'Cirros',
+ # 'containerName': 'Cirros Template', # 'container':
+ # 'https://172.16.254.206/api/vAppTemplate/vappTemplate-b966453d-c361-4505-9e38-ccef45815e5d',
+ # 'name': 'Cirros', 'pvdcHighestSupportedHardwareVersion': '11', 'isPublished': 'false',
+ # 'numberOfCpus': '1', 'vdc': 'https://172.16.254.206/api/vdc/a5056f85-418c-4bfd-8041-adb0f48be9d9',
+ # 'guestOs': 'Other (32-bit)', 'isVdcEnabled': 'true'}
+
+ if vapp_dict is None:
+ return
+
+ vm_table = PrettyTable(['vm uuid',
+ 'vapp name',
+ 'vapp uuid',
+ 'network name',
+ 'storage name',
+ 'vcpu', 'memory', 'hw ver','deployed','status'])
+ for k in vapp_dict:
+ entry = []
+ entry.append(k)
+ entry.append(vapp_dict[k]['containerName'])
+ # vm-b1f5cd4c-2239-4c89-8fdc-a41ff18e0d61
+ entry.append(vapp_dict[k]['container'].split('/')[-1:][0][5:])
+ entry.append(vapp_dict[k]['networkName'])
+ entry.append(vapp_dict[k]['storageProfileName'])
+ entry.append(vapp_dict[k]['numberOfCpus'])
+ entry.append(vapp_dict[k]['memoryMB'])
+ entry.append(vapp_dict[k]['pvdcHighestSupportedHardwareVersion'])
+ entry.append(vapp_dict[k]['isDeployed'])
+ entry.append(vapp_dict[k]['status'])
+
+ vm_table.add_row(entry)
+
+ print vm_table
+
+
+def print_org(org_dict=None):
+ """ Method takes vapp_dict and print in tabular format
+
+ Args:
+ org_dict: dictionary of organization where key is org uuid.
+
+ Returns:
+ The return nothing
+ """
+
+ if org_dict is None:
+ return
+
+ org_table = PrettyTable(['org uuid', 'name'])
+ for k in org_dict:
+ entry = [k, org_dict[k]]
+ org_table.add_row(entry)
+
+ print org_table
+
+
+def print_vm_list(vm_dict=None):
+ """ Method takes vapp_dict and print in tabular format
+
+ Args:
+ vm_dict: dictionary of organization where key is org uuid.
+
+ Returns:
+ The return nothing
+ """
+ if vm_dict is None:
+ return
+
+ vm_table = PrettyTable(
+ ['vm uuid', 'vm name', 'vapp uuid', 'vdc uuid', 'network name', 'is deployed', 'vcpu', 'memory', 'status'])
+
+ try:
+ for k in vm_dict:
+ entry = []
+ entry.append(k)
+ entry.append(vm_dict[k]['name'])
+ entry.append(vm_dict[k]['container'].split('/')[-1:][0][5:])
+ entry.append(vm_dict[k]['vdc'].split('/')[-1:][0])
+ entry.append(vm_dict[k]['networkName'])
+ entry.append(vm_dict[k]['isDeployed'])
+ entry.append(vm_dict[k]['numberOfCpus'])
+ entry.append(vm_dict[k]['memoryMB'])
+ entry.append(vm_dict[k]['status'])
+ vm_table.add_row(entry)
+ print vm_table
+ except KeyError:
+ logger.error("wrong key {}".format(KeyError.message))
+ pass
+
+
+def print_vdc_list(org_dict=None):
+ """ Method takes vapp_dict and print in tabular format
+
+ Args:
+ org_dict: dictionary of organization where key is org uuid.
+
+ Returns:
+ The return nothing
+ """
+ if org_dict is None:
+ return
+ try:
+ vdcs_dict = {}
+ if org_dict.has_key('vdcs'):
+ vdcs_dict = org_dict['vdcs']
+ vdc_table = PrettyTable(['vdc uuid', 'vdc name'])
+ for k in vdcs_dict:
+ entry = [k, vdcs_dict[k]]
+ vdc_table.add_row(entry)
+
+ print vdc_table
+ except KeyError:
+ logger.error("wrong key {}".format(KeyError.message))
+ logger.logger.debug(traceback.format_exc())
+
+
+def print_network_list(org_dict=None):
+ """ Method print network list.
+
+ Args:
+ org_dict: dictionary of organization that contain key networks with a list of all
+ network for for specific VDC
+
+ Returns:
+ The return nothing
+ """
+ if org_dict is None:
+ return
+ try:
+ network_dict = {}
+ if org_dict.has_key('networks'):
+ network_dict = org_dict['networks']
+ network_table = PrettyTable(['network uuid', 'network name'])
+ for k in network_dict:
+ entry = [k, network_dict[k]]
+ network_table.add_row(entry)
+
+ print network_table
+
+ except KeyError:
+ logger.error("wrong key {}".format(KeyError.message))
+ logger.logger.debug(traceback.format_exc())
+
+
+def print_org_details(org_dict=None):
+ """ Method takes vapp_dict and print in tabular format
+
+ Args:
+ org_dict: dictionary of organization where key is org uuid.
+
+ Returns:
+ The return nothing
+ """
+ if org_dict is None:
+ return
+ try:
+ catalogs_dict = {}
+
+ print_vdc_list(org_dict=org_dict)
+ print_network_list(org_dict=org_dict)
+
+ if org_dict.has_key('catalogs'):
+ catalogs_dict = org_dict['catalogs']
+
+ catalog_table = PrettyTable(['catalog uuid', 'catalog name'])
+ for k in catalogs_dict:
+ entry = [k, catalogs_dict[k]]
+ catalog_table.add_row(entry)
+
+ print catalog_table
+
+ except KeyError:
+ logger.error("wrong key {}".format(KeyError.message))
+ logger.logger.debug(traceback.format_exc())
+
+
+def delete_actions(vim=None, action=None, namespace=None):
+ if action == 'network' or namespace.action == 'network':
+ logger.debug("Requesting delete for network {}".format(namespace.network_name))
+ network_uuid = namespace.network_name
+ # if request name based we need find UUID
+ # TODO optimize it or move to external function
+ if not namespace.uuid:
+ org_dict = vim.get_org_list()
+ for org in org_dict:
+ org_net = vim.get_org(org)['networks']
+ for network in org_net:
+ if org_net[network] == namespace.network_name:
+ network_uuid = network
+
+ vim.delete_network_action(network_uuid=network_uuid)
+
+
+def list_actions(vim=None, action=None, namespace=None):
+ """ Method provide list object from VDC action
+
+ Args:
+ vim - is vcloud director vim connector.
+ action - is action for list ( vdc / org etc)
+ namespace - must contain VDC / Org information.
+
+ Returns:
+ The return nothing
+ """
+
+ org_id = None
+ myorgs = vim.get_org_list()
+ for org in myorgs:
+ if myorgs[org] == namespace.vcdorg:
+ org_id = org
+ break
+ else:
+ print(" Invalid organization.")
+ return
+
+ if action == 'vms' or namespace.action == 'vms':
+ vm_dict = vim.get_vm_list(vdc_name=namespace.vcdvdc)
+ print_vm_list(vm_dict=vm_dict)
+ elif action == 'vapps' or namespace.action == 'vapps':
+ vapp_dict = vim.get_vapp_list(vdc_name=namespace.vcdvdc)
+ print_vapp(vapp_dict=vapp_dict)
+ elif action == 'networks' or namespace.action == 'networks':
+ if namespace.osm:
+ osm_print(vim.get_network_list(filter_dict={}))
+ else:
+ print_network_list(vim.get_org(org_uuid=org_id))
+ elif action == 'vdc' or namespace.action == 'vdc':
+ if namespace.osm:
+ osm_print(vim.get_tenant_list(filter_dict=None))
+ else:
+ print_vdc_list(vim.get_org(org_uuid=org_id))
+ elif action == 'org' or namespace.action == 'org':
+ print_org(org_dict=vim.get_org_list())
+ else:
+ return None
+
+
+def print_network_details(network_dict=None):
+ try:
+ network_table = PrettyTable(network_dict.keys())
+ entry = [network_dict.values()]
+ network_table.add_row(entry[0])
+ print network_table
+ except KeyError:
+ logger.error("wrong key {}".format(KeyError.message))
+ logger.logger.debug(traceback.format_exc())
+
+
+def osm_print(generic_dict=None):
+
+ try:
+ for element in generic_dict:
+ table = PrettyTable(element.keys())
+ entry = [element.values()]
+ table.add_row(entry[0])
+ print table
+ except KeyError:
+ logger.error("wrong key {}".format(KeyError.message))
+ logger.logger.debug(traceback.format_exc())
+
+
+def view_actions(vim=None, action=None, namespace=None):
+ org_id = None
+ orgs = vim.get_org_list()
+ for org in orgs:
+ if orgs[org] == namespace.vcdorg:
+ org_id = org
+ break
+ else:
+ print(" Invalid organization.")
+ return
+
+ myorg = vim.get_org(org_uuid=org_id)
+
+ # view org
+ if action == 'org' or namespace.action == 'org':
+ org_id = None
+ orgs = vim.get_org_list()
+ if namespace.uuid:
+ if namespace.org_name in orgs:
+ org_id = namespace.org_name
+ else:
+ # we need find UUID based on name provided
+ for org in orgs:
+ if orgs[org] == namespace.org_name:
+ org_id = org
+ break
+
+ logger.debug("Requesting view for orgs {}".format(org_id))
+ print_org_details(vim.get_org(org_uuid=org_id))
+
+ # view vapp action
+ if action == 'vapp' or namespace.action == 'vapp':
+ if namespace.vapp_name is not None and namespace.uuid:
+ logger.debug("Requesting vapp {} for vdc {}".format(namespace.vapp_name, namespace.vcdvdc))
+ vapp_dict = {}
+ vapp_uuid = namespace.vapp_name
+ # if request based on just name we need get UUID
+ if not namespace.uuid:
+ vapp_uuid = vim.get_vappid(vdc=namespace.vcdvdc, vapp_name=namespace.vapp_name)
+ if vapp_uuid is None:
+ print("Can't find vapp by given name {}".format(namespace.vapp_name))
+ return
+
+ print " namespace {}".format(namespace)
+ if vapp_dict is not None and namespace.osm:
+ vm_info_dict = vim.get_vminstance(vim_vm_uuid=vapp_uuid)
+ print vm_info_dict
+ if vapp_dict is not None and namespace.osm != True:
+ vapp_dict = vim.get_vapp(vdc_name=namespace.vcdvdc, vapp_name=vapp_uuid, isuuid=True)
+ print_vapp(vapp_dict=vapp_dict)
+
+ # view network
+ if action == 'network' or namespace.action == 'network':
+ logger.debug("Requesting view for network {}".format(namespace.network_name))
+ network_uuid = namespace.network_name
+ # if request name based we need find UUID
+ # TODO optimize it or move to external function
+ if not namespace.uuid:
+ if not myorg.has_key('networks'):
+ print("Network {} is undefined in vcloud director for org {} vdc {}".format(namespace.network_name,
+ vim.name,
+ vim.tenant_name))
+ return
+
+ my_org_net = myorg['networks']
+ for network in my_org_net:
+ if my_org_net[network] == namespace.network_name:
+ network_uuid = network
+ break
+
+ print print_network_details(network_dict=vim.get_vcd_network(network_uuid=network_uuid))
+
+
+def create_actions(vim=None, action=None, namespace=None):
+ """Method gets provider vdc view from vcloud director
+
+ Args:
+ vim - is Cloud director vim connector
+ action - action for create ( network / vdc etc)
+
+ Returns:
+ The return xml content of respond or None
+ """
+ if action == 'network' or namespace.action == 'network':
+ logger.debug("Creating a network in vcloud director".format(namespace.network_name))
+ network_uuid = vim.create_network(namespace.network_name)
+ if network_uuid is not None:
+ print ("Crated new network {} and uuid: {}".format(namespace.network_name, network_uuid))
+ else:
+ print ("Failed create a new network {}".format(namespace.network_name))
+ elif action == 'vdc' or namespace.action == 'vdc':
+ logger.debug("Creating a new vdc in vcloud director.".format(namespace.vdc_name))
+ vdc_uuid = vim.create_vdc(namespace.vdc_name)
+ if vdc_uuid is not None:
+ print ("Crated new vdc {} and uuid: {}".format(namespace.vdc_name, vdc_uuid))
+ else:
+ print ("Failed create a new vdc {}".format(namespace.vdc_name))
+ else:
+ return None
+
+
+def validate_uuid4(uuid_string):
+ """Function validate that string contain valid uuid4
+
+ Args:
+ uuid_string - valid UUID string
+
+ Returns:
+ The return true if string contain valid UUID format
+ """
+ try:
+ val = uuid.UUID(uuid_string, version=4)
+ except ValueError:
+ return False
+ return True
+
+
+def upload_image(vim=None, image_file=None):
+ """Function upload image to vcloud director
+
+ Args:
+ image_file - valid UUID string
+
+ Returns:
+ The return true if image uploaded correctly
+ """
+ try:
+ catalog_uuid = vim.get_image_id_from_path(path=image_file, progress=True)
+ if catalog_uuid is not None and validate_uuid4(catalog_uuid):
+ print("Image uploaded and uuid {}".format(catalog_uuid))
+ return True
+ except vimconn.vimconnException as upload_exception:
+ print("Failed uploaded {} image".format(image_file))
+ print("Error Reason: {}".format(upload_exception.message))
+ return False
+
+
+def boot_image(vim=None, image_name=None, vm_name=None):
+ """ Function boot image that resided in vcloud director.
+ The image name can be UUID of name.
+
+ Args:
+ vim - vim connector
+ image_name - image identified by UUID or text string.
+ vm_name - vmname
+
+
+ Returns:
+ The return true if image uploaded correctly
+ """
+
+ vim_catalog = None
+ try:
+ catalogs = vim.vca.get_catalogs()
+ if not validate_uuid4(image_name):
+ vim_catalog = vim.get_catalogid(catalog_name=image_name, catalogs=catalogs)
+ if vim_catalog is None:
+ return None
+ else:
+ vim_catalog = vim.get_catalogid(catalog_name=image_name, catalogs=catalogs)
+ if vim_catalog is None:
+ return None
+
+ print (" Booting {} image id {} ".format(vm_name, vim_catalog))
+ vm_uuid = vim.new_vminstance(name=vm_name, image_id=vim_catalog)
+ if vm_uuid is not None and validate_uuid4(vm_uuid):
+ print("Image booted and vm uuid {}".format(vm_uuid))
+ vapp_dict = vim.get_vapp(vdc_name=namespace.vcdvdc, vapp_name=vm_uuid, isuuid=True)
+ if vapp_dict is not None:
+ print_vapp(vapp_dict=vapp_dict)
+ return True
+ except vimconn.vimconnNotFoundException as notFound:
+ print("Failed boot {} image".format(image_name))
+ print(notFound.message)
+ except vimconn.vimconnException as vimconError:
+ print("Failed boot {} image".format(image_name))
+ print(vimconError.message)
+ except:
+ print("Failed boot {} image".format(image_name))
+
+
+ return False
+
+
+def image_action(vim=None, action=None, namespace=None):
+ """ Function present set of action to manipulate with image.
+ - upload image
+ - boot image.
+ - delete image ( not yet done )
+
+ Args:
+ vim - vcloud director connector
+ action - string (upload/boot etc)
+ namespace - contain other attributes image name etc
+
+ Returns:
+ The return nothing
+ """
+
+ if action == 'upload' or namespace.action == 'upload':
+ upload_image(vim=vim, image_file=namespace.image)
+ elif action == 'boot' or namespace.action == 'boot':
+ boot_image(vim=vim, image_name=namespace.image, vm_name=namespace.vmname)
+ else:
+ return None
+
+
+def vmwarecli(command=None, action=None, namespace=None):
+ logger.debug("Namespace {}".format(namespace))
+ urllib3.disable_warnings()
+
+ vcduser = None
+ vcdpasword = None
+ vcdhost = None
+ vcdorg = None
+
+ if hasattr(__builtins__, 'raw_input'):
+ input = raw_input
+
+ if namespace.vcdvdc is None:
+ while True:
+ vcduser = input("Enter vcd username: ")
+ if vcduser is not None and len(vcduser) > 0:
+ break
+ else:
+ vcduser = namespace.vcduser
+
+ if namespace.vcdpassword is None:
+ while True:
+ vcdpasword = input("Please enter vcd password: ")
+ if vcdpasword is not None and len(vcdpasword) > 0:
+ break
+ else:
+ vcdpasword = namespace.vcdpassword
+
+ if namespace.vcdhost is None:
+ while True:
+ vcdhost = input("Please enter vcd host name or ip: ")
+ if vcdhost is not None and len(vcdhost) > 0:
+ break
+ else:
+ vcdhost = namespace.vcdhost
+
+ if namespace.vcdorg is None:
+ while True:
+ vcdorg = input("Please enter vcd organization name: ")
+ if vcdorg is not None and len(vcdorg) > 0:
+ break
+ else:
+ vcdorg = namespace.vcdorg
+
+ try:
+ vim = vimconnector(uuid=None,
+ name=vcdorg,
+ tenant_id=None,
+ tenant_name=namespace.vcdvdc,
+ url=vcdhost,
+ url_admin=vcdhost,
+ user=vcduser,
+ passwd=vcdpasword,
+ log_level="DEBUG",
+ config={'admin_username': namespace.vcdamdin, 'admin_password': namespace.vcdadminpassword})
+ vim.vca = vim.connect()
+
+ except vimconn.vimconnConnectionException:
+ print("Failed connect to vcloud director. Please check credential and hostname.")
+ return
+
+ # list
+ if command == 'list' or namespace.command == 'list':
+ logger.debug("Client requested list action")
+ # route request to list actions
+ list_actions(vim=vim, action=action, namespace=namespace)
+
+ # view action
+ if command == 'view' or namespace.command == 'view':
+ logger.debug("Client requested view action")
+ view_actions(vim=vim, action=action, namespace=namespace)
+
+ # delete action
+ if command == 'delete' or namespace.command == 'delete':
+ logger.debug("Client requested delete action")
+ delete_actions(vim=vim, action=action, namespace=namespace)
+
+ # create action
+ if command == 'create' or namespace.command == 'create':
+ logger.debug("Client requested create action")
+ create_actions(vim=vim, action=action, namespace=namespace)
+
+ # image action
+ if command == 'image' or namespace.command == 'image':
+ logger.debug("Client requested create action")
+ image_action(vim=vim, action=action, namespace=namespace)
+
+
+if __name__ == '__main__':
+ defaults = {'vcdvdc': 'default',
+ 'vcduser': 'admin',
+ 'vcdpassword': 'admin',
+ 'vcdhost': 'https://localhost',
+ 'vcdorg': 'default',
+ 'debug': 'INFO'}
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-u', '--vcduser', help='vcloud director username', type=str)
+ parser.add_argument('-p', '--vcdpassword', help='vcloud director password', type=str)
+ parser.add_argument('-U', '--vcdamdin', help='vcloud director password', type=str)
+ parser.add_argument('-P', '--vcdadminpassword', help='vcloud director password', type=str)
+ parser.add_argument('-c', '--vcdhost', help='vcloud director host', type=str)
+ parser.add_argument('-o', '--vcdorg', help='vcloud director org', type=str)
+ parser.add_argument('-v', '--vcdvdc', help='vcloud director vdc', type=str)
+ parser.add_argument('-d', '--debug', help='debug level', type=int)
+
+ parser_subparsers = parser.add_subparsers(help='commands', dest='command')
+ sub = parser_subparsers.add_parser('list', help='List objects (VMs, vApps, networks)')
+ sub_subparsers = sub.add_subparsers(dest='action')
+
+ list_vms = sub_subparsers.add_parser('vms', help='list - all vm deployed in vCloud director')
+ list_vapps = sub_subparsers.add_parser('vapps', help='list - all vapps deployed in vCloud director')
+ list_network = sub_subparsers.add_parser('networks', help='list - all networks deployed')
+ list_network.add_argument('-o', '--osm', default=False, action='store_true', help='provide view in OSM format')
+
+ #list vdc
+ list_vdc = sub_subparsers.add_parser('vdc', help='list - list all vdc for organization accessible to you')
+ list_vdc.add_argument('-o', '--osm', default=False, action='store_true', help='provide view in OSM format')
+
+ list_org = sub_subparsers.add_parser('org', help='list - list of organizations accessible to you.')
+
+ create_sub = parser_subparsers.add_parser('create')
+ create_sub_subparsers = create_sub.add_subparsers(dest='action')
+ create_vms = create_sub_subparsers.add_parser('vms')
+ create_vapp = create_sub_subparsers.add_parser('vapp')
+ create_vapp.add_argument('uuid')
+
+ # add network
+ create_network = create_sub_subparsers.add_parser('network')
+ create_network.add_argument('network_name', action='store', help='create a network for a vdc')
+
+ # add VDC
+ create_vdc = create_sub_subparsers.add_parser('vdc')
+ create_vdc.add_argument('vdc_name', action='store', help='create a new VDC for org')
+
+ delete_sub = parser_subparsers.add_parser('delete')
+ del_sub_subparsers = delete_sub.add_subparsers(dest='action')
+ del_vms = del_sub_subparsers.add_parser('vms')
+ del_vapp = del_sub_subparsers.add_parser('vapp')
+ del_vapp.add_argument('uuid', help='view vapp based on UUID')
+
+ # delete network
+ del_network = del_sub_subparsers.add_parser('network')
+ del_network.add_argument('network_name', action='store',
+ help='- delete network for vcloud director by provided name')
+ del_network.add_argument('-u', '--uuid', default=False, action='store_true',
+ help='delete network for vcloud director by provided uuid')
+
+ # delete vdc
+ del_vdc = del_sub_subparsers.add_parser('vdc')
+
+ view_sub = parser_subparsers.add_parser('view')
+ view_sub_subparsers = view_sub.add_subparsers(dest='action')
+
+ view_vms_parser = view_sub_subparsers.add_parser('vms')
+ view_vms_parser.add_argument('uuid', default=False, action='store_true',
+ help='- View VM for specific uuid in vcloud director')
+ view_vms_parser.add_argument('name', default=False, action='store_true',
+ help='- View VM for specific vapp name in vcloud director')
+
+ # view vapp
+ view_vapp_parser = view_sub_subparsers.add_parser('vapp')
+ view_vapp_parser.add_argument('vapp_name', action='store',
+ help='- view vapp for specific vapp name in vcloud director')
+ view_vapp_parser.add_argument('-u', '--uuid', default=False, action='store_true', help='view vapp based on uuid')
+ view_vapp_parser.add_argument('-o', '--osm', default=False, action='store_true', help='provide view in OSM format')
+
+ # view network
+ view_network = view_sub_subparsers.add_parser('network')
+ view_network.add_argument('network_name', action='store',
+ help='- view network for specific network name in vcloud director')
+ view_network.add_argument('-u', '--uuid', default=False, action='store_true', help='view network based on uuid')
+
+ # view VDC command and actions
+ view_vdc = view_sub_subparsers.add_parser('vdc')
+ view_vdc.add_argument('vdc_name', action='store',
+ help='- View VDC based and action based on provided vdc uuid')
+ view_vdc.add_argument('-u', '--uuid', default=False, action='store_true', help='view vdc based on uuid')
+
+ # view organization command and actions
+ view_org = view_sub_subparsers.add_parser('org')
+ view_org.add_argument('org_name', action='store',
+ help='- View VDC based and action based on provided vdc uuid')
+ view_org.add_argument('-u', '--uuid', default=False, action='store_true', help='view org based on uuid')
+
+ # upload image action
+ image_sub = parser_subparsers.add_parser('image')
+ image_subparsers = image_sub.add_subparsers(dest='action')
+ upload_parser = image_subparsers.add_parser('upload')
+ upload_parser.add_argument('image', default=False, action='store', help='- valid path to OVF image ')
+ upload_parser.add_argument('catalog', default=False, action='store_true', help='- catalog name')
+
+ # boot vm action
+ boot_parser = image_subparsers.add_parser('boot')
+ boot_parser.add_argument('image', default=False, action='store', help='- Image name')
+ boot_parser.add_argument('vmname', default=False, action='store', help='- VM name')
+ boot_parser.add_argument('-u', '--uuid', default=False, action='store_true', help='view org based on uuid')
+
+ namespace = parser.parse_args()
+ # put command_line args to mapping
+ command_line_args = {k: v for k, v in vars(namespace).items() if v}
+
+ d = defaults.copy()
+ d.update(os.environ)
+ d.update(command_line_args)
+
+ logger = logging.getLogger('mano.vim.vmware')
+ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+ ch = logging.StreamHandler()
+ ch.setLevel(str.upper(d['debug']))
+ ch.setFormatter(formatter)
+ logger.addHandler(ch)
+ logger.setLevel(getattr(logging, str.upper(d['debug'])))
+ logger.info(
+ "Connecting {} username: {} org: {} vdc: {} ".format(d['vcdhost'], d['vcduser'], d['vcdorg'], d['vcdvdc']))
+
+ logger.debug("command: \"{}\" actio: \"{}\"".format(d['command'], d['action']))
+
+ # main entry point.
+ vmwarecli(namespace=namespace)
--- /dev/null
+# -*- coding: utf-8 -*-
+##
+# This file is standalone vmware vcloud director util
+# 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: mbayramov@vmware.com
+##
+
+"""
+
+Standalone application that leverage openmano vmware connector work with vCloud director rest api.
+
+ - Provides capability to create and delete VDC for specific organization.
+ - Create, delete and manage network for specific VDC
+ - List deployed VM's , VAPPs, VDSs, Organization
+ - View detail information about VM / Vapp , Organization etc
+ - Operate with images upload / boot / power on etc
+
+ Usage example.
+
+ List organization created in vCloud director
+ vmwarecli.py -u admin -p qwerty123 -c 172.16.254.206 -U Administrator -P qwerty123 -o test -v TEF list org
+
+ List VDC for particular organization
+ vmwarecli.py -u admin -p qwerty123 -c 172.16.254.206 -U Administrator -P qwerty123 -o test -v TEF list vdc
+
+ Upload image
+ python vmwarerecli.py image upload /Users/spyroot/Developer/Openmano/Ro/vnfs/cirros/cirros.ovf
+
+ Boot Image
+ python vmwarerecli.py -u admin -p qwerty123 -c 172.16.254.206 -o test -v TEF image boot cirros cirros
+
+ View vApp
+ python vmwarerecli.py -u admin -p qwerty123 -c 172.16.254.206 -o test -v TEF view vapp 90bd2b4e-f782-46cf-b5e2-c3817dcf6633 -u
+
+ List VMS
+ python vmwarerecli.py -u admin -p qwerty123 -c 172.16.254.206 -o test -v TEF list vms
+
+ List VDC in OSM format
+ python vmwarerecli.py -u admin -p qwerty123 -c 172.16.254.206 -o test -v TEF list vdc -o
+
+Mustaafa Bayramov
+mbayramov@vmware.com
+"""
+import os
+import argparse
+import traceback
+import uuid
+
+from xml.etree import ElementTree as ET
+
+import sys
+from pyvcloud import Http
+
+import logging
+import vimconn
+import time
+import uuid
+import urllib3
+import requests
+
+from vimconn_vmware import vimconnector
+from requests.packages.urllib3.exceptions import InsecureRequestWarning
+from prettytable import PrettyTable
+
+requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
+
+__author__ = "Mustafa Bayramov"
+__date__ = "$16-Sep-2016 11:09:29$"
+
+
+# TODO move to main vim
+def delete_network_action(vca=None, network_uuid=None):
+ """
+ Method leverages vCloud director and query network based on network uuid
+
+ Args:
+ vca - is active VCA connection.
+ network_uuid - is a network uuid
+
+ Returns:
+ The return XML respond
+ """
+
+ if vca is None or network_uuid is None:
+ return None
+
+ url_list = [vca.host, '/api/admin/network/', network_uuid]
+ vm_list_rest_call = ''.join(url_list)
+
+ if not (not vca.vcloud_session or not vca.vcloud_session.organization):
+ response = Http.get(url=vm_list_rest_call,
+ headers=vca.vcloud_session.get_vcloud_headers(),
+ verify=vca.verify,
+ logger=vca.logger)
+ if response.status_code == requests.codes.ok:
+ print response.content
+ return response.content
+
+ return None
+
+
+def print_vapp(vapp_dict=None):
+ """ Method takes vapp_dict and print in tabular format
+
+ Args:
+ vapp_dict: container vapp object.
+
+ Returns:
+ The return nothing
+ """
+
+ # following key available to print
+ # {'status': 'POWERED_OFF', 'storageProfileName': '*', 'hardwareVersion': '7', 'vmToolsVersion': '0',
+ # 'memoryMB': '384',
+ # 'href': 'https://172.16.254.206/api/vAppTemplate/vm-129e22e8-08dc-4cb6-8358-25f635e65d3b',
+ # 'isBusy': 'false', 'isDeployed': 'false', 'isInMaintenanceMode': 'false', 'isVAppTemplate': 'true',
+ # 'networkName': 'nat', 'isDeleted': 'false', 'catalogName': 'Cirros',
+ # 'containerName': 'Cirros Template', # 'container':
+ # 'https://172.16.254.206/api/vAppTemplate/vappTemplate-b966453d-c361-4505-9e38-ccef45815e5d',
+ # 'name': 'Cirros', 'pvdcHighestSupportedHardwareVersion': '11', 'isPublished': 'false',
+ # 'numberOfCpus': '1', 'vdc': 'https://172.16.254.206/api/vdc/a5056f85-418c-4bfd-8041-adb0f48be9d9',
+ # 'guestOs': 'Other (32-bit)', 'isVdcEnabled': 'true'}
+
+ if vapp_dict is None:
+ return
+
+ vm_table = PrettyTable(['vm uuid',
+ 'vapp name',
+ 'vapp uuid',
+ 'network name',
+ 'storage name',
+ 'vcpu', 'memory', 'hw ver','deployed','status'])
+ for k in vapp_dict:
+ entry = []
+ entry.append(k)
+ entry.append(vapp_dict[k]['containerName'])
+ # vm-b1f5cd4c-2239-4c89-8fdc-a41ff18e0d61
+ entry.append(vapp_dict[k]['container'].split('/')[-1:][0][5:])
+ entry.append(vapp_dict[k]['networkName'])
+ entry.append(vapp_dict[k]['storageProfileName'])
+ entry.append(vapp_dict[k]['numberOfCpus'])
+ entry.append(vapp_dict[k]['memoryMB'])
+ entry.append(vapp_dict[k]['pvdcHighestSupportedHardwareVersion'])
+ entry.append(vapp_dict[k]['isDeployed'])
+ entry.append(vapp_dict[k]['status'])
+
+ vm_table.add_row(entry)
+
+ print vm_table
+
+
+def print_org(org_dict=None):
+ """ Method takes vapp_dict and print in tabular format
+
+ Args:
+ org_dict: dictionary of organization where key is org uuid.
+
+ Returns:
+ The return nothing
+ """
+
+ if org_dict is None:
+ return
+
+ org_table = PrettyTable(['org uuid', 'name'])
+ for k in org_dict:
+ entry = [k, org_dict[k]]
+ org_table.add_row(entry)
+
+ print org_table
+
+
+def print_vm_list(vm_dict=None):
+ """ Method takes vapp_dict and print in tabular format
+
+ Args:
+ vm_dict: dictionary of organization where key is org uuid.
+
+ Returns:
+ The return nothing
+ """
+ if vm_dict is None:
+ return
+
+ vm_table = PrettyTable(
+ ['vm uuid', 'vm name', 'vapp uuid', 'vdc uuid', 'network name', 'is deployed', 'vcpu', 'memory', 'status'])
+
+ try:
+ for k in vm_dict:
+ entry = []
+ entry.append(k)
+ entry.append(vm_dict[k]['name'])
+ entry.append(vm_dict[k]['container'].split('/')[-1:][0][5:])
+ entry.append(vm_dict[k]['vdc'].split('/')[-1:][0])
+ entry.append(vm_dict[k]['networkName'])
+ entry.append(vm_dict[k]['isDeployed'])
+ entry.append(vm_dict[k]['numberOfCpus'])
+ entry.append(vm_dict[k]['memoryMB'])
+ entry.append(vm_dict[k]['status'])
+ vm_table.add_row(entry)
+ print vm_table
+ except KeyError:
+ logger.error("wrong key {}".format(KeyError.message))
+ pass
+
+
+def print_vdc_list(org_dict=None):
+ """ Method takes vapp_dict and print in tabular format
+
+ Args:
+ org_dict: dictionary of organization where key is org uuid.
+
+ Returns:
+ The return nothing
+ """
+ if org_dict is None:
+ return
+ try:
+ vdcs_dict = {}
+ if org_dict.has_key('vdcs'):
+ vdcs_dict = org_dict['vdcs']
+ vdc_table = PrettyTable(['vdc uuid', 'vdc name'])
+ for k in vdcs_dict:
+ entry = [k, vdcs_dict[k]]
+ vdc_table.add_row(entry)
+
+ print vdc_table
+ except KeyError:
+ logger.error("wrong key {}".format(KeyError.message))
+ logger.logger.debug(traceback.format_exc())
+
+
+def print_network_list(org_dict=None):
+ """ Method print network list.
+
+ Args:
+ org_dict: dictionary of organization that contain key networks with a list of all
+ network for for specific VDC
+
+ Returns:
+ The return nothing
+ """
+ if org_dict is None:
+ return
+ try:
+ network_dict = {}
+ if org_dict.has_key('networks'):
+ network_dict = org_dict['networks']
+ network_table = PrettyTable(['network uuid', 'network name'])
+ for k in network_dict:
+ entry = [k, network_dict[k]]
+ network_table.add_row(entry)
+
+ print network_table
+
+ except KeyError:
+ logger.error("wrong key {}".format(KeyError.message))
+ logger.logger.debug(traceback.format_exc())
+
+
+def print_org_details(org_dict=None):
+ """ Method takes vapp_dict and print in tabular format
+
+ Args:
+ org_dict: dictionary of organization where key is org uuid.
+
+ Returns:
+ The return nothing
+ """
+ if org_dict is None:
+ return
+ try:
+ catalogs_dict = {}
+
+ print_vdc_list(org_dict=org_dict)
+ print_network_list(org_dict=org_dict)
+
+ if org_dict.has_key('catalogs'):
+ catalogs_dict = org_dict['catalogs']
+
+ catalog_table = PrettyTable(['catalog uuid', 'catalog name'])
+ for k in catalogs_dict:
+ entry = [k, catalogs_dict[k]]
+ catalog_table.add_row(entry)
+
+ print catalog_table
+
+ except KeyError:
+ logger.error("wrong key {}".format(KeyError.message))
+ logger.logger.debug(traceback.format_exc())
+
+
+def delete_actions(vim=None, action=None, namespace=None):
+ if action == 'network' or namespace.action == 'network':
+ logger.debug("Requesting delete for network {}".format(namespace.network_name))
+ network_uuid = namespace.network_name
+ # if request name based we need find UUID
+ # TODO optimize it or move to external function
+ if not namespace.uuid:
+ org_dict = vim.get_org_list()
+ for org in org_dict:
+ org_net = vim.get_org(org)['networks']
+ for network in org_net:
+ if org_net[network] == namespace.network_name:
+ network_uuid = network
+
+ vim.delete_network_action(network_uuid=network_uuid)
+
+
+def list_actions(vim=None, action=None, namespace=None):
+ """ Method provide list object from VDC action
+
+ Args:
+ vim - is vcloud director vim connector.
+ action - is action for list ( vdc / org etc)
+ namespace - must contain VDC / Org information.
+
+ Returns:
+ The return nothing
+ """
+
+ org_id = None
+ myorgs = vim.get_org_list()
+ for org in myorgs:
+ if myorgs[org] == namespace.vcdorg:
+ org_id = org
+ break
+ else:
+ print(" Invalid organization.")
+ return
+
+ if action == 'vms' or namespace.action == 'vms':
+ vm_dict = vim.get_vm_list(vdc_name=namespace.vcdvdc)
+ print_vm_list(vm_dict=vm_dict)
+ elif action == 'vapps' or namespace.action == 'vapps':
+ vapp_dict = vim.get_vapp_list(vdc_name=namespace.vcdvdc)
+ print_vapp(vapp_dict=vapp_dict)
+ elif action == 'networks' or namespace.action == 'networks':
+ if namespace.osm:
+ osm_print(vim.get_network_list(filter_dict={}))
+ else:
+ print_network_list(vim.get_org(org_uuid=org_id))
+ elif action == 'vdc' or namespace.action == 'vdc':
+ if namespace.osm:
+ osm_print(vim.get_tenant_list(filter_dict=None))
+ else:
+ print_vdc_list(vim.get_org(org_uuid=org_id))
+ elif action == 'org' or namespace.action == 'org':
+ print_org(org_dict=vim.get_org_list())
+ else:
+ return None
+
+
+def print_network_details(network_dict=None):
+ try:
+ network_table = PrettyTable(network_dict.keys())
+ entry = [network_dict.values()]
+ network_table.add_row(entry[0])
+ print network_table
+ except KeyError:
+ logger.error("wrong key {}".format(KeyError.message))
+ logger.logger.debug(traceback.format_exc())
+
+
+def osm_print(generic_dict=None):
+
+ try:
+ for element in generic_dict:
+ table = PrettyTable(element.keys())
+ entry = [element.values()]
+ table.add_row(entry[0])
+ print table
+ except KeyError:
+ logger.error("wrong key {}".format(KeyError.message))
+ logger.logger.debug(traceback.format_exc())
+
+
+def view_actions(vim=None, action=None, namespace=None):
+ org_id = None
+ orgs = vim.get_org_list()
+ for org in orgs:
+ if orgs[org] == namespace.vcdorg:
+ org_id = org
+ break
+ else:
+ print(" Invalid organization.")
+ return
+
+ myorg = vim.get_org(org_uuid=org_id)
+
+ # view org
+ if action == 'org' or namespace.action == 'org':
+ org_id = None
+ orgs = vim.get_org_list()
+ if namespace.uuid:
+ if namespace.org_name in orgs:
+ org_id = namespace.org_name
+ else:
+ # we need find UUID based on name provided
+ for org in orgs:
+ if orgs[org] == namespace.org_name:
+ org_id = org
+ break
+
+ logger.debug("Requesting view for orgs {}".format(org_id))
+ print_org_details(vim.get_org(org_uuid=org_id))
+
+ # view vapp action
+ if action == 'vapp' or namespace.action == 'vapp':
+ if namespace.vapp_name is not None and namespace.uuid:
+ logger.debug("Requesting vapp {} for vdc {}".format(namespace.vapp_name, namespace.vcdvdc))
+ vapp_dict = {}
+ vapp_uuid = namespace.vapp_name
+ # if request based on just name we need get UUID
+ if not namespace.uuid:
+ vapp_uuid = vim.get_vappid(vdc=namespace.vcdvdc, vapp_name=namespace.vapp_name)
+ if vapp_uuid is None:
+ print("Can't find vapp by given name {}".format(namespace.vapp_name))
+ return
+
+ print " namespace {}".format(namespace)
+ if vapp_dict is not None and namespace.osm:
+ vm_info_dict = vim.get_vminstance(vim_vm_uuid=vapp_uuid)
+ print vm_info_dict
+ if vapp_dict is not None and namespace.osm != True:
+ vapp_dict = vim.get_vapp(vdc_name=namespace.vcdvdc, vapp_name=vapp_uuid, isuuid=True)
+ print_vapp(vapp_dict=vapp_dict)
+
+ # view network
+ if action == 'network' or namespace.action == 'network':
+ logger.debug("Requesting view for network {}".format(namespace.network_name))
+ network_uuid = namespace.network_name
+ # if request name based we need find UUID
+ # TODO optimize it or move to external function
+ if not namespace.uuid:
+ if not myorg.has_key('networks'):
+ print("Network {} is undefined in vcloud director for org {} vdc {}".format(namespace.network_name,
+ vim.name,
+ vim.tenant_name))
+ return
+
+ my_org_net = myorg['networks']
+ for network in my_org_net:
+ if my_org_net[network] == namespace.network_name:
+ network_uuid = network
+ break
+
+ print print_network_details(network_dict=vim.get_vcd_network(network_uuid=network_uuid))
+
+
+def create_actions(vim=None, action=None, namespace=None):
+ """Method gets provider vdc view from vcloud director
+
+ Args:
+ vim - is Cloud director vim connector
+ action - action for create ( network / vdc etc)
+
+ Returns:
+ The return xml content of respond or None
+ """
+ if action == 'network' or namespace.action == 'network':
+ logger.debug("Creating a network in vcloud director".format(namespace.network_name))
+ network_uuid = vim.create_network(namespace.network_name)
+ if network_uuid is not None:
+ print ("Crated new network {} and uuid: {}".format(namespace.network_name, network_uuid))
+ else:
+ print ("Failed create a new network {}".format(namespace.network_name))
+ elif action == 'vdc' or namespace.action == 'vdc':
+ logger.debug("Creating a new vdc in vcloud director.".format(namespace.vdc_name))
+ vdc_uuid = vim.create_vdc(namespace.vdc_name)
+ if vdc_uuid is not None:
+ print ("Crated new vdc {} and uuid: {}".format(namespace.vdc_name, vdc_uuid))
+ else:
+ print ("Failed create a new vdc {}".format(namespace.vdc_name))
+ else:
+ return None
+
+
+def validate_uuid4(uuid_string):
+ """Function validate that string contain valid uuid4
+
+ Args:
+ uuid_string - valid UUID string
+
+ Returns:
+ The return true if string contain valid UUID format
+ """
+ try:
+ val = uuid.UUID(uuid_string, version=4)
+ except ValueError:
+ return False
+ return True
+
+
+def upload_image(vim=None, image_file=None):
+ """Function upload image to vcloud director
+
+ Args:
+ image_file - valid UUID string
+
+ Returns:
+ The return true if image uploaded correctly
+ """
+ try:
+ catalog_uuid = vim.get_image_id_from_path(path=image_file, progress=True)
+ if catalog_uuid is not None and validate_uuid4(catalog_uuid):
+ print("Image uploaded and uuid {}".format(catalog_uuid))
+ return True
+ except vimconn.vimconnException as upload_exception:
+ print("Failed uploaded {} image".format(image_file))
+ print("Error Reason: {}".format(upload_exception.message))
+ return False
+
+
+def boot_image(vim=None, image_name=None, vm_name=None):
+ """ Function boot image that resided in vcloud director.
+ The image name can be UUID of name.
+
+ Args:
+ vim - vim connector
+ image_name - image identified by UUID or text string.
+ vm_name - vmname
+
+
+ Returns:
+ The return true if image uploaded correctly
+ """
+
+ vim_catalog = None
+ try:
+ catalogs = vim.vca.get_catalogs()
+ if not validate_uuid4(image_name):
+ vim_catalog = vim.get_catalogid(catalog_name=image_name, catalogs=catalogs)
+ if vim_catalog is None:
+ return None
+ else:
+ vim_catalog = vim.get_catalogid(catalog_name=image_name, catalogs=catalogs)
+ if vim_catalog is None:
+ return None
+
+ print (" Booting {} image id {} ".format(vm_name, vim_catalog))
+ vm_uuid = vim.new_vminstance(name=vm_name, image_id=vim_catalog)
+ if vm_uuid is not None and validate_uuid4(vm_uuid):
+ print("Image booted and vm uuid {}".format(vm_uuid))
+ vapp_dict = vim.get_vapp(vdc_name=namespace.vcdvdc, vapp_name=vm_uuid, isuuid=True)
+ if vapp_dict is not None:
+ print_vapp(vapp_dict=vapp_dict)
+ return True
+ except vimconn.vimconnNotFoundException as notFound:
+ print("Failed boot {} image".format(image_name))
+ print(notFound.message)
+ except vimconn.vimconnException as vimconError:
+ print("Failed boot {} image".format(image_name))
+ print(vimconError.message)
+ except:
+ print("Failed boot {} image".format(image_name))
+
+
+ return False
+
+
+def image_action(vim=None, action=None, namespace=None):
+ """ Function present set of action to manipulate with image.
+ - upload image
+ - boot image.
+ - delete image ( not yet done )
+
+ Args:
+ vim - vcloud director connector
+ action - string (upload/boot etc)
+ namespace - contain other attributes image name etc
+
+ Returns:
+ The return nothing
+ """
+
+ if action == 'upload' or namespace.action == 'upload':
+ upload_image(vim=vim, image_file=namespace.image)
+ elif action == 'boot' or namespace.action == 'boot':
+ boot_image(vim=vim, image_name=namespace.image, vm_name=namespace.vmname)
+ else:
+ return None
+
+
+def vmwarecli(command=None, action=None, namespace=None):
+ logger.debug("Namespace {}".format(namespace))
+ urllib3.disable_warnings()
+
+ vcduser = None
+ vcdpasword = None
+ vcdhost = None
+ vcdorg = None
+
+ if hasattr(__builtins__, 'raw_input'):
+ input = raw_input
+
+ if namespace.vcdvdc is None:
+ while True:
+ vcduser = input("Enter vcd username: ")
+ if vcduser is not None and len(vcduser) > 0:
+ break
+ else:
+ vcduser = namespace.vcduser
+
+ if namespace.vcdpassword is None:
+ while True:
+ vcdpasword = input("Please enter vcd password: ")
+ if vcdpasword is not None and len(vcdpasword) > 0:
+ break
+ else:
+ vcdpasword = namespace.vcdpassword
+
+ if namespace.vcdhost is None:
+ while True:
+ vcdhost = input("Please enter vcd host name or ip: ")
+ if vcdhost is not None and len(vcdhost) > 0:
+ break
+ else:
+ vcdhost = namespace.vcdhost
+
+ if namespace.vcdorg is None:
+ while True:
+ vcdorg = input("Please enter vcd organization name: ")
+ if vcdorg is not None and len(vcdorg) > 0:
+ break
+ else:
+ vcdorg = namespace.vcdorg
+
+ try:
+ vim = vimconnector(uuid=None,
+ name=vcdorg,
+ tenant_id=None,
+ tenant_name=namespace.vcdvdc,
+ url=vcdhost,
+ url_admin=vcdhost,
+ user=vcduser,
+ passwd=vcdpasword,
+ log_level="DEBUG",
+ config={'admin_username': namespace.vcdamdin, 'admin_password': namespace.vcdadminpassword})
+ vim.vca = vim.connect()
+
+ except vimconn.vimconnConnectionException:
+ print("Failed connect to vcloud director. Please check credential and hostname.")
+ return
+
+ # list
+ if command == 'list' or namespace.command == 'list':
+ logger.debug("Client requested list action")
+ # route request to list actions
+ list_actions(vim=vim, action=action, namespace=namespace)
+
+ # view action
+ if command == 'view' or namespace.command == 'view':
+ logger.debug("Client requested view action")
+ view_actions(vim=vim, action=action, namespace=namespace)
+
+ # delete action
+ if command == 'delete' or namespace.command == 'delete':
+ logger.debug("Client requested delete action")
+ delete_actions(vim=vim, action=action, namespace=namespace)
+
+ # create action
+ if command == 'create' or namespace.command == 'create':
+ logger.debug("Client requested create action")
+ create_actions(vim=vim, action=action, namespace=namespace)
+
+ # image action
+ if command == 'image' or namespace.command == 'image':
+ logger.debug("Client requested create action")
+ image_action(vim=vim, action=action, namespace=namespace)
+
+
+if __name__ == '__main__':
+ defaults = {'vcdvdc': 'default',
+ 'vcduser': 'admin',
+ 'vcdpassword': 'admin',
+ 'vcdhost': 'https://localhost',
+ 'vcdorg': 'default',
+ 'debug': 'INFO'}
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-u', '--vcduser', help='vcloud director username', type=str)
+ parser.add_argument('-p', '--vcdpassword', help='vcloud director password', type=str)
+ parser.add_argument('-U', '--vcdamdin', help='vcloud director password', type=str)
+ parser.add_argument('-P', '--vcdadminpassword', help='vcloud director password', type=str)
+ parser.add_argument('-c', '--vcdhost', help='vcloud director host', type=str)
+ parser.add_argument('-o', '--vcdorg', help='vcloud director org', type=str)
+ parser.add_argument('-v', '--vcdvdc', help='vcloud director vdc', type=str)
+ parser.add_argument('-d', '--debug', help='debug level', type=int)
+
+ parser_subparsers = parser.add_subparsers(help='commands', dest='command')
+ sub = parser_subparsers.add_parser('list', help='List objects (VMs, vApps, networks)')
+ sub_subparsers = sub.add_subparsers(dest='action')
+
+ list_vms = sub_subparsers.add_parser('vms', help='list - all vm deployed in vCloud director')
+ list_vapps = sub_subparsers.add_parser('vapps', help='list - all vapps deployed in vCloud director')
+ list_network = sub_subparsers.add_parser('networks', help='list - all networks deployed')
+ list_network.add_argument('-o', '--osm', default=False, action='store_true', help='provide view in OSM format')
+
+ #list vdc
+ list_vdc = sub_subparsers.add_parser('vdc', help='list - list all vdc for organization accessible to you')
+ list_vdc.add_argument('-o', '--osm', default=False, action='store_true', help='provide view in OSM format')
+
+ list_org = sub_subparsers.add_parser('org', help='list - list of organizations accessible to you.')
+
+ create_sub = parser_subparsers.add_parser('create')
+ create_sub_subparsers = create_sub.add_subparsers(dest='action')
+ create_vms = create_sub_subparsers.add_parser('vms')
+ create_vapp = create_sub_subparsers.add_parser('vapp')
+ create_vapp.add_argument('uuid')
+
+ # add network
+ create_network = create_sub_subparsers.add_parser('network')
+ create_network.add_argument('network_name', action='store', help='create a network for a vdc')
+
+ # add VDC
+ create_vdc = create_sub_subparsers.add_parser('vdc')
+ create_vdc.add_argument('vdc_name', action='store', help='create a new VDC for org')
+
+ delete_sub = parser_subparsers.add_parser('delete')
+ del_sub_subparsers = delete_sub.add_subparsers(dest='action')
+ del_vms = del_sub_subparsers.add_parser('vms')
+ del_vapp = del_sub_subparsers.add_parser('vapp')
+ del_vapp.add_argument('uuid', help='view vapp based on UUID')
+
+ # delete network
+ del_network = del_sub_subparsers.add_parser('network')
+ del_network.add_argument('network_name', action='store',
+ help='- delete network for vcloud director by provided name')
+ del_network.add_argument('-u', '--uuid', default=False, action='store_true',
+ help='delete network for vcloud director by provided uuid')
+
+ # delete vdc
+ del_vdc = del_sub_subparsers.add_parser('vdc')
+
+ view_sub = parser_subparsers.add_parser('view')
+ view_sub_subparsers = view_sub.add_subparsers(dest='action')
+
+ view_vms_parser = view_sub_subparsers.add_parser('vms')
+ view_vms_parser.add_argument('uuid', default=False, action='store_true',
+ help='- View VM for specific uuid in vcloud director')
+ view_vms_parser.add_argument('name', default=False, action='store_true',
+ help='- View VM for specific vapp name in vcloud director')
+
+ # view vapp
+ view_vapp_parser = view_sub_subparsers.add_parser('vapp')
+ view_vapp_parser.add_argument('vapp_name', action='store',
+ help='- view vapp for specific vapp name in vcloud director')
+ view_vapp_parser.add_argument('-u', '--uuid', default=False, action='store_true', help='view vapp based on uuid')
+ view_vapp_parser.add_argument('-o', '--osm', default=False, action='store_true', help='provide view in OSM format')
+
+ # view network
+ view_network = view_sub_subparsers.add_parser('network')
+ view_network.add_argument('network_name', action='store',
+ help='- view network for specific network name in vcloud director')
+ view_network.add_argument('-u', '--uuid', default=False, action='store_true', help='view network based on uuid')
+
+ # view VDC command and actions
+ view_vdc = view_sub_subparsers.add_parser('vdc')
+ view_vdc.add_argument('vdc_name', action='store',
+ help='- View VDC based and action based on provided vdc uuid')
+ view_vdc.add_argument('-u', '--uuid', default=False, action='store_true', help='view vdc based on uuid')
+
+ # view organization command and actions
+ view_org = view_sub_subparsers.add_parser('org')
+ view_org.add_argument('org_name', action='store',
+ help='- View VDC based and action based on provided vdc uuid')
+ view_org.add_argument('-u', '--uuid', default=False, action='store_true', help='view org based on uuid')
+
+ # upload image action
+ image_sub = parser_subparsers.add_parser('image')
+ image_subparsers = image_sub.add_subparsers(dest='action')
+ upload_parser = image_subparsers.add_parser('upload')
+ upload_parser.add_argument('image', default=False, action='store', help='- valid path to OVF image ')
+ upload_parser.add_argument('catalog', default=False, action='store_true', help='- catalog name')
+
+ # boot vm action
+ boot_parser = image_subparsers.add_parser('boot')
+ boot_parser.add_argument('image', default=False, action='store', help='- Image name')
+ boot_parser.add_argument('vmname', default=False, action='store', help='- VM name')
+ boot_parser.add_argument('-u', '--uuid', default=False, action='store_true', help='view org based on uuid')
+
+ namespace = parser.parse_args()
+ # put command_line args to mapping
+ command_line_args = {k: v for k, v in vars(namespace).items() if v}
+
+ d = defaults.copy()
+ d.update(os.environ)
+ d.update(command_line_args)
+
+ logger = logging.getLogger('mano.vim.vmware')
+ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+ ch = logging.StreamHandler()
+ ch.setLevel(str.upper(d['debug']))
+ ch.setFormatter(formatter)
+ logger.addHandler(ch)
+ logger.setLevel(getattr(logging, str.upper(d['debug'])))
+ logger.info(
+ "Connecting {} username: {} org: {} vdc: {} ".format(d['vcdhost'], d['vcduser'], d['vcdorg'], d['vcdvdc']))
+
+ logger.debug("command: \"{}\" actio: \"{}\"".format(d['command'], d['action']))
+
+ # main entry point.
+ vmwarecli(namespace=namespace)
--index-url https://pypi.python.org/simple/
-PyYAML==3.10
-bottle==0.12.7
-PyMySQL==0.7.2
-jsonschema==2.5.1
-paramiko==1.16.0
-argcomplete==0.8.1
-requests==2.13.0
-logutils==0.3.3
-python-cinderclient==1.6.0
-python-glanceclient==2.0.0
-python-keystoneclient==2.3.1
-python-neutronclient==4.1.1
-python-novaclient==3.3.1
-pyvcloud==16
-pyvmomi==6.5
-progressbar==2.3
-prettytable==0.7.2
-boto==2.38.0
+PyYAML
+bottle
+MySQL-python
+jsonschema
+paramiko
+argcomplete
+requests
+logutils
+python-novaclient
+python-keystoneclient
+python-glanceclient
+python-neutronclient
+python-cinderclient
+pyvcloud
+pyvmomi
+progressbar
+prettytable
+boto
running=y
done
#if installed as a service and it is not provided a screen name call service
- [[ -f /etc/systemd/system/openmano.service ]] && [[ -z $option_screen_name ]] && running=y #&& service openmano status
+ [[ -f /etc/systemd/system/osm-ro.service ]] && [[ -z $option_screen_name ]] && running=y #&& service osm-ro status
if [ -z "$running" ]
then
echo -n " $om_name not running" && [[ -n "$option_screen_name" ]] && echo " on screen '$option_screen_name'" || echo
fi
#if installed as a service and it is not provided a screen name call service
- [[ -f /etc/systemd/system/openmano.service ]] && [[ -z $option_screen_name ]] && service openmano $om_action && ( [[ $om_action == status ]] || sleep 5 ) && exit $?
+ [[ -f /etc/systemd/system/osm-ro.service ]] && [[ -z $option_screen_name ]] && service osm-ro $om_action && ( [[ $om_action == status ]] || sleep 5 ) && exit $?
#stop
#!/usr/bin/env python
from setuptools import setup
+from setuptools.command.install import install
+from os import system
#import glob
-requirements = [
+_name = 'osm_ro'
+_version = '1.0.0'
+_description = 'OSM Resource Orchestrator'
+_author = 'ETSI OSM'
+_author_email = 'alfonso.tiernosepulveda@telefonica.com'
+_maintainer = 'garciadeblas'
+_maintainer_email = 'gerardo.garciadeblas@telefonica.com'
+_license = 'Apache 2.0'
+_url = 'https://osm.etsi.org/gitweb/?p=osm/RO.git;a=summary'
+_requirements = [
"PyYAML",
"bottle",
- "mysqldb",
+ "MySQL-python",
"jsonschema",
"paramiko",
"argcomplete",
"requests",
"logutils",
- "pip",
- "novaclient",
- "keystoneclient",
- "glanceclient",
- "neutronclient",
- "cinderclient",
- "boto",
+ "python-novaclient",
+ "python-keystoneclient",
+ "python-glanceclient",
+ "python-neutronclient",
+ "python-cinderclient",
"pyvcloud",
"progressbar",
"prettytable",
"pyvmomi",
+ "boto",
]
-setup(name='osm-ro',
- version='1.0',
- description='OSM Resource Orchestrator',
- long_description=open('README.rst').read(),
- author='ETSI OSM',
- author_email='alfonso.tiernosepulveda@telefonica.com',
- maintainer='garciadeblas',
- maintainer_email='gerardo.garciadeblas@telefonica.com',
- url='https://osm.etsi.org/gitweb/?p=osm/RO.git;a=summary',
- license='Apache 2.0',
- #packages=['ro-server', 'ro-client'],
- #py_modules=glob.glob('*.py')
- py_modules=['console_proxy_thread',
- 'db_base',
- 'httpserver',
- 'nfvo_db',
- 'nfvo',
- 'openmanoclient',
- 'openmanoconfig',
- 'openmanod',
- 'openmano_schemas',
- 'utils',
- 'vimconn_openstack',
- 'vimconn_openvim',
- 'vimconn',
- 'vimconn_vmware',
- 'vmwarecli',
- 'vmwarerecli',
- ],
- install_requires=requirements,
- #package_data={'': ['vnfs', 'scenarios', 'instance-scenarios', 'database_utils/mano_db_structure.sql']}
- data_files=[('/etc/osm/', ['openmanod.cfg']),
- ('/etc/systemd/system/', ['openmano.service']),
- ('vnfs', ['vnfs']),
- ('scenarios', ['scenarios']),
- ('instance-scenarios', ['instance-scenarios']),
- ('database_utils', ['database-utils']),
- ],
- scripts=['openmanod.py', 'openmano', 'scripts/service-openmano', 'scripts/openmano-report',]
- )
+class ROInstaller(install):
+ def run(self):
+ cmd = 'echo "Running install script"'
+ system(cmd)
+ install.run(self)
+
+setup(name=_name,
+ version = _version,
+ description = _description,
+ long_description = open('README.rst').read(),
+ author = _author,
+ author_email = _author_email,
+ maintainer = _maintainer,
+ maintainer_email = _maintainer_email,
+ url = _url,
+ license = _license,
+ packages = [_name],
+ #packages = ['osm_ro', 'osm_roclient'],
+ package_dir = {_name: _name},
+ package_data = {_name: ['vnfs/*.yaml', 'vnfs/examples/*.yaml',
+ 'scenarios/*.yaml', 'scenarios/examples/*.yaml',
+ 'instance-scenarios/examples/*.yaml', 'database_utils/*',
+ 'scripts/install-openmano*.sh']},
+ data_files = [('/etc/osm/', ['openmanod.cfg']),
+ ('/etc/systemd/system/', ['osm-ro.service']),
+ ],
+ scripts=['openmanod.py', 'openmano', 'osm_ro/scripts/service-openmano.sh', 'osm_ro/scripts/openmano-report.sh',],
+ install_requires=_requirements,
+ include_package_data=True,
+ cmdclass = {'install': ROInstaller},
+ )
+++ /dev/null
-# -*- 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
-##
-
-'''
-utils is a module that implements functions that are used by all openmano modules,
-dealing with aspects such as reading/writing files, formatting inputs/outputs for quick translation
-from dictionaries to appropriate database dictionaries, etc.
-'''
-__author__="Alfonso Tierno, Gerardo Garcia"
-__date__ ="$08-sep-2014 12:21:22$"
-
-import datetime
-from jsonschema import validate as js_v, exceptions as js_e
-#from bs4 import BeautifulSoup
-
-def read_file(file_to_read):
- """Reads a file specified by 'file_to_read' and returns (True,<its content as a string>) in case of success or (False, <error message>) in case of failure"""
- try:
- f = open(file_to_read, 'r')
- read_data = f.read()
- f.close()
- except Exception as e:
- return (False, str(e))
-
- return (True, read_data)
-
-def write_file(file_to_write, text):
- """Write a file specified by 'file_to_write' and returns (True,NOne) in case of success or (False, <error message>) in case of failure"""
- try:
- f = open(file_to_write, 'w')
- f.write(text)
- f.close()
- except Exception as e:
- return (False, str(e))
-
- return (True, None)
-
-def format_in(http_response, schema):
- try:
- client_data = http_response.json()
- js_v(client_data, schema)
- #print "Input data: ", str(client_data)
- return True, client_data
- except js_e.ValidationError as exc:
- print "validate_in error, jsonschema exception ", exc.message, "at", exc.path
- return False, ("validate_in error, jsonschema exception ", exc.message, "at", exc.path)
-
-def remove_extra_items(data, schema):
- deleted=[]
- if type(data) is tuple or type(data) is list:
- for d in data:
- a= remove_extra_items(d, schema['items'])
- if a is not None: deleted.append(a)
- elif type(data) is dict:
- #TODO deal with patternProperties
- if 'properties' not in schema:
- return None
- for k in data.keys():
- if k not in schema['properties'].keys():
- del data[k]
- deleted.append(k)
- else:
- a = remove_extra_items(data[k], schema['properties'][k])
- if a is not None: deleted.append({k:a})
- if len(deleted) == 0: return None
- elif len(deleted) == 1: return deleted[0]
- else: return deleted
-
-#def format_html2text(http_content):
-# soup=BeautifulSoup(http_content)
-# text = soup.p.get_text() + " " + soup.pre.get_text()
-# return text
-
-
-def convert_bandwidth(data, reverse=False):
- '''Check the field bandwidth recursivelly and when found, it removes units and convert to number
- It assumes that bandwidth is well formed
- Attributes:
- 'data': dictionary bottle.FormsDict variable to be checked. None or empty is consideted valid
- 'reverse': by default convert form str to int (Mbps), if True it convert from number to units
- Return:
- None
- '''
- 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:
- convert_bandwidth(data[k], reverse)
- if "bandwidth" in data:
- try:
- value=str(data["bandwidth"])
- if not reverse:
- pos = value.find("bps")
- if pos>0:
- if value[pos-1]=="G": data["bandwidth"] = int(data["bandwidth"][:pos-1]) * 1000
- elif value[pos-1]=="k": data["bandwidth"]= int(data["bandwidth"][:pos-1]) / 1000
- else: data["bandwidth"]= int(data["bandwidth"][:pos-1])
- else:
- value = int(data["bandwidth"])
- if value % 1000 == 0: data["bandwidth"]=str(value/1000) + " Gbps"
- else: data["bandwidth"]=str(value) + " Mbps"
- except:
- print "convert_bandwidth exception for type", type(data["bandwidth"]), " data", data["bandwidth"]
- return
- if type(data) is tuple or type(data) is list:
- for k in data:
- if type(k) is dict or type(k) is tuple or type(k) is list:
- convert_bandwidth(k, reverse)
-
-
-
-def convert_datetime2str(var):
- '''Converts a datetime variable to a string with the format '%Y-%m-%dT%H:%i:%s'
- It enters recursively in the dict var finding this kind of variables
- '''
- if type(var) is dict:
- for k,v in var.items():
- if type(v) is datetime.datetime:
- var[k]= v.strftime('%Y-%m-%dT%H:%M:%S')
- elif type(v) is dict or type(v) is list or type(v) is tuple:
- convert_datetime2str(v)
- if len(var) == 0: return True
- elif type(var) is list or type(var) is tuple:
- for v in var:
- convert_datetime2str(v)
-
-def convert_str2boolean(data, items):
- '''Check recursively the content of data, and if there is an key contained in items, convert value from string to boolean
- Done recursively
- Attributes:
- 'data': dictionary variable to be checked. None or empty is considered valid
- 'items': tuple of keys to convert
- Return:
- None
- '''
- 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:
- convert_str2boolean(data[k], items)
- if k in items:
- if type(data[k]) is str:
- if data[k]=="false" or data[k]=="False": data[k]=False
- elif data[k]=="true" or data[k]=="True": data[k]=True
- if type(data) is tuple or type(data) is list:
- for k in data:
- if type(k) is dict or type(k) is tuple or type(k) is list:
- convert_str2boolean(k, items)
-
-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}$"}
- id_schema2 = {"type" : "string", "pattern": "^[a-fA-F0-9]{32}$"}
- try:
- js_v(uuid, id_schema)
- return True
- except js_e.ValidationError:
- try:
- js_v(uuid, id_schema2)
- return True
- except js_e.ValidationError:
- return False
- return False
-
+++ /dev/null
-# -*- coding: utf-8 -*-
-
-##
-# 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 thread that interact with the host and the libvirt to manage VM
-One thread will be launched per host
-'''
-__author__ = "Alfonso Tierno"
-__date__ = "$10-feb-2017 12:07:15$"
-
-import threading
-import time
-import Queue
-import logging
-import vimconn
-from db_base import db_base_Exception
-
-
-# from logging import Logger
-# import auxiliary_functions as af
-
-
-def is_task_id(id):
- return True if id[:5] == "TASK." else False
-
-
-class vim_thread(threading.Thread):
-
- def __init__(self, vimconn, task_lock, name=None, datacenter_name=None, datacenter_tenant_id=None, db=None, db_lock=None):
- """Init a thread.
- Arguments:
- 'id' number of thead
- 'name' name of thread
- 'host','user': host ip or name to manage and user
- 'db', 'db_lock': database class and lock to use it in exclusion
- """
- self.tasksResult = {}
- """ It will contain a dictionary with
- task_id:
- status: enqueued,done,error,deleted,processing
- result: VIM result,
- """
- threading.Thread.__init__(self)
- self.vim = vimconn
- self.datacenter_name = datacenter_name
- self.datacenter_tenant_id = datacenter_tenant_id
- if not name:
- self.name = vimconn["id"] + "." + vimconn["config"]["datacenter_tenant_id"]
- else:
- self.name = name
-
- self.logger = logging.getLogger('openmano.vim.'+self.name)
- self.db = db
- self.db_lock = db_lock
-
- self.task_lock = task_lock
- self.task_queue = Queue.Queue(2000)
-
- def insert_task(self, task):
- try:
- self.task_queue.put(task, False)
- return task["id"]
- except Queue.Full:
- raise vimconn.vimconnException(self.name + ": timeout inserting a task")
-
- def del_task(self, task):
- with self.task_lock:
- if task["status"] == "enqueued":
- task["status"] == "deleted"
- return True
- else: # task["status"] == "processing"
- self.task_lock.release()
- return False
-
- def run(self):
- self.logger.debug("Starting")
- while True:
- #TODO reload service
- while True:
- if not self.task_queue.empty():
- task = self.task_queue.get()
- self.task_lock.acquire()
- if task["status"] == "deleted":
- self.task_lock.release()
- continue
- task["status"] == "processing"
- self.task_lock.release()
- else:
- now=time.time()
- time.sleep(1)
- continue
- self.logger.debug("processing task id={} name={} params={}".format(task["id"], task["name"],
- str(task["params"])))
- if task["name"] == 'exit' or task["name"] == 'reload':
- result, content = self.terminate(task)
- elif task["name"] == 'new-vm':
- result, content = self.new_vm(task)
- elif task["name"] == 'del-vm':
- result, content = self.del_vm(task)
- elif task["name"] == 'new-net':
- result, content = self.new_net(task)
- elif task["name"] == 'del-net':
- result, content = self.del_net(task)
- else:
- error_text = "unknown task {}".format(task["name"])
- self.logger.error(error_text)
- result = False
- content = error_text
-
- with self.task_lock:
- task["status"] = "done" if result else "error"
- task["result"] = content
- self.task_queue.task_done()
-
- if task["name"] == 'exit':
- return 0
- elif task["name"] == 'reload':
- break
-
- self.logger.debug("Finishing")
-
- def terminate(self, task):
- return True, None
-
- def new_net(self, task):
- try:
- task_id = task["id"]
- params = task["params"]
- net_id = self.vim.new_network(*params)
- with self.db_lock:
- self.db.update_rows("instance_nets", UPDATE={"vim_net_id": net_id}, WHERE={"vim_net_id": task_id})
- return True, net_id
- except db_base_Exception as e:
- self.logger.error("Error updating database %s", str(e))
- return True, net_id
- except vimconn.vimconnException as e:
- return False, str(e)
-
- def new_vm(self, task):
- try:
- params = task["params"]
- task_id = task["id"]
- depends = task.get("depends")
- net_list = params[5]
- for net in net_list:
- if is_task_id(net["net_id"]): # change task_id into network_id
- try:
- task_net = depends[net["net_id"]]
- with self.task_lock:
- if task_net["status"] == "error":
- return False, "Cannot create VM because depends on a network that cannot be created: " + \
- str(task_net["result"])
- elif task_net["status"] == "enqueued" or task_net["status"] == "processing":
- return False, "Cannot create VM because depends on a network still not created"
- network_id = task_net["result"]
- net["net_id"] = network_id
- except Exception as e:
- return False, "Error trying to map from task_id={} to task result: {}".format(net["net_id"],
- str(e))
- vm_id = self.vim.new_vminstance(*params)
- with self.db_lock:
- self.db.update_rows("instance_vms", UPDATE={"vim_vm_id": vm_id}, WHERE={"vim_vm_id": task_id})
- return True, vm_id
- except db_base_Exception as e:
- self.logger.error("Error updtaing database %s", str(e))
- return True, vm_id
- except vimconn.vimconnException as e:
- return False, str(e)
-
- def del_vm(self, task):
- vm_id = task["params"]
- if is_task_id(vm_id):
- try:
- task_create = task["depends"][vm_id]
- with self.task_lock:
- if task_create["status"] == "error":
- return True, "VM was not created. It has error: " + str(task_create["result"])
- elif task_create["status"] == "enqueued" or task_create["status"] == "processing":
- return False, "Cannot delete VM because still creating"
- vm_id = task_create["result"]
- except Exception as e:
- return False, "Error trying to get task_id='{}':".format(vm_id, str(e))
- try:
- return True, self.vim.delete_vminstance(vm_id)
- except vimconn.vimconnException as e:
- return False, str(e)
-
- def del_net(self, task):
- net_id = task["params"]
- if is_task_id(net_id):
- try:
- task_create = task["depends"][net_id]
- with self.task_lock:
- if task_create["status"] == "error":
- return True, "net was not created. It has error: " + str(task_create["result"])
- elif task_create["status"] == "enqueued" or task_create["status"] == "processing":
- return False, "Cannot delete net because still creating"
- net_id = task_create["result"]
- except Exception as e:
- return False, "Error trying to get task_id='{}':".format(net_id, str(e))
- try:
- return True, self.vim.delete_network(net_id)
- except vimconn.vimconnException as e:
- return False, str(e)
-
-
+++ /dev/null
-# -*- 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
-##
-
-"""
-vimconn implement an Abstract class for the vim connector plugins
- with the definition of the method to be implemented.
-"""
-__author__="Alfonso Tierno"
-__date__ ="$16-oct-2015 11:09:29$"
-
-import logging
-
-#Error variables
-HTTP_Bad_Request = 400
-HTTP_Unauthorized = 401
-HTTP_Not_Found = 404
-HTTP_Method_Not_Allowed = 405
-HTTP_Request_Timeout = 408
-HTTP_Conflict = 409
-HTTP_Not_Implemented = 501
-HTTP_Service_Unavailable = 503
-HTTP_Internal_Server_Error = 500
-
-class vimconnException(Exception):
- """Common and base class Exception for all vimconnector exceptions"""
- def __init__(self, message, http_code=HTTP_Bad_Request):
- Exception.__init__(self, message)
- self.http_code = http_code
-
-class vimconnConnectionException(vimconnException):
- """Connectivity error with the VIM"""
- def __init__(self, message, http_code=HTTP_Service_Unavailable):
- vimconnException.__init__(self, message, http_code)
-
-class vimconnUnexpectedResponse(vimconnException):
- """Get an wrong response from VIM"""
- def __init__(self, message, http_code=HTTP_Service_Unavailable):
- vimconnException.__init__(self, message, http_code)
-
-class vimconnAuthException(vimconnException):
- """Invalid credentials or authorization to perform this action over the VIM"""
- def __init__(self, message, http_code=HTTP_Unauthorized):
- vimconnException.__init__(self, message, http_code)
-
-class vimconnNotFoundException(vimconnException):
- """The item is not found at VIM"""
- def __init__(self, message, http_code=HTTP_Not_Found):
- vimconnException.__init__(self, message, http_code)
-
-class vimconnConflictException(vimconnException):
- """There is a conflict, e.g. more item found than one"""
- def __init__(self, message, http_code=HTTP_Conflict):
- vimconnException.__init__(self, message, http_code)
-
-class vimconnNotSupportedException(vimconnException):
- """The request is not supported by connector"""
- def __init__(self, message, http_code=HTTP_Service_Unavailable):
- vimconnException.__init__(self, message, http_code)
-
-class vimconnNotImplemented(vimconnException):
- """The method is not implemented by the connected"""
- def __init__(self, message, http_code=HTTP_Not_Implemented):
- vimconnException.__init__(self, message, http_code)
-
-class vimconnector():
- """Abstract base class for all the VIM connector plugins
- These plugins must implement a vimconnector class derived from this
- and all these privated methods
- """
- def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level=None,
- config={}, persitent_info={}):
- """Constructor of VIM
- Params:
- 'uuid': id asigned to this VIM
- 'name': name assigned to this VIM, can be used for logging
- 'tenant_id', 'tenant_name': (only one of them is mandatory) VIM tenant to be used
- 'url_admin': (optional), url used for administrative tasks
- 'user', 'passwd': credentials of the VIM user
- 'log_level': provider if it should use a different log_level than the general one
- 'config': dictionary with extra VIM information. This contains a consolidate version of general VIM config
- at creation and particular VIM config at teh attachment
- 'persistent_info': dict where the class can store information that will be available among class
- destroy/creation cycles. This info is unique per VIM/credential. At first call it will contain an
- empty dict. Useful to store login/tokens information for speed up communication
-
- Returns: Raise an exception is some needed parameter is missing, but it must not do any connectivity
- check against the VIM
- """
- self.id = uuid
- self.name = name
- self.url = url
- self.url_admin = url_admin
- self.tenant_id = tenant_id
- self.tenant_name = tenant_name
- self.user = user
- self.passwd = passwd
- self.config = config
- self.logger = logging.getLogger('openmano.vim')
- if log_level:
- self.logger.setLevel( getattr(logging, log_level) )
- if not self.url_admin: #try to use normal url
- self.url_admin = self.url
-
- def __getitem__(self,index):
- if index=='tenant_id':
- return self.tenant_id
- if index=='tenant_name':
- return self.tenant_name
- elif index=='id':
- return self.id
- elif index=='name':
- return self.name
- elif index=='user':
- return self.user
- elif index=='passwd':
- return self.passwd
- elif index=='url':
- return self.url
- elif index=='url_admin':
- return self.url_admin
- elif index=="config":
- return self.config
- else:
- raise KeyError("Invalid key '%s'" %str(index))
-
- def __setitem__(self,index, value):
- if index=='tenant_id':
- self.tenant_id = value
- if index=='tenant_name':
- self.tenant_name = value
- elif index=='id':
- self.id = value
- elif index=='name':
- self.name = value
- elif index=='user':
- self.user = value
- elif index=='passwd':
- self.passwd = value
- elif index=='url':
- self.url = value
- elif index=='url_admin':
- self.url_admin = value
- else:
- raise KeyError("Invalid key '%s'" %str(index))
-
- def check_vim_connectivity(self):
- """Checks VIM can be reached and user credentials are ok.
- Returns None if success or raised vimconnConnectionException, vimconnAuthException, ...
- """
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def new_tenant(self,tenant_name,tenant_description):
- """Adds a new tenant to VIM with this name and description, this is done using admin_url if provided
- "tenant_name": string max lenght 64
- "tenant_description": string max length 256
- returns the tenant identifier or raise exception
- """
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def delete_tenant(self,tenant_id,):
- """Delete a tenant from VIM
- tenant_id: returned VIM tenant_id on "new_tenant"
- Returns None on success. Raises and exception of failure. If tenant is not found raises vimconnNotFoundException
- """
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def get_tenant_list(self, filter_dict={}):
- """Obtain tenants of VIM
- filter_dict dictionary that can contain the following keys:
- name: filter by tenant name
- id: filter by tenant uuid/id
- <other VIM specific>
- Returns the tenant list of dictionaries, and empty list if no tenant match all the filers:
- [{'name':'<name>, 'id':'<id>, ...}, ...]
- """
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def new_network(self, net_name, net_type, ip_profile=None, shared=False, vlan=None):
- """Adds a tenant network to VIM
- Params:
- 'net_name': name of the network
- 'net_type': one of:
- 'bridge': overlay isolated network
- 'data': underlay E-LAN network for Passthrough and SRIOV interfaces
- 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces.
- 'ip_profile': is a dict containing the IP parameters of the network (Currently only IPv4 is implemented)
- 'ip-version': can be one of ["IPv4","IPv6"]
- 'subnet-address': ip_prefix_schema, that is X.X.X.X/Y
- 'gateway-address': (Optional) ip_schema, that is X.X.X.X
- 'dns-address': (Optional) ip_schema,
- 'dhcp': (Optional) dict containing
- 'enabled': {"type": "boolean"},
- 'start-address': ip_schema, first IP to grant
- 'count': number of IPs to grant.
- 'shared': if this network can be seen/use by other tenants/organization
- 'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network
- Returns the network identifier on success or raises and exception on failure
- """
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def get_network_list(self, filter_dict={}):
- """Obtain tenant networks of VIM
- Params:
- 'filter_dict' (optional) contains entries to return only networks that matches ALL entries:
- name: string => returns only networks with this name
- id: string => returns networks with this VIM id, this imply returns one network at most
- shared: boolean >= returns only networks that are (or are not) shared
- tenant_id: sting => returns only networks that belong to this tenant/project
- ,#(not used yet) admin_state_up: boolean => returns only networks that are (or are not) in admin state active
- #(not used yet) status: 'ACTIVE','ERROR',... => filter networks that are on this status
- Returns the network list of dictionaries. each dictionary contains:
- 'id': (mandatory) VIM network id
- 'name': (mandatory) VIM network name
- 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
- 'error_msg': (optional) text that explains the ERROR status
- other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
- List can be empty if no network map the filter_dict. Raise an exception only upon VIM connectivity,
- authorization, or some other unspecific error
- """
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def get_network(self, net_id):
- """Obtain network details from the 'net_id' VIM network
- Return a dict that contains:
- 'id': (mandatory) VIM network id, that is, net_id
- 'name': (mandatory) VIM network name
- 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
- 'error_msg': (optional) text that explains the ERROR status
- other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
- Raises an exception upon error or when network is not found
- """
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def delete_network(self, net_id):
- """Deletes a tenant network from VIM
- Returns the network identifier or raises an exception upon error or when network is not found
- """
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def refresh_nets_status(self, net_list):
- """Get the status of the networks
- Params:
- 'net_list': a list with the VIM network id to be get the status
- Returns a dictionary with:
- 'net_id': #VIM id of this network
- status: #Mandatory. Text with one of:
- # DELETED (not found at vim)
- # VIM_ERROR (Cannot connect to VIM, authentication problems, VIM response error, ...)
- # OTHER (Vim reported other status not understood)
- # ERROR (VIM indicates an ERROR status)
- # ACTIVE, INACTIVE, DOWN (admin down),
- # BUILD (on building process)
- error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
- vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
- 'net_id2': ...
- """
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def get_flavor(self, flavor_id):
- """Obtain flavor details from the VIM
- Returns the flavor dict details {'id':<>, 'name':<>, other vim specific }
- Raises an exception upon error or if not found
- """
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def get_flavor_id_from_data(self, flavor_dict):
- """Obtain flavor id that match the flavor description
- Params:
- 'flavor_dict': dictionary that contains:
- 'disk': main hard disk in GB
- 'ram': meomry in MB
- 'vcpus': number of virtual cpus
- #TODO: complete parameters for EPA
- Returns the flavor_id or raises a vimconnNotFoundException
- """
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def new_flavor(self, flavor_data):
- """Adds a tenant flavor to VIM
- flavor_data contains a dictionary with information, keys:
- name: flavor name
- ram: memory (cloud type) in MBytes
- vpcus: cpus (cloud type)
- extended: EPA parameters
- - numas: #items requested in same NUMA
- memory: number of 1G huge pages memory
- paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
- interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
- - name: interface name
- dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
- bandwidth: X Gbps; requested guarantee bandwidth
- vpci: requested virtual PCI address
- disk: disk size
- is_public:
- #TODO to concrete
- Returns the flavor identifier"""
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def delete_flavor(self, flavor_id):
- """Deletes a tenant flavor from VIM identify by its id
- Returns the used id or raise an exception"""
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def new_image(self, image_dict):
- """ Adds a tenant image to VIM
- Returns the image id or raises an exception if failed
- """
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def delete_image(self, image_id):
- """Deletes a tenant image from VIM
- Returns the image_id if image is deleted or raises an exception on error"""
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def get_image_id_from_path(self, path):
- """Get the image id from image path in the VIM database.
- Returns the image_id or raises a vimconnNotFoundException
- """
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def get_image_list(self, filter_dict={}):
- """Obtain tenant images from VIM
- Filter_dict can be:
- name: image name
- id: image uuid
- checksum: image checksum
- location: image path
- Returns the image list of dictionaries:
- [{<the fields at Filter_dict plus some VIM specific>}, ...]
- List can be empty
- """
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def new_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None,
- disk_list=None):
- """Adds a VM instance to VIM
- Params:
- 'start': (boolean) indicates if VM must start or created in pause mode.
- 'image_id','flavor_id': image and flavor VIM id to use for the VM
- 'net_list': list of interfaces, each one is a dictionary with:
- 'name': (optional) name for the interface.
- 'net_id': VIM network id where this interface must be connect to. Mandatory for type==virtual
- 'vpci': (optional) virtual vPCI address to assign at the VM. Can be ignored depending on VIM capabilities
- 'model': (optional and only have sense for type==virtual) interface model: virtio, e2000, ...
- 'mac_address': (optional) mac address to assign to this interface
- #TODO: CHECK if an optional 'vlan' parameter is needed for VIMs when type if VF and net_id is not provided,
- the VLAN tag to be used. In case net_id is provided, the internal network vlan is used for tagging VF
- 'type': (mandatory) can be one of:
- 'virtual', in this case always connected to a network of type 'net_type=bridge'
- 'PF' (passthrough): depending on VIM capabilities it can be connected to a data/ptp network ot it
- can created unconnected
- 'VF' (SRIOV with VLAN tag): same as PF for network connectivity.
- 'VFnotShared'(SRIOV without VLAN tag) same as PF for network connectivity. VF where no other VFs
- are allocated on the same physical NIC
- 'bw': (optional) only for PF/VF/VFnotShared. Minimal Bandwidth required for the interface in GBPS
- 'port_security': (optional) If False it must avoid any traffic filtering at this interface. If missing
- or True, it must apply the default VIM behaviour
- After execution the method will add the key:
- 'vim_id': must be filled/added by this method with the VIM identifier generated by the VIM for this
- interface. 'net_list' is modified
- 'cloud_config': (optional) dictionary with:
- 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
- 'users': (optional) list of users to be inserted, each item is a dict with:
- 'name': (mandatory) user name,
- 'key-pairs': (optional) list of strings with the public key to be inserted to the user
- 'user-data': (optional) string is a text script to be passed directly to cloud-init
- 'config-files': (optional). List of files to be transferred. Each item is a dict with:
- 'dest': (mandatory) string with the destination absolute path
- 'encoding': (optional, by default text). Can be one of:
- 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
- 'content' (mandatory): string with the content of the file
- 'permissions': (optional) string with file permissions, typically octal notation '0644'
- 'owner': (optional) file owner, string with the format 'owner:group'
- 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk)
- 'disk_list': (optional) list with additional disks to the VM. Each item is a dict with:
- 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
- 'size': (mandatory) string with the size of the disk in GB
- Returns the instance identifier or raises an exception on error
- """
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def get_vminstance(self,vm_id):
- """Returns the VM instance information from VIM"""
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def delete_vminstance(self, vm_id):
- """Removes a VM instance from VIM
- Returns the instance identifier"""
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def refresh_vms_status(self, vm_list):
- """Get the status of the virtual machines and their interfaces/ports
- Params: the list of VM identifiers
- Returns a dictionary with:
- vm_id: #VIM id of this Virtual Machine
- status: #Mandatory. Text with one of:
- # DELETED (not found at vim)
- # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
- # OTHER (Vim reported other status not understood)
- # ERROR (VIM indicates an ERROR status)
- # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
- # BUILD (on building process), ERROR
- # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
- #
- error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
- vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
- interfaces: list with interface info. Each item a dictionary with:
- vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
- mac_address: #Text format XX:XX:XX:XX:XX:XX
- vim_net_id: #network id where this interface is connected, if provided at creation
- vim_interface_id: #interface/port VIM id
- ip_address: #null, or text with IPv4, IPv6 address
- physical_compute: #identification of compute node where PF,VF interface is allocated
- physical_pci: #PCI address of the NIC that hosts the PF,VF
- physical_vlan: #physical VLAN used for VF
- """
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def action_vminstance(self, vm_id, action_dict):
- """Send and action over a VM instance from VIM
- Returns the vm_id if the action was successfully sent to the VIM"""
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def get_vminstance_console(self, vm_id, console_type="vnc"):
- """
- Get a console for the virtual machine
- Params:
- vm_id: uuid of the VM
- console_type, can be:
- "novnc" (by default), "xvpvnc" for VNC types,
- "rdp-html5" for RDP types, "spice-html5" for SPICE types
- Returns dict with the console parameters:
- protocol: ssh, ftp, http, https, ...
- server: usually ip address
- port: the http, ssh, ... port
- suffix: extra text, e.g. the http path and query string
- """
- raise vimconnNotImplemented( "Should have implemented this" )
-
-#NOT USED METHODS in current version
-
- def host_vim2gui(self, host, server_dict):
- """Transform host dictionary from VIM format to GUI format,
- and append to the server_dict
- """
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def get_hosts_info(self):
- """Get the information of deployed hosts
- Returns the hosts content"""
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def get_hosts(self, vim_tenant):
- """Get the hosts and deployed instances
- Returns the hosts content"""
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def get_processor_rankings(self):
- """Get the processor rankings in the VIM database"""
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def new_host(self, host_data):
- """Adds a new host to VIM"""
- """Returns status code of the VIM response"""
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def new_external_port(self, port_data):
- """Adds a external port to VIM"""
- """Returns the port identifier"""
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def new_external_network(self,net_name,net_type):
- """Adds a external network to VIM (shared)"""
- """Returns the network identifier"""
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def connect_port_network(self, port_id, network_id, admin=False):
- """Connects a external port to a network"""
- """Returns status code of the VIM response"""
- raise vimconnNotImplemented( "Should have implemented this" )
-
- def new_vminstancefromJSON(self, vm_data):
- """Adds a VM instance to VIM"""
- """Returns the instance identifier"""
- raise vimconnNotImplemented( "Should have implemented this" )
-
+++ /dev/null
-# -*- 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
-##
-
-'''
-osconnector implements all the methods to interact with openstack using the python-client.
-'''
-__author__="Alfonso Tierno, Gerardo Garcia, Pablo Montes, xFlow Research"
-__date__ ="$22-jun-2014 11:19:29$"
-
-import vimconn
-import json
-import yaml
-import logging
-import netaddr
-import time
-import yaml
-import random
-
-from novaclient import client as nClient_v2, exceptions as nvExceptions
-from novaclient import api_versions
-import keystoneclient.v2_0.client as ksClient_v2
-from novaclient.v2.client import Client as nClient
-import keystoneclient.v3.client as ksClient
-import keystoneclient.exceptions as ksExceptions
-import glanceclient.v2.client as glClient
-import glanceclient.client as gl1Client
-import glanceclient.exc as gl1Exceptions
-import cinderclient.v2.client as cClient_v2
-from httplib import HTTPException
-from neutronclient.neutron import client as neClient_v2
-from neutronclient.v2_0 import client as neClient
-from neutronclient.common import exceptions as neExceptions
-from requests.exceptions import ConnectionError
-
-'''contain the openstack virtual machine status to openmano status'''
-vmStatus2manoFormat={'ACTIVE':'ACTIVE',
- 'PAUSED':'PAUSED',
- 'SUSPENDED': 'SUSPENDED',
- 'SHUTOFF':'INACTIVE',
- 'BUILD':'BUILD',
- 'ERROR':'ERROR','DELETED':'DELETED'
- }
-netStatus2manoFormat={'ACTIVE':'ACTIVE','PAUSED':'PAUSED','INACTIVE':'INACTIVE','BUILD':'BUILD','ERROR':'ERROR','DELETED':'DELETED'
- }
-
-#global var to have a timeout creating and deleting volumes
-volume_timeout = 60
-server_timeout = 60
-
-class vimconnector(vimconn.vimconnector):
- def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None,
- log_level=None, config={}, persistent_info={}):
- '''using common constructor parameters. In this case
- 'url' is the keystone authorization url,
- 'url_admin' is not use
- '''
- self.osc_api_version = 'v2.0'
- if config.get('APIversion') == 'v3.3':
- self.osc_api_version = 'v3.3'
- vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level, config)
-
- self.persistent_info = persistent_info
- self.k_creds={}
- self.n_creds={}
- if self.config.get("insecure"):
- self.k_creds["insecure"] = True
- self.n_creds["insecure"] = True
- if not url:
- raise TypeError, 'url param can not be NoneType'
- self.k_creds['auth_url'] = url
- self.n_creds['auth_url'] = url
- if tenant_name:
- self.k_creds['tenant_name'] = tenant_name
- self.n_creds['project_id'] = tenant_name
- if tenant_id:
- self.k_creds['tenant_id'] = tenant_id
- self.n_creds['tenant_id'] = tenant_id
- if user:
- self.k_creds['username'] = user
- self.n_creds['username'] = user
- if passwd:
- self.k_creds['password'] = passwd
- self.n_creds['api_key'] = passwd
- if self.osc_api_version == 'v3.3':
- self.k_creds['project_name'] = tenant_name
- self.k_creds['project_id'] = tenant_id
- if config.get('region_name'):
- self.k_creds['region_name'] = config.get('region_name')
- self.n_creds['region_name'] = config.get('region_name')
-
- self.reload_client = True
- self.logger = logging.getLogger('openmano.vim.openstack')
- if log_level:
- self.logger.setLevel( getattr(logging, log_level) )
-
- def __setitem__(self,index, value):
- '''Set individuals parameters
- Throw TypeError, KeyError
- '''
- if index=='tenant_id':
- self.reload_client=True
- self.tenant_id = value
- if self.osc_api_version == 'v3.3':
- if value:
- self.k_creds['project_id'] = value
- self.n_creds['project_id'] = value
- else:
- del self.k_creds['project_id']
- del self.n_creds['project_id']
- else:
- if value:
- self.k_creds['tenant_id'] = value
- self.n_creds['tenant_id'] = value
- else:
- del self.k_creds['tenant_id']
- del self.n_creds['tenant_id']
- elif index=='tenant_name':
- self.reload_client=True
- self.tenant_name = value
- if self.osc_api_version == 'v3.3':
- if value:
- self.k_creds['project_name'] = value
- self.n_creds['project_name'] = value
- else:
- del self.k_creds['project_name']
- del self.n_creds['project_name']
- else:
- if value:
- self.k_creds['tenant_name'] = value
- self.n_creds['project_id'] = value
- else:
- del self.k_creds['tenant_name']
- del self.n_creds['project_id']
- elif index=='user':
- self.reload_client=True
- self.user = value
- if value:
- self.k_creds['username'] = value
- self.n_creds['username'] = value
- else:
- del self.k_creds['username']
- del self.n_creds['username']
- elif index=='passwd':
- self.reload_client=True
- self.passwd = value
- if value:
- self.k_creds['password'] = value
- self.n_creds['api_key'] = value
- else:
- del self.k_creds['password']
- del self.n_creds['api_key']
- elif index=='url':
- self.reload_client=True
- self.url = value
- if value:
- self.k_creds['auth_url'] = value
- self.n_creds['auth_url'] = value
- else:
- raise TypeError, 'url param can not be NoneType'
- else:
- vimconn.vimconnector.__setitem__(self,index, value)
-
- def _reload_connection(self):
- '''Called before any operation, it check if credentials has changed
- Throw keystoneclient.apiclient.exceptions.AuthorizationFailure
- '''
- #TODO control the timing and possible token timeout, but it seams that python client does this task for us :-)
- if self.reload_client:
- #test valid params
- if len(self.n_creds) <4:
- raise ksExceptions.ClientException("Not enough parameters to connect to openstack")
- if self.osc_api_version == 'v3.3':
- self.nova = nClient(api_version=api_versions.APIVersion(version_str='2.0'), **self.n_creds)
- #TODO To be updated for v3
- #self.cinder = cClient.Client(**self.n_creds)
- self.keystone = ksClient.Client(**self.k_creds)
- self.ne_endpoint=self.keystone.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
- self.neutron = neClient.Client(api_version=api_versions.APIVersion(version_str='2.0'), endpoint_url=self.ne_endpoint, token=self.keystone.auth_token, **self.k_creds)
- else:
- self.nova = nClient_v2.Client(version='2', **self.n_creds)
- self.cinder = cClient_v2.Client(**self.n_creds)
- self.keystone = ksClient_v2.Client(**self.k_creds)
- self.ne_endpoint=self.keystone.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
- self.neutron = neClient_v2.Client('2.0', endpoint_url=self.ne_endpoint, token=self.keystone.auth_token, **self.k_creds)
- self.glance_endpoint = self.keystone.service_catalog.url_for(service_type='image', endpoint_type='publicURL')
- self.glance = glClient.Client(self.glance_endpoint, token=self.keystone.auth_token, **self.k_creds) #TODO check k_creds vs n_creds
- self.reload_client = False
-
- def __net_os2mano(self, net_list_dict):
- '''Transform the net openstack format to mano format
- net_list_dict can be a list of dict or a single dict'''
- if type(net_list_dict) is dict:
- net_list_=(net_list_dict,)
- elif type(net_list_dict) is list:
- net_list_=net_list_dict
- else:
- raise TypeError("param net_list_dict must be a list or a dictionary")
- for net in net_list_:
- if net.get('provider:network_type') == "vlan":
- net['type']='data'
- else:
- net['type']='bridge'
-
-
-
- def _format_exception(self, exception):
- '''Transform a keystone, nova, neutron exception into a vimconn exception'''
- if isinstance(exception, (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError,
- ConnectionError, ksExceptions.ConnectionError, neExceptions.ConnectionFailed
- )):
- raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception))
- elif isinstance(exception, (nvExceptions.ClientException, ksExceptions.ClientException,
- neExceptions.NeutronException, nvExceptions.BadRequest)):
- raise vimconn.vimconnUnexpectedResponse(type(exception).__name__ + ": " + str(exception))
- elif isinstance(exception, (neExceptions.NetworkNotFoundClient, nvExceptions.NotFound)):
- raise vimconn.vimconnNotFoundException(type(exception).__name__ + ": " + str(exception))
- elif isinstance(exception, nvExceptions.Conflict):
- raise vimconn.vimconnConflictException(type(exception).__name__ + ": " + str(exception))
- else: # ()
- raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception))
-
- def get_tenant_list(self, filter_dict={}):
- '''Obtain tenants of VIM
- filter_dict can contain the following keys:
- name: filter by tenant name
- id: filter by tenant uuid/id
- <other VIM specific>
- Returns the tenant list of dictionaries: [{'name':'<name>, 'id':'<id>, ...}, ...]
- '''
- self.logger.debug("Getting tenants from VIM filter: '%s'", str(filter_dict))
- try:
- self._reload_connection()
- if self.osc_api_version == 'v3.3':
- project_class_list=self.keystone.projects.findall(**filter_dict)
- else:
- project_class_list=self.keystone.tenants.findall(**filter_dict)
- project_list=[]
- for project in project_class_list:
- project_list.append(project.to_dict())
- return project_list
- except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e:
- self._format_exception(e)
-
- def new_tenant(self, tenant_name, tenant_description):
- '''Adds a new tenant to openstack VIM. Returns the tenant identifier'''
- self.logger.debug("Adding a new tenant name: %s", tenant_name)
- try:
- self._reload_connection()
- if self.osc_api_version == 'v3.3':
- project=self.keystone.projects.create(tenant_name, tenant_description)
- else:
- project=self.keystone.tenants.create(tenant_name, tenant_description)
- return project.id
- except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e:
- self._format_exception(e)
-
- def delete_tenant(self, tenant_id):
- '''Delete a tenant from openstack VIM. Returns the old tenant identifier'''
- self.logger.debug("Deleting tenant %s from VIM", tenant_id)
- try:
- self._reload_connection()
- if self.osc_api_version == 'v3.3':
- self.keystone.projects.delete(tenant_id)
- else:
- self.keystone.tenants.delete(tenant_id)
- return tenant_id
- except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e:
- self._format_exception(e)
-
- def new_network(self,net_name, net_type, ip_profile=None, shared=False, vlan=None):
- '''Adds a tenant network to VIM. Returns the network identifier'''
- self.logger.debug("Adding a new network to VIM name '%s', type '%s'", net_name, net_type)
- #self.logger.debug(">>>>>>>>>>>>>>>>>> IP profile %s", str(ip_profile))
- try:
- new_net = None
- self._reload_connection()
- network_dict = {'name': net_name, 'admin_state_up': True}
- if net_type=="data" or net_type=="ptp":
- if self.config.get('dataplane_physical_net') == None:
- raise vimconn.vimconnConflictException("You must provide a 'dataplane_physical_net' at config value before creating sriov network")
- network_dict["provider:physical_network"] = self.config['dataplane_physical_net'] #"physnet_sriov" #TODO physical
- network_dict["provider:network_type"] = "vlan"
- if vlan!=None:
- network_dict["provider:network_type"] = vlan
- network_dict["shared"]=shared
- new_net=self.neutron.create_network({'network':network_dict})
- #print new_net
- #create subnetwork, even if there is no profile
- if not ip_profile:
- ip_profile = {}
- if 'subnet_address' not in ip_profile:
- #Fake subnet is required
- subnet_rand = random.randint(0, 255)
- ip_profile['subnet_address'] = "192.168.{}.0/24".format(subnet_rand)
- if 'ip_version' not in ip_profile:
- ip_profile['ip_version'] = "IPv4"
- subnet={"name":net_name+"-subnet",
- "network_id": new_net["network"]["id"],
- "ip_version": 4 if ip_profile['ip_version']=="IPv4" else 6,
- "cidr": ip_profile['subnet_address']
- }
- if 'gateway_address' in ip_profile:
- subnet['gateway_ip'] = ip_profile['gateway_address']
- if ip_profile.get('dns_address'):
- #TODO: manage dns_address as a list of addresses separated by commas
- subnet['dns_nameservers'] = []
- subnet['dns_nameservers'].append(ip_profile['dns_address'])
- if 'dhcp_enabled' in ip_profile:
- subnet['enable_dhcp'] = False if ip_profile['dhcp_enabled']=="false" else True
- if 'dhcp_start_address' in ip_profile:
- subnet['allocation_pools']=[]
- subnet['allocation_pools'].append(dict())
- subnet['allocation_pools'][0]['start'] = ip_profile['dhcp_start_address']
- if 'dhcp_count' in ip_profile:
- #parts = ip_profile['dhcp_start_address'].split('.')
- #ip_int = (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])
- ip_int = int(netaddr.IPAddress(ip_profile['dhcp_start_address']))
- ip_int += ip_profile['dhcp_count'] - 1
- ip_str = str(netaddr.IPAddress(ip_int))
- subnet['allocation_pools'][0]['end'] = ip_str
- #self.logger.debug(">>>>>>>>>>>>>>>>>> Subnet: %s", str(subnet))
- self.neutron.create_subnet({"subnet": subnet} )
- return new_net["network"]["id"]
- except (neExceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
- if new_net:
- self.neutron.delete_network(new_net['network']['id'])
- self._format_exception(e)
-
- def get_network_list(self, filter_dict={}):
- '''Obtain tenant networks of VIM
- Filter_dict can be:
- name: network name
- id: network uuid
- shared: boolean
- tenant_id: tenant
- admin_state_up: boolean
- status: 'ACTIVE'
- Returns the network list of dictionaries
- '''
- self.logger.debug("Getting network from VIM filter: '%s'", str(filter_dict))
- try:
- self._reload_connection()
- if self.osc_api_version == 'v3.3' and "tenant_id" in filter_dict:
- filter_dict['project_id'] = filter_dict.pop('tenant_id')
- net_dict=self.neutron.list_networks(**filter_dict)
- net_list=net_dict["networks"]
- self.__net_os2mano(net_list)
- return net_list
- except (neExceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
- self._format_exception(e)
-
- def get_network(self, net_id):
- '''Obtain details of network from VIM
- Returns the network information from a network id'''
- self.logger.debug(" Getting tenant network %s from VIM", net_id)
- filter_dict={"id": net_id}
- net_list = self.get_network_list(filter_dict)
- if len(net_list)==0:
- raise vimconn.vimconnNotFoundException("Network '{}' not found".format(net_id))
- elif len(net_list)>1:
- raise vimconn.vimconnConflictException("Found more than one network with this criteria")
- net = net_list[0]
- subnets=[]
- for subnet_id in net.get("subnets", () ):
- try:
- subnet = self.neutron.show_subnet(subnet_id)
- except Exception as e:
- self.logger.error("osconnector.get_network(): Error getting subnet %s %s" % (net_id, str(e)))
- subnet = {"id": subnet_id, "fault": str(e)}
- subnets.append(subnet)
- net["subnets"] = subnets
- return net
-
- def delete_network(self, net_id):
- '''Deletes a tenant network from VIM. Returns the old network identifier'''
- self.logger.debug("Deleting network '%s' from VIM", net_id)
- try:
- self._reload_connection()
- #delete VM ports attached to this networks before the network
- ports = self.neutron.list_ports(network_id=net_id)
- for p in ports['ports']:
- try:
- self.neutron.delete_port(p["id"])
- except Exception as e:
- self.logger.error("Error deleting port %s: %s", p["id"], str(e))
- self.neutron.delete_network(net_id)
- return net_id
- except (neExceptions.ConnectionFailed, neExceptions.NetworkNotFoundClient, neExceptions.NeutronException,
- ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
- self._format_exception(e)
-
- def refresh_nets_status(self, net_list):
- '''Get the status of the networks
- Params: the list of network identifiers
- Returns a dictionary with:
- net_id: #VIM id of this network
- status: #Mandatory. Text with one of:
- # DELETED (not found at vim)
- # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
- # OTHER (Vim reported other status not understood)
- # ERROR (VIM indicates an ERROR status)
- # ACTIVE, INACTIVE, DOWN (admin down),
- # BUILD (on building process)
- #
- error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
- vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
-
- '''
- net_dict={}
- for net_id in net_list:
- net = {}
- try:
- net_vim = self.get_network(net_id)
- if net_vim['status'] in netStatus2manoFormat:
- net["status"] = netStatus2manoFormat[ net_vim['status'] ]
- else:
- net["status"] = "OTHER"
- net["error_msg"] = "VIM status reported " + net_vim['status']
-
- if net['status'] == "ACTIVE" and not net_vim['admin_state_up']:
- net['status'] = 'DOWN'
- try:
- net['vim_info'] = yaml.safe_dump(net_vim, default_flow_style=True, width=256)
- except yaml.representer.RepresenterError:
- net['vim_info'] = str(net_vim)
- if net_vim.get('fault'): #TODO
- net['error_msg'] = str(net_vim['fault'])
- except vimconn.vimconnNotFoundException as e:
- self.logger.error("Exception getting net status: %s", str(e))
- net['status'] = "DELETED"
- net['error_msg'] = str(e)
- except vimconn.vimconnException as e:
- self.logger.error("Exception getting net status: %s", str(e))
- net['status'] = "VIM_ERROR"
- net['error_msg'] = str(e)
- net_dict[net_id] = net
- return net_dict
-
- def get_flavor(self, flavor_id):
- '''Obtain flavor details from the VIM. Returns the flavor dict details'''
- self.logger.debug("Getting flavor '%s'", flavor_id)
- try:
- self._reload_connection()
- flavor = self.nova.flavors.find(id=flavor_id)
- #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
- return flavor.to_dict()
- except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException, ConnectionError) as e:
- self._format_exception(e)
-
- def get_flavor_id_from_data(self, flavor_dict):
- """Obtain flavor id that match the flavor description
- Returns the flavor_id or raises a vimconnNotFoundException
- """
- try:
- self._reload_connection()
- numa=None
- numas = flavor_dict.get("extended",{}).get("numas")
- if numas:
- #TODO
- raise vimconn.vimconnNotFoundException("Flavor with EPA still not implemted")
- # if len(numas) > 1:
- # raise vimconn.vimconnNotFoundException("Cannot find any flavor with more than one numa")
- # numa=numas[0]
- # numas = extended.get("numas")
- for flavor in self.nova.flavors.list():
- epa = flavor.get_keys()
- if epa:
- continue
- #TODO
- if flavor.ram != flavor_dict["ram"]:
- continue
- if flavor.vcpus != flavor_dict["vcpus"]:
- continue
- if flavor.disk != flavor_dict["disk"]:
- continue
- return flavor.id
- raise vimconn.vimconnNotFoundException("Cannot find any flavor matching '{}'".format(str(flavor_dict)))
- except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException, ConnectionError) as e:
- self._format_exception(e)
-
-
- def new_flavor(self, flavor_data, change_name_if_used=True):
- '''Adds a tenant flavor to openstack VIM
- if change_name_if_used is True, it will change name in case of conflict, because it is not supported name repetition
- Returns the flavor identifier
- '''
- self.logger.debug("Adding flavor '%s'", str(flavor_data))
- retry=0
- max_retries=3
- name_suffix = 0
- name=flavor_data['name']
- while retry<max_retries:
- retry+=1
- try:
- self._reload_connection()
- if change_name_if_used:
- #get used names
- fl_names=[]
- fl=self.nova.flavors.list()
- for f in fl:
- fl_names.append(f.name)
- while name in fl_names:
- name_suffix += 1
- name = flavor_data['name']+"-" + str(name_suffix)
-
- ram = flavor_data.get('ram',64)
- vcpus = flavor_data.get('vcpus',1)
- numa_properties=None
-
- extended = flavor_data.get("extended")
- if extended:
- numas=extended.get("numas")
- if numas:
- numa_nodes = len(numas)
- if numa_nodes > 1:
- return -1, "Can not add flavor with more than one numa"
- numa_properties = {"hw:numa_nodes":str(numa_nodes)}
- numa_properties["hw:mem_page_size"] = "large"
- numa_properties["hw:cpu_policy"] = "dedicated"
- numa_properties["hw:numa_mempolicy"] = "strict"
- for numa in numas:
- #overwrite ram and vcpus
- ram = numa['memory']*1024
- if 'paired-threads' in numa:
- vcpus = numa['paired-threads']*2
- numa_properties["hw:cpu_threads_policy"] = "prefer"
- elif 'cores' in numa:
- vcpus = numa['cores']
- #numa_properties["hw:cpu_threads_policy"] = "prefer"
- elif 'threads' in numa:
- vcpus = numa['threads']
- numa_properties["hw:cpu_policy"] = "isolated"
- for interface in numa.get("interfaces",() ):
- if interface["dedicated"]=="yes":
- raise vimconn.vimconnException("Passthrough interfaces are not supported for the openstack connector", http_code=vimconn.HTTP_Service_Unavailable)
- #TODO, add the key 'pci_passthrough:alias"="<label at config>:<number ifaces>"' when a way to connect it is available
-
- #create flavor
- new_flavor=self.nova.flavors.create(name,
- ram,
- vcpus,
- flavor_data.get('disk',1),
- is_public=flavor_data.get('is_public', True)
- )
- #add metadata
- if numa_properties:
- new_flavor.set_keys(numa_properties)
- return new_flavor.id
- except nvExceptions.Conflict as e:
- if change_name_if_used and retry < max_retries:
- continue
- self._format_exception(e)
- #except nvExceptions.BadRequest as e:
- except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
- self._format_exception(e)
-
- def delete_flavor(self,flavor_id):
- '''Deletes a tenant flavor from openstack VIM. Returns the old flavor_id
- '''
- try:
- self._reload_connection()
- self.nova.flavors.delete(flavor_id)
- return flavor_id
- #except nvExceptions.BadRequest as e:
- except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
- self._format_exception(e)
-
- def new_image(self,image_dict):
- '''
- Adds a tenant image to VIM. imge_dict is a dictionary with:
- name: name
- disk_format: qcow2, vhd, vmdk, raw (by default), ...
- location: path or URI
- public: "yes" or "no"
- metadata: metadata of the image
- Returns the image_id
- '''
- #using version 1 of glance client
- glancev1 = gl1Client.Client('1',self.glance_endpoint, token=self.keystone.auth_token, **self.k_creds) #TODO check k_creds vs n_creds
- retry=0
- max_retries=3
- while retry<max_retries:
- retry+=1
- try:
- self._reload_connection()
- #determine format http://docs.openstack.org/developer/glance/formats.html
- if "disk_format" in image_dict:
- disk_format=image_dict["disk_format"]
- else: #autodiscover based on extension
- if image_dict['location'][-6:]==".qcow2":
- disk_format="qcow2"
- elif image_dict['location'][-4:]==".vhd":
- disk_format="vhd"
- elif image_dict['location'][-5:]==".vmdk":
- disk_format="vmdk"
- elif image_dict['location'][-4:]==".vdi":
- disk_format="vdi"
- elif image_dict['location'][-4:]==".iso":
- disk_format="iso"
- elif image_dict['location'][-4:]==".aki":
- disk_format="aki"
- elif image_dict['location'][-4:]==".ari":
- disk_format="ari"
- elif image_dict['location'][-4:]==".ami":
- disk_format="ami"
- else:
- disk_format="raw"
- self.logger.debug("new_image: '%s' loading from '%s'", image_dict['name'], image_dict['location'])
- if image_dict['location'][0:4]=="http":
- new_image = glancev1.images.create(name=image_dict['name'], is_public=image_dict.get('public',"yes")=="yes",
- container_format="bare", location=image_dict['location'], disk_format=disk_format)
- else: #local path
- with open(image_dict['location']) as fimage:
- new_image = glancev1.images.create(name=image_dict['name'], is_public=image_dict.get('public',"yes")=="yes",
- container_format="bare", data=fimage, disk_format=disk_format)
- #insert metadata. We cannot use 'new_image.properties.setdefault'
- #because nova and glance are "INDEPENDENT" and we are using nova for reading metadata
- new_image_nova=self.nova.images.find(id=new_image.id)
- new_image_nova.metadata.setdefault('location',image_dict['location'])
- metadata_to_load = image_dict.get('metadata')
- if metadata_to_load:
- for k,v in yaml.load(metadata_to_load).iteritems():
- new_image_nova.metadata.setdefault(k,v)
- return new_image.id
- except (nvExceptions.Conflict, ksExceptions.ClientException, nvExceptions.ClientException) as e:
- self._format_exception(e)
- except (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError, ConnectionError) as e:
- if retry==max_retries:
- continue
- self._format_exception(e)
- except IOError as e: #can not open the file
- raise vimconn.vimconnConnectionException(type(e).__name__ + ": " + str(e)+ " for " + image_dict['location'],
- http_code=vimconn.HTTP_Bad_Request)
-
- def delete_image(self, image_id):
- '''Deletes a tenant image from openstack VIM. Returns the old id
- '''
- try:
- self._reload_connection()
- self.nova.images.delete(image_id)
- return image_id
- except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e: #TODO remove
- self._format_exception(e)
-
- def get_image_id_from_path(self, path):
- '''Get the image id from image path in the VIM database. Returns the image_id'''
- try:
- self._reload_connection()
- images = self.nova.images.list()
- for image in images:
- if image.metadata.get("location")==path:
- return image.id
- raise vimconn.vimconnNotFoundException("image with location '{}' not found".format( path))
- except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e:
- self._format_exception(e)
-
- def get_image_list(self, filter_dict={}):
- '''Obtain tenant images from VIM
- Filter_dict can be:
- id: image id
- name: image name
- checksum: image checksum
- Returns the image list of dictionaries:
- [{<the fields at Filter_dict plus some VIM specific>}, ...]
- List can be empty
- '''
- self.logger.debug("Getting image list from VIM filter: '%s'", str(filter_dict))
- try:
- self._reload_connection()
- filter_dict_os=filter_dict.copy()
- #First we filter by the available filter fields: name, id. The others are removed.
- filter_dict_os.pop('checksum',None)
- image_list=self.nova.images.findall(**filter_dict_os)
- if len(image_list)==0:
- return []
- #Then we filter by the rest of filter fields: checksum
- filtered_list = []
- for image in image_list:
- image_class=self.glance.images.get(image.id)
- if 'checksum' not in filter_dict or image_class['checksum']==filter_dict.get('checksum'):
- filtered_list.append(image_class.copy())
- return filtered_list
- except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e:
- self._format_exception(e)
-
- def new_vminstance(self,name,description,start,image_id,flavor_id,net_list,cloud_config=None,disk_list=None):
- '''Adds a VM instance to VIM
- Params:
- start: indicates if VM must start or boot in pause mode. Ignored
- image_id,flavor_id: iamge and flavor uuid
- net_list: list of interfaces, each one is a dictionary with:
- name:
- net_id: network uuid to connect
- vpci: virtual vcpi to assign, ignored because openstack lack #TODO
- model: interface model, ignored #TODO
- mac_address: used for SR-IOV ifaces #TODO for other types
- use: 'data', 'bridge', 'mgmt'
- type: 'virtual', 'PF', 'VF', 'VFnotShared'
- vim_id: filled/added by this function
- floating_ip: True/False (or it can be None)
- #TODO ip, security groups
- Returns the instance identifier
- '''
- self.logger.debug("new_vminstance input: image='%s' flavor='%s' nics='%s'",image_id, flavor_id,str(net_list))
- try:
- metadata={}
- net_list_vim=[]
- external_network=[] #list of external networks to be connected to instance, later on used to create floating_ip
- self._reload_connection()
- metadata_vpci={} #For a specific neutron plugin
- for net in net_list:
- if not net.get("net_id"): #skip non connected iface
- continue
- if net["type"]=="virtual" or net["type"]=="VF":
- port_dict={
- "network_id": net["net_id"],
- "name": net.get("name"),
- "admin_state_up": True
- }
- if net["type"]=="virtual":
- if "vpci" in net:
- metadata_vpci[ net["net_id"] ] = [[ net["vpci"], "" ]]
- else: # for VF
- if "vpci" in net:
- if "VF" not in metadata_vpci:
- metadata_vpci["VF"]=[]
- metadata_vpci["VF"].append([ net["vpci"], "" ])
- port_dict["binding:vnic_type"]="direct"
- if not port_dict["name"]:
- port_dict["name"]=name
- if net.get("mac_address"):
- port_dict["mac_address"]=net["mac_address"]
- if net.get("port_security") == False:
- port_dict["port_security_enabled"]=net["port_security"]
- new_port = self.neutron.create_port({"port": port_dict })
- net["mac_adress"] = new_port["port"]["mac_address"]
- net["vim_id"] = new_port["port"]["id"]
- net["ip"] = new_port["port"].get("fixed_ips", [{}])[0].get("ip_address")
- net_list_vim.append({"port-id": new_port["port"]["id"]})
- else: # for PF
- self.logger.warn("new_vminstance: Warning, can not connect a passthrough interface ")
- #TODO insert this when openstack consider passthrough ports as openstack neutron ports
- if net.get('floating_ip', False):
- net['exit_on_floating_ip_error'] = True
- external_network.append(net)
- elif net['use'] == 'mgmt' and self.config.get('use_floating_ip'):
- net['exit_on_floating_ip_error'] = False
- external_network.append(net)
-
- if metadata_vpci:
- metadata = {"pci_assignement": json.dumps(metadata_vpci)}
- if len(metadata["pci_assignement"]) >255:
- #limit the metadata size
- #metadata["pci_assignement"] = metadata["pci_assignement"][0:255]
- self.logger.warn("Metadata deleted since it exceeds the expected length (255) ")
- metadata = {}
-
- self.logger.debug("name '%s' image_id '%s'flavor_id '%s' net_list_vim '%s' description '%s' metadata %s",
- name, image_id, flavor_id, str(net_list_vim), description, str(metadata))
-
- security_groups = self.config.get('security_groups')
- if type(security_groups) is str:
- security_groups = ( security_groups, )
- #cloud config
- userdata=None
- config_drive = None
- if isinstance(cloud_config, dict):
- if cloud_config.get("user-data"):
- userdata=cloud_config["user-data"]
- if cloud_config.get("boot-data-drive") != None:
- config_drive = cloud_config["boot-data-drive"]
- if cloud_config.get("config-files") or cloud_config.get("users") or cloud_config.get("key-pairs"):
- if userdata:
- raise vimconn.vimconnConflictException("Cloud-config cannot contain both 'userdata' and 'config-files'/'users'/'key-pairs'")
- userdata_dict={}
- #default user
- if cloud_config.get("key-pairs"):
- userdata_dict["ssh-authorized-keys"] = cloud_config["key-pairs"]
- userdata_dict["users"] = [{"default": None, "ssh-authorized-keys": cloud_config["key-pairs"] }]
- if cloud_config.get("users"):
- if "users" not in userdata_dict:
- userdata_dict["users"] = [ "default" ]
- for user in cloud_config["users"]:
- user_info = {
- "name" : user["name"],
- "sudo": "ALL = (ALL)NOPASSWD:ALL"
- }
- if "user-info" in user:
- user_info["gecos"] = user["user-info"]
- if user.get("key-pairs"):
- user_info["ssh-authorized-keys"] = user["key-pairs"]
- userdata_dict["users"].append(user_info)
-
- if cloud_config.get("config-files"):
- userdata_dict["write_files"] = []
- for file in cloud_config["config-files"]:
- file_info = {
- "path" : file["dest"],
- "content": file["content"]
- }
- if file.get("encoding"):
- file_info["encoding"] = file["encoding"]
- if file.get("permissions"):
- file_info["permissions"] = file["permissions"]
- if file.get("owner"):
- file_info["owner"] = file["owner"]
- userdata_dict["write_files"].append(file_info)
- userdata = "#cloud-config\n"
- userdata += yaml.safe_dump(userdata_dict, indent=4, default_flow_style=False)
- self.logger.debug("userdata: %s", userdata)
- elif isinstance(cloud_config, str):
- userdata = cloud_config
-
- #Create additional volumes in case these are present in disk_list
- block_device_mapping = None
- base_disk_index = ord('b')
- if disk_list != None:
- block_device_mapping = dict()
- for disk in disk_list:
- if 'image_id' in disk:
- volume = self.cinder.volumes.create(size = disk['size'],name = name + '_vd' +
- chr(base_disk_index), imageRef = disk['image_id'])
- else:
- volume = self.cinder.volumes.create(size=disk['size'], name=name + '_vd' +
- chr(base_disk_index))
- block_device_mapping['_vd' + chr(base_disk_index)] = volume.id
- base_disk_index += 1
-
- #wait until volumes are with status available
- keep_waiting = True
- elapsed_time = 0
- while keep_waiting and elapsed_time < volume_timeout:
- keep_waiting = False
- for volume_id in block_device_mapping.itervalues():
- if self.cinder.volumes.get(volume_id).status != 'available':
- keep_waiting = True
- if keep_waiting:
- time.sleep(1)
- elapsed_time += 1
-
- #if we exceeded the timeout rollback
- if elapsed_time >= volume_timeout:
- #delete the volumes we just created
- for volume_id in block_device_mapping.itervalues():
- self.cinder.volumes.delete(volume_id)
-
- #delete ports we just created
- for net_item in net_list_vim:
- if 'port-id' in net_item:
- self.neutron.delete_port(net_item['port-id'])
-
- raise vimconn.vimconnException('Timeout creating volumes for instance ' + name,
- http_code=vimconn.HTTP_Request_Timeout)
-
- server = self.nova.servers.create(name, image_id, flavor_id, nics=net_list_vim, meta=metadata,
- security_groups=security_groups,
- availability_zone=self.config.get('availability_zone'),
- key_name=self.config.get('keypair'),
- userdata=userdata,
- config_drive = config_drive,
- block_device_mapping = block_device_mapping
- ) # , description=description)
- #print "DONE :-)", server
- pool_id = None
- floating_ips = self.neutron.list_floatingips().get("floatingips", ())
- for floating_network in external_network:
- try:
- # wait until vm is active
- elapsed_time = 0
- while elapsed_time < server_timeout:
- status = self.nova.servers.get(server.id).status
- if status == 'ACTIVE':
- break
- time.sleep(1)
- elapsed_time += 1
-
- #if we exceeded the timeout rollback
- if elapsed_time >= server_timeout:
- raise vimconn.vimconnException('Timeout creating instance ' + name,
- http_code=vimconn.HTTP_Request_Timeout)
-
- assigned = False
- while(assigned == False):
- if floating_ips:
- ip = floating_ips.pop(0)
- if not ip.get("port_id", False) and ip.get('tenant_id') == server.tenant_id:
- free_floating_ip = ip.get("floating_ip_address")
- try:
- fix_ip = floating_network.get('ip')
- server.add_floating_ip(free_floating_ip, fix_ip)
- assigned = True
- except Exception as e:
- raise vimconn.vimconnException(type(e).__name__ + ": Cannot create floating_ip "+ str(e), http_code=vimconn.HTTP_Conflict)
- else:
- #Find the external network
- external_nets = list()
- for net in self.neutron.list_networks()['networks']:
- if net['router:external']:
- external_nets.append(net)
-
- if len(external_nets) == 0:
- raise vimconn.vimconnException("Cannot create floating_ip automatically since no external "
- "network is present",
- http_code=vimconn.HTTP_Conflict)
- if len(external_nets) > 1:
- raise vimconn.vimconnException("Cannot create floating_ip automatically since multiple "
- "external networks are present",
- http_code=vimconn.HTTP_Conflict)
-
- pool_id = external_nets[0].get('id')
- param = {'floatingip': {'floating_network_id': pool_id, 'tenant_id': server.tenant_id}}
- try:
- #self.logger.debug("Creating floating IP")
- new_floating_ip = self.neutron.create_floatingip(param)
- free_floating_ip = new_floating_ip['floatingip']['floating_ip_address']
- fix_ip = floating_network.get('ip')
- server.add_floating_ip(free_floating_ip, fix_ip)
- assigned=True
- except Exception as e:
- raise vimconn.vimconnException(type(e).__name__ + ": Cannot assign floating_ip "+ str(e), http_code=vimconn.HTTP_Conflict)
- except Exception as e:
- if not floating_network['exit_on_floating_ip_error']:
- self.logger.warn("Cannot create floating_ip. %s", str(e))
- continue
- self.delete_vminstance(server.id)
- raise
-
- return server.id
-# except nvExceptions.NotFound as e:
-# error_value=-vimconn.HTTP_Not_Found
-# error_text= "vm instance %s not found" % vm_id
- except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
- # delete the volumes we just created
- if block_device_mapping != None:
- for volume_id in block_device_mapping.itervalues():
- self.cinder.volumes.delete(volume_id)
-
- # delete ports we just created
- for net_item in net_list_vim:
- if 'port-id' in net_item:
- self.neutron.delete_port(net_item['port-id'])
- self._format_exception(e)
- except TypeError as e:
- raise vimconn.vimconnException(type(e).__name__ + ": "+ str(e), http_code=vimconn.HTTP_Bad_Request)
-
- def get_vminstance(self,vm_id):
- '''Returns the VM instance information from VIM'''
- #self.logger.debug("Getting VM from VIM")
- try:
- self._reload_connection()
- server = self.nova.servers.find(id=vm_id)
- #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
- return server.to_dict()
- except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound, ConnectionError) as e:
- self._format_exception(e)
-
- def get_vminstance_console(self,vm_id, console_type="vnc"):
- '''
- Get a console for the virtual machine
- Params:
- vm_id: uuid of the VM
- console_type, can be:
- "novnc" (by default), "xvpvnc" for VNC types,
- "rdp-html5" for RDP types, "spice-html5" for SPICE types
- Returns dict with the console parameters:
- protocol: ssh, ftp, http, https, ...
- server: usually ip address
- port: the http, ssh, ... port
- suffix: extra text, e.g. the http path and query string
- '''
- self.logger.debug("Getting VM CONSOLE from VIM")
- try:
- self._reload_connection()
- server = self.nova.servers.find(id=vm_id)
- if console_type == None or console_type == "novnc":
- console_dict = server.get_vnc_console("novnc")
- elif console_type == "xvpvnc":
- console_dict = server.get_vnc_console(console_type)
- elif console_type == "rdp-html5":
- console_dict = server.get_rdp_console(console_type)
- elif console_type == "spice-html5":
- console_dict = server.get_spice_console(console_type)
- else:
- raise vimconn.vimconnException("console type '{}' not allowed".format(console_type), http_code=vimconn.HTTP_Bad_Request)
-
- console_dict1 = console_dict.get("console")
- if console_dict1:
- console_url = console_dict1.get("url")
- if console_url:
- #parse console_url
- protocol_index = console_url.find("//")
- suffix_index = console_url[protocol_index+2:].find("/") + protocol_index+2
- port_index = console_url[protocol_index+2:suffix_index].find(":") + protocol_index+2
- if protocol_index < 0 or port_index<0 or suffix_index<0:
- return -vimconn.HTTP_Internal_Server_Error, "Unexpected response from VIM"
- console_dict={"protocol": console_url[0:protocol_index],
- "server": console_url[protocol_index+2:port_index],
- "port": console_url[port_index:suffix_index],
- "suffix": console_url[suffix_index+1:]
- }
- protocol_index += 2
- return console_dict
- raise vimconn.vimconnUnexpectedResponse("Unexpected response from VIM")
-
- except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.BadRequest, ConnectionError) as e:
- self._format_exception(e)
-
- def delete_vminstance(self, vm_id):
- '''Removes a VM instance from VIM. Returns the old identifier
- '''
- #print "osconnector: Getting VM from VIM"
- try:
- self._reload_connection()
- #delete VM ports attached to this networks before the virtual machine
- ports = self.neutron.list_ports(device_id=vm_id)
- for p in ports['ports']:
- try:
- self.neutron.delete_port(p["id"])
- except Exception as e:
- self.logger.error("Error deleting port: " + type(e).__name__ + ": "+ str(e))
-
- #commented because detaching the volumes makes the servers.delete not work properly ?!?
- #dettach volumes attached
- server = self.nova.servers.get(vm_id)
- volumes_attached_dict = server._info['os-extended-volumes:volumes_attached']
- #for volume in volumes_attached_dict:
- # self.cinder.volumes.detach(volume['id'])
-
- self.nova.servers.delete(vm_id)
-
- #delete volumes.
- #Although having detached them should have them in active status
- #we ensure in this loop
- keep_waiting = True
- elapsed_time = 0
- while keep_waiting and elapsed_time < volume_timeout:
- keep_waiting = False
- for volume in volumes_attached_dict:
- if self.cinder.volumes.get(volume['id']).status != 'available':
- keep_waiting = True
- else:
- self.cinder.volumes.delete(volume['id'])
- if keep_waiting:
- time.sleep(1)
- elapsed_time += 1
-
- return vm_id
- except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
- self._format_exception(e)
- #TODO insert exception vimconn.HTTP_Unauthorized
- #if reaching here is because an exception
-
- def refresh_vms_status(self, vm_list):
- '''Get the status of the virtual machines and their interfaces/ports
- Params: the list of VM identifiers
- Returns a dictionary with:
- vm_id: #VIM id of this Virtual Machine
- status: #Mandatory. Text with one of:
- # DELETED (not found at vim)
- # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
- # OTHER (Vim reported other status not understood)
- # ERROR (VIM indicates an ERROR status)
- # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
- # CREATING (on building process), ERROR
- # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
- #
- error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
- vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
- interfaces:
- - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
- mac_address: #Text format XX:XX:XX:XX:XX:XX
- vim_net_id: #network id where this interface is connected
- vim_interface_id: #interface/port VIM id
- ip_address: #null, or text with IPv4, IPv6 address
- '''
- vm_dict={}
- self.logger.debug("refresh_vms status: Getting tenant VM instance information from VIM")
- for vm_id in vm_list:
- vm={}
- try:
- vm_vim = self.get_vminstance(vm_id)
- if vm_vim['status'] in vmStatus2manoFormat:
- vm['status'] = vmStatus2manoFormat[ vm_vim['status'] ]
- else:
- vm['status'] = "OTHER"
- vm['error_msg'] = "VIM status reported " + vm_vim['status']
- try:
- vm['vim_info'] = yaml.safe_dump(vm_vim, default_flow_style=True, width=256)
- except yaml.representer.RepresenterError:
- vm['vim_info'] = str(vm_vim)
- vm["interfaces"] = []
- if vm_vim.get('fault'):
- vm['error_msg'] = str(vm_vim['fault'])
- #get interfaces
- try:
- self._reload_connection()
- port_dict=self.neutron.list_ports(device_id=vm_id)
- for port in port_dict["ports"]:
- interface={}
- try:
- interface['vim_info'] = yaml.safe_dump(port, default_flow_style=True, width=256)
- except yaml.representer.RepresenterError:
- interface['vim_info'] = str(port)
- interface["mac_address"] = port.get("mac_address")
- interface["vim_net_id"] = port["network_id"]
- interface["vim_interface_id"] = port["id"]
- ips=[]
- #look for floating ip address
- floating_ip_dict = self.neutron.list_floatingips(port_id=port["id"])
- if floating_ip_dict.get("floatingips"):
- ips.append(floating_ip_dict["floatingips"][0].get("floating_ip_address") )
-
- for subnet in port["fixed_ips"]:
- ips.append(subnet["ip_address"])
- interface["ip_address"] = ";".join(ips)
- vm["interfaces"].append(interface)
- except Exception as e:
- self.logger.error("Error getting vm interface information " + type(e).__name__ + ": "+ str(e))
- except vimconn.vimconnNotFoundException as e:
- self.logger.error("Exception getting vm status: %s", str(e))
- vm['status'] = "DELETED"
- vm['error_msg'] = str(e)
- except vimconn.vimconnException as e:
- self.logger.error("Exception getting vm status: %s", str(e))
- vm['status'] = "VIM_ERROR"
- vm['error_msg'] = str(e)
- vm_dict[vm_id] = vm
- return vm_dict
-
- def action_vminstance(self, vm_id, action_dict):
- '''Send and action over a VM instance from VIM
- Returns the vm_id if the action was successfully sent to the VIM'''
- self.logger.debug("Action over VM '%s': %s", vm_id, str(action_dict))
- try:
- self._reload_connection()
- server = self.nova.servers.find(id=vm_id)
- if "start" in action_dict:
- if action_dict["start"]=="rebuild":
- server.rebuild()
- else:
- if server.status=="PAUSED":
- server.unpause()
- elif server.status=="SUSPENDED":
- server.resume()
- elif server.status=="SHUTOFF":
- server.start()
- elif "pause" in action_dict:
- server.pause()
- elif "resume" in action_dict:
- server.resume()
- elif "shutoff" in action_dict or "shutdown" in action_dict:
- server.stop()
- elif "forceOff" in action_dict:
- server.stop() #TODO
- elif "terminate" in action_dict:
- server.delete()
- elif "createImage" in action_dict:
- server.create_image()
- #"path":path_schema,
- #"description":description_schema,
- #"name":name_schema,
- #"metadata":metadata_schema,
- #"imageRef": id_schema,
- #"disk": {"oneOf":[{"type": "null"}, {"type":"string"}] },
- elif "rebuild" in action_dict:
- server.rebuild(server.image['id'])
- elif "reboot" in action_dict:
- server.reboot() #reboot_type='SOFT'
- elif "console" in action_dict:
- console_type = action_dict["console"]
- if console_type == None or console_type == "novnc":
- console_dict = server.get_vnc_console("novnc")
- elif console_type == "xvpvnc":
- console_dict = server.get_vnc_console(console_type)
- elif console_type == "rdp-html5":
- console_dict = server.get_rdp_console(console_type)
- elif console_type == "spice-html5":
- console_dict = server.get_spice_console(console_type)
- else:
- raise vimconn.vimconnException("console type '{}' not allowed".format(console_type),
- http_code=vimconn.HTTP_Bad_Request)
- try:
- console_url = console_dict["console"]["url"]
- #parse console_url
- protocol_index = console_url.find("//")
- suffix_index = console_url[protocol_index+2:].find("/") + protocol_index+2
- port_index = console_url[protocol_index+2:suffix_index].find(":") + protocol_index+2
- if protocol_index < 0 or port_index<0 or suffix_index<0:
- raise vimconn.vimconnException("Unexpected response from VIM " + str(console_dict))
- console_dict2={"protocol": console_url[0:protocol_index],
- "server": console_url[protocol_index+2 : port_index],
- "port": int(console_url[port_index+1 : suffix_index]),
- "suffix": console_url[suffix_index+1:]
- }
- return console_dict2
- except Exception as e:
- raise vimconn.vimconnException("Unexpected response from VIM " + str(console_dict))
-
- return vm_id
- except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound, ConnectionError) as e:
- self._format_exception(e)
- #TODO insert exception vimconn.HTTP_Unauthorized
-
-#NOT USED FUNCTIONS
-
- def new_external_port(self, port_data):
- #TODO openstack if needed
- '''Adds a external port to VIM'''
- '''Returns the port identifier'''
- return -vimconn.HTTP_Internal_Server_Error, "osconnector.new_external_port() not implemented"
-
- def connect_port_network(self, port_id, network_id, admin=False):
- #TODO openstack if needed
- '''Connects a external port to a network'''
- '''Returns status code of the VIM response'''
- return -vimconn.HTTP_Internal_Server_Error, "osconnector.connect_port_network() not implemented"
-
- def new_user(self, user_name, user_passwd, tenant_id=None):
- '''Adds a new user to openstack VIM'''
- '''Returns the user identifier'''
- self.logger.debug("osconnector: Adding a new user to VIM")
- try:
- self._reload_connection()
- user=self.keystone.users.create(user_name, user_passwd, tenant_id=tenant_id)
- #self.keystone.tenants.add_user(self.k_creds["username"], #role)
- return user.id
- except ksExceptions.ConnectionError as e:
- error_value=-vimconn.HTTP_Bad_Request
- error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
- except ksExceptions.ClientException as e: #TODO remove
- error_value=-vimconn.HTTP_Bad_Request
- error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
- #TODO insert exception vimconn.HTTP_Unauthorized
- #if reaching here is because an exception
- if self.debug:
- self.logger.debug("new_user " + error_text)
- return error_value, error_text
-
- def delete_user(self, user_id):
- '''Delete a user from openstack VIM'''
- '''Returns the user identifier'''
- if self.debug:
- print "osconnector: Deleting a user from VIM"
- try:
- self._reload_connection()
- self.keystone.users.delete(user_id)
- return 1, user_id
- except ksExceptions.ConnectionError as e:
- error_value=-vimconn.HTTP_Bad_Request
- error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
- except ksExceptions.NotFound as e:
- error_value=-vimconn.HTTP_Not_Found
- error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
- except ksExceptions.ClientException as e: #TODO remove
- error_value=-vimconn.HTTP_Bad_Request
- error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
- #TODO insert exception vimconn.HTTP_Unauthorized
- #if reaching here is because an exception
- if self.debug:
- print "delete_tenant " + error_text
- return error_value, error_text
-
- def get_hosts_info(self):
- '''Get the information of deployed hosts
- Returns the hosts content'''
- if self.debug:
- print "osconnector: Getting Host info from VIM"
- try:
- h_list=[]
- self._reload_connection()
- hypervisors = self.nova.hypervisors.list()
- for hype in hypervisors:
- h_list.append( hype.to_dict() )
- return 1, {"hosts":h_list}
- except nvExceptions.NotFound as e:
- error_value=-vimconn.HTTP_Not_Found
- error_text= (str(e) if len(e.args)==0 else str(e.args[0]))
- except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
- error_value=-vimconn.HTTP_Bad_Request
- error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
- #TODO insert exception vimconn.HTTP_Unauthorized
- #if reaching here is because an exception
- if self.debug:
- print "get_hosts_info " + error_text
- return error_value, error_text
-
- def get_hosts(self, vim_tenant):
- '''Get the hosts and deployed instances
- Returns the hosts content'''
- r, hype_dict = self.get_hosts_info()
- if r<0:
- return r, hype_dict
- hypervisors = hype_dict["hosts"]
- try:
- servers = self.nova.servers.list()
- for hype in hypervisors:
- for server in servers:
- if server.to_dict()['OS-EXT-SRV-ATTR:hypervisor_hostname']==hype['hypervisor_hostname']:
- if 'vm' in hype:
- hype['vm'].append(server.id)
- else:
- hype['vm'] = [server.id]
- return 1, hype_dict
- except nvExceptions.NotFound as e:
- error_value=-vimconn.HTTP_Not_Found
- error_text= (str(e) if len(e.args)==0 else str(e.args[0]))
- except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
- error_value=-vimconn.HTTP_Bad_Request
- error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
- #TODO insert exception vimconn.HTTP_Unauthorized
- #if reaching here is because an exception
- if self.debug:
- print "get_hosts " + error_text
- return error_value, error_text
-
-
+++ /dev/null
-# -*- 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
-##
-
-'''
-vimconnector implements all the methods to interact with openvim using the openvim API.
-'''
-__author__="Alfonso Tierno, Gerardo Garcia"
-__date__ ="$26-aug-2014 11:09:29$"
-
-import vimconn
-import requests
-import json
-import yaml
-import logging
-from openmano_schemas import id_schema, name_schema, nameshort_schema, description_schema, \
- vlan1000_schema, integer0_schema
-from jsonschema import validate as js_v, exceptions as js_e
-from urllib import quote
-
-'''contain the openvim virtual machine status to openmano status'''
-vmStatus2manoFormat={'ACTIVE':'ACTIVE',
- 'PAUSED':'PAUSED',
- 'SUSPENDED': 'SUSPENDED',
- 'INACTIVE':'INACTIVE',
- 'CREATING':'BUILD',
- 'ERROR':'ERROR','DELETED':'DELETED'
- }
-netStatus2manoFormat={'ACTIVE':'ACTIVE','INACTIVE':'INACTIVE','BUILD':'BUILD','ERROR':'ERROR','DELETED':'DELETED', 'DOWN':'DOWN'
- }
-
-
-host_schema = {
- "type":"object",
- "properties":{
- "id": id_schema,
- "name": name_schema,
- },
- "required": ["id"]
-}
-image_schema = {
- "type":"object",
- "properties":{
- "id": id_schema,
- "name": name_schema,
- },
- "required": ["id","name"]
-}
-server_schema = {
- "type":"object",
- "properties":{
- "id":id_schema,
- "name": name_schema,
- },
- "required": ["id","name"]
-}
-new_host_response_schema = {
- "title":"host response information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "host": host_schema
- },
- "required": ["host"],
- "additionalProperties": False
-}
-
-get_images_response_schema = {
- "title":"openvim images response information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "images":{
- "type":"array",
- "items": image_schema,
- }
- },
- "required": ["images"],
- "additionalProperties": False
-}
-
-get_hosts_response_schema = {
- "title":"openvim hosts response information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "hosts":{
- "type":"array",
- "items": host_schema,
- }
- },
- "required": ["hosts"],
- "additionalProperties": False
-}
-
-get_host_detail_response_schema = new_host_response_schema # TODO: Content is not parsed yet
-
-get_server_response_schema = {
- "title":"openvim server response information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "servers":{
- "type":"array",
- "items": server_schema,
- }
- },
- "required": ["servers"],
- "additionalProperties": False
-}
-
-new_tenant_response_schema = {
- "title":"tenant response information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "tenant":{
- "type":"object",
- "properties":{
- "id": id_schema,
- "name": nameshort_schema,
- "description":description_schema,
- "enabled":{"type" : "boolean"}
- },
- "required": ["id"]
- }
- },
- "required": ["tenant"],
- "additionalProperties": False
-}
-
-new_network_response_schema = {
- "title":"network response information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "network":{
- "type":"object",
- "properties":{
- "id":id_schema,
- "name":name_schema,
- "type":{"type":"string", "enum":["bridge_man","bridge_data","data", "ptp"]},
- "shared":{"type":"boolean"},
- "tenant_id":id_schema,
- "admin_state_up":{"type":"boolean"},
- "vlan":vlan1000_schema
- },
- "required": ["id"]
- }
- },
- "required": ["network"],
- "additionalProperties": False
-}
-
-
-# get_network_response_schema = {
-# "title":"get network response information schema",
-# "$schema": "http://json-schema.org/draft-04/schema#",
-# "type":"object",
-# "properties":{
-# "network":{
-# "type":"object",
-# "properties":{
-# "id":id_schema,
-# "name":name_schema,
-# "type":{"type":"string", "enum":["bridge_man","bridge_data","data", "ptp"]},
-# "shared":{"type":"boolean"},
-# "tenant_id":id_schema,
-# "admin_state_up":{"type":"boolean"},
-# "vlan":vlan1000_schema
-# },
-# "required": ["id"]
-# }
-# },
-# "required": ["network"],
-# "additionalProperties": False
-# }
-
-
-new_port_response_schema = {
- "title":"port response information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "port":{
- "type":"object",
- "properties":{
- "id":id_schema,
- },
- "required": ["id"]
- }
- },
- "required": ["port"],
- "additionalProperties": False
-}
-
-get_flavor_response_schema = {
- "title":"openvim flavors response information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "flavor":{
- "type":"object",
- "properties":{
- "id": id_schema,
- "name": name_schema,
- "extended": {"type":"object"},
- },
- "required": ["id", "name"],
- }
- },
- "required": ["flavor"],
- "additionalProperties": False
-}
-
-new_flavor_response_schema = {
- "title":"flavor response information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "flavor":{
- "type":"object",
- "properties":{
- "id":id_schema,
- },
- "required": ["id"]
- }
- },
- "required": ["flavor"],
- "additionalProperties": False
-}
-
-get_image_response_schema = {
- "title":"openvim images response information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "image":{
- "type":"object",
- "properties":{
- "id": id_schema,
- "name": name_schema,
- },
- "required": ["id", "name"],
- }
- },
- "required": ["flavor"],
- "additionalProperties": False
-}
-new_image_response_schema = {
- "title":"image response information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "image":{
- "type":"object",
- "properties":{
- "id":id_schema,
- },
- "required": ["id"]
- }
- },
- "required": ["image"],
- "additionalProperties": False
-}
-
-new_vminstance_response_schema = {
- "title":"server response information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "server":{
- "type":"object",
- "properties":{
- "id":id_schema,
- },
- "required": ["id"]
- }
- },
- "required": ["server"],
- "additionalProperties": False
-}
-
-get_processor_rankings_response_schema = {
- "title":"processor rankings information schema",
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type":"object",
- "properties":{
- "rankings":{
- "type":"array",
- "items":{
- "type":"object",
- "properties":{
- "model": description_schema,
- "value": integer0_schema
- },
- "additionalProperties": False,
- "required": ["model","value"]
- }
- },
- "additionalProperties": False,
- "required": ["rankings"]
- }
-}
-
-class vimconnector(vimconn.vimconnector):
- def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None,
- log_level="DEBUG", config={}, persistent_info={}):
- vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level, config)
- self.tenant = None
- self.headers_req = {'content-type': 'application/json'}
- self.logger = logging.getLogger('openmano.vim.openvim')
- self.persistent_info = persistent_info
- if tenant_id:
- self.tenant = tenant_id
-
- def __setitem__(self,index, value):
- '''Set individuals parameters
- Throw TypeError, KeyError
- '''
- if index=='tenant_id':
- self.tenant = value
- elif index=='tenant_name':
- self.tenant = None
- vimconn.vimconnector.__setitem__(self,index, value)
-
- def _get_my_tenant(self):
- '''Obtain uuid of my tenant from name
- '''
- if self.tenant:
- return self.tenant
-
- url = self.url+'/tenants?name='+ quote(self.tenant_name)
- self.logger.info("Getting VIM tenant_id GET %s", url)
- vim_response = requests.get(url, headers = self.headers_req)
- self._check_http_request_response(vim_response)
- try:
- tenant_list = vim_response.json()["tenants"]
- if len(tenant_list) == 0:
- raise vimconn.vimconnNotFoundException("No tenant found for name '%s'" % str(self.tenant_name))
- elif len(tenant_list) > 1:
- raise vimconn.vimconnConflictException ("More that one tenant found for name '%s'" % str(self.tenant_name))
- self.tenant = tenant_list[0]["id"]
- return self.tenant
- except Exception as e:
- raise vimconn.vimconnUnexpectedResponse("Get VIM tenant {} '{}'".format(type(e).__name__, str(e)))
-
- def _format_jsonerror(self,http_response):
- #DEPRECATED, to delete in the future
- try:
- data = http_response.json()
- return data["error"]["description"]
- except:
- return http_response.text
-
- def _format_in(self, http_response, schema):
- #DEPRECATED, to delete in the future
- try:
- client_data = http_response.json()
- js_v(client_data, schema)
- #print "Input data: ", str(client_data)
- return True, client_data
- except js_e.ValidationError as exc:
- print "validate_in error, jsonschema exception ", exc.message, "at", exc.path
- return False, ("validate_in error, jsonschema exception ", exc.message, "at", exc.path)
-
- def _remove_extra_items(self, data, schema):
- deleted=[]
- if type(data) is tuple or type(data) is list:
- for d in data:
- a= self._remove_extra_items(d, schema['items'])
- if a is not None: deleted.append(a)
- elif type(data) is dict:
- for k in data.keys():
- if 'properties' not in schema or k not in schema['properties'].keys():
- del data[k]
- deleted.append(k)
- else:
- a = self._remove_extra_items(data[k], schema['properties'][k])
- if a is not None: deleted.append({k:a})
- if len(deleted) == 0: return None
- elif len(deleted) == 1: return deleted[0]
- else: return deleted
-
- def _format_request_exception(self, request_exception):
- '''Transform a request exception into a vimconn exception'''
- if isinstance(request_exception, js_e.ValidationError):
- raise vimconn.vimconnUnexpectedResponse("jsonschema exception '{}' at '{}'".format(request_exception.message, request_exception.path))
- elif isinstance(request_exception, requests.exceptions.HTTPError):
- raise vimconn.vimconnUnexpectedResponse(type(request_exception).__name__ + ": " + str(request_exception))
- else:
- raise vimconn.vimconnConnectionException(type(request_exception).__name__ + ": " + str(request_exception))
-
- def _check_http_request_response(self, request_response):
- '''Raise a vimconn exception if the response is not Ok'''
- if request_response.status_code >= 200 and request_response.status_code < 300:
- return
- if request_response.status_code == vimconn.HTTP_Unauthorized:
- raise vimconn.vimconnAuthException(request_response.text)
- elif request_response.status_code == vimconn.HTTP_Not_Found:
- raise vimconn.vimconnNotFoundException(request_response.text)
- elif request_response.status_code == vimconn.HTTP_Conflict:
- raise vimconn.vimconnConflictException(request_response.text)
- else:
- raise vimconn.vimconnUnexpectedResponse("VIM HTTP_response {}, {}".format(request_response.status_code, str(request_response.text)))
-
- def new_tenant(self,tenant_name,tenant_description):
- '''Adds a new tenant to VIM with this name and description, returns the tenant identifier'''
- #print "VIMConnector: Adding a new tenant to VIM"
- payload_dict = {"tenant": {"name":tenant_name,"description": tenant_description, "enabled": True}}
- payload_req = json.dumps(payload_dict)
- try:
- url = self.url_admin+'/tenants'
- self.logger.info("Adding a new tenant %s", url)
- vim_response = requests.post(url, headers = self.headers_req, data=payload_req)
- self._check_http_request_response(vim_response)
- self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- response = vim_response.json()
- js_v(response, new_tenant_response_schema)
- #r = self._remove_extra_items(response, new_tenant_response_schema)
- #if r is not None:
- # self.logger.warn("Warning: remove extra items %s", str(r))
- tenant_id = response['tenant']['id']
- return tenant_id
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
- def delete_tenant(self,tenant_id):
- '''Delete a tenant from VIM. Returns the old tenant identifier'''
- try:
- url = self.url_admin+'/tenants/'+tenant_id
- self.logger.info("Delete a tenant DELETE %s", url)
- vim_response = requests.delete(url, headers = self.headers_req)
- self._check_http_request_response(vim_response)
- self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- return tenant_id
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
- def get_tenant_list(self, filter_dict={}):
- '''Obtain tenants of VIM
- filter_dict can contain the following keys:
- name: filter by tenant name
- id: filter by tenant uuid/id
- <other VIM specific>
- Returns the tenant list of dictionaries: [{'name':'<name>, 'id':'<id>, ...}, ...]
- '''
- filterquery=[]
- filterquery_text=''
- for k,v in filter_dict.iteritems():
- filterquery.append(str(k)+'='+str(v))
- if len(filterquery)>0:
- filterquery_text='?'+ '&'.join(filterquery)
- try:
- url = self.url+'/tenants'+filterquery_text
- self.logger.info("get_tenant_list GET %s", url)
- vim_response = requests.get(url, headers = self.headers_req)
- self._check_http_request_response(vim_response)
- self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- return vim_response.json()["tenants"]
- except requests.exceptions.RequestException as e:
- self._format_request_exception(e)
-
- def new_network(self,net_name, net_type, ip_profile=None, shared=False, vlan=None): #, **vim_specific):
- '''Adds a tenant network to VIM'''
- '''Returns the network identifier'''
- try:
- self._get_my_tenant()
- if net_type=="bridge":
- net_type="bridge_data"
- payload_req = {"name": net_name, "type": net_type, "tenant_id": self.tenant, "shared": shared}
- if vlan:
- payload_req["provider:vlan"] = vlan
- # payload_req.update(vim_specific)
- url = self.url+'/networks'
- self.logger.info("Adding a new network POST: %s DATA: %s", url, str(payload_req))
- vim_response = requests.post(url, headers = self.headers_req, data=json.dumps({"network": payload_req}) )
- self._check_http_request_response(vim_response)
- self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- response = vim_response.json()
- js_v(response, new_network_response_schema)
- #r = self._remove_extra_items(response, new_network_response_schema)
- #if r is not None:
- # self.logger.warn("Warning: remove extra items %s", str(r))
- network_id = response['network']['id']
- return network_id
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
- def get_network_list(self, filter_dict={}):
- '''Obtain tenant networks of VIM
- Filter_dict can be:
- name: network name
- id: network uuid
- public: boolean
- tenant_id: tenant
- admin_state_up: boolean
- status: 'ACTIVE'
- Returns the network list of dictionaries
- '''
- try:
- if 'tenant_id' not in filter_dict:
- filter_dict["tenant_id"] = self._get_my_tenant()
- elif not filter_dict["tenant_id"]:
- del filter_dict["tenant_id"]
- filterquery=[]
- filterquery_text=''
- for k,v in filter_dict.iteritems():
- filterquery.append(str(k)+'='+str(v))
- if len(filterquery)>0:
- filterquery_text='?'+ '&'.join(filterquery)
- url = self.url+'/networks'+filterquery_text
- self.logger.info("Getting network list GET %s", url)
- vim_response = requests.get(url, headers = self.headers_req)
- self._check_http_request_response(vim_response)
- self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- response = vim_response.json()
- return response['networks']
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
- def get_network(self, net_id):
- '''Obtain network details of network id'''
- try:
- url = self.url+'/networks/'+net_id
- self.logger.info("Getting network GET %s", url)
- vim_response = requests.get(url, headers = self.headers_req)
- self._check_http_request_response(vim_response)
- self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- response = vim_response.json()
- return response['network']
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
- def delete_network(self, net_id):
- '''Deletes a tenant network from VIM'''
- '''Returns the network identifier'''
- try:
- self._get_my_tenant()
- url = self.url+'/networks/'+net_id
- self.logger.info("Deleting VIM network DELETE %s", url)
- vim_response = requests.delete(url, headers=self.headers_req)
- self._check_http_request_response(vim_response)
- #self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- return net_id
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
- def get_flavor(self, flavor_id):
- '''Obtain flavor details from the VIM'''
- try:
- self._get_my_tenant()
- url = self.url+'/'+self.tenant+'/flavors/'+flavor_id
- self.logger.info("Getting flavor GET %s", url)
- vim_response = requests.get(url, headers = self.headers_req)
- self._check_http_request_response(vim_response)
- self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- response = vim_response.json()
- js_v(response, get_flavor_response_schema)
- r = self._remove_extra_items(response, get_flavor_response_schema)
- if r is not None:
- self.logger.warn("Warning: remove extra items %s", str(r))
- return response['flavor']
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
- def new_flavor(self, flavor_data):
- '''Adds a tenant flavor to VIM'''
- '''Returns the flavor identifier'''
- try:
- new_flavor_dict = flavor_data.copy()
- new_flavor_dict["name"] = flavor_data["name"][:64]
- self._get_my_tenant()
- payload_req = json.dumps({'flavor': new_flavor_dict})
- url = self.url+'/'+self.tenant+'/flavors'
- self.logger.info("Adding a new VIM flavor POST %s", url)
- vim_response = requests.post(url, headers = self.headers_req, data=payload_req)
- self._check_http_request_response(vim_response)
- self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- response = vim_response.json()
- js_v(response, new_flavor_response_schema)
- r = self._remove_extra_items(response, new_flavor_response_schema)
- if r is not None:
- self.logger.warn("Warning: remove extra items %s", str(r))
- flavor_id = response['flavor']['id']
- return flavor_id
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
- def delete_flavor(self,flavor_id):
- '''Deletes a tenant flavor from VIM'''
- '''Returns the old flavor_id'''
- try:
- self._get_my_tenant()
- url = self.url+'/'+self.tenant+'/flavors/'+flavor_id
- self.logger.info("Deleting VIM flavor DELETE %s", url)
- vim_response = requests.delete(url, headers=self.headers_req)
- self._check_http_request_response(vim_response)
- #self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- return flavor_id
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
- def get_image(self, image_id):
- '''Obtain image details from the VIM'''
- try:
- self._get_my_tenant()
- url = self.url+'/'+self.tenant+'/images/'+image_id
- self.logger.info("Getting image GET %s", url)
- vim_response = requests.get(url, headers = self.headers_req)
- self._check_http_request_response(vim_response)
- self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- response = vim_response.json()
- js_v(response, get_image_response_schema)
- r = self._remove_extra_items(response, get_image_response_schema)
- if r is not None:
- self.logger.warn("Warning: remove extra items %s", str(r))
- return response['image']
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
- def new_image(self,image_dict):
- ''' Adds a tenant image to VIM, returns image_id'''
- try:
- self._get_my_tenant()
- new_image_dict={'name': image_dict['name'][:64]}
- if image_dict.get('description'):
- new_image_dict['description'] = image_dict['description']
- if image_dict.get('metadata'):
- new_image_dict['metadata'] = yaml.load(image_dict['metadata'])
- if image_dict.get('location'):
- new_image_dict['path'] = image_dict['location']
- payload_req = json.dumps({"image":new_image_dict})
- url=self.url + '/' + self.tenant + '/images'
- self.logger.info("Adding a new VIM image POST %s", url)
- vim_response = requests.post(url, headers = self.headers_req, data=payload_req)
- self._check_http_request_response(vim_response)
- self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- response = vim_response.json()
- js_v(response, new_image_response_schema)
- r = self._remove_extra_items(response, new_image_response_schema)
- if r is not None:
- self.logger.warn("Warning: remove extra items %s", str(r))
- image_id = response['image']['id']
- return image_id
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
- def delete_image(self, image_id):
- '''Deletes a tenant image from VIM'''
- '''Returns the deleted image_id'''
- try:
- self._get_my_tenant()
- url = self.url + '/'+ self.tenant +'/images/'+image_id
- self.logger.info("Deleting VIM image DELETE %s", url)
- vim_response = requests.delete(url, headers=self.headers_req)
- self._check_http_request_response(vim_response)
- #self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- return image_id
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
-
- def get_image_id_from_path(self, path):
- '''Get the image id from image path in the VIM database. Returns the image_id'''
- try:
- self._get_my_tenant()
- url=self.url + '/' + self.tenant + '/images?path='+quote(path)
- self.logger.info("Getting images GET %s", url)
- vim_response = requests.get(url)
- self._check_http_request_response(vim_response)
- self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- response = vim_response.json()
- js_v(response, get_images_response_schema)
- #r = self._remove_extra_items(response, get_images_response_schema)
- #if r is not None:
- # self.logger.warn("Warning: remove extra items %s", str(r))
- if len(response['images'])==0:
- raise vimconn.vimconnNotFoundException("Image not found at VIM with path '%s'", path)
- elif len(response['images'])>1:
- raise vimconn.vimconnConflictException("More than one image found at VIM with path '%s'", path)
- return response['images'][0]['id']
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
- def get_image_list(self, filter_dict={}):
- '''Obtain tenant images from VIM
- Filter_dict can be:
- name: image name
- id: image uuid
- checksum: image checksum
- location: image path
- Returns the image list of dictionaries:
- [{<the fields at Filter_dict plus some VIM specific>}, ...]
- List can be empty
- '''
- try:
- self._get_my_tenant()
- filterquery=[]
- filterquery_text=''
- for k,v in filter_dict.iteritems():
- filterquery.append(str(k)+'='+str(v))
- if len(filterquery)>0:
- filterquery_text='?'+ '&'.join(filterquery)
- url = self.url+'/'+self.tenant+'/images'+filterquery_text
- self.logger.info("Getting image list GET %s", url)
- vim_response = requests.get(url, headers = self.headers_req)
- self._check_http_request_response(vim_response)
- self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- response = vim_response.json()
- return response['images']
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
- def new_vminstancefromJSON(self, vm_data):
- '''Adds a VM instance to VIM'''
- '''Returns the instance identifier'''
- try:
- self._get_my_tenant()
- except Exception as e:
- return -vimconn.HTTP_Not_Found, str(e)
- print "VIMConnector: Adding a new VM instance from JSON to VIM"
- payload_req = vm_data
- try:
- vim_response = requests.post(self.url+'/'+self.tenant+'/servers', headers = self.headers_req, data=payload_req)
- except requests.exceptions.RequestException as e:
- print "new_vminstancefromJSON Exception: ", e.args
- return -vimconn.HTTP_Not_Found, str(e.args[0])
- 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)
- res,http_content = self._format_in(vim_response, new_image_response_schema)
- #print http_content
- if res:
- r = self._remove_extra_items(http_content, new_image_response_schema)
- if r is not None: print "Warning: remove extra items ", r
- #print http_content
- vminstance_id = http_content['server']['id']
- print "Tenant image id: ",vminstance_id
- return vim_response.status_code,vminstance_id
- else: return -vimconn.HTTP_Bad_Request,http_content
- else:
- #print vim_response.text
- jsonerror = self._format_jsonerror(vim_response)
- text = 'Error in VIM "%s": not possible to add new vm instance. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror)
- #print text
- return -vim_response.status_code,text
-
- def new_vminstance(self,name,description,start,image_id,flavor_id,net_list, cloud_config=None, disk_list=None):
- '''Adds a VM instance to VIM
- Params:
- start: indicates if VM must start or boot in pause mode. Ignored
- image_id,flavor_id: image and flavor uuid
- net_list: list of interfaces, each one is a dictionary with:
- name:
- net_id: network uuid to connect
- vpci: virtual vcpi to assign
- model: interface model, virtio, e2000, ...
- mac_address:
- use: 'data', 'bridge', 'mgmt'
- type: 'virtual', 'PF', 'VF', 'VFnotShared'
- vim_id: filled/added by this function
- #TODO ip, security groups
- Returns the instance identifier
- '''
- self.logger.debug("new_vminstance input: image='%s' flavor='%s' nics='%s'", image_id, flavor_id, str(net_list))
- try:
- self._get_my_tenant()
-# net_list = []
-# for k,v in net_dict.items():
-# print k,v
-# net_list.append('{"name":"' + k + '", "uuid":"' + v + '"}')
-# net_list_string = ', '.join(net_list)
- virtio_net_list=[]
- for net in net_list:
- if not net.get("net_id"):
- continue
- net_dict={'uuid': net["net_id"]}
- if net.get("type"): net_dict["type"] = net["type"]
- if net.get("name"): net_dict["name"] = net["name"]
- if net.get("vpci"): net_dict["vpci"] = net["vpci"]
- if net.get("model"): net_dict["model"] = net["model"]
- if net.get("mac_address"): net_dict["mac_address"] = net["mac_address"]
- virtio_net_list.append(net_dict)
- payload_dict={ "name": name[:64],
- "description": description,
- "imageRef": image_id,
- "flavorRef": flavor_id,
- "networks": virtio_net_list
- }
- if start != None:
- payload_dict["start"] = start
- payload_req = json.dumps({"server": payload_dict})
- url = self.url+'/'+self.tenant+'/servers'
- self.logger.info("Adding a new vm POST %s DATA %s", url, payload_req)
- vim_response = requests.post(url, headers = self.headers_req, data=payload_req)
- self._check_http_request_response(vim_response)
- self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- response = vim_response.json()
- js_v(response, new_vminstance_response_schema)
- #r = self._remove_extra_items(response, new_vminstance_response_schema)
- #if r is not None:
- # self.logger.warn("Warning: remove extra items %s", str(r))
- vminstance_id = response['server']['id']
-
- #connect data plane interfaces to network
- for net in net_list:
- if net["type"]=="virtual":
- if not net.get("net_id"):
- continue
- for iface in response['server']['networks']:
- if "name" in net:
- if net["name"]==iface["name"]:
- net["vim_id"] = iface['iface_id']
- break
- elif "net_id" in net:
- if net["net_id"]==iface["net_id"]:
- net["vim_id"] = iface['iface_id']
- break
- else: #dataplane
- for numa in response['server'].get('extended',{}).get('numas',() ):
- for iface in numa.get('interfaces',() ):
- if net['name'] == iface['name']:
- net['vim_id'] = iface['iface_id']
- #Code bellow is not needed, current openvim connect dataplane interfaces
- #if net.get("net_id"):
- ##connect dataplane interface
- # result, port_id = self.connect_port_network(iface['iface_id'], net["net_id"])
- # if result < 0:
- # error_text = "Error attaching port %s to network %s: %s." % (iface['iface_id'], net["net_id"], port_id)
- # print "new_vminstance: " + error_text
- # self.delete_vminstance(vminstance_id)
- # return result, error_text
- break
-
- return vminstance_id
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
- def get_vminstance(self, vm_id):
- '''Returns the VM instance information from VIM'''
- try:
- self._get_my_tenant()
- url = self.url+'/'+self.tenant+'/servers/'+vm_id
- self.logger.info("Getting vm GET %s", url)
- vim_response = requests.get(url, headers = self.headers_req)
- vim_response = requests.get(url, headers = self.headers_req)
- self._check_http_request_response(vim_response)
- self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- response = vim_response.json()
- js_v(response, new_vminstance_response_schema)
- #r = self._remove_extra_items(response, new_vminstance_response_schema)
- #if r is not None:
- # self.logger.warn("Warning: remove extra items %s", str(r))
- return response['server']
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
- def delete_vminstance(self, vm_id):
- '''Removes a VM instance from VIM, returns the deleted vm_id'''
- try:
- self._get_my_tenant()
- url = self.url+'/'+self.tenant+'/servers/'+vm_id
- self.logger.info("Deleting VIM vm DELETE %s", url)
- vim_response = requests.delete(url, headers=self.headers_req)
- self._check_http_request_response(vim_response)
- #self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- return vm_id
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
- def refresh_vms_status(self, vm_list):
- '''Refreshes the status of the virtual machines'''
- try:
- self._get_my_tenant()
- except requests.exceptions.RequestException as e:
- self._format_request_exception(e)
- vm_dict={}
- for vm_id in vm_list:
- vm={}
- #print "VIMConnector refresh_tenant_vms and nets: Getting tenant VM instance information from VIM"
- try:
- url = self.url+'/'+self.tenant+'/servers/'+ vm_id
- self.logger.info("Getting vm GET %s", url)
- vim_response = requests.get(url, headers = self.headers_req)
- self._check_http_request_response(vim_response)
- response = vim_response.json()
- js_v(response, new_vminstance_response_schema)
- if response['server']['status'] in vmStatus2manoFormat:
- vm['status'] = vmStatus2manoFormat[ response['server']['status'] ]
- else:
- vm['status'] = "OTHER"
- vm['error_msg'] = "VIM status reported " + response['server']['status']
- if response['server'].get('last_error'):
- vm['error_msg'] = response['server']['last_error']
- vm["vim_info"] = yaml.safe_dump(response['server'])
- #get interfaces info
- try:
- management_ip = False
- url2 = self.url+'/ports?device_id='+ quote(vm_id)
- self.logger.info("Getting PORTS GET %s", url2)
- vim_response2 = requests.get(url2, headers = self.headers_req)
- self._check_http_request_response(vim_response2)
- client_data = vim_response2.json()
- if isinstance(client_data.get("ports"), list):
- vm["interfaces"]=[]
- for port in client_data.get("ports"):
- interface={}
- interface['vim_info'] = yaml.safe_dump(port)
- interface["mac_address"] = port.get("mac_address")
- interface["vim_net_id"] = port.get("network_id")
- interface["vim_interface_id"] = port["id"]
- interface["ip_address"] = port.get("ip_address")
- if interface["ip_address"]:
- management_ip = True
- if interface["ip_address"] == "0.0.0.0":
- interface["ip_address"] = None
- vm["interfaces"].append(interface)
-
- except Exception as e:
- self.logger.error("refresh_vms_and_nets. Port get %s: %s", type(e).__name__, str(e))
-
- if vm['status'] == "ACTIVE" and not management_ip:
- vm['status'] = "ACTIVE:NoMgmtIP"
-
- except vimconn.vimconnNotFoundException as e:
- self.logger.error("Exception getting vm status: %s", str(e))
- vm['status'] = "DELETED"
- vm['error_msg'] = str(e)
- except (requests.exceptions.RequestException, js_e.ValidationError, vimconn.vimconnException) as e:
- self.logger.error("Exception getting vm status: %s", str(e))
- vm['status'] = "VIM_ERROR"
- vm['error_msg'] = str(e)
- vm_dict[vm_id] = vm
- return vm_dict
-
- def refresh_nets_status(self, net_list):
- '''Get the status of the networks
- Params: the list of network identifiers
- Returns a dictionary with:
- net_id: #VIM id of this network
- status: #Mandatory. Text with one of:
- # DELETED (not found at vim)
- # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
- # OTHER (Vim reported other status not understood)
- # ERROR (VIM indicates an ERROR status)
- # ACTIVE, INACTIVE, DOWN (admin down),
- # BUILD (on building process)
- #
- error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
- vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
-
- '''
- try:
- self._get_my_tenant()
- except requests.exceptions.RequestException as e:
- self._format_request_exception(e)
-
- net_dict={}
- for net_id in net_list:
- net = {}
- #print "VIMConnector refresh_tenant_vms_and_nets: Getting tenant network from VIM (tenant: " + str(self.tenant) + "): "
- try:
- net_vim = self.get_network(net_id)
- if net_vim['status'] in netStatus2manoFormat:
- net["status"] = netStatus2manoFormat[ net_vim['status'] ]
- else:
- net["status"] = "OTHER"
- net["error_msg"] = "VIM status reported " + net_vim['status']
-
- if net["status"] == "ACTIVE" and not net_vim['admin_state_up']:
- net["status"] = "DOWN"
- if net_vim.get('last_error'):
- net['error_msg'] = net_vim['last_error']
- net["vim_info"] = yaml.safe_dump(net_vim)
- except vimconn.vimconnNotFoundException as e:
- self.logger.error("Exception getting net status: %s", str(e))
- net['status'] = "DELETED"
- net['error_msg'] = str(e)
- except (requests.exceptions.RequestException, js_e.ValidationError, vimconn.vimconnException) as e:
- self.logger.error("Exception getting net status: %s", str(e))
- net['status'] = "VIM_ERROR"
- net['error_msg'] = str(e)
- net_dict[net_id] = net
- return net_dict
-
- def action_vminstance(self, vm_id, action_dict):
- '''Send and action over a VM instance from VIM'''
- '''Returns the status'''
- try:
- self._get_my_tenant()
- if "console" in action_dict:
- raise vimconn.vimconnException("getting console is not available at openvim", http_code=vimconn.HTTP_Service_Unavailable)
- url = self.url+'/'+self.tenant+'/servers/'+vm_id+"/action"
- self.logger.info("Action over VM instance POST %s", url)
- vim_response = requests.post(url, headers = self.headers_req, data=json.dumps(action_dict) )
- self._check_http_request_response(vim_response)
- return vm_id
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
-#NOT USED METHODS in current version
-
- def host_vim2gui(self, host, server_dict):
- '''Transform host dictionary from VIM format to GUI format,
- and append to the server_dict
- '''
- if type(server_dict) is not dict:
- print 'vimconnector.host_vim2gui() ERROR, param server_dict must be a dictionary'
- return
- RAD={}
- occupation={}
- for numa in host['host']['numas']:
- RAD_item={}
- occupation_item={}
- #memory
- RAD_item['memory']={'size': str(numa['memory'])+'GB', 'eligible': str(numa['hugepages'])+'GB'}
- occupation_item['memory']= str(numa['hugepages_consumed'])+'GB'
- #cpus
- RAD_item['cpus']={}
- RAD_item['cpus']['cores'] = []
- RAD_item['cpus']['eligible_cores'] = []
- occupation_item['cores']=[]
- for _ in range(0, len(numa['cores']) / 2):
- RAD_item['cpus']['cores'].append( [] )
- for core in numa['cores']:
- RAD_item['cpus']['cores'][core['core_id']].append(core['thread_id'])
- if not 'status' in core: RAD_item['cpus']['eligible_cores'].append(core['thread_id'])
- if 'instance_id' in core: occupation_item['cores'].append(core['thread_id'])
- #ports
- RAD_item['ports']={}
- occupation_item['ports']={}
- for iface in numa['interfaces']:
- RAD_item['ports'][ iface['pci'] ] = 'speed:'+str(iface['Mbps'])+'M'
- occupation_item['ports'][ iface['pci'] ] = { 'occupied': str(100*iface['Mbps_consumed'] / iface['Mbps']) + "%" }
-
- RAD[ numa['numa_socket'] ] = RAD_item
- occupation[ numa['numa_socket'] ] = occupation_item
- server_dict[ host['host']['name'] ] = {'RAD':RAD, 'occupation':occupation}
-
- def get_hosts_info(self):
- '''Get the information of deployed hosts
- Returns the hosts content'''
- #obtain hosts list
- url=self.url+'/hosts'
- try:
- vim_response = requests.get(url)
- except requests.exceptions.RequestException as e:
- print "get_hosts_info Exception: ", e.args
- return -vimconn.HTTP_Not_Found, str(e.args[0])
- print "vim get", url, "response:", vim_response.status_code, vim_response.json()
- #print vim_response.status_code
- #print json.dumps(vim_response.json(), indent=4)
- if vim_response.status_code != 200:
- #TODO: get error
- print 'vimconnector.get_hosts_info error getting host list %d %s' %(vim_response.status_code, vim_response.json())
- return -vim_response.status_code, "Error getting host list"
-
- res,hosts = self._format_in(vim_response, get_hosts_response_schema)
-
- if res==False:
- print "vimconnector.get_hosts_info error parsing GET HOSTS vim response", hosts
- return vimconn.HTTP_Internal_Server_Error, hosts
- #obtain hosts details
- hosts_dict={}
- for host in hosts['hosts']:
- url=self.url+'/hosts/'+host['id']
- try:
- vim_response = requests.get(url)
- except requests.exceptions.RequestException as e:
- print "get_hosts_info Exception: ", e.args
- return -vimconn.HTTP_Not_Found, str(e.args[0])
- print "vim get", url, "response:", vim_response.status_code, vim_response.json()
- if vim_response.status_code != 200:
- print 'vimconnector.get_hosts_info error getting detailed host %d %s' %(vim_response.status_code, vim_response.json())
- continue
- res,host_detail = self._format_in(vim_response, get_host_detail_response_schema)
- if res==False:
- print "vimconnector.get_hosts_info error parsing GET HOSTS/%s vim response" % host['id'], host_detail
- continue
- #print 'host id '+host['id'], json.dumps(host_detail, indent=4)
- self.host_vim2gui(host_detail, hosts_dict)
- return 200, hosts_dict
-
- def get_hosts(self, vim_tenant):
- '''Get the hosts and deployed instances
- Returns the hosts content'''
- #obtain hosts list
- url=self.url+'/hosts'
- try:
- vim_response = requests.get(url)
- except requests.exceptions.RequestException as e:
- print "get_hosts Exception: ", e.args
- return -vimconn.HTTP_Not_Found, str(e.args[0])
- print "vim get", url, "response:", vim_response.status_code, vim_response.json()
- #print vim_response.status_code
- #print json.dumps(vim_response.json(), indent=4)
- if vim_response.status_code != 200:
- #TODO: get error
- print 'vimconnector.get_hosts error getting host list %d %s' %(vim_response.status_code, vim_response.json())
- return -vim_response.status_code, "Error getting host list"
-
- res,hosts = self._format_in(vim_response, get_hosts_response_schema)
-
- if res==False:
- print "vimconnector.get_host error parsing GET HOSTS vim response", hosts
- return vimconn.HTTP_Internal_Server_Error, hosts
- #obtain instances from hosts
- for host in hosts['hosts']:
- url=self.url+'/' + vim_tenant + '/servers?hostId='+host['id']
- try:
- vim_response = requests.get(url)
- except requests.exceptions.RequestException as e:
- print "get_hosts Exception: ", e.args
- return -vimconn.HTTP_Not_Found, str(e.args[0])
- print "vim get", url, "response:", vim_response.status_code, vim_response.json()
- if vim_response.status_code != 200:
- print 'vimconnector.get_hosts error getting instances at host %d %s' %(vim_response.status_code, vim_response.json())
- continue
- res,servers = self._format_in(vim_response, get_server_response_schema)
- if res==False:
- print "vimconnector.get_host error parsing GET SERVERS/%s vim response" % host['id'], servers
- continue
- #print 'host id '+host['id'], json.dumps(host_detail, indent=4)
- host['instances'] = servers['servers']
- return 200, hosts['hosts']
-
- def get_processor_rankings(self):
- '''Get the processor rankings in the VIM database'''
- url=self.url+'/processor_ranking'
- try:
- vim_response = requests.get(url)
- except requests.exceptions.RequestException as e:
- print "get_processor_rankings Exception: ", e.args
- return -vimconn.HTTP_Not_Found, str(e.args[0])
- print "vim get", url, "response:", vim_response.status_code, vim_response.json()
- #print vim_response.status_code
- #print json.dumps(vim_response.json(), indent=4)
- if vim_response.status_code != 200:
- #TODO: get error
- print 'vimconnector.get_processor_rankings error getting processor rankings %d %s' %(vim_response.status_code, vim_response.json())
- return -vim_response.status_code, "Error getting processor rankings"
-
- res,rankings = self._format_in(vim_response, get_processor_rankings_response_schema)
- return res, rankings['rankings']
-
- def new_host(self, host_data):
- '''Adds a new host to VIM'''
- '''Returns status code of the VIM response'''
- payload_req = host_data
- try:
- url = self.url_admin+'/hosts'
- self.logger.info("Adding a new host POST %s", url)
- vim_response = requests.post(url, headers = self.headers_req, data=payload_req)
- self._check_http_request_response(vim_response)
- self.logger.debug(vim_response.text)
- #print json.dumps(vim_response.json(), indent=4)
- response = vim_response.json()
- js_v(response, new_host_response_schema)
- r = self._remove_extra_items(response, new_host_response_schema)
- if r is not None:
- self.logger.warn("Warning: remove extra items %s", str(r))
- host_id = response['host']['id']
- return host_id
- except (requests.exceptions.RequestException, js_e.ValidationError) as e:
- self._format_request_exception(e)
-
- def new_external_port(self, port_data):
- '''Adds a external port to VIM'''
- '''Returns the port identifier'''
- #TODO change to logging exception code policies
- print "VIMConnector: Adding a new external port"
- payload_req = port_data
- try:
- vim_response = requests.post(self.url_admin+'/ports', headers = self.headers_req, data=payload_req)
- except requests.exceptions.RequestException as e:
- self.logger.error("new_external_port Exception: ", str(e))
- return -vimconn.HTTP_Not_Found, str(e.args[0])
- 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)
- res, http_content = self._format_in(vim_response, new_port_response_schema)
- #print http_content
- if res:
- r = self._remove_extra_items(http_content, new_port_response_schema)
- if r is not None: print "Warning: remove extra items ", r
- #print http_content
- port_id = http_content['port']['id']
- print "Port id: ",port_id
- return vim_response.status_code,port_id
- else: return -vimconn.HTTP_Bad_Request,http_content
- else:
- #print vim_response.text
- jsonerror = self._format_jsonerror(vim_response)
- text = 'Error in VIM "%s": not possible to add new external port. HTTP Response: %d. Error: %s' % (self.url_admin, vim_response.status_code, jsonerror)
- #print text
- return -vim_response.status_code,text
-
- def new_external_network(self,net_name,net_type):
- '''Adds a external network to VIM (shared)'''
- '''Returns the network identifier'''
- #TODO change to logging exception code policies
- print "VIMConnector: Adding external shared network to VIM (type " + net_type + "): "+ net_name
-
- payload_req = '{"network":{"name": "' + net_name + '","shared":true,"type": "' + net_type + '"}}'
- try:
- vim_response = requests.post(self.url+'/networks', headers = self.headers_req, data=payload_req)
- except requests.exceptions.RequestException as e:
- self.logger.error( "new_external_network Exception: ", e.args)
- return -vimconn.HTTP_Not_Found, str(e.args[0])
- 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)
- res,http_content = self._format_in(vim_response, new_network_response_schema)
- #print http_content
- if res:
- r = self._remove_extra_items(http_content, new_network_response_schema)
- if r is not None: print "Warning: remove extra items ", r
- #print http_content
- network_id = http_content['network']['id']
- print "Network id: ",network_id
- return vim_response.status_code,network_id
- else: return -vimconn.HTTP_Bad_Request,http_content
- else:
- #print vim_response.text
- jsonerror = self._format_jsonerror(vim_response)
- text = 'Error in VIM "%s": not possible to add new external network. HTTP Response: %d. Error: %s' % (self.url, vim_response.status_code, jsonerror)
- #print text
- return -vim_response.status_code,text
-
- def connect_port_network(self, port_id, network_id, admin=False):
- '''Connects a external port to a network'''
- '''Returns status code of the VIM response'''
- #TODO change to logging exception code policies
- print "VIMConnector: Connecting external port to network"
-
- payload_req = '{"port":{"network_id":"' + network_id + '"}}'
- if admin:
- if self.url_admin==None:
- return -vimconn.HTTP_Unauthorized, "datacenter cannot contain admin URL"
- url= self.url_admin
- else:
- url= self.url
- try:
- vim_response = requests.put(url +'/ports/'+port_id, headers = self.headers_req, data=payload_req)
- except requests.exceptions.RequestException as e:
- print "connect_port_network Exception: ", e.args
- return -vimconn.HTTP_Not_Found, str(e.args[0])
- 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)
- res,http_content = self._format_in(vim_response, new_port_response_schema)
- #print http_content
- if res:
- r = self._remove_extra_items(http_content, new_port_response_schema)
- if r is not None: print "Warning: remove extra items ", r
- #print http_content
- port_id = http_content['port']['id']
- print "Port id: ",port_id
- return vim_response.status_code,port_id
- else: return -vimconn.HTTP_Bad_Request,http_content
- else:
- print vim_response.text
- jsonerror = self._format_jsonerror(vim_response)
- text = 'Error in VIM "%s": not possible to connect external port to network. HTTP Response: %d. Error: %s' % (self.url_admin, vim_response.status_code, jsonerror)
- print text
- return -vim_response.status_code,text
-
-
+++ /dev/null
-# -*- 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
-##
-
-"""
-vimconn_vmware implementation an Abstract class in order to interact with VMware vCloud Director.
-mbayramov@vmware.com
-"""
-from progressbar import Percentage, Bar, ETA, FileTransferSpeed, ProgressBar
-
-import vimconn
-import os
-import traceback
-import itertools
-import requests
-import ssl
-import atexit
-
-from pyVmomi import vim, vmodl
-from pyVim.connect import SmartConnect, Disconnect
-
-from xml.etree import ElementTree as XmlElementTree
-from lxml import etree as lxmlElementTree
-
-import yaml
-from pyvcloud import Http
-from pyvcloud.vcloudair import VCA
-from pyvcloud.schema.vcd.v1_5.schemas.vcloud import sessionType, organizationType, \
- vAppType, organizationListType, vdcType, catalogType, queryRecordViewType, \
- networkType, vcloudType, taskType, diskType, vmsType, vdcTemplateListType, mediaType
-from xml.sax.saxutils import escape
-
-from pyvcloud.schema.vcd.v1_5.schemas.admin.vCloudEntities import TaskType
-from pyvcloud.schema.vcd.v1_5.schemas.vcloud.taskType import TaskType as GenericTask
-from pyvcloud.schema.vcd.v1_5.schemas.vcloud.vAppType import TaskType as VappTask
-from pyvcloud.schema.vcd.v1_5.schemas.admin.vCloudEntities import TasksInProgressType
-
-import logging
-import json
-import time
-import uuid
-import httplib
-import hashlib
-import socket
-import struct
-import netaddr
-
-# global variable for vcd connector type
-STANDALONE = 'standalone'
-
-# key for flavor dicts
-FLAVOR_RAM_KEY = 'ram'
-FLAVOR_VCPUS_KEY = 'vcpus'
-FLAVOR_DISK_KEY = 'disk'
-DEFAULT_IP_PROFILE = {'gateway_address':"192.168.1.1",
- 'dhcp_count':50,
- 'subnet_address':"192.168.1.0/24",
- 'dhcp_enabled':True,
- 'dhcp_start_address':"192.168.1.3",
- 'ip_version':"IPv4",
- 'dns_address':"192.168.1.2"
- }
-# global variable for wait time
-INTERVAL_TIME = 5
-MAX_WAIT_TIME = 1800
-
-VCAVERSION = '5.9'
-
-__author__ = "Mustafa Bayramov, Arpita Kate, Sachin Bhangare"
-__date__ = "$12-Jan-2017 11:09:29$"
-__version__ = '0.1'
-
-# -1: "Could not be created",
-# 0: "Unresolved",
-# 1: "Resolved",
-# 2: "Deployed",
-# 3: "Suspended",
-# 4: "Powered on",
-# 5: "Waiting for user input",
-# 6: "Unknown state",
-# 7: "Unrecognized state",
-# 8: "Powered off",
-# 9: "Inconsistent state",
-# 10: "Children do not all have the same status",
-# 11: "Upload initiated, OVF descriptor pending",
-# 12: "Upload initiated, copying contents",
-# 13: "Upload initiated , disk contents pending",
-# 14: "Upload has been quarantined",
-# 15: "Upload quarantine period has expired"
-
-# mapping vCD status to MANO
-vcdStatusCode2manoFormat = {4: 'ACTIVE',
- 7: 'PAUSED',
- 3: 'SUSPENDED',
- 8: 'INACTIVE',
- 12: 'BUILD',
- -1: 'ERROR',
- 14: 'DELETED'}
-
-#
-netStatus2manoFormat = {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
- 'ERROR': 'ERROR', 'DELETED': 'DELETED'
- }
-
-class vimconnector(vimconn.vimconnector):
- # dict used to store flavor in memory
- flavorlist = {}
-
- def __init__(self, uuid=None, name=None, tenant_id=None, tenant_name=None,
- url=None, url_admin=None, user=None, passwd=None, log_level=None, config={}, persistent_info={}):
- """
- Constructor create vmware connector to vCloud director.
-
- By default construct doesn't validate connection state. So client can create object with None arguments.
- If client specified username , password and host and VDC name. Connector initialize other missing attributes.
-
- a) It initialize organization UUID
- b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
-
- Args:
- uuid - is organization uuid.
- name - is organization name that must be presented in vCloud director.
- tenant_id - is VDC uuid it must be presented in vCloud director
- tenant_name - is VDC name.
- url - is hostname or ip address of vCloud director
- url_admin - same as above.
- user - is user that administrator for organization. Caller must make sure that
- username has right privileges.
-
- password - is password for a user.
-
- VMware connector also requires PVDC administrative privileges and separate account.
- This variables must be passed via config argument dict contains keys
-
- dict['admin_username']
- dict['admin_password']
- config - Provide NSX and vCenter information
-
- Returns:
- Nothing.
- """
-
- vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url,
- url_admin, user, passwd, log_level, config)
-
- self.logger = logging.getLogger('openmano.vim.vmware')
- self.logger.setLevel(10)
- self.persistent_info = persistent_info
-
- self.name = name
- self.id = uuid
- self.url = url
- self.url_admin = url_admin
- self.tenant_id = tenant_id
- self.tenant_name = tenant_name
- self.user = user
- self.passwd = passwd
- self.config = config
- self.admin_password = None
- self.admin_user = None
- self.org_name = ""
- self.nsx_manager = None
- self.nsx_user = None
- self.nsx_password = None
- self.vcenter_ip = None
- self.vcenter_port = None
- self.vcenter_user = None
- self.vcenter_password = None
-
- if tenant_name is not None:
- orgnameandtenant = tenant_name.split(":")
- if len(orgnameandtenant) == 2:
- self.tenant_name = orgnameandtenant[1]
- self.org_name = orgnameandtenant[0]
- else:
- self.tenant_name = tenant_name
- if "orgname" in config:
- self.org_name = config['orgname']
-
- if log_level:
- self.logger.setLevel(getattr(logging, log_level))
-
- try:
- self.admin_user = config['admin_username']
- self.admin_password = config['admin_password']
- except KeyError:
- raise vimconn.vimconnException(message="Error admin username or admin password is empty.")
-
- try:
- self.nsx_manager = config['nsx_manager']
- self.nsx_user = config['nsx_user']
- self.nsx_password = config['nsx_password']
- except KeyError:
- raise vimconn.vimconnException(message="Error: nsx manager or nsx user or nsx password is empty in Config")
-
- self.vcenter_ip = config.get("vcenter_ip", None)
- self.vcenter_port = config.get("vcenter_port", None)
- self.vcenter_user = config.get("vcenter_user", None)
- self.vcenter_password = config.get("vcenter_password", None)
-
- self.org_uuid = None
- self.vca = None
-
- if not url:
- raise vimconn.vimconnException('url param can not be NoneType')
-
- if not self.url_admin: # try to use normal url
- self.url_admin = self.url
-
- logging.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self.id, self.org_name,
- self.tenant_id, self.tenant_name))
- logging.debug("vcd url {} vcd username: {} vcd password: {}".format(self.url, self.user, self.passwd))
- logging.debug("vcd admin username {} vcd admin passowrd {}".format(self.admin_user, self.admin_password))
-
- # initialize organization
- if self.user is not None and self.passwd is not None and self.url:
- self.init_organization()
-
- def __getitem__(self, index):
- if index == 'name':
- return self.name
- if index == 'tenant_id':
- return self.tenant_id
- if index == 'tenant_name':
- return self.tenant_name
- elif index == 'id':
- return self.id
- elif index == 'org_name':
- return self.org_name
- elif index == 'org_uuid':
- return self.org_uuid
- elif index == 'user':
- return self.user
- elif index == 'passwd':
- return self.passwd
- elif index == 'url':
- return self.url
- elif index == 'url_admin':
- return self.url_admin
- elif index == "config":
- return self.config
- else:
- raise KeyError("Invalid key '%s'" % str(index))
-
- def __setitem__(self, index, value):
- if index == 'name':
- self.name = value
- if index == 'tenant_id':
- self.tenant_id = value
- if index == 'tenant_name':
- self.tenant_name = value
- elif index == 'id':
- self.id = value
- elif index == 'org_name':
- self.org_name = value
- elif index == 'org_uuid':
- self.org_uuid = value
- elif index == 'user':
- self.user = value
- elif index == 'passwd':
- self.passwd = value
- elif index == 'url':
- self.url = value
- elif index == 'url_admin':
- self.url_admin = value
- else:
- raise KeyError("Invalid key '%s'" % str(index))
-
- def connect_as_admin(self):
- """ Method connect as pvdc admin user to vCloud director.
- There are certain action that can be done only by provider vdc admin user.
- Organization creation / provider network creation etc.
-
- Returns:
- The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
- """
-
- self.logger.debug("Logging in to a vca {} as admin.".format(self.org_name))
-
- vca_admin = VCA(host=self.url,
- username=self.admin_user,
- service_type=STANDALONE,
- version=VCAVERSION,
- verify=False,
- log=False)
- result = vca_admin.login(password=self.admin_password, org='System')
- if not result:
- raise vimconn.vimconnConnectionException(
- "Can't connect to a vCloud director as: {}".format(self.admin_user))
- result = vca_admin.login(token=vca_admin.token, org='System', org_url=vca_admin.vcloud_session.org_url)
- if result is True:
- self.logger.info(
- "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self.admin_user))
-
- return vca_admin
-
- def connect(self):
- """ Method connect as normal user to vCloud director.
-
- Returns:
- The return vca object that letter can be used to connect to vCloud director as admin for VDC
- """
-
- try:
- self.logger.debug("Logging in to a vca {} as {} to datacenter {}.".format(self.org_name,
- self.user,
- self.org_name))
- vca = VCA(host=self.url,
- username=self.user,
- service_type=STANDALONE,
- version=VCAVERSION,
- verify=False,
- log=False)
-
- result = vca.login(password=self.passwd, org=self.org_name)
- if not result:
- raise vimconn.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self.user))
- result = vca.login(token=vca.token, org=self.org_name, org_url=vca.vcloud_session.org_url)
- if result is True:
- self.logger.info(
- "Successfully logged to a vcloud direct org: {} as user: {}".format(self.org_name, self.user))
-
- except:
- raise vimconn.vimconnConnectionException("Can't connect to a vCloud director org: "
- "{} as user: {}".format(self.org_name, self.user))
-
- return vca
-
- def init_organization(self):
- """ Method initialize organization UUID and VDC parameters.
-
- At bare minimum client must provide organization name that present in vCloud director and VDC.
-
- The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
- The Org - UUID will be initialized at the run time if data center present in vCloud director.
-
- Returns:
- The return vca object that letter can be used to connect to vcloud direct as admin
- """
- try:
- if self.org_uuid is None:
- org_dict = self.get_org_list()
- for org in org_dict:
- # we set org UUID at the init phase but we can do it only when we have valid credential.
- if org_dict[org] == self.org_name:
- self.org_uuid = org
- self.logger.debug("Setting organization UUID {}".format(self.org_uuid))
- break
- else:
- raise vimconn.vimconnException("Vcloud director organization {} not found".format(self.org_name))
-
- # if well good we require for org details
- org_details_dict = self.get_org(org_uuid=self.org_uuid)
-
- # we have two case if we want to initialize VDC ID or VDC name at run time
- # tenant_name provided but no tenant id
- if self.tenant_id is None and self.tenant_name is not None and 'vdcs' in org_details_dict:
- vdcs_dict = org_details_dict['vdcs']
- for vdc in vdcs_dict:
- if vdcs_dict[vdc] == self.tenant_name:
- self.tenant_id = vdc
- self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id,
- self.org_name))
- break
- else:
- raise vimconn.vimconnException("Tenant name indicated but not present in vcloud director.")
- # case two we have tenant_id but we don't have tenant name so we find and set it.
- if self.tenant_id is not None and self.tenant_name is None and 'vdcs' in org_details_dict:
- vdcs_dict = org_details_dict['vdcs']
- for vdc in vdcs_dict:
- if vdc == self.tenant_id:
- self.tenant_name = vdcs_dict[vdc]
- self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id,
- self.org_name))
- break
- else:
- raise vimconn.vimconnException("Tenant id indicated but not present in vcloud director")
- self.logger.debug("Setting organization uuid {}".format(self.org_uuid))
- except:
- self.logger.debug("Failed initialize organization UUID for org {}".format(self.org_name))
- self.logger.debug(traceback.format_exc())
- self.org_uuid = None
-
- def new_tenant(self, tenant_name=None, tenant_description=None):
- """ Method adds a new tenant to VIM with this name.
- This action requires access to create VDC action in vCloud director.
-
- Args:
- tenant_name is tenant_name to be created.
- tenant_description not used for this call
-
- Return:
- returns the tenant identifier in UUID format.
- If action is failed method will throw vimconn.vimconnException method
- """
- vdc_task = self.create_vdc(vdc_name=tenant_name)
- if vdc_task is not None:
- vdc_uuid, value = vdc_task.popitem()
- self.logger.info("Crated new vdc {} and uuid: {}".format(tenant_name, vdc_uuid))
- return vdc_uuid
- else:
- raise vimconn.vimconnException("Failed create tenant {}".format(tenant_name))
-
- def delete_tenant(self, tenant_id=None):
- """Delete a tenant from VIM"""
- 'Returns the tenant identifier'
- raise vimconn.vimconnNotImplemented("Should have implemented this")
-
- def get_tenant_list(self, filter_dict={}):
- """Obtain tenants of VIM
- filter_dict can contain the following keys:
- name: filter by tenant name
- id: filter by tenant uuid/id
- <other VIM specific>
- Returns the tenant list of dictionaries:
- [{'name':'<name>, 'id':'<id>, ...}, ...]
-
- """
- org_dict = self.get_org(self.org_uuid)
- vdcs_dict = org_dict['vdcs']
-
- vdclist = []
- try:
- for k in vdcs_dict:
- entry = {'name': vdcs_dict[k], 'id': k}
- # if caller didn't specify dictionary we return all tenants.
- if filter_dict is not None and filter_dict:
- filtered_entry = entry.copy()
- filtered_dict = set(entry.keys()) - set(filter_dict)
- for unwanted_key in filtered_dict: del entry[unwanted_key]
- if filter_dict == entry:
- vdclist.append(filtered_entry)
- else:
- vdclist.append(entry)
- except:
- self.logger.debug("Error in get_tenant_list()")
- self.logger.debug(traceback.format_exc())
- raise vimconn.vimconnException("Incorrect state. {}")
-
- return vdclist
-
- def new_network(self, net_name, net_type, ip_profile=None, shared=False):
- """Adds a tenant network to VIM
- net_name is the name
- net_type can be 'bridge','data'.'ptp'.
- ip_profile is a dict containing the IP parameters of the network
- shared is a boolean
- Returns the network identifier"""
-
- self.logger.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
- .format(net_name, net_type, ip_profile, shared))
-
- isshared = 'false'
- if shared:
- isshared = 'true'
-
- network_uuid = self.create_network(network_name=net_name, net_type=net_type,
- ip_profile=ip_profile, isshared=isshared)
- if network_uuid is not None:
- return network_uuid
- else:
- raise vimconn.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name))
-
- def get_vcd_network_list(self):
- """ Method available organization for a logged in tenant
-
- Returns:
- The return vca object that letter can be used to connect to vcloud direct as admin
- """
-
- self.logger.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self.tenant_name))
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed.")
-
- if not self.tenant_name:
- raise vimconn.vimconnConnectionException("Tenant name is empty.")
-
- vdc = vca.get_vdc(self.tenant_name)
- if vdc is None:
- raise vimconn.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self.tenant_name))
-
- vdc_uuid = vdc.get_id().split(":")[3]
- networks = vca.get_networks(vdc.get_name())
- network_list = []
- try:
- for network in networks:
- filter_dict = {}
- netid = network.get_id().split(":")
- if len(netid) != 4:
- continue
-
- filter_dict["name"] = network.get_name()
- filter_dict["id"] = netid[3]
- filter_dict["shared"] = network.get_IsShared()
- filter_dict["tenant_id"] = vdc_uuid
- if network.get_status() == 1:
- filter_dict["admin_state_up"] = True
- else:
- filter_dict["admin_state_up"] = False
- filter_dict["status"] = "ACTIVE"
- filter_dict["type"] = "bridge"
- network_list.append(filter_dict)
- self.logger.debug("get_vcd_network_list adding entry {}".format(filter_dict))
- except:
- self.logger.debug("Error in get_vcd_network_list")
- self.logger.debug(traceback.format_exc())
- pass
-
- self.logger.debug("get_vcd_network_list returning {}".format(network_list))
- return network_list
-
- def get_network_list(self, filter_dict={}):
- """Obtain tenant networks of VIM
- Filter_dict can be:
- name: network name OR/AND
- id: network uuid OR/AND
- shared: boolean OR/AND
- tenant_id: tenant OR/AND
- admin_state_up: boolean
- status: 'ACTIVE'
-
- [{key : value , key : value}]
-
- Returns the network list of dictionaries:
- [{<the fields at Filter_dict plus some VIM specific>}, ...]
- List can be empty
- """
-
- self.logger.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self.tenant_name))
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed.")
-
- if not self.tenant_name:
- raise vimconn.vimconnConnectionException("Tenant name is empty.")
-
- vdc = vca.get_vdc(self.tenant_name)
- if vdc is None:
- raise vimconn.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self.tenant_name))
-
- vdcid = vdc.get_id().split(":")[3]
- networks = vca.get_networks(vdc.get_name())
- network_list = []
-
- try:
- for network in networks:
- filter_entry = {}
- net_uuid = network.get_id().split(":")
- if len(net_uuid) != 4:
- continue
- else:
- net_uuid = net_uuid[3]
- # create dict entry
- self.logger.debug("Adding {} to a list vcd id {} network {}".format(net_uuid,
- vdcid,
- network.get_name()))
- filter_entry["name"] = network.get_name()
- filter_entry["id"] = net_uuid
- filter_entry["shared"] = network.get_IsShared()
- filter_entry["tenant_id"] = vdcid
- if network.get_status() == 1:
- filter_entry["admin_state_up"] = True
- else:
- filter_entry["admin_state_up"] = False
- filter_entry["status"] = "ACTIVE"
- filter_entry["type"] = "bridge"
- filtered_entry = filter_entry.copy()
-
- if filter_dict is not None and filter_dict:
- # we remove all the key : value we don't care and match only
- # respected field
- filtered_dict = set(filter_entry.keys()) - set(filter_dict)
- for unwanted_key in filtered_dict: del filter_entry[unwanted_key]
- if filter_dict == filter_entry:
- network_list.append(filtered_entry)
- else:
- network_list.append(filtered_entry)
- except:
- self.logger.debug("Error in get_vcd_network_list")
- self.logger.debug(traceback.format_exc())
-
- self.logger.debug("Returning {}".format(network_list))
- return network_list
-
- def get_network(self, net_id):
- """Method obtains network details of net_id VIM network
- Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
-
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed")
-
- vdc = vca.get_vdc(self.tenant_name)
- vdc_id = vdc.get_id().split(":")[3]
-
- networks = vca.get_networks(vdc.get_name())
- filter_dict = {}
-
- try:
- for network in networks:
- vdc_network_id = network.get_id().split(":")
- if len(vdc_network_id) == 4 and vdc_network_id[3] == net_id:
- filter_dict["name"] = network.get_name()
- filter_dict["id"] = vdc_network_id[3]
- filter_dict["shared"] = network.get_IsShared()
- filter_dict["tenant_id"] = vdc_id
- if network.get_status() == 1:
- filter_dict["admin_state_up"] = True
- else:
- filter_dict["admin_state_up"] = False
- filter_dict["status"] = "ACTIVE"
- filter_dict["type"] = "bridge"
- self.logger.debug("Returning {}".format(filter_dict))
- return filter_dict
- except:
- self.logger.debug("Error in get_network")
- self.logger.debug(traceback.format_exc())
-
- return filter_dict
-
- def delete_network(self, net_id):
- """
- Method Deletes a tenant network from VIM, provide the network id.
-
- Returns the network identifier or raise an exception
- """
-
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self.tenant_name))
-
- vcd_network = self.get_vcd_network(network_uuid=net_id)
- if vcd_network is not None and vcd_network:
- if self.delete_network_action(network_uuid=net_id):
- return net_id
- else:
- raise vimconn.vimconnNotFoundException("Network {} not found".format(net_id))
-
- def refresh_nets_status(self, net_list):
- """Get the status of the networks
- Params: the list of network identifiers
- Returns a dictionary with:
- net_id: #VIM id of this network
- status: #Mandatory. Text with one of:
- # DELETED (not found at vim)
- # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
- # OTHER (Vim reported other status not understood)
- # ERROR (VIM indicates an ERROR status)
- # ACTIVE, INACTIVE, DOWN (admin down),
- # BUILD (on building process)
- #
- error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
- vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
-
- """
-
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed")
-
- dict_entry = {}
- try:
- for net in net_list:
- errormsg = ''
- vcd_network = self.get_vcd_network(network_uuid=net)
- if vcd_network is not None and vcd_network:
- if vcd_network['status'] == '1':
- status = 'ACTIVE'
- else:
- status = 'DOWN'
- else:
- status = 'DELETED'
- errormsg = 'Network not found.'
-
- dict_entry[net] = {'status': status, 'error_msg': errormsg,
- 'vim_info': yaml.safe_dump(vcd_network)}
- except:
- self.logger.debug("Error in refresh_nets_status")
- self.logger.debug(traceback.format_exc())
-
- return dict_entry
-
- def get_flavor(self, flavor_id):
- """Obtain flavor details from the VIM
- Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
- """
- if flavor_id not in vimconnector.flavorlist:
- raise vimconn.vimconnNotFoundException("Flavor not found.")
- return vimconnector.flavorlist[flavor_id]
-
- def new_flavor(self, flavor_data):
- """Adds a tenant flavor to VIM
- flavor_data contains a dictionary with information, keys:
- name: flavor name
- ram: memory (cloud type) in MBytes
- vpcus: cpus (cloud type)
- extended: EPA parameters
- - numas: #items requested in same NUMA
- memory: number of 1G huge pages memory
- paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
- interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
- - name: interface name
- dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
- bandwidth: X Gbps; requested guarantee bandwidth
- vpci: requested virtual PCI address
- disk: disk size
- is_public:
- #TODO to concrete
- Returns the flavor identifier"""
-
- # generate a new uuid put to internal dict and return it.
- self.logger.debug("Creating new flavor - flavor_data: {}".format(flavor_data))
- new_flavor=flavor_data
- ram = flavor_data.get(FLAVOR_RAM_KEY, 1024)
- cpu = flavor_data.get(FLAVOR_VCPUS_KEY, 1)
- disk = flavor_data.get(FLAVOR_DISK_KEY, 1)
-
- extended_flv = flavor_data.get("extended")
- if extended_flv:
- numas=extended_flv.get("numas")
- if numas:
- for numa in numas:
- #overwrite ram and vcpus
- ram = numa['memory']*1024
- if 'paired-threads' in numa:
- cpu = numa['paired-threads']*2
- elif 'cores' in numa:
- cpu = numa['cores']
- elif 'threads' in numa:
- cpu = numa['threads']
-
- new_flavor[FLAVOR_RAM_KEY] = ram
- new_flavor[FLAVOR_VCPUS_KEY] = cpu
- new_flavor[FLAVOR_DISK_KEY] = disk
- # generate a new uuid put to internal dict and return it.
- flavor_id = uuid.uuid4()
- vimconnector.flavorlist[str(flavor_id)] = new_flavor
- self.logger.debug("Created flavor - {} : {}".format(flavor_id, new_flavor))
-
- return str(flavor_id)
-
- def delete_flavor(self, flavor_id):
- """Deletes a tenant flavor from VIM identify by its id
-
- Returns the used id or raise an exception
- """
- if flavor_id not in vimconnector.flavorlist:
- raise vimconn.vimconnNotFoundException("Flavor not found.")
-
- vimconnector.flavorlist.pop(flavor_id, None)
- return flavor_id
-
- def new_image(self, image_dict):
- """
- Adds a tenant image to VIM
- Returns:
- 200, image-id if the image is created
- <0, message if there is an error
- """
-
- return self.get_image_id_from_path(image_dict['location'])
-
- def delete_image(self, image_id):
- """
-
- :param image_id:
- :return:
- """
-
- raise vimconn.vimconnNotImplemented("Should have implemented this")
-
- def catalog_exists(self, catalog_name, catalogs):
- """
-
- :param catalog_name:
- :param catalogs:
- :return:
- """
- for catalog in catalogs:
- if catalog.name == catalog_name:
- return True
- return False
-
- def create_vimcatalog(self, vca=None, catalog_name=None):
- """ Create new catalog entry in vCloud director.
-
- Args
- vca: vCloud director.
- catalog_name catalog that client wish to create. Note no validation done for a name.
- Client must make sure that provide valid string representation.
-
- Return (bool) True if catalog created.
-
- """
- try:
- task = vca.create_catalog(catalog_name, catalog_name)
- result = vca.block_until_completed(task)
- if not result:
- return False
- catalogs = vca.get_catalogs()
- except:
- return False
- return self.catalog_exists(catalog_name, catalogs)
-
- # noinspection PyIncorrectDocstring
- def upload_ovf(self, vca=None, catalog_name=None, image_name=None, media_file_name=None,
- description='', progress=False, chunk_bytes=128 * 1024):
- """
- Uploads a OVF file to a vCloud catalog
-
- :param chunk_bytes:
- :param progress:
- :param description:
- :param image_name:
- :param vca:
- :param catalog_name: (str): The name of the catalog to upload the media.
- :param media_file_name: (str): The name of the local media file to upload.
- :return: (bool) True if the media file was successfully uploaded, false otherwise.
- """
- os.path.isfile(media_file_name)
- statinfo = os.stat(media_file_name)
-
- # find a catalog entry where we upload OVF.
- # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
- # status change.
- # if VCD can parse OVF we upload VMDK file
- for catalog in vca.get_catalogs():
- if catalog_name != catalog.name:
- continue
- link = filter(lambda link: link.get_type() == "application/vnd.vmware.vcloud.media+xml" and
- link.get_rel() == 'add', catalog.get_Link())
- assert len(link) == 1
- data = """
- <UploadVAppTemplateParams name="%s" xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"><Description>%s vApp Template</Description></UploadVAppTemplateParams>
- """ % (escape(catalog_name), escape(description))
- headers = vca.vcloud_session.get_vcloud_headers()
- headers['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
- response = Http.post(link[0].get_href(), headers=headers, data=data, verify=vca.verify, logger=self.logger)
- if response.status_code == requests.codes.created:
- catalogItem = XmlElementTree.fromstring(response.content)
- entity = [child for child in catalogItem if
- child.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
- href = entity.get('href')
- template = href
- response = Http.get(href, headers=vca.vcloud_session.get_vcloud_headers(),
- verify=vca.verify, logger=self.logger)
-
- if response.status_code == requests.codes.ok:
- media = mediaType.parseString(response.content, True)
- link = filter(lambda link: link.get_rel() == 'upload:default',
- media.get_Files().get_File()[0].get_Link())[0]
- headers = vca.vcloud_session.get_vcloud_headers()
- headers['Content-Type'] = 'Content-Type text/xml'
- response = Http.put(link.get_href(),
- data=open(media_file_name, 'rb'),
- headers=headers,
- verify=vca.verify, logger=self.logger)
- if response.status_code != requests.codes.ok:
- self.logger.debug(
- "Failed create vApp template for catalog name {} and image {}".format(catalog_name,
- media_file_name))
- return False
-
- # TODO fix this with aync block
- time.sleep(5)
-
- self.logger.debug("vApp template for catalog name {} and image {}".format(catalog_name, media_file_name))
-
- # uploading VMDK file
- # check status of OVF upload and upload remaining files.
- response = Http.get(template,
- headers=vca.vcloud_session.get_vcloud_headers(),
- verify=vca.verify,
- logger=self.logger)
-
- if response.status_code == requests.codes.ok:
- media = mediaType.parseString(response.content, True)
- number_of_files = len(media.get_Files().get_File())
- for index in xrange(0, number_of_files):
- links_list = filter(lambda link: link.get_rel() == 'upload:default',
- media.get_Files().get_File()[index].get_Link())
- for link in links_list:
- # we skip ovf since it already uploaded.
- if 'ovf' in link.get_href():
- continue
- # The OVF file and VMDK must be in a same directory
- head, tail = os.path.split(media_file_name)
- file_vmdk = head + '/' + link.get_href().split("/")[-1]
- if not os.path.isfile(file_vmdk):
- return False
- statinfo = os.stat(file_vmdk)
- if statinfo.st_size == 0:
- return False
- hrefvmdk = link.get_href()
-
- if progress:
- print("Uploading file: {}".format(file_vmdk))
- if progress:
- widgets = ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
- FileTransferSpeed()]
- progress_bar = ProgressBar(widgets=widgets, maxval=statinfo.st_size).start()
-
- bytes_transferred = 0
- f = open(file_vmdk, 'rb')
- while bytes_transferred < statinfo.st_size:
- my_bytes = f.read(chunk_bytes)
- if len(my_bytes) <= chunk_bytes:
- headers = vca.vcloud_session.get_vcloud_headers()
- headers['Content-Range'] = 'bytes %s-%s/%s' % (
- bytes_transferred, len(my_bytes) - 1, statinfo.st_size)
- headers['Content-Length'] = str(len(my_bytes))
- response = Http.put(hrefvmdk,
- headers=headers,
- data=my_bytes,
- verify=vca.verify,
- logger=None)
-
- if response.status_code == requests.codes.ok:
- bytes_transferred += len(my_bytes)
- if progress:
- progress_bar.update(bytes_transferred)
- else:
- self.logger.debug(
- 'file upload failed with error: [%s] %s' % (response.status_code,
- response.content))
-
- f.close()
- return False
- f.close()
- if progress:
- progress_bar.finish()
- time.sleep(10)
- return True
- else:
- self.logger.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
- format(catalog_name, media_file_name))
- return False
-
- self.logger.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name, media_file_name))
- return False
-
- def upload_vimimage(self, vca=None, catalog_name=None, media_name=None, medial_file_name=None, progress=False):
- """Upload media file"""
- # TODO add named parameters for readability
-
- return self.upload_ovf(vca=vca, catalog_name=catalog_name, image_name=media_name.split(".")[0],
- media_file_name=medial_file_name, description='medial_file_name', progress=progress)
-
- def validate_uuid4(self, uuid_string=None):
- """ Method validate correct format of UUID.
-
- Return: true if string represent valid uuid
- """
- try:
- val = uuid.UUID(uuid_string, version=4)
- except ValueError:
- return False
- return True
-
- def get_catalogid(self, catalog_name=None, catalogs=None):
- """ Method check catalog and return catalog ID in UUID format.
-
- Args
- catalog_name: catalog name as string
- catalogs: list of catalogs.
-
- Return: catalogs uuid
- """
-
- for catalog in catalogs:
- if catalog.name == catalog_name:
- catalog_id = catalog.get_id().split(":")
- return catalog_id[3]
- return None
-
- def get_catalogbyid(self, catalog_uuid=None, catalogs=None):
- """ Method check catalog and return catalog name lookup done by catalog UUID.
-
- Args
- catalog_name: catalog name as string
- catalogs: list of catalogs.
-
- Return: catalogs name or None
- """
-
- if not self.validate_uuid4(uuid_string=catalog_uuid):
- return None
-
- for catalog in catalogs:
- catalog_id = catalog.get_id().split(":")[3]
- if catalog_id == catalog_uuid:
- return catalog.name
- return None
-
- def get_image_id_from_path(self, path=None, progress=False):
- """ Method upload OVF image to vCloud director.
-
- Each OVF image represented as single catalog entry in vcloud director.
- The method check for existing catalog entry. The check done by file name without file extension.
-
- if given catalog name already present method will respond with existing catalog uuid otherwise
- it will create new catalog entry and upload OVF file to newly created catalog.
-
- If method can't create catalog entry or upload a file it will throw exception.
-
- Method accept boolean flag progress that will output progress bar. It useful method
- for standalone upload use case. In case to test large file upload.
-
- Args
- path: - valid path to OVF file.
- progress - boolean progress bar show progress bar.
-
- Return: if image uploaded correct method will provide image catalog UUID.
- """
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed.")
-
- if not path:
- raise vimconn.vimconnException("Image path can't be None.")
-
- if not os.path.isfile(path):
- raise vimconn.vimconnException("Can't read file. File not found.")
-
- if not os.access(path, os.R_OK):
- raise vimconn.vimconnException("Can't read file. Check file permission to read.")
-
- self.logger.debug("get_image_id_from_path() client requesting {} ".format(path))
-
- dirpath, filename = os.path.split(path)
- flname, file_extension = os.path.splitext(path)
- if file_extension != '.ovf':
- self.logger.debug("Wrong file extension {} connector support only OVF container.".format(file_extension))
- raise vimconn.vimconnException("Wrong container. vCloud director supports only OVF.")
-
- catalog_name = os.path.splitext(filename)[0]
- catalog_md5_name = hashlib.md5(path).hexdigest()
- self.logger.debug("File name {} Catalog Name {} file path {} "
- "vdc catalog name {}".format(filename, catalog_name, path, catalog_md5_name))
-
- catalogs = vca.get_catalogs()
- if len(catalogs) == 0:
- self.logger.info("Creating a new catalog entry {} in vcloud director".format(catalog_name))
- result = self.create_vimcatalog(vca, catalog_md5_name)
- if not result:
- raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name))
- result = self.upload_vimimage(vca=vca, catalog_name=catalog_md5_name,
- media_name=filename, medial_file_name=path, progress=progress)
- if not result:
- raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name))
- return self.get_catalogid(catalog_name, vca.get_catalogs())
- else:
- for catalog in catalogs:
- # search for existing catalog if we find same name we return ID
- # TODO optimize this
- if catalog.name == catalog_md5_name:
- self.logger.debug("Found existing catalog entry for {} "
- "catalog id {}".format(catalog_name,
- self.get_catalogid(catalog_md5_name, catalogs)))
- return self.get_catalogid(catalog_md5_name, vca.get_catalogs())
-
- # if we didn't find existing catalog we create a new one and upload image.
- self.logger.debug("Creating new catalog entry {} - {}".format(catalog_name, catalog_md5_name))
- result = self.create_vimcatalog(vca, catalog_md5_name)
- if not result:
- raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name))
-
- result = self.upload_vimimage(vca=vca, catalog_name=catalog_md5_name,
- media_name=filename, medial_file_name=path, progress=progress)
- if not result:
- raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name))
-
- return self.get_catalogid(catalog_md5_name, vca.get_catalogs())
-
- def get_image_list(self, filter_dict={}):
- '''Obtain tenant images from VIM
- Filter_dict can be:
- name: image name
- id: image uuid
- checksum: image checksum
- location: image path
- Returns the image list of dictionaries:
- [{<the fields at Filter_dict plus some VIM specific>}, ...]
- List can be empty
- '''
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed.")
- try:
- image_list = []
- catalogs = vca.get_catalogs()
- if len(catalogs) == 0:
- return image_list
- else:
- for catalog in catalogs:
- catalog_uuid = catalog.get_id().split(":")[3]
- name = catalog.name
- filtered_dict = {}
- if filter_dict.get("name") and filter_dict["name"] != name:
- continue
- if filter_dict.get("id") and filter_dict["id"] != catalog_uuid:
- continue
- filtered_dict ["name"] = name
- filtered_dict ["id"] = catalog_uuid
- image_list.append(filtered_dict)
-
- self.logger.debug("List of already created catalog items: {}".format(image_list))
- return image_list
- except Exception as exp:
- raise vimconn.vimconnException("Exception occured while retriving catalog items {}".format(exp))
-
- def get_vappid(self, vdc=None, vapp_name=None):
- """ Method takes vdc object and vApp name and returns vapp uuid or None
-
- Args:
- vdc: The VDC object.
- vapp_name: is application vappp name identifier
-
- Returns:
- The return vApp name otherwise None
- """
- if vdc is None or vapp_name is None:
- return None
- # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
- try:
- refs = filter(lambda ref: ref.name == vapp_name and ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
- vdc.ResourceEntities.ResourceEntity)
- if len(refs) == 1:
- return refs[0].href.split("vapp")[1][1:]
- except Exception as e:
- self.logger.exception(e)
- return False
- return None
-
- def check_vapp(self, vdc=None, vapp_uuid=None):
- """ Method Method returns True or False if vapp deployed in vCloud director
-
- Args:
- vca: Connector to VCA
- vdc: The VDC object.
- vappid: vappid is application identifier
-
- Returns:
- The return True if vApp deployed
- :param vdc:
- :param vapp_uuid:
- """
- try:
- refs = filter(lambda ref:
- ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
- vdc.ResourceEntities.ResourceEntity)
- for ref in refs:
- vappid = ref.href.split("vapp")[1][1:]
- # find vapp with respected vapp uuid
- if vappid == vapp_uuid:
- return True
- except Exception as e:
- self.logger.exception(e)
- return False
- return False
-
- def get_namebyvappid(self, vca=None, vdc=None, vapp_uuid=None):
- """Method returns vApp name from vCD and lookup done by vapp_id.
-
- Args:
- vca: Connector to VCA
- vdc: The VDC object.
- vapp_uuid: vappid is application identifier
-
- Returns:
- The return vApp name otherwise None
- """
-
- try:
- refs = filter(lambda ref: ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
- vdc.ResourceEntities.ResourceEntity)
- for ref in refs:
- # we care only about UUID the rest doesn't matter
- vappid = ref.href.split("vapp")[1][1:]
- if vappid == vapp_uuid:
- response = Http.get(ref.href, headers=vca.vcloud_session.get_vcloud_headers(), verify=vca.verify,
- logger=self.logger)
- tree = XmlElementTree.fromstring(response.content)
- return tree.attrib['name']
- except Exception as e:
- self.logger.exception(e)
- return None
- return None
-
- def new_vminstance(self, name=None, description="", start=False, image_id=None, flavor_id=None, net_list={},
- cloud_config=None, disk_list=None):
- """Adds a VM instance to VIM
- Params:
- start: indicates if VM must start or boot in pause mode. Ignored
- image_id,flavor_id: image and flavor uuid
- net_list: list of interfaces, each one is a dictionary with:
- name:
- net_id: network uuid to connect
- vpci: virtual vcpi to assign
- model: interface model, virtio, e2000, ...
- mac_address:
- use: 'data', 'bridge', 'mgmt'
- type: 'virtual', 'PF', 'VF', 'VFnotShared'
- vim_id: filled/added by this function
- cloud_config: can be a text script to be passed directly to cloud-init,
- or an object to inject users and ssh keys with format:
- key-pairs: [] list of keys to install to the default user
- users: [{ name, key-pairs: []}] list of users to add with their key-pair
- #TODO ip, security groups
- Returns >=0, the instance identifier
- <0, error_text
- """
-
- self.logger.info("Creating new instance for entry {}".format(name))
- self.logger.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".format(
- description, start, image_id, flavor_id, net_list, cloud_config))
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed.")
-
- #new vm name = vmname + tenant_id + uuid
- new_vm_name = [name, '-', str(uuid.uuid4())]
- vmname_andid = ''.join(new_vm_name)
-
- # if vm already deployed we return existing uuid
- # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
- # if vapp_uuid is not None:
- # return vapp_uuid
-
- # we check for presence of VDC, Catalog entry and Flavor.
- vdc = vca.get_vdc(self.tenant_name)
- if vdc is None:
- raise vimconn.vimconnNotFoundException(
- "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name))
- catalogs = vca.get_catalogs()
- if catalogs is None:
- raise vimconn.vimconnNotFoundException(
- "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name))
-
- catalog_hash_name = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
- if catalog_hash_name:
- self.logger.info("Found catalog entry {} for image id {}".format(catalog_hash_name, image_id))
- else:
- raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
- "(Failed retrieve catalog information {})".format(name, image_id))
-
-
- # Set vCPU and Memory based on flavor.
- #
- vm_cpus = None
- vm_memory = None
- vm_disk = None
- pci_devices_info = []
- if flavor_id is not None:
- if flavor_id not in vimconnector.flavorlist:
- raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
- "Failed retrieve flavor information "
- "flavor id {}".format(name, flavor_id))
- else:
- try:
- flavor = vimconnector.flavorlist[flavor_id]
- vm_cpus = flavor[FLAVOR_VCPUS_KEY]
- vm_memory = flavor[FLAVOR_RAM_KEY]
- vm_disk = flavor[FLAVOR_DISK_KEY]
- extended = flavor.get("extended", None)
- if extended:
- numas=extended.get("numas", None)
- if numas:
- for numa in numas:
- for interface in numa.get("interfaces",() ):
- if interface["dedicated"].strip()=="yes":
- pci_devices_info.append(interface)
- except Exception as exp:
- raise vimconn.vimconnException("Corrupted flavor. {}.Exception: {}".format(flavor_id, exp))
-
- # image upload creates template name as catalog name space Template.
- templateName = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
- power_on = 'false'
- if start:
- power_on = 'true'
-
- # client must provide at least one entry in net_list if not we report error
- #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
- #If no mgmt, then the 1st NN in netlist is considered as primary net.
- primary_net = None
- primary_netname = None
- network_mode = 'bridged'
- if net_list is not None and len(net_list) > 0:
- for net in net_list:
- if 'use' in net and net['use'] == 'mgmt':
- primary_net = net
- if primary_net is None:
- primary_net = net_list[0]
-
- try:
- primary_net_id = primary_net['net_id']
- network_dict = self.get_vcd_network(network_uuid=primary_net_id)
- if 'name' in network_dict:
- primary_netname = network_dict['name']
-
- except KeyError:
- raise vimconn.vimconnException("Corrupted flavor. {}".format(primary_net))
- else:
- raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name))
-
- # use: 'data', 'bridge', 'mgmt'
- # create vApp. Set vcpu and ram based on flavor id.
- vapptask = vca.create_vapp(self.tenant_name, vmname_andid, templateName,
- self.get_catalogbyid(image_id, catalogs),
- network_name=None, # None while creating vapp
- network_mode=network_mode,
- vm_name=vmname_andid,
- vm_cpus=vm_cpus, # can be None if flavor is None
- vm_memory=vm_memory) # can be None if flavor is None
-
- if vapptask is None or vapptask is False:
- raise vimconn.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(vmname_andid))
- if type(vapptask) is VappTask:
- vca.block_until_completed(vapptask)
-
- # we should have now vapp in undeployed state.
- vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid)
- vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), vmname_andid)
- if vapp is None:
- raise vimconn.vimconnUnexpectedResponse(
- "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(
- vmname_andid))
-
- #Add PCI passthrough configrations
- PCI_devices_status = False
- vm_obj = None
- si = None
- if len(pci_devices_info) > 0:
- self.logger.info("Need to add PCI devices {} into VM {}".format(pci_devices_info,
- vmname_andid ))
- PCI_devices_status, vm_obj, vcenter_conect = self.add_pci_devices(vapp_uuid,
- pci_devices_info,
- vmname_andid)
- if PCI_devices_status:
- self.logger.info("Added PCI devives {} to VM {}".format(
- pci_devices_info,
- vmname_andid)
- )
- else:
- self.logger.info("Fail to add PCI devives {} to VM {}".format(
- pci_devices_info,
- vmname_andid)
- )
- # add vm disk
- if vm_disk:
- #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
- result = self.modify_vm_disk(vapp_uuid, vm_disk)
- if result :
- self.logger.debug("Modified Disk size of VM {} ".format(vmname_andid))
-
- # add NICs & connect to networks in netlist
- try:
- self.logger.info("Request to connect VM to a network: {}".format(net_list))
- nicIndex = 0
- primary_nic_index = 0
- for net in net_list:
- # openmano uses network id in UUID format.
- # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
- # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
- # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
-
- if 'net_id' not in net:
- continue
-
- interface_net_id = net['net_id']
- interface_net_name = self.get_network_name_by_id(network_uuid=interface_net_id)
- interface_network_mode = net['use']
-
- if interface_network_mode == 'mgmt':
- primary_nic_index = nicIndex
-
- """- POOL (A static IP address is allocated automatically from a pool of addresses.)
- - DHCP (The IP address is obtained from a DHCP service.)
- - MANUAL (The IP address is assigned manually in the IpAddress element.)
- - NONE (No IP addressing mode specified.)"""
-
- if primary_netname is not None:
- nets = filter(lambda n: n.name == interface_net_name, vca.get_networks(self.tenant_name))
- if len(nets) == 1:
- self.logger.info("new_vminstance(): Found requested network: {}".format(nets[0].name))
- task = vapp.connect_to_network(nets[0].name, nets[0].href)
- if type(task) is GenericTask:
- vca.block_until_completed(task)
- # connect network to VM - with all DHCP by default
- self.logger.info("new_vminstance(): Connecting VM to a network {}".format(nets[0].name))
- task = vapp.connect_vms(nets[0].name,
- connection_index=nicIndex,
- connections_primary_index=primary_nic_index,
- ip_allocation_mode='DHCP')
- if type(task) is GenericTask:
- vca.block_until_completed(task)
- nicIndex += 1
- except KeyError:
- # it might be a case if specific mandatory entry in dict is empty
- self.logger.debug("Key error {}".format(KeyError.message))
- raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
-
- # deploy and power on vm
- self.logger.debug("new_vminstance(): Deploying vApp {} ".format(name))
- deploytask = vapp.deploy(powerOn=False)
- if type(deploytask) is GenericTask:
- vca.block_until_completed(deploytask)
-
- # If VM has PCI devices reserve memory for VM
- if PCI_devices_status and vm_obj and vcenter_conect:
- memReserve = vm_obj.config.hardware.memoryMB
- spec = vim.vm.ConfigSpec()
- spec.memoryAllocation = vim.ResourceAllocationInfo(reservation=memReserve)
- task = vm_obj.ReconfigVM_Task(spec=spec)
- if task:
- result = self.wait_for_vcenter_task(task, vcenter_conect)
- self.logger.info("Reserved memmoery {} MB for "\
- "VM VM status: {}".format(str(memReserve),result))
- else:
- self.logger.info("Fail to reserved memmoery {} to VM {}".format(
- str(memReserve),str(vm_obj)))
-
- self.logger.debug("new_vminstance(): power on vApp {} ".format(name))
- poweron_task = vapp.poweron()
- if type(poweron_task) is GenericTask:
- vca.block_until_completed(poweron_task)
-
- # check if vApp deployed and if that the case return vApp UUID otherwise -1
- wait_time = 0
- vapp_uuid = None
- while wait_time <= MAX_WAIT_TIME:
- vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid)
- if vapp and vapp.me.deployed:
- vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), vmname_andid)
- break
- else:
- self.logger.debug("new_vminstance(): Wait for vApp {} to deploy".format(name))
- time.sleep(INTERVAL_TIME)
-
- wait_time +=INTERVAL_TIME
-
- if vapp_uuid is not None:
- return vapp_uuid
- else:
- raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
-
- ##
- ##
- ## based on current discussion
- ##
- ##
- ## server:
- # created: '2016-09-08T11:51:58'
- # description: simple-instance.linux1.1
- # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
- # hostId: e836c036-74e7-11e6-b249-0800273e724c
- # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
- # status: ACTIVE
- # error_msg:
- # interfaces: …
- #
- def get_vminstance(self, vim_vm_uuid=None):
- """Returns the VM instance information from VIM"""
-
- self.logger.debug("Client requesting vm instance {} ".format(vim_vm_uuid))
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed.")
-
- vdc = vca.get_vdc(self.tenant_name)
- if vdc is None:
- raise vimconn.vimconnConnectionException(
- "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
-
- vm_info_dict = self.get_vapp_details_rest(vapp_uuid=vim_vm_uuid)
- if not vm_info_dict:
- self.logger.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid))
- raise vimconn.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid))
-
- status_key = vm_info_dict['status']
- error = ''
- try:
- vm_dict = {'created': vm_info_dict['created'],
- 'description': vm_info_dict['name'],
- 'status': vcdStatusCode2manoFormat[int(status_key)],
- 'hostId': vm_info_dict['vmuuid'],
- 'error_msg': error,
- 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
-
- if 'interfaces' in vm_info_dict:
- vm_dict['interfaces'] = vm_info_dict['interfaces']
- else:
- vm_dict['interfaces'] = []
- except KeyError:
- vm_dict = {'created': '',
- 'description': '',
- 'status': vcdStatusCode2manoFormat[int(-1)],
- 'hostId': vm_info_dict['vmuuid'],
- 'error_msg': "Inconsistency state",
- 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
-
- return vm_dict
-
- def delete_vminstance(self, vm__vim_uuid):
- """Method poweroff and remove VM instance from vcloud director network.
-
- Args:
- vm__vim_uuid: VM UUID
-
- Returns:
- Returns the instance identifier
- """
-
- self.logger.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid))
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed.")
-
- vdc = vca.get_vdc(self.tenant_name)
- if vdc is None:
- self.logger.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
- self.tenant_name))
- raise vimconn.vimconnException(
- "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
-
- try:
- vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
- if vapp_name is None:
- self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
- return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
- else:
- self.logger.info("Deleting vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
-
- # Delete vApp and wait for status change if task executed and vApp is None.
- vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
-
- if vapp:
- if vapp.me.deployed:
- self.logger.info("Powering off vApp {}".format(vapp_name))
- #Power off vApp
- powered_off = False
- wait_time = 0
- while wait_time <= MAX_WAIT_TIME:
- vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
- if not vapp:
- self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
- return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
-
- power_off_task = vapp.poweroff()
- if type(power_off_task) is GenericTask:
- result = vca.block_until_completed(power_off_task)
- if result:
- powered_off = True
- break
- else:
- self.logger.info("Wait for vApp {} to power off".format(vapp_name))
- time.sleep(INTERVAL_TIME)
-
- wait_time +=INTERVAL_TIME
- if not powered_off:
- self.logger.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid))
- else:
- self.logger.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid))
-
- #Undeploy vApp
- self.logger.info("Undeploy vApp {}".format(vapp_name))
- wait_time = 0
- undeployed = False
- while wait_time <= MAX_WAIT_TIME:
- vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
- if not vapp:
- self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
- return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
- undeploy_task = vapp.undeploy(action='powerOff')
-
- if type(undeploy_task) is GenericTask:
- result = vca.block_until_completed(undeploy_task)
- if result:
- undeployed = True
- break
- else:
- self.logger.debug("Wait for vApp {} to undeploy".format(vapp_name))
- time.sleep(INTERVAL_TIME)
-
- wait_time +=INTERVAL_TIME
-
- if not undeployed:
- self.logger.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid))
-
- # delete vapp
- self.logger.info("Start deletion of vApp {} ".format(vapp_name))
- vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
-
- if vapp is not None:
- wait_time = 0
- result = False
-
- while wait_time <= MAX_WAIT_TIME:
- vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
- if not vapp:
- self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
- return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
-
- delete_task = vapp.delete()
-
- if type(delete_task) is GenericTask:
- vca.block_until_completed(delete_task)
- result = vca.block_until_completed(delete_task)
- if result:
- break
- else:
- self.logger.debug("Wait for vApp {} to delete".format(vapp_name))
- time.sleep(INTERVAL_TIME)
-
- wait_time +=INTERVAL_TIME
-
- if not result:
- self.logger.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid))
-
- except:
- self.logger.debug(traceback.format_exc())
- raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
-
- if vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name) is None:
- self.logger.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid))
- return vm__vim_uuid
- else:
- raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
-
- def refresh_vms_status(self, vm_list):
- """Get the status of the virtual machines and their interfaces/ports
- Params: the list of VM identifiers
- Returns a dictionary with:
- vm_id: #VIM id of this Virtual Machine
- status: #Mandatory. Text with one of:
- # DELETED (not found at vim)
- # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
- # OTHER (Vim reported other status not understood)
- # ERROR (VIM indicates an ERROR status)
- # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
- # CREATING (on building process), ERROR
- # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
- #
- error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
- vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
- interfaces:
- - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
- mac_address: #Text format XX:XX:XX:XX:XX:XX
- vim_net_id: #network id where this interface is connected
- vim_interface_id: #interface/port VIM id
- ip_address: #null, or text with IPv4, IPv6 address
- """
-
- self.logger.debug("Client requesting refresh vm status for {} ".format(vm_list))
-
- mac_ip_addr={}
- rheaders = {'Content-Type': 'application/xml'}
- iso_edges = ['edge-2','edge-3','edge-6','edge-7','edge-8','edge-9','edge-10']
-
- try:
- for edge in iso_edges:
- nsx_api_url = '/api/4.0/edges/'+ edge +'/dhcp/leaseInfo'
- self.logger.debug("refresh_vms_status: NSX Manager url: {}".format(nsx_api_url))
-
- resp = requests.get(self.nsx_manager + nsx_api_url,
- auth = (self.nsx_user, self.nsx_password),
- verify = False, headers = rheaders)
-
- if resp.status_code == requests.codes.ok:
- dhcp_leases = XmlElementTree.fromstring(resp.text)
- for child in dhcp_leases:
- if child.tag == 'dhcpLeaseInfo':
- dhcpLeaseInfo = child
- for leaseInfo in dhcpLeaseInfo:
- for elem in leaseInfo:
- if (elem.tag)=='macAddress':
- mac_addr = elem.text
- if (elem.tag)=='ipAddress':
- ip_addr = elem.text
- if (mac_addr) is not None:
- mac_ip_addr[mac_addr]= ip_addr
- self.logger.debug("NSX Manager DHCP Lease info: mac_ip_addr : {}".format(mac_ip_addr))
- else:
- self.logger.debug("Error occurred while getting DHCP lease info from NSX Manager: {}".format(resp.content))
- except KeyError:
- self.logger.debug("Error in response from NSX Manager {}".format(KeyError.message))
- self.logger.debug(traceback.format_exc())
-
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed.")
-
- vdc = vca.get_vdc(self.tenant_name)
- if vdc is None:
- raise vimconn.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
-
- vms_dict = {}
- for vmuuid in vm_list:
- vmname = self.get_namebyvappid(vca, vdc, vmuuid)
- if vmname is not None:
-
- the_vapp = vca.get_vapp(vdc, vmname)
- vm_info = the_vapp.get_vms_details()
- vm_status = vm_info[0]['status']
- vm_pci_details = self.get_vm_pci_details(vmuuid)
- vm_info[0].update(vm_pci_details)
-
- vm_dict = {'status': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
- 'error_msg': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
- 'vim_info': yaml.safe_dump(vm_info), 'interfaces': []}
-
- # get networks
- try:
- vm_app_networks = the_vapp.get_vms_network_info()
- for vapp_network in vm_app_networks:
- for vm_network in vapp_network:
- if vm_network['name'] == vmname:
- #Assign IP Address based on MAC Address in NSX DHCP lease info
- for mac_adres,ip_adres in mac_ip_addr.iteritems():
- if mac_adres == vm_network['mac']:
- vm_network['ip']=ip_adres
- interface = {"mac_address": vm_network['mac'],
- "vim_net_id": self.get_network_id_by_name(vm_network['network_name']),
- "vim_interface_id": self.get_network_id_by_name(vm_network['network_name']),
- 'ip_address': vm_network['ip']}
- # interface['vim_info'] = yaml.safe_dump(vm_network)
- vm_dict["interfaces"].append(interface)
- # add a vm to vm dict
- vms_dict.setdefault(vmuuid, vm_dict)
- except KeyError:
- self.logger.debug("Error in respond {}".format(KeyError.message))
- self.logger.debug(traceback.format_exc())
-
- return vms_dict
-
- def action_vminstance(self, vm__vim_uuid=None, action_dict=None):
- """Send and action over a VM instance from VIM
- Returns the vm_id if the action was successfully sent to the VIM"""
-
- self.logger.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid, action_dict))
- if vm__vim_uuid is None or action_dict is None:
- raise vimconn.vimconnException("Invalid request. VM id or action is None.")
-
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed.")
-
- vdc = vca.get_vdc(self.tenant_name)
- if vdc is None:
- return -1, "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)
-
- vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
- if vapp_name is None:
- self.logger.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
- raise vimconn.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
- else:
- self.logger.info("Action_vminstance vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
-
- try:
- the_vapp = vca.get_vapp(vdc, vapp_name)
- # TODO fix all status
- if "start" in action_dict:
- vm_info = the_vapp.get_vms_details()
- vm_status = vm_info[0]['status']
- self.logger.info("Power on vApp: vm_status:{} {}".format(type(vm_status),vm_status))
- if vm_status == "Suspended" or vm_status == "Powered off":
- power_on_task = the_vapp.poweron()
- if power_on_task is not None and type(power_on_task) is GenericTask:
- result = vca.block_until_completed(power_on_task)
- if result:
- self.logger.info("action_vminstance: Powered on vApp: {}".format(vapp_name))
- else:
- self.logger.info("action_vminstance: Failed to power on vApp: {}".format(vapp_name))
- else:
- self.logger.info("action_vminstance: Wait for vApp {} to power on".format(vapp_name))
- elif "rebuild" in action_dict:
- self.logger.info("action_vminstance: Rebuilding vApp: {}".format(vapp_name))
- power_on_task = the_vapp.deploy(powerOn=True)
- if type(power_on_task) is GenericTask:
- result = vca.block_until_completed(power_on_task)
- if result:
- self.logger.info("action_vminstance: Rebuilt vApp: {}".format(vapp_name))
- else:
- self.logger.info("action_vminstance: Failed to rebuild vApp: {}".format(vapp_name))
- else:
- self.logger.info("action_vminstance: Wait for vApp rebuild {} to power on".format(vapp_name))
- elif "pause" in action_dict:
- pass
- ## server.pause()
- elif "resume" in action_dict:
- pass
- ## server.resume()
- elif "shutoff" in action_dict or "shutdown" in action_dict:
- power_off_task = the_vapp.undeploy(action='powerOff')
- if type(power_off_task) is GenericTask:
- result = vca.block_until_completed(power_off_task)
- if result:
- self.logger.info("action_vminstance: Powered off vApp: {}".format(vapp_name))
- else:
- self.logger.info("action_vminstance: Failed to power off vApp: {}".format(vapp_name))
- else:
- self.logger.info("action_vminstance: Wait for vApp {} to power off".format(vapp_name))
- elif "forceOff" in action_dict:
- the_vapp.reset()
- elif "terminate" in action_dict:
- the_vapp.delete()
- # elif "createImage" in action_dict:
- # server.create_image()
- else:
- pass
- except:
- pass
-
- def get_vminstance_console(self, vm_id, console_type="vnc"):
- """
- Get a console for the virtual machine
- Params:
- vm_id: uuid of the VM
- console_type, can be:
- "novnc" (by default), "xvpvnc" for VNC types,
- "rdp-html5" for RDP types, "spice-html5" for SPICE types
- Returns dict with the console parameters:
- protocol: ssh, ftp, http, https, ...
- server: usually ip address
- port: the http, ssh, ... port
- suffix: extra text, e.g. the http path and query string
- """
- raise vimconn.vimconnNotImplemented("Should have implemented this")
-
- # NOT USED METHODS in current version
-
- def host_vim2gui(self, host, server_dict):
- """Transform host dictionary from VIM format to GUI format,
- and append to the server_dict
- """
- raise vimconn.vimconnNotImplemented("Should have implemented this")
-
- def get_hosts_info(self):
- """Get the information of deployed hosts
- Returns the hosts content"""
- raise vimconn.vimconnNotImplemented("Should have implemented this")
-
- def get_hosts(self, vim_tenant):
- """Get the hosts and deployed instances
- Returns the hosts content"""
- raise vimconn.vimconnNotImplemented("Should have implemented this")
-
- def get_processor_rankings(self):
- """Get the processor rankings in the VIM database"""
- raise vimconn.vimconnNotImplemented("Should have implemented this")
-
- def new_host(self, host_data):
- """Adds a new host to VIM"""
- '''Returns status code of the VIM response'''
- raise vimconn.vimconnNotImplemented("Should have implemented this")
-
- def new_external_port(self, port_data):
- """Adds a external port to VIM"""
- '''Returns the port identifier'''
- raise vimconn.vimconnNotImplemented("Should have implemented this")
-
- def new_external_network(self, net_name, net_type):
- """Adds a external network to VIM (shared)"""
- '''Returns the network identifier'''
- raise vimconn.vimconnNotImplemented("Should have implemented this")
-
- def connect_port_network(self, port_id, network_id, admin=False):
- """Connects a external port to a network"""
- '''Returns status code of the VIM response'''
- raise vimconn.vimconnNotImplemented("Should have implemented this")
-
- def new_vminstancefromJSON(self, vm_data):
- """Adds a VM instance to VIM"""
- '''Returns the instance identifier'''
- raise vimconn.vimconnNotImplemented("Should have implemented this")
-
- def get_network_name_by_id(self, network_uuid=None):
- """Method gets vcloud director network named based on supplied uuid.
-
- Args:
- network_uuid: network_id
-
- Returns:
- The return network name.
- """
-
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed.")
-
- if not network_uuid:
- return None
-
- try:
- org_dict = self.get_org(self.org_uuid)
- if 'networks' in org_dict:
- org_network_dict = org_dict['networks']
- for net_uuid in org_network_dict:
- if net_uuid == network_uuid:
- return org_network_dict[net_uuid]
- except:
- self.logger.debug("Exception in get_network_name_by_id")
- self.logger.debug(traceback.format_exc())
-
- return None
-
- def get_network_id_by_name(self, network_name=None):
- """Method gets vcloud director network uuid based on supplied name.
-
- Args:
- network_name: network_name
- Returns:
- The return network uuid.
- network_uuid: network_id
- """
-
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed.")
-
- if not network_name:
- self.logger.debug("get_network_id_by_name() : Network name is empty")
- return None
-
- try:
- org_dict = self.get_org(self.org_uuid)
- if org_dict and 'networks' in org_dict:
- org_network_dict = org_dict['networks']
- for net_uuid,net_name in org_network_dict.iteritems():
- if net_name == network_name:
- return net_uuid
-
- except KeyError as exp:
- self.logger.debug("get_network_id_by_name() : KeyError- {} ".format(exp))
-
- return None
-
- def list_org_action(self):
- """
- Method leverages vCloud director and query for available organization for particular user
-
- Args:
- vca - is active VCA connection.
- vdc_name - is a vdc name that will be used to query vms action
-
- Returns:
- The return XML respond
- """
-
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed")
-
- url_list = [vca.host, '/api/org']
- vm_list_rest_call = ''.join(url_list)
-
- if not (not vca.vcloud_session or not vca.vcloud_session.organization):
- response = Http.get(url=vm_list_rest_call,
- headers=vca.vcloud_session.get_vcloud_headers(),
- verify=vca.verify,
- logger=vca.logger)
- if response.status_code == requests.codes.ok:
- return response.content
-
- return None
-
- def get_org_action(self, org_uuid=None):
- """
- Method leverages vCloud director and retrieve available object fdr organization.
-
- Args:
- vca - is active VCA connection.
- vdc_name - is a vdc name that will be used to query vms action
-
- Returns:
- The return XML respond
- """
-
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed")
-
- if org_uuid is None:
- return None
-
- url_list = [vca.host, '/api/org/', org_uuid]
- vm_list_rest_call = ''.join(url_list)
-
- if not (not vca.vcloud_session or not vca.vcloud_session.organization):
- response = Http.get(url=vm_list_rest_call,
- headers=vca.vcloud_session.get_vcloud_headers(),
- verify=vca.verify,
- logger=vca.logger)
- if response.status_code == requests.codes.ok:
- return response.content
-
- return None
-
- def get_org(self, org_uuid=None):
- """
- Method retrieves available organization in vCloud Director
-
- Args:
- org_uuid - is a organization uuid.
-
- Returns:
- The return dictionary with following key
- "network" - for network list under the org
- "catalogs" - for network list under the org
- "vdcs" - for vdc list under org
- """
-
- org_dict = {}
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed")
-
- if org_uuid is None:
- return org_dict
-
- content = self.get_org_action(org_uuid=org_uuid)
- try:
- vdc_list = {}
- network_list = {}
- catalog_list = {}
- vm_list_xmlroot = XmlElementTree.fromstring(content)
- for child in vm_list_xmlroot:
- if child.attrib['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
- vdc_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
- org_dict['vdcs'] = vdc_list
- if child.attrib['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
- network_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
- org_dict['networks'] = network_list
- if child.attrib['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
- catalog_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
- org_dict['catalogs'] = catalog_list
- except:
- pass
-
- return org_dict
-
- def get_org_list(self):
- """
- Method retrieves available organization in vCloud Director
-
- Args:
- vca - is active VCA connection.
-
- Returns:
- The return dictionary and key for each entry VDC UUID
- """
-
- org_dict = {}
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed")
-
- content = self.list_org_action()
- try:
- vm_list_xmlroot = XmlElementTree.fromstring(content)
- for vm_xml in vm_list_xmlroot:
- if vm_xml.tag.split("}")[1] == 'Org':
- org_uuid = vm_xml.attrib['href'].split('/')[-1:]
- org_dict[org_uuid[0]] = vm_xml.attrib['name']
- except:
- pass
-
- return org_dict
-
- def vms_view_action(self, vdc_name=None):
- """ Method leverages vCloud director vms query call
-
- Args:
- vca - is active VCA connection.
- vdc_name - is a vdc name that will be used to query vms action
-
- Returns:
- The return XML respond
- """
- vca = self.connect()
- if vdc_name is None:
- return None
-
- url_list = [vca.host, '/api/vms/query']
- vm_list_rest_call = ''.join(url_list)
-
- if not (not vca.vcloud_session or not vca.vcloud_session.organization):
- refs = filter(lambda ref: ref.name == vdc_name and ref.type_ == 'application/vnd.vmware.vcloud.vdc+xml',
- vca.vcloud_session.organization.Link)
- if len(refs) == 1:
- response = Http.get(url=vm_list_rest_call,
- headers=vca.vcloud_session.get_vcloud_headers(),
- verify=vca.verify,
- logger=vca.logger)
- if response.status_code == requests.codes.ok:
- return response.content
-
- return None
-
- def get_vapp_list(self, vdc_name=None):
- """
- Method retrieves vApp list deployed vCloud director and returns a dictionary
- contains a list of all vapp deployed for queried VDC.
- The key for a dictionary is vApp UUID
-
-
- Args:
- vca - is active VCA connection.
- vdc_name - is a vdc name that will be used to query vms action
-
- Returns:
- The return dictionary and key for each entry vapp UUID
- """
-
- vapp_dict = {}
- if vdc_name is None:
- return vapp_dict
-
- content = self.vms_view_action(vdc_name=vdc_name)
- try:
- vm_list_xmlroot = XmlElementTree.fromstring(content)
- for vm_xml in vm_list_xmlroot:
- if vm_xml.tag.split("}")[1] == 'VMRecord':
- if vm_xml.attrib['isVAppTemplate'] == 'true':
- rawuuid = vm_xml.attrib['container'].split('/')[-1:]
- if 'vappTemplate-' in rawuuid[0]:
- # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
- # vm and use raw UUID as key
- vapp_dict[rawuuid[0][13:]] = vm_xml.attrib
- except:
- pass
-
- return vapp_dict
-
- def get_vm_list(self, vdc_name=None):
- """
- Method retrieves VM's list deployed vCloud director. It returns a dictionary
- contains a list of all VM's deployed for queried VDC.
- The key for a dictionary is VM UUID
-
-
- Args:
- vca - is active VCA connection.
- vdc_name - is a vdc name that will be used to query vms action
-
- Returns:
- The return dictionary and key for each entry vapp UUID
- """
- vm_dict = {}
-
- if vdc_name is None:
- return vm_dict
-
- content = self.vms_view_action(vdc_name=vdc_name)
- try:
- vm_list_xmlroot = XmlElementTree.fromstring(content)
- for vm_xml in vm_list_xmlroot:
- if vm_xml.tag.split("}")[1] == 'VMRecord':
- if vm_xml.attrib['isVAppTemplate'] == 'false':
- rawuuid = vm_xml.attrib['href'].split('/')[-1:]
- if 'vm-' in rawuuid[0]:
- # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
- # vm and use raw UUID as key
- vm_dict[rawuuid[0][3:]] = vm_xml.attrib
- except:
- pass
-
- return vm_dict
-
- def get_vapp(self, vdc_name=None, vapp_name=None, isuuid=False):
- """
- Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
- contains a list of all VM's deployed for queried VDC.
- The key for a dictionary is VM UUID
-
-
- Args:
- vca - is active VCA connection.
- vdc_name - is a vdc name that will be used to query vms action
-
- Returns:
- The return dictionary and key for each entry vapp UUID
- """
- vm_dict = {}
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed")
-
- if vdc_name is None:
- return vm_dict
-
- content = self.vms_view_action(vdc_name=vdc_name)
- try:
- vm_list_xmlroot = XmlElementTree.fromstring(content)
- for vm_xml in vm_list_xmlroot:
- if vm_xml.tag.split("}")[1] == 'VMRecord' and vm_xml.attrib['isVAppTemplate'] == 'false':
- # lookup done by UUID
- if isuuid:
- if vapp_name in vm_xml.attrib['container']:
- rawuuid = vm_xml.attrib['href'].split('/')[-1:]
- if 'vm-' in rawuuid[0]:
- vm_dict[rawuuid[0][3:]] = vm_xml.attrib
- break
- # lookup done by Name
- else:
- if vapp_name in vm_xml.attrib['name']:
- rawuuid = vm_xml.attrib['href'].split('/')[-1:]
- if 'vm-' in rawuuid[0]:
- vm_dict[rawuuid[0][3:]] = vm_xml.attrib
- break
- except:
- pass
-
- return vm_dict
-
- def get_network_action(self, network_uuid=None):
- """
- Method leverages vCloud director and query network based on network uuid
-
- Args:
- vca - is active VCA connection.
- network_uuid - is a network uuid
-
- Returns:
- The return XML respond
- """
-
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed")
-
- if network_uuid is None:
- return None
-
- url_list = [vca.host, '/api/network/', network_uuid]
- vm_list_rest_call = ''.join(url_list)
-
- if not (not vca.vcloud_session or not vca.vcloud_session.organization):
- response = Http.get(url=vm_list_rest_call,
- headers=vca.vcloud_session.get_vcloud_headers(),
- verify=vca.verify,
- logger=vca.logger)
- if response.status_code == requests.codes.ok:
- return response.content
-
- return None
-
- def get_vcd_network(self, network_uuid=None):
- """
- Method retrieves available network from vCloud Director
-
- Args:
- network_uuid - is VCD network UUID
-
- Each element serialized as key : value pair
-
- Following keys available for access. network_configuration['Gateway'}
- <Configuration>
- <IpScopes>
- <IpScope>
- <IsInherited>true</IsInherited>
- <Gateway>172.16.252.100</Gateway>
- <Netmask>255.255.255.0</Netmask>
- <Dns1>172.16.254.201</Dns1>
- <Dns2>172.16.254.202</Dns2>
- <DnsSuffix>vmwarelab.edu</DnsSuffix>
- <IsEnabled>true</IsEnabled>
- <IpRanges>
- <IpRange>
- <StartAddress>172.16.252.1</StartAddress>
- <EndAddress>172.16.252.99</EndAddress>
- </IpRange>
- </IpRanges>
- </IpScope>
- </IpScopes>
- <FenceMode>bridged</FenceMode>
-
- Returns:
- The return dictionary and key for each entry vapp UUID
- """
-
- network_configuration = {}
- if network_uuid is None:
- return network_uuid
-
- content = self.get_network_action(network_uuid=network_uuid)
- try:
- vm_list_xmlroot = XmlElementTree.fromstring(content)
-
- network_configuration['status'] = vm_list_xmlroot.get("status")
- network_configuration['name'] = vm_list_xmlroot.get("name")
- network_configuration['uuid'] = vm_list_xmlroot.get("id").split(":")[3]
-
- for child in vm_list_xmlroot:
- if child.tag.split("}")[1] == 'IsShared':
- network_configuration['isShared'] = child.text.strip()
- if child.tag.split("}")[1] == 'Configuration':
- for configuration in child.iter():
- tagKey = configuration.tag.split("}")[1].strip()
- if tagKey != "":
- network_configuration[tagKey] = configuration.text.strip()
- return network_configuration
- except:
- pass
-
- return network_configuration
-
- def delete_network_action(self, network_uuid=None):
- """
- Method delete given network from vCloud director
-
- Args:
- network_uuid - is a network uuid that client wish to delete
-
- Returns:
- The return None or XML respond or false
- """
-
- vca = self.connect_as_admin()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed")
- if network_uuid is None:
- return False
-
- url_list = [vca.host, '/api/admin/network/', network_uuid]
- vm_list_rest_call = ''.join(url_list)
-
- if not (not vca.vcloud_session or not vca.vcloud_session.organization):
- response = Http.delete(url=vm_list_rest_call,
- headers=vca.vcloud_session.get_vcloud_headers(),
- verify=vca.verify,
- logger=vca.logger)
-
- if response.status_code == 202:
- return True
-
- return False
-
- def create_network(self, network_name=None, net_type='bridge', parent_network_uuid=None,
- ip_profile=None, isshared='true'):
- """
- Method create network in vCloud director
-
- Args:
- network_name - is network name to be created.
- net_type - can be 'bridge','data','ptp','mgmt'.
- ip_profile is a dict containing the IP parameters of the network
- isshared - is a boolean
- parent_network_uuid - is parent provider vdc network that will be used for mapping.
- It optional attribute. by default if no parent network indicate the first available will be used.
-
- Returns:
- The return network uuid or return None
- """
-
- new_network_name = [network_name, '-', str(uuid.uuid4())]
- content = self.create_network_rest(network_name=''.join(new_network_name),
- ip_profile=ip_profile,
- net_type=net_type,
- parent_network_uuid=parent_network_uuid,
- isshared=isshared)
- if content is None:
- self.logger.debug("Failed create network {}.".format(network_name))
- return None
-
- try:
- vm_list_xmlroot = XmlElementTree.fromstring(content)
- vcd_uuid = vm_list_xmlroot.get('id').split(":")
- if len(vcd_uuid) == 4:
- self.logger.info("Create new network name: {} uuid: {}".format(network_name, vcd_uuid[3]))
- return vcd_uuid[3]
- except:
- self.logger.debug("Failed create network {}".format(network_name))
- return None
-
- def create_network_rest(self, network_name=None, net_type='bridge', parent_network_uuid=None,
- ip_profile=None, isshared='true'):
- """
- Method create network in vCloud director
-
- Args:
- network_name - is network name to be created.
- net_type - can be 'bridge','data','ptp','mgmt'.
- ip_profile is a dict containing the IP parameters of the network
- isshared - is a boolean
- parent_network_uuid - is parent provider vdc network that will be used for mapping.
- It optional attribute. by default if no parent network indicate the first available will be used.
-
- Returns:
- The return network uuid or return None
- """
-
- vca = self.connect_as_admin()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed.")
- if network_name is None:
- return None
-
- url_list = [vca.host, '/api/admin/vdc/', self.tenant_id]
- vm_list_rest_call = ''.join(url_list)
- if not (not vca.vcloud_session or not vca.vcloud_session.organization):
- response = Http.get(url=vm_list_rest_call,
- headers=vca.vcloud_session.get_vcloud_headers(),
- verify=vca.verify,
- logger=vca.logger)
-
- provider_network = None
- available_networks = None
- add_vdc_rest_url = None
-
- if response.status_code != requests.codes.ok:
- self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
- response.status_code))
- return None
- else:
- try:
- vm_list_xmlroot = XmlElementTree.fromstring(response.content)
- for child in vm_list_xmlroot:
- if child.tag.split("}")[1] == 'ProviderVdcReference':
- provider_network = child.attrib.get('href')
- # application/vnd.vmware.admin.providervdc+xml
- if child.tag.split("}")[1] == 'Link':
- if child.attrib.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
- and child.attrib.get('rel') == 'add':
- add_vdc_rest_url = child.attrib.get('href')
- except:
- self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
- self.logger.debug("Respond body {}".format(response.content))
- return None
-
- # find pvdc provided available network
- response = Http.get(url=provider_network,
- headers=vca.vcloud_session.get_vcloud_headers(),
- verify=vca.verify,
- logger=vca.logger)
- if response.status_code != requests.codes.ok:
- self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
- response.status_code))
- return None
-
- # available_networks.split("/")[-1]
-
- if parent_network_uuid is None:
- try:
- vm_list_xmlroot = XmlElementTree.fromstring(response.content)
- for child in vm_list_xmlroot.iter():
- if child.tag.split("}")[1] == 'AvailableNetworks':
- for networks in child.iter():
- # application/vnd.vmware.admin.network+xml
- if networks.attrib.get('href') is not None:
- available_networks = networks.attrib.get('href')
- break
- except:
- return None
-
- #Configure IP profile of the network
- ip_profile = ip_profile if ip_profile is not None else DEFAULT_IP_PROFILE
-
- gateway_address=ip_profile['gateway_address']
- dhcp_count=int(ip_profile['dhcp_count'])
- subnet_address=self.convert_cidr_to_netmask(ip_profile['subnet_address'])
-
- if ip_profile['dhcp_enabled']==True:
- dhcp_enabled='true'
- else:
- dhcp_enabled='false'
- dhcp_start_address=ip_profile['dhcp_start_address']
-
- #derive dhcp_end_address from dhcp_start_address & dhcp_count
- end_ip_int = int(netaddr.IPAddress(dhcp_start_address))
- end_ip_int += dhcp_count - 1
- dhcp_end_address = str(netaddr.IPAddress(end_ip_int))
-
- ip_version=ip_profile['ip_version']
- dns_address=ip_profile['dns_address']
-
- # either use client provided UUID or search for a first available
- # if both are not defined we return none
- if parent_network_uuid is not None:
- url_list = [vca.host, '/api/admin/network/', parent_network_uuid]
- add_vdc_rest_url = ''.join(url_list)
-
- if net_type=='ptp':
- fence_mode="isolated"
- isshared='false'
- is_inherited='false'
- data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
- <Description>Openmano created</Description>
- <Configuration>
- <IpScopes>
- <IpScope>
- <IsInherited>{1:s}</IsInherited>
- <Gateway>{2:s}</Gateway>
- <Netmask>{3:s}</Netmask>
- <Dns1>{4:s}</Dns1>
- <IsEnabled>{5:s}</IsEnabled>
- <IpRanges>
- <IpRange>
- <StartAddress>{6:s}</StartAddress>
- <EndAddress>{7:s}</EndAddress>
- </IpRange>
- </IpRanges>
- </IpScope>
- </IpScopes>
- <FenceMode>{8:s}</FenceMode>
- </Configuration>
- <IsShared>{9:s}</IsShared>
- </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
- subnet_address, dns_address, dhcp_enabled,
- dhcp_start_address, dhcp_end_address, fence_mode, isshared)
-
- else:
- fence_mode="bridged"
- is_inherited='false'
- data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
- <Description>Openmano created</Description>
- <Configuration>
- <IpScopes>
- <IpScope>
- <IsInherited>{1:s}</IsInherited>
- <Gateway>{2:s}</Gateway>
- <Netmask>{3:s}</Netmask>
- <Dns1>{4:s}</Dns1>
- <IsEnabled>{5:s}</IsEnabled>
- <IpRanges>
- <IpRange>
- <StartAddress>{6:s}</StartAddress>
- <EndAddress>{7:s}</EndAddress>
- </IpRange>
- </IpRanges>
- </IpScope>
- </IpScopes>
- <ParentNetwork href="{8:s}"/>
- <FenceMode>{9:s}</FenceMode>
- </Configuration>
- <IsShared>{10:s}</IsShared>
- </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
- subnet_address, dns_address, dhcp_enabled,
- dhcp_start_address, dhcp_end_address, available_networks,
- fence_mode, isshared)
-
- headers = vca.vcloud_session.get_vcloud_headers()
- headers['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
- try:
- response = Http.post(url=add_vdc_rest_url,
- headers=headers,
- data=data,
- verify=vca.verify,
- logger=vca.logger)
-
- if response.status_code != 201:
- self.logger.debug("Create Network POST REST API call failed. Return status code {}"
- .format(response.status_code))
- else:
- network = networkType.parseString(response.content, True)
- create_nw_task = network.get_Tasks().get_Task()[0]
-
- # if we all ok we respond with content after network creation completes
- # otherwise by default return None
- if create_nw_task is not None:
- self.logger.debug("Create Network REST : Waiting for Nw creation complete")
- status = vca.block_until_completed(create_nw_task)
- if status:
- return response.content
- else:
- self.logger.debug("create_network_rest task failed. Network Create response : {}"
- .format(response.content))
- except Exception as exp:
- self.logger.debug("create_network_rest : Exception : {} ".format(exp))
-
- return None
-
- def convert_cidr_to_netmask(self, cidr_ip=None):
- """
- Method sets convert CIDR netmask address to normal IP format
- Args:
- cidr_ip : CIDR IP address
- Returns:
- netmask : Converted netmask
- """
- if cidr_ip is not None:
- if '/' in cidr_ip:
- network, net_bits = cidr_ip.split('/')
- netmask = socket.inet_ntoa(struct.pack(">I", (0xffffffff << (32 - int(net_bits))) & 0xffffffff))
- else:
- netmask = cidr_ip
- return netmask
- return None
-
- def get_provider_rest(self, vca=None):
- """
- Method gets provider vdc view from vcloud director
-
- Args:
- network_name - is network name to be created.
- parent_network_uuid - is parent provider vdc network that will be used for mapping.
- It optional attribute. by default if no parent network indicate the first available will be used.
-
- Returns:
- The return xml content of respond or None
- """
-
- url_list = [vca.host, '/api/admin']
- response = Http.get(url=''.join(url_list),
- headers=vca.vcloud_session.get_vcloud_headers(),
- verify=vca.verify,
- logger=vca.logger)
-
- if response.status_code == requests.codes.ok:
- return response.content
- return None
-
- def create_vdc(self, vdc_name=None):
-
- vdc_dict = {}
-
- xml_content = self.create_vdc_from_tmpl_rest(vdc_name=vdc_name)
- if xml_content is not None:
- try:
- task_resp_xmlroot = XmlElementTree.fromstring(xml_content)
- for child in task_resp_xmlroot:
- if child.tag.split("}")[1] == 'Owner':
- vdc_id = child.attrib.get('href').split("/")[-1]
- vdc_dict[vdc_id] = task_resp_xmlroot.get('href')
- return vdc_dict
- except:
- self.logger.debug("Respond body {}".format(xml_content))
-
- return None
-
- def create_vdc_from_tmpl_rest(self, vdc_name=None):
- """
- Method create vdc in vCloud director based on VDC template.
- it uses pre-defined template that must be named openmano
-
- Args:
- vdc_name - name of a new vdc.
-
- Returns:
- The return xml content of respond or None
- """
-
- self.logger.info("Creating new vdc {}".format(vdc_name))
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed")
- if vdc_name is None:
- return None
-
- url_list = [vca.host, '/api/vdcTemplates']
- vm_list_rest_call = ''.join(url_list)
- response = Http.get(url=vm_list_rest_call,
- headers=vca.vcloud_session.get_vcloud_headers(),
- verify=vca.verify,
- logger=vca.logger)
-
- # container url to a template
- vdc_template_ref = None
- try:
- vm_list_xmlroot = XmlElementTree.fromstring(response.content)
- for child in vm_list_xmlroot:
- # application/vnd.vmware.admin.providervdc+xml
- # we need find a template from witch we instantiate VDC
- if child.tag.split("}")[1] == 'VdcTemplate':
- if child.attrib.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child.attrib.get(
- 'name') == 'openmano':
- vdc_template_ref = child.attrib.get('href')
- except:
- self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
- self.logger.debug("Respond body {}".format(response.content))
- return None
-
- # if we didn't found required pre defined template we return None
- if vdc_template_ref is None:
- return None
-
- try:
- # instantiate vdc
- url_list = [vca.host, '/api/org/', self.org_uuid, '/action/instantiate']
- vm_list_rest_call = ''.join(url_list)
- data = """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
- <Source href="{1:s}"></Source>
- <Description>opnemano</Description>
- </InstantiateVdcTemplateParams>""".format(vdc_name, vdc_template_ref)
- headers = vca.vcloud_session.get_vcloud_headers()
- headers['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
- response = Http.post(url=vm_list_rest_call, headers=headers, data=data, verify=vca.verify,
- logger=vca.logger)
- # if we all ok we respond with content otherwise by default None
- if response.status_code >= 200 and response.status_code < 300:
- return response.content
- return None
- except:
- self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
- self.logger.debug("Respond body {}".format(response.content))
-
- return None
-
- def create_vdc_rest(self, vdc_name=None):
- """
- Method create network in vCloud director
-
- Args:
- network_name - is network name to be created.
- parent_network_uuid - is parent provider vdc network that will be used for mapping.
- It optional attribute. by default if no parent network indicate the first available will be used.
-
- Returns:
- The return network uuid or return None
- """
-
- self.logger.info("Creating new vdc {}".format(vdc_name))
-
- vca = self.connect_as_admin()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed")
- if vdc_name is None:
- return None
-
- url_list = [vca.host, '/api/admin/org/', self.org_uuid]
- vm_list_rest_call = ''.join(url_list)
- if not (not vca.vcloud_session or not vca.vcloud_session.organization):
- response = Http.get(url=vm_list_rest_call,
- headers=vca.vcloud_session.get_vcloud_headers(),
- verify=vca.verify,
- logger=vca.logger)
-
- provider_vdc_ref = None
- add_vdc_rest_url = None
- available_networks = None
-
- if response.status_code != requests.codes.ok:
- self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
- response.status_code))
- return None
- else:
- try:
- vm_list_xmlroot = XmlElementTree.fromstring(response.content)
- for child in vm_list_xmlroot:
- # application/vnd.vmware.admin.providervdc+xml
- if child.tag.split("}")[1] == 'Link':
- if child.attrib.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
- and child.attrib.get('rel') == 'add':
- add_vdc_rest_url = child.attrib.get('href')
- except:
- self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
- self.logger.debug("Respond body {}".format(response.content))
- return None
-
- response = self.get_provider_rest(vca=vca)
- try:
- vm_list_xmlroot = XmlElementTree.fromstring(response)
- for child in vm_list_xmlroot:
- if child.tag.split("}")[1] == 'ProviderVdcReferences':
- for sub_child in child:
- provider_vdc_ref = sub_child.attrib.get('href')
- except:
- self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
- self.logger.debug("Respond body {}".format(response))
- return None
-
- if add_vdc_rest_url is not None and provider_vdc_ref is not None:
- data = """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
- <AllocationModel>ReservationPool</AllocationModel>
- <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
- <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
- </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
- <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
- <ProviderVdcReference
- name="Main Provider"
- href="{2:s}" />
- <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name),
- escape(vdc_name),
- provider_vdc_ref)
-
- headers = vca.vcloud_session.get_vcloud_headers()
- headers['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
- response = Http.post(url=add_vdc_rest_url, headers=headers, data=data, verify=vca.verify,
- logger=vca.logger)
-
- # if we all ok we respond with content otherwise by default None
- if response.status_code == 201:
- return response.content
- return None
-
- def get_vapp_details_rest(self, vapp_uuid=None, need_admin_access=False):
- """
- Method retrieve vapp detail from vCloud director
-
- Args:
- vapp_uuid - is vapp identifier.
-
- Returns:
- The return network uuid or return None
- """
-
- parsed_respond = {}
- vca = None
-
- if need_admin_access:
- vca = self.connect_as_admin()
- else:
- vca = self.connect()
-
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed")
- if vapp_uuid is None:
- return None
-
- url_list = [vca.host, '/api/vApp/vapp-', vapp_uuid]
- get_vapp_restcall = ''.join(url_list)
-
- if vca.vcloud_session and vca.vcloud_session.organization:
- response = Http.get(url=get_vapp_restcall,
- headers=vca.vcloud_session.get_vcloud_headers(),
- verify=vca.verify,
- logger=vca.logger)
-
- if response.status_code != requests.codes.ok:
- self.logger.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall,
- response.status_code))
- return parsed_respond
-
- try:
- xmlroot_respond = XmlElementTree.fromstring(response.content)
- parsed_respond['ovfDescriptorUploaded'] = xmlroot_respond.attrib['ovfDescriptorUploaded']
-
- namespaces = {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
- 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
- 'vmw': 'http://www.vmware.com/schema/ovf',
- 'vm': 'http://www.vmware.com/vcloud/v1.5',
- 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
- "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
- "xmlns":"http://www.vmware.com/vcloud/v1.5"
- }
-
- created_section = xmlroot_respond.find('vm:DateCreated', namespaces)
- if created_section is not None:
- parsed_respond['created'] = created_section.text
-
- network_section = xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces)
- if network_section is not None and 'networkName' in network_section.attrib:
- parsed_respond['networkname'] = network_section.attrib['networkName']
-
- ipscopes_section = \
- xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
- namespaces)
- if ipscopes_section is not None:
- for ipscope in ipscopes_section:
- for scope in ipscope:
- tag_key = scope.tag.split("}")[1]
- if tag_key == 'IpRanges':
- ip_ranges = scope.getchildren()
- for ipblock in ip_ranges:
- for block in ipblock:
- parsed_respond[block.tag.split("}")[1]] = block.text
- else:
- parsed_respond[tag_key] = scope.text
-
- # parse children section for other attrib
- children_section = xmlroot_respond.find('vm:Children/', namespaces)
- if children_section is not None:
- parsed_respond['name'] = children_section.attrib['name']
- parsed_respond['nestedHypervisorEnabled'] = children_section.attrib['nestedHypervisorEnabled'] \
- if "nestedHypervisorEnabled" in children_section.attrib else None
- parsed_respond['deployed'] = children_section.attrib['deployed']
- parsed_respond['status'] = children_section.attrib['status']
- parsed_respond['vmuuid'] = children_section.attrib['id'].split(":")[-1]
- network_adapter = children_section.find('vm:NetworkConnectionSection', namespaces)
- nic_list = []
- for adapters in network_adapter:
- adapter_key = adapters.tag.split("}")[1]
- if adapter_key == 'PrimaryNetworkConnectionIndex':
- parsed_respond['primarynetwork'] = adapters.text
- if adapter_key == 'NetworkConnection':
- vnic = {}
- if 'network' in adapters.attrib:
- vnic['network'] = adapters.attrib['network']
- for adapter in adapters:
- setting_key = adapter.tag.split("}")[1]
- vnic[setting_key] = adapter.text
- nic_list.append(vnic)
-
- for link in children_section:
- if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
- if link.attrib['rel'] == 'screen:acquireTicket':
- parsed_respond['acquireTicket'] = link.attrib
- if link.attrib['rel'] == 'screen:acquireMksTicket':
- parsed_respond['acquireMksTicket'] = link.attrib
-
- parsed_respond['interfaces'] = nic_list
- vCloud_extension_section = children_section.find('xmlns:VCloudExtension', namespaces)
- if vCloud_extension_section is not None:
- vm_vcenter_info = {}
- vim_info = vCloud_extension_section.find('vmext:VmVimInfo', namespaces)
- vmext = vim_info.find('vmext:VmVimObjectRef', namespaces)
- if vmext is not None:
- vm_vcenter_info["vm_moref_id"] = vmext.find('vmext:MoRef', namespaces).text
- parsed_respond["vm_vcenter_info"]= vm_vcenter_info
-
- virtual_hardware_section = children_section.find('ovf:VirtualHardwareSection', namespaces)
- vm_virtual_hardware_info = {}
- if virtual_hardware_section is not None:
- for item in virtual_hardware_section.iterfind('ovf:Item',namespaces):
- if item.find("rasd:Description",namespaces).text == "Hard disk":
- disk_size = item.find("rasd:HostResource" ,namespaces
- ).attrib["{"+namespaces['vm']+"}capacity"]
-
- vm_virtual_hardware_info["disk_size"]= disk_size
- break
-
- for link in virtual_hardware_section:
- if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
- if link.attrib['rel'] == 'edit' and link.attrib['href'].endswith("/disks"):
- vm_virtual_hardware_info["disk_edit_href"] = link.attrib['href']
- break
-
- parsed_respond["vm_virtual_hardware"]= vm_virtual_hardware_info
- except Exception as exp :
- self.logger.info("Error occurred calling rest api for getting vApp details {}".format(exp))
- return parsed_respond
-
- def acuire_console(self, vm_uuid=None):
-
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed")
- if vm_uuid is None:
- return None
-
- if not (not vca.vcloud_session or not vca.vcloud_session.organization):
- vm_dict = self.get_vapp_details_rest(self, vapp_uuid=vm_uuid)
- console_dict = vm_dict['acquireTicket']
- console_rest_call = console_dict['href']
-
- response = Http.post(url=console_rest_call,
- headers=vca.vcloud_session.get_vcloud_headers(),
- verify=vca.verify,
- logger=vca.logger)
-
- if response.status_code == requests.codes.ok:
- return response.content
-
- return None
-
- def modify_vm_disk(self, vapp_uuid, flavor_disk):
- """
- Method retrieve vm disk details
-
- Args:
- vapp_uuid - is vapp identifier.
- flavor_disk - disk size as specified in VNFD (flavor)
-
- Returns:
- The return network uuid or return None
- """
- status = None
- try:
- #Flavor disk is in GB convert it into MB
- flavor_disk = int(flavor_disk) * 1024
- vm_details = self.get_vapp_details_rest(vapp_uuid)
- if vm_details:
- vm_name = vm_details["name"]
- self.logger.info("VM: {} flavor_disk :{}".format(vm_name , flavor_disk))
-
- if vm_details and "vm_virtual_hardware" in vm_details:
- vm_disk = int(vm_details["vm_virtual_hardware"]["disk_size"])
- disk_edit_href = vm_details["vm_virtual_hardware"]["disk_edit_href"]
-
- self.logger.info("VM: {} VM_disk :{}".format(vm_name , vm_disk))
-
- if flavor_disk > vm_disk:
- status = self.modify_vm_disk_rest(disk_edit_href ,flavor_disk)
- self.logger.info("Modify disk of VM {} from {} to {} MB".format(vm_name,
- vm_disk, flavor_disk ))
- else:
- status = True
- self.logger.info("No need to modify disk of VM {}".format(vm_name))
-
- return status
- except Exception as exp:
- self.logger.info("Error occurred while modifing disk size {}".format(exp))
-
-
- def modify_vm_disk_rest(self, disk_href , disk_size):
- """
- Method retrieve modify vm disk size
-
- Args:
- disk_href - vCD API URL to GET and PUT disk data
- disk_size - disk size as specified in VNFD (flavor)
-
- Returns:
- The return network uuid or return None
- """
- vca = self.connect()
- if not vca:
- raise vimconn.vimconnConnectionException("self.connect() is failed")
- if disk_href is None or disk_size is None:
- return None
-
- if vca.vcloud_session and vca.vcloud_session.organization:
- response = Http.get(url=disk_href,
- headers=vca.vcloud_session.get_vcloud_headers(),
- verify=vca.verify,
- logger=vca.logger)
-
- if response.status_code != requests.codes.ok:
- self.logger.debug("GET REST API call {} failed. Return status code {}".format(disk_href,
- response.status_code))
- return None
- try:
- lxmlroot_respond = lxmlElementTree.fromstring(response.content)
- namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.iteritems() if prefix}
- namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
-
- for item in lxmlroot_respond.iterfind('xmlns:Item',namespaces):
- if item.find("rasd:Description",namespaces).text == "Hard disk":
- disk_item = item.find("rasd:HostResource" ,namespaces )
- if disk_item is not None:
- disk_item.attrib["{"+namespaces['xmlns']+"}capacity"] = str(disk_size)
- break
-
- data = lxmlElementTree.tostring(lxmlroot_respond, encoding='utf8', method='xml',
- xml_declaration=True)
-
- #Send PUT request to modify disk size
- headers = vca.vcloud_session.get_vcloud_headers()
- headers['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
-
- response = Http.put(url=disk_href,
- data=data,
- headers=headers,
- verify=vca.verify, logger=self.logger)
-
- if response.status_code != 202:
- self.logger.debug("PUT REST API call {} failed. Return status code {}".format(disk_href,
- response.status_code))
- else:
- modify_disk_task = taskType.parseString(response.content, True)
- if type(modify_disk_task) is GenericTask:
- status = vca.block_until_completed(modify_disk_task)
- return status
-
- return None
-
- except Exception as exp :
- self.logger.info("Error occurred calling rest api for modifing disk size {}".format(exp))
- return None
-
- def add_pci_devices(self, vapp_uuid , pci_devices , vmname_andid):
- """
- Method to attach pci devices to VM
-
- Args:
- vapp_uuid - uuid of vApp/VM
- pci_devices - pci devices infromation as specified in VNFD (flavor)
-
- Returns:
- The status of add pci device task , vm object and
- vcenter_conect object
- """
- vm_obj = None
- vcenter_conect = None
- self.logger.info("Add pci devices {} into vApp {}".format(pci_devices , vapp_uuid))
- try:
- vm_vcenter_info = self.get_vm_vcenter_info(vapp_uuid)
- except Exception as exp:
- self.logger.error("Error occurred while getting vCenter infromationn"\
- " for VM : {}".format(exp))
- raise vimconn.vimconnException(message=exp)
-
- if vm_vcenter_info["vm_moref_id"]:
- context = None
- if hasattr(ssl, '_create_unverified_context'):
- context = ssl._create_unverified_context()
- try:
- no_of_pci_devices = len(pci_devices)
- if no_of_pci_devices > 0:
- vcenter_conect = SmartConnect(
- host=vm_vcenter_info["vm_vcenter_ip"],
- user=vm_vcenter_info["vm_vcenter_user"],
- pwd=vm_vcenter_info["vm_vcenter_password"],
- port=int(vm_vcenter_info["vm_vcenter_port"]),
- sslContext=context)
- atexit.register(Disconnect, vcenter_conect)
- content = vcenter_conect.RetrieveContent()
-
- #Get VM and its host
- host_obj, vm_obj = self.get_vm_obj(content ,vm_vcenter_info["vm_moref_id"])
- self.logger.info("VM {} is currently on host {}".format(vm_obj, host_obj))
- if host_obj and vm_obj:
- #get PCI devies from host on which vapp is currently installed
- avilable_pci_devices = self.get_pci_devices(host_obj, no_of_pci_devices)
-
- if avilable_pci_devices is None:
- #find other hosts with active pci devices
- new_host_obj , avilable_pci_devices = self.get_host_and_PCIdevices(
- content,
- no_of_pci_devices
- )
-
- if new_host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
- #Migrate vm to the host where PCI devices are availble
- self.logger.info("Relocate VM {} on new host {}".format(vm_obj, new_host_obj))
- task = self.relocate_vm(new_host_obj, vm_obj)
- if task is not None:
- result = self.wait_for_vcenter_task(task, vcenter_conect)
- self.logger.info("Migrate VM status: {}".format(result))
- host_obj = new_host_obj
- else:
- self.logger.info("Fail to migrate VM : {}".format(result))
- raise vimconn.vimconnNotFoundException(
- "Fail to migrate VM : {} to host {}".format(
- vmname_andid,
- new_host_obj)
- )
-
- if host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
- #Add PCI devices one by one
- for pci_device in avilable_pci_devices:
- task = self.add_pci_to_vm(host_obj, vm_obj, pci_device)
- if task:
- status= self.wait_for_vcenter_task(task, vcenter_conect)
- if status:
- self.logger.info("Added PCI device {} to VM {}".format(pci_device,str(vm_obj)))
- else:
- self.logger.error("Fail to add PCI device {} to VM {}".format(pci_device,str(vm_obj)))
- return True, vm_obj, vcenter_conect
- else:
- self.logger.error("Currently there is no host with"\
- " {} number of avaialble PCI devices required for VM {}".format(
- no_of_pci_devices,
- vmname_andid)
- )
- raise vimconn.vimconnNotFoundException(
- "Currently there is no host with {} "\
- "number of avaialble PCI devices required for VM {}".format(
- no_of_pci_devices,
- vmname_andid))
- else:
- self.logger.debug("No infromation about PCI devices {} ",pci_devices)
-
- except vmodl.MethodFault as error:
- self.logger.error("Error occurred while adding PCI devices {} ",error)
- return None, vm_obj, vcenter_conect
-
- def get_vm_obj(self, content, mob_id):
- """
- Method to get the vsphere VM object associated with a given morf ID
- Args:
- vapp_uuid - uuid of vApp/VM
- content - vCenter content object
- mob_id - mob_id of VM
-
- Returns:
- VM and host object
- """
- vm_obj = None
- host_obj = None
- try :
- container = content.viewManager.CreateContainerView(content.rootFolder,
- [vim.VirtualMachine], True
- )
- for vm in container.view:
- mobID = vm._GetMoId()
- if mobID == mob_id:
- vm_obj = vm
- host_obj = vm_obj.runtime.host
- break
- except Exception as exp:
- self.logger.error("Error occurred while finding VM object : {}".format(exp))
- return host_obj, vm_obj
-
- def get_pci_devices(self, host, need_devices):
- """
- Method to get the details of pci devices on given host
- Args:
- host - vSphere host object
- need_devices - number of pci devices needed on host
-
- Returns:
- array of pci devices
- """
- all_devices = []
- all_device_ids = []
- used_devices_ids = []
-
- try:
- if host:
- pciPassthruInfo = host.config.pciPassthruInfo
- pciDevies = host.hardware.pciDevice
-
- for pci_status in pciPassthruInfo:
- if pci_status.passthruActive:
- for device in pciDevies:
- if device.id == pci_status.id:
- all_device_ids.append(device.id)
- all_devices.append(device)
-
- #check if devices are in use
- avalible_devices = all_devices
- for vm in host.vm:
- if vm.runtime.powerState == vim.VirtualMachinePowerState.poweredOn:
- vm_devices = vm.config.hardware.device
- for device in vm_devices:
- if type(device) is vim.vm.device.VirtualPCIPassthrough:
- if device.backing.id in all_device_ids:
- for use_device in avalible_devices:
- if use_device.id == device.backing.id:
- avalible_devices.remove(use_device)
- used_devices_ids.append(device.backing.id)
- self.logger.debug("Device {} from devices {}"\
- "is in use".format(device.backing.id,
- device)
- )
- if len(avalible_devices) < need_devices:
- self.logger.debug("Host {} don't have {} number of active devices".format(host,
- need_devices))
- self.logger.debug("found only {} devives {}".format(len(avalible_devices),
- avalible_devices))
- return None
- else:
- required_devices = avalible_devices[:need_devices]
- self.logger.info("Found {} PCI devivces on host {} but required only {}".format(
- len(avalible_devices),
- host,
- need_devices))
- self.logger.info("Retruning {} devices as {}".format(need_devices,
- required_devices ))
- return required_devices
-
- except Exception as exp:
- self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host))
-
- return None
-
- def get_host_and_PCIdevices(self, content, need_devices):
- """
- Method to get the details of pci devices infromation on all hosts
-
- Args:
- content - vSphere host object
- need_devices - number of pci devices needed on host
-
- Returns:
- array of pci devices and host object
- """
- host_obj = None
- pci_device_objs = None
- try:
- if content:
- container = content.viewManager.CreateContainerView(content.rootFolder,
- [vim.HostSystem], True)
- for host in container.view:
- devices = self.get_pci_devices(host, need_devices)
- if devices:
- host_obj = host
- pci_device_objs = devices
- break
- except Exception as exp:
- self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host_obj))
-
- return host_obj,pci_device_objs
-
- def relocate_vm(self, dest_host, vm) :
- """
- Method to get the relocate VM to new host
-
- Args:
- dest_host - vSphere host object
- vm - vSphere VM object
-
- Returns:
- task object
- """
- task = None
- try:
- relocate_spec = vim.vm.RelocateSpec(host=dest_host)
- task = vm.Relocate(relocate_spec)
- self.logger.info("Migrating {} to destination host {}".format(vm, dest_host))
- except Exception as exp:
- self.logger.error("Error occurred while relocate VM {} to new host {}: {}".format(
- dest_host, vm, exp))
- return task
-
- def wait_for_vcenter_task(self, task, actionName='job', hideResult=False):
- """
- Waits and provides updates on a vSphere task
- """
- while task.info.state == vim.TaskInfo.State.running:
- time.sleep(2)
-
- if task.info.state == vim.TaskInfo.State.success:
- if task.info.result is not None and not hideResult:
- self.logger.info('{} completed successfully, result: {}'.format(
- actionName,
- task.info.result))
- else:
- self.logger.info('Task {} completed successfully.'.format(actionName))
- else:
- self.logger.error('{} did not complete successfully: {} '.format(
- actionName,
- task.info.error)
- )
-
- return task.info.result
-
- def add_pci_to_vm(self,host_object, vm_object, host_pci_dev):
- """
- Method to add pci device in given VM
-
- Args:
- host_object - vSphere host object
- vm_object - vSphere VM object
- host_pci_dev - host_pci_dev must be one of the devices from the
- host_object.hardware.pciDevice list
- which is configured as a PCI passthrough device
-
- Returns:
- task object
- """
- task = None
- if vm_object and host_object and host_pci_dev:
- try :
- #Add PCI device to VM
- pci_passthroughs = vm_object.environmentBrowser.QueryConfigTarget(host=None).pciPassthrough
- systemid_by_pciid = {item.pciDevice.id: item.systemId for item in pci_passthroughs}
-
- if host_pci_dev.id not in systemid_by_pciid:
- self.logger.error("Device {} is not a passthrough device ".format(host_pci_dev))
- return None
-
- deviceId = hex(host_pci_dev.deviceId % 2**16).lstrip('0x')
- backing = vim.VirtualPCIPassthroughDeviceBackingInfo(deviceId=deviceId,
- id=host_pci_dev.id,
- systemId=systemid_by_pciid[host_pci_dev.id],
- vendorId=host_pci_dev.vendorId,
- deviceName=host_pci_dev.deviceName)
-
- hba_object = vim.VirtualPCIPassthrough(key=-100, backing=backing)
-
- new_device_config = vim.VirtualDeviceConfigSpec(device=hba_object)
- new_device_config.operation = "add"
- vmConfigSpec = vim.vm.ConfigSpec()
- vmConfigSpec.deviceChange = [new_device_config]
-
- task = vm_object.ReconfigVM_Task(spec=vmConfigSpec)
- self.logger.info("Adding PCI device {} into VM {} from host {} ".format(
- host_pci_dev, vm_object, host_object)
- )
- except Exception as exp:
- self.logger.error("Error occurred while adding pci devive {} to VM {}: {}".format(
- host_pci_dev,
- vm_object,
- exp))
- return task
-
- def get_vm_vcenter_info(self , vapp_uuid):
- """
- Method to get details of vCenter and vm
-
- Args:
- vapp_uuid - uuid of vApp or VM
-
- Returns:
- Moref Id of VM and deails of vCenter
- """
- vm_vcenter_info = {}
-
- if self.vcenter_ip is not None:
- vm_vcenter_info["vm_vcenter_ip"] = self.vcenter_ip
- else:
- raise vimconn.vimconnException(message="vCenter IP is not provided."\
- " Please provide vCenter IP while attaching datacenter to tenant in --config")
- if self.vcenter_port is not None:
- vm_vcenter_info["vm_vcenter_port"] = self.vcenter_port
- else:
- raise vimconn.vimconnException(message="vCenter port is not provided."\
- " Please provide vCenter port while attaching datacenter to tenant in --config")
- if self.vcenter_user is not None:
- vm_vcenter_info["vm_vcenter_user"] = self.vcenter_user
- else:
- raise vimconn.vimconnException(message="vCenter user is not provided."\
- " Please provide vCenter user while attaching datacenter to tenant in --config")
-
- if self.vcenter_password is not None:
- vm_vcenter_info["vm_vcenter_password"] = self.vcenter_password
- else:
- raise vimconn.vimconnException(message="vCenter user password is not provided."\
- " Please provide vCenter user password while attaching datacenter to tenant in --config")
- try:
- vm_details = self.get_vapp_details_rest(vapp_uuid, need_admin_access=True)
- if vm_details and "vm_vcenter_info" in vm_details:
- vm_vcenter_info["vm_moref_id"] = vm_details["vm_vcenter_info"].get("vm_moref_id", None)
-
- return vm_vcenter_info
-
- except Exception as exp:
- self.logger.error("Error occurred while getting vCenter infromationn"\
- " for VM : {}".format(exp))
-
-
- def get_vm_pci_details(self, vmuuid):
- """
- Method to get VM PCI device details from vCenter
-
- Args:
- vm_obj - vSphere VM object
-
- Returns:
- dict of PCI devives attached to VM
-
- """
- vm_pci_devices_info = {}
- try:
- vm_vcenter_info = self.get_vm_vcenter_info(vmuuid)
- if vm_vcenter_info["vm_moref_id"]:
- context = None
- if hasattr(ssl, '_create_unverified_context'):
- context = ssl._create_unverified_context()
- vcenter_conect = SmartConnect(host=vm_vcenter_info["vm_vcenter_ip"],
- user=vm_vcenter_info["vm_vcenter_user"],
- pwd=vm_vcenter_info["vm_vcenter_password"],
- port=int(vm_vcenter_info["vm_vcenter_port"]),
- sslContext=context
- )
- atexit.register(Disconnect, vcenter_conect)
- content = vcenter_conect.RetrieveContent()
-
- #Get VM and its host
- if content:
- host_obj, vm_obj = self.get_vm_obj(content ,vm_vcenter_info["vm_moref_id"])
- if host_obj and vm_obj:
- vm_pci_devices_info["host_name"]= host_obj.name
- vm_pci_devices_info["host_ip"]= host_obj.config.network.vnic[0].spec.ip.ipAddress
- for device in vm_obj.config.hardware.device:
- if type(device) == vim.vm.device.VirtualPCIPassthrough:
- device_details={'devide_id':device.backing.id,
- 'pciSlotNumber':device.slotInfo.pciSlotNumber,
- }
- vm_pci_devices_info[device.deviceInfo.label] = device_details
- else:
- self.logger.error("Can not connect to vCenter while getting "\
- "PCI devices infromationn")
- return vm_pci_devices_info
- except Exception as exp:
- self.logger.error("Error occurred while getting VM infromationn"\
- " for VM : {}".format(exp))
- raise vimconn.vimconnException(message=exp)
-
+++ /dev/null
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-##
-# This file is standalone vmware vcloud director util
-# 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: mbayramov@vmware.com
-##
-
-"""
-
-Standalone application that leverage openmano vmware connector work with vCloud director rest api.
-
- - Provides capability to create and delete VDC for specific organization.
- - Create, delete and manage network for specific VDC
- - List deployed VM's , VAPPs, VDSs, Organization
- - View detail information about VM / Vapp , Organization etc
- - Operate with images upload / boot / power on etc
-
- Usage example.
-
- List organization created in vCloud director
- vmwarecli.py -u admin -p qwerty123 -c 172.16.254.206 -U Administrator -P qwerty123 -o test -v TEF list org
-
- List VDC for particular organization
- vmwarecli.py -u admin -p qwerty123 -c 172.16.254.206 -U Administrator -P qwerty123 -o test -v TEF list vdc
-
- Upload image
- python vmwarerecli.py image upload /Users/spyroot/Developer/Openmano/Ro/vnfs/cirros/cirros.ovf
-
- Boot Image
- python vmwarerecli.py -u admin -p qwerty123 -c 172.16.254.206 -o test -v TEF image boot cirros cirros
-
- View vApp
- python vmwarerecli.py -u admin -p qwerty123 -c 172.16.254.206 -o test -v TEF view vapp 90bd2b4e-f782-46cf-b5e2-c3817dcf6633 -u
-
- List VMS
- python vmwarerecli.py -u admin -p qwerty123 -c 172.16.254.206 -o test -v TEF list vms
-
- List VDC in OSM format
- python vmwarerecli.py -u admin -p qwerty123 -c 172.16.254.206 -o test -v TEF list vdc -o
-
-Mustaafa Bayramov
-mbayramov@vmware.com
-"""
-import os
-import argparse
-import traceback
-import uuid
-
-from xml.etree import ElementTree as ET
-
-import sys
-from pyvcloud import Http
-
-import logging
-import vimconn
-import time
-import uuid
-import urllib3
-import requests
-
-from vimconn_vmware import vimconnector
-from requests.packages.urllib3.exceptions import InsecureRequestWarning
-from prettytable import PrettyTable
-
-requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
-
-__author__ = "Mustafa Bayramov"
-__date__ = "$16-Sep-2016 11:09:29$"
-
-
-# TODO move to main vim
-def delete_network_action(vca=None, network_uuid=None):
- """
- Method leverages vCloud director and query network based on network uuid
-
- Args:
- vca - is active VCA connection.
- network_uuid - is a network uuid
-
- Returns:
- The return XML respond
- """
-
- if vca is None or network_uuid is None:
- return None
-
- url_list = [vca.host, '/api/admin/network/', network_uuid]
- vm_list_rest_call = ''.join(url_list)
-
- if not (not vca.vcloud_session or not vca.vcloud_session.organization):
- response = Http.get(url=vm_list_rest_call,
- headers=vca.vcloud_session.get_vcloud_headers(),
- verify=vca.verify,
- logger=vca.logger)
- if response.status_code == requests.codes.ok:
- print response.content
- return response.content
-
- return None
-
-
-def print_vapp(vapp_dict=None):
- """ Method takes vapp_dict and print in tabular format
-
- Args:
- vapp_dict: container vapp object.
-
- Returns:
- The return nothing
- """
-
- # following key available to print
- # {'status': 'POWERED_OFF', 'storageProfileName': '*', 'hardwareVersion': '7', 'vmToolsVersion': '0',
- # 'memoryMB': '384',
- # 'href': 'https://172.16.254.206/api/vAppTemplate/vm-129e22e8-08dc-4cb6-8358-25f635e65d3b',
- # 'isBusy': 'false', 'isDeployed': 'false', 'isInMaintenanceMode': 'false', 'isVAppTemplate': 'true',
- # 'networkName': 'nat', 'isDeleted': 'false', 'catalogName': 'Cirros',
- # 'containerName': 'Cirros Template', # 'container':
- # 'https://172.16.254.206/api/vAppTemplate/vappTemplate-b966453d-c361-4505-9e38-ccef45815e5d',
- # 'name': 'Cirros', 'pvdcHighestSupportedHardwareVersion': '11', 'isPublished': 'false',
- # 'numberOfCpus': '1', 'vdc': 'https://172.16.254.206/api/vdc/a5056f85-418c-4bfd-8041-adb0f48be9d9',
- # 'guestOs': 'Other (32-bit)', 'isVdcEnabled': 'true'}
-
- if vapp_dict is None:
- return
-
- vm_table = PrettyTable(['vm uuid',
- 'vapp name',
- 'vapp uuid',
- 'network name',
- 'storage name',
- 'vcpu', 'memory', 'hw ver','deployed','status'])
- for k in vapp_dict:
- entry = []
- entry.append(k)
- entry.append(vapp_dict[k]['containerName'])
- # vm-b1f5cd4c-2239-4c89-8fdc-a41ff18e0d61
- entry.append(vapp_dict[k]['container'].split('/')[-1:][0][5:])
- entry.append(vapp_dict[k]['networkName'])
- entry.append(vapp_dict[k]['storageProfileName'])
- entry.append(vapp_dict[k]['numberOfCpus'])
- entry.append(vapp_dict[k]['memoryMB'])
- entry.append(vapp_dict[k]['pvdcHighestSupportedHardwareVersion'])
- entry.append(vapp_dict[k]['isDeployed'])
- entry.append(vapp_dict[k]['status'])
-
- vm_table.add_row(entry)
-
- print vm_table
-
-
-def print_org(org_dict=None):
- """ Method takes vapp_dict and print in tabular format
-
- Args:
- org_dict: dictionary of organization where key is org uuid.
-
- Returns:
- The return nothing
- """
-
- if org_dict is None:
- return
-
- org_table = PrettyTable(['org uuid', 'name'])
- for k in org_dict:
- entry = [k, org_dict[k]]
- org_table.add_row(entry)
-
- print org_table
-
-
-def print_vm_list(vm_dict=None):
- """ Method takes vapp_dict and print in tabular format
-
- Args:
- vm_dict: dictionary of organization where key is org uuid.
-
- Returns:
- The return nothing
- """
- if vm_dict is None:
- return
-
- vm_table = PrettyTable(
- ['vm uuid', 'vm name', 'vapp uuid', 'vdc uuid', 'network name', 'is deployed', 'vcpu', 'memory', 'status'])
-
- try:
- for k in vm_dict:
- entry = []
- entry.append(k)
- entry.append(vm_dict[k]['name'])
- entry.append(vm_dict[k]['container'].split('/')[-1:][0][5:])
- entry.append(vm_dict[k]['vdc'].split('/')[-1:][0])
- entry.append(vm_dict[k]['networkName'])
- entry.append(vm_dict[k]['isDeployed'])
- entry.append(vm_dict[k]['numberOfCpus'])
- entry.append(vm_dict[k]['memoryMB'])
- entry.append(vm_dict[k]['status'])
- vm_table.add_row(entry)
- print vm_table
- except KeyError:
- logger.error("wrong key {}".format(KeyError.message))
- pass
-
-
-def print_vdc_list(org_dict=None):
- """ Method takes vapp_dict and print in tabular format
-
- Args:
- org_dict: dictionary of organization where key is org uuid.
-
- Returns:
- The return nothing
- """
- if org_dict is None:
- return
- try:
- vdcs_dict = {}
- if org_dict.has_key('vdcs'):
- vdcs_dict = org_dict['vdcs']
- vdc_table = PrettyTable(['vdc uuid', 'vdc name'])
- for k in vdcs_dict:
- entry = [k, vdcs_dict[k]]
- vdc_table.add_row(entry)
-
- print vdc_table
- except KeyError:
- logger.error("wrong key {}".format(KeyError.message))
- logger.logger.debug(traceback.format_exc())
-
-
-def print_network_list(org_dict=None):
- """ Method print network list.
-
- Args:
- org_dict: dictionary of organization that contain key networks with a list of all
- network for for specific VDC
-
- Returns:
- The return nothing
- """
- if org_dict is None:
- return
- try:
- network_dict = {}
- if org_dict.has_key('networks'):
- network_dict = org_dict['networks']
- network_table = PrettyTable(['network uuid', 'network name'])
- for k in network_dict:
- entry = [k, network_dict[k]]
- network_table.add_row(entry)
-
- print network_table
-
- except KeyError:
- logger.error("wrong key {}".format(KeyError.message))
- logger.logger.debug(traceback.format_exc())
-
-
-def print_org_details(org_dict=None):
- """ Method takes vapp_dict and print in tabular format
-
- Args:
- org_dict: dictionary of organization where key is org uuid.
-
- Returns:
- The return nothing
- """
- if org_dict is None:
- return
- try:
- catalogs_dict = {}
-
- print_vdc_list(org_dict=org_dict)
- print_network_list(org_dict=org_dict)
-
- if org_dict.has_key('catalogs'):
- catalogs_dict = org_dict['catalogs']
-
- catalog_table = PrettyTable(['catalog uuid', 'catalog name'])
- for k in catalogs_dict:
- entry = [k, catalogs_dict[k]]
- catalog_table.add_row(entry)
-
- print catalog_table
-
- except KeyError:
- logger.error("wrong key {}".format(KeyError.message))
- logger.logger.debug(traceback.format_exc())
-
-
-def delete_actions(vim=None, action=None, namespace=None):
- if action == 'network' or namespace.action == 'network':
- logger.debug("Requesting delete for network {}".format(namespace.network_name))
- network_uuid = namespace.network_name
- # if request name based we need find UUID
- # TODO optimize it or move to external function
- if not namespace.uuid:
- org_dict = vim.get_org_list()
- for org in org_dict:
- org_net = vim.get_org(org)['networks']
- for network in org_net:
- if org_net[network] == namespace.network_name:
- network_uuid = network
-
- vim.delete_network_action(network_uuid=network_uuid)
-
-
-def list_actions(vim=None, action=None, namespace=None):
- """ Method provide list object from VDC action
-
- Args:
- vim - is vcloud director vim connector.
- action - is action for list ( vdc / org etc)
- namespace - must contain VDC / Org information.
-
- Returns:
- The return nothing
- """
-
- org_id = None
- myorgs = vim.get_org_list()
- for org in myorgs:
- if myorgs[org] == namespace.vcdorg:
- org_id = org
- break
- else:
- print(" Invalid organization.")
- return
-
- if action == 'vms' or namespace.action == 'vms':
- vm_dict = vim.get_vm_list(vdc_name=namespace.vcdvdc)
- print_vm_list(vm_dict=vm_dict)
- elif action == 'vapps' or namespace.action == 'vapps':
- vapp_dict = vim.get_vapp_list(vdc_name=namespace.vcdvdc)
- print_vapp(vapp_dict=vapp_dict)
- elif action == 'networks' or namespace.action == 'networks':
- if namespace.osm:
- osm_print(vim.get_network_list(filter_dict={}))
- else:
- print_network_list(vim.get_org(org_uuid=org_id))
- elif action == 'vdc' or namespace.action == 'vdc':
- if namespace.osm:
- osm_print(vim.get_tenant_list(filter_dict=None))
- else:
- print_vdc_list(vim.get_org(org_uuid=org_id))
- elif action == 'org' or namespace.action == 'org':
- print_org(org_dict=vim.get_org_list())
- else:
- return None
-
-
-def print_network_details(network_dict=None):
- try:
- network_table = PrettyTable(network_dict.keys())
- entry = [network_dict.values()]
- network_table.add_row(entry[0])
- print network_table
- except KeyError:
- logger.error("wrong key {}".format(KeyError.message))
- logger.logger.debug(traceback.format_exc())
-
-
-def osm_print(generic_dict=None):
-
- try:
- for element in generic_dict:
- table = PrettyTable(element.keys())
- entry = [element.values()]
- table.add_row(entry[0])
- print table
- except KeyError:
- logger.error("wrong key {}".format(KeyError.message))
- logger.logger.debug(traceback.format_exc())
-
-
-def view_actions(vim=None, action=None, namespace=None):
- org_id = None
- orgs = vim.get_org_list()
- for org in orgs:
- if orgs[org] == namespace.vcdorg:
- org_id = org
- break
- else:
- print(" Invalid organization.")
- return
-
- myorg = vim.get_org(org_uuid=org_id)
-
- # view org
- if action == 'org' or namespace.action == 'org':
- org_id = None
- orgs = vim.get_org_list()
- if namespace.uuid:
- if namespace.org_name in orgs:
- org_id = namespace.org_name
- else:
- # we need find UUID based on name provided
- for org in orgs:
- if orgs[org] == namespace.org_name:
- org_id = org
- break
-
- logger.debug("Requesting view for orgs {}".format(org_id))
- print_org_details(vim.get_org(org_uuid=org_id))
-
- # view vapp action
- if action == 'vapp' or namespace.action == 'vapp':
- if namespace.vapp_name is not None and namespace.uuid:
- logger.debug("Requesting vapp {} for vdc {}".format(namespace.vapp_name, namespace.vcdvdc))
- vapp_dict = {}
- vapp_uuid = namespace.vapp_name
- # if request based on just name we need get UUID
- if not namespace.uuid:
- vapp_uuid = vim.get_vappid(vdc=namespace.vcdvdc, vapp_name=namespace.vapp_name)
- if vapp_uuid is None:
- print("Can't find vapp by given name {}".format(namespace.vapp_name))
- return
-
- print " namespace {}".format(namespace)
- if vapp_dict is not None and namespace.osm:
- vm_info_dict = vim.get_vminstance(vim_vm_uuid=vapp_uuid)
- print vm_info_dict
- if vapp_dict is not None and namespace.osm != True:
- vapp_dict = vim.get_vapp(vdc_name=namespace.vcdvdc, vapp_name=vapp_uuid, isuuid=True)
- print_vapp(vapp_dict=vapp_dict)
-
- # view network
- if action == 'network' or namespace.action == 'network':
- logger.debug("Requesting view for network {}".format(namespace.network_name))
- network_uuid = namespace.network_name
- # if request name based we need find UUID
- # TODO optimize it or move to external function
- if not namespace.uuid:
- if not myorg.has_key('networks'):
- print("Network {} is undefined in vcloud director for org {} vdc {}".format(namespace.network_name,
- vim.name,
- vim.tenant_name))
- return
-
- my_org_net = myorg['networks']
- for network in my_org_net:
- if my_org_net[network] == namespace.network_name:
- network_uuid = network
- break
-
- print print_network_details(network_dict=vim.get_vcd_network(network_uuid=network_uuid))
-
-
-def create_actions(vim=None, action=None, namespace=None):
- """Method gets provider vdc view from vcloud director
-
- Args:
- vim - is Cloud director vim connector
- action - action for create ( network / vdc etc)
-
- Returns:
- The return xml content of respond or None
- """
- if action == 'network' or namespace.action == 'network':
- logger.debug("Creating a network in vcloud director".format(namespace.network_name))
- network_uuid = vim.create_network(namespace.network_name)
- if network_uuid is not None:
- print ("Crated new network {} and uuid: {}".format(namespace.network_name, network_uuid))
- else:
- print ("Failed create a new network {}".format(namespace.network_name))
- elif action == 'vdc' or namespace.action == 'vdc':
- logger.debug("Creating a new vdc in vcloud director.".format(namespace.vdc_name))
- vdc_uuid = vim.create_vdc(namespace.vdc_name)
- if vdc_uuid is not None:
- print ("Crated new vdc {} and uuid: {}".format(namespace.vdc_name, vdc_uuid))
- else:
- print ("Failed create a new vdc {}".format(namespace.vdc_name))
- else:
- return None
-
-
-def validate_uuid4(uuid_string):
- """Function validate that string contain valid uuid4
-
- Args:
- uuid_string - valid UUID string
-
- Returns:
- The return true if string contain valid UUID format
- """
- try:
- val = uuid.UUID(uuid_string, version=4)
- except ValueError:
- return False
- return True
-
-
-def upload_image(vim=None, image_file=None):
- """Function upload image to vcloud director
-
- Args:
- image_file - valid UUID string
-
- Returns:
- The return true if image uploaded correctly
- """
- try:
- catalog_uuid = vim.get_image_id_from_path(path=image_file, progress=True)
- if catalog_uuid is not None and validate_uuid4(catalog_uuid):
- print("Image uploaded and uuid {}".format(catalog_uuid))
- return True
- except vimconn.vimconnException as upload_exception:
- print("Failed uploaded {} image".format(image_file))
- print("Error Reason: {}".format(upload_exception.message))
- return False
-
-
-def boot_image(vim=None, image_name=None, vm_name=None):
- """ Function boot image that resided in vcloud director.
- The image name can be UUID of name.
-
- Args:
- vim - vim connector
- image_name - image identified by UUID or text string.
- vm_name - vmname
-
-
- Returns:
- The return true if image uploaded correctly
- """
-
- vim_catalog = None
- try:
- catalogs = vim.vca.get_catalogs()
- if not validate_uuid4(image_name):
- vim_catalog = vim.get_catalogid(catalog_name=image_name, catalogs=catalogs)
- if vim_catalog is None:
- return None
- else:
- vim_catalog = vim.get_catalogid(catalog_name=image_name, catalogs=catalogs)
- if vim_catalog is None:
- return None
-
- print (" Booting {} image id {} ".format(vm_name, vim_catalog))
- vm_uuid = vim.new_vminstance(name=vm_name, image_id=vim_catalog)
- if vm_uuid is not None and validate_uuid4(vm_uuid):
- print("Image booted and vm uuid {}".format(vm_uuid))
- vapp_dict = vim.get_vapp(vdc_name=namespace.vcdvdc, vapp_name=vm_uuid, isuuid=True)
- if vapp_dict is not None:
- print_vapp(vapp_dict=vapp_dict)
- return True
- except vimconn.vimconnNotFoundException as notFound:
- print("Failed boot {} image".format(image_name))
- print(notFound.message)
- except vimconn.vimconnException as vimconError:
- print("Failed boot {} image".format(image_name))
- print(vimconError.message)
- except:
- print("Failed boot {} image".format(image_name))
-
-
- return False
-
-
-def image_action(vim=None, action=None, namespace=None):
- """ Function present set of action to manipulate with image.
- - upload image
- - boot image.
- - delete image ( not yet done )
-
- Args:
- vim - vcloud director connector
- action - string (upload/boot etc)
- namespace - contain other attributes image name etc
-
- Returns:
- The return nothing
- """
-
- if action == 'upload' or namespace.action == 'upload':
- upload_image(vim=vim, image_file=namespace.image)
- elif action == 'boot' or namespace.action == 'boot':
- boot_image(vim=vim, image_name=namespace.image, vm_name=namespace.vmname)
- else:
- return None
-
-
-def vmwarecli(command=None, action=None, namespace=None):
- logger.debug("Namespace {}".format(namespace))
- urllib3.disable_warnings()
-
- vcduser = None
- vcdpasword = None
- vcdhost = None
- vcdorg = None
-
- if hasattr(__builtins__, 'raw_input'):
- input = raw_input
-
- if namespace.vcdvdc is None:
- while True:
- vcduser = input("Enter vcd username: ")
- if vcduser is not None and len(vcduser) > 0:
- break
- else:
- vcduser = namespace.vcduser
-
- if namespace.vcdpassword is None:
- while True:
- vcdpasword = input("Please enter vcd password: ")
- if vcdpasword is not None and len(vcdpasword) > 0:
- break
- else:
- vcdpasword = namespace.vcdpassword
-
- if namespace.vcdhost is None:
- while True:
- vcdhost = input("Please enter vcd host name or ip: ")
- if vcdhost is not None and len(vcdhost) > 0:
- break
- else:
- vcdhost = namespace.vcdhost
-
- if namespace.vcdorg is None:
- while True:
- vcdorg = input("Please enter vcd organization name: ")
- if vcdorg is not None and len(vcdorg) > 0:
- break
- else:
- vcdorg = namespace.vcdorg
-
- try:
- vim = vimconnector(uuid=None,
- name=vcdorg,
- tenant_id=None,
- tenant_name=namespace.vcdvdc,
- url=vcdhost,
- url_admin=vcdhost,
- user=vcduser,
- passwd=vcdpasword,
- log_level="DEBUG",
- config={'admin_username': namespace.vcdamdin, 'admin_password': namespace.vcdadminpassword})
- vim.vca = vim.connect()
-
- except vimconn.vimconnConnectionException:
- print("Failed connect to vcloud director. Please check credential and hostname.")
- return
-
- # list
- if command == 'list' or namespace.command == 'list':
- logger.debug("Client requested list action")
- # route request to list actions
- list_actions(vim=vim, action=action, namespace=namespace)
-
- # view action
- if command == 'view' or namespace.command == 'view':
- logger.debug("Client requested view action")
- view_actions(vim=vim, action=action, namespace=namespace)
-
- # delete action
- if command == 'delete' or namespace.command == 'delete':
- logger.debug("Client requested delete action")
- delete_actions(vim=vim, action=action, namespace=namespace)
-
- # create action
- if command == 'create' or namespace.command == 'create':
- logger.debug("Client requested create action")
- create_actions(vim=vim, action=action, namespace=namespace)
-
- # image action
- if command == 'image' or namespace.command == 'image':
- logger.debug("Client requested create action")
- image_action(vim=vim, action=action, namespace=namespace)
-
-
-if __name__ == '__main__':
- defaults = {'vcdvdc': 'default',
- 'vcduser': 'admin',
- 'vcdpassword': 'admin',
- 'vcdhost': 'https://localhost',
- 'vcdorg': 'default',
- 'debug': 'INFO'}
-
- parser = argparse.ArgumentParser()
- parser.add_argument('-u', '--vcduser', help='vcloud director username', type=str)
- parser.add_argument('-p', '--vcdpassword', help='vcloud director password', type=str)
- parser.add_argument('-U', '--vcdamdin', help='vcloud director password', type=str)
- parser.add_argument('-P', '--vcdadminpassword', help='vcloud director password', type=str)
- parser.add_argument('-c', '--vcdhost', help='vcloud director host', type=str)
- parser.add_argument('-o', '--vcdorg', help='vcloud director org', type=str)
- parser.add_argument('-v', '--vcdvdc', help='vcloud director vdc', type=str)
- parser.add_argument('-d', '--debug', help='debug level', type=int)
-
- parser_subparsers = parser.add_subparsers(help='commands', dest='command')
- sub = parser_subparsers.add_parser('list', help='List objects (VMs, vApps, networks)')
- sub_subparsers = sub.add_subparsers(dest='action')
-
- list_vms = sub_subparsers.add_parser('vms', help='list - all vm deployed in vCloud director')
- list_vapps = sub_subparsers.add_parser('vapps', help='list - all vapps deployed in vCloud director')
- list_network = sub_subparsers.add_parser('networks', help='list - all networks deployed')
- list_network.add_argument('-o', '--osm', default=False, action='store_true', help='provide view in OSM format')
-
- #list vdc
- list_vdc = sub_subparsers.add_parser('vdc', help='list - list all vdc for organization accessible to you')
- list_vdc.add_argument('-o', '--osm', default=False, action='store_true', help='provide view in OSM format')
-
- list_org = sub_subparsers.add_parser('org', help='list - list of organizations accessible to you.')
-
- create_sub = parser_subparsers.add_parser('create')
- create_sub_subparsers = create_sub.add_subparsers(dest='action')
- create_vms = create_sub_subparsers.add_parser('vms')
- create_vapp = create_sub_subparsers.add_parser('vapp')
- create_vapp.add_argument('uuid')
-
- # add network
- create_network = create_sub_subparsers.add_parser('network')
- create_network.add_argument('network_name', action='store', help='create a network for a vdc')
-
- # add VDC
- create_vdc = create_sub_subparsers.add_parser('vdc')
- create_vdc.add_argument('vdc_name', action='store', help='create a new VDC for org')
-
- delete_sub = parser_subparsers.add_parser('delete')
- del_sub_subparsers = delete_sub.add_subparsers(dest='action')
- del_vms = del_sub_subparsers.add_parser('vms')
- del_vapp = del_sub_subparsers.add_parser('vapp')
- del_vapp.add_argument('uuid', help='view vapp based on UUID')
-
- # delete network
- del_network = del_sub_subparsers.add_parser('network')
- del_network.add_argument('network_name', action='store',
- help='- delete network for vcloud director by provided name')
- del_network.add_argument('-u', '--uuid', default=False, action='store_true',
- help='delete network for vcloud director by provided uuid')
-
- # delete vdc
- del_vdc = del_sub_subparsers.add_parser('vdc')
-
- view_sub = parser_subparsers.add_parser('view')
- view_sub_subparsers = view_sub.add_subparsers(dest='action')
-
- view_vms_parser = view_sub_subparsers.add_parser('vms')
- view_vms_parser.add_argument('uuid', default=False, action='store_true',
- help='- View VM for specific uuid in vcloud director')
- view_vms_parser.add_argument('name', default=False, action='store_true',
- help='- View VM for specific vapp name in vcloud director')
-
- # view vapp
- view_vapp_parser = view_sub_subparsers.add_parser('vapp')
- view_vapp_parser.add_argument('vapp_name', action='store',
- help='- view vapp for specific vapp name in vcloud director')
- view_vapp_parser.add_argument('-u', '--uuid', default=False, action='store_true', help='view vapp based on uuid')
- view_vapp_parser.add_argument('-o', '--osm', default=False, action='store_true', help='provide view in OSM format')
-
- # view network
- view_network = view_sub_subparsers.add_parser('network')
- view_network.add_argument('network_name', action='store',
- help='- view network for specific network name in vcloud director')
- view_network.add_argument('-u', '--uuid', default=False, action='store_true', help='view network based on uuid')
-
- # view VDC command and actions
- view_vdc = view_sub_subparsers.add_parser('vdc')
- view_vdc.add_argument('vdc_name', action='store',
- help='- View VDC based and action based on provided vdc uuid')
- view_vdc.add_argument('-u', '--uuid', default=False, action='store_true', help='view vdc based on uuid')
-
- # view organization command and actions
- view_org = view_sub_subparsers.add_parser('org')
- view_org.add_argument('org_name', action='store',
- help='- View VDC based and action based on provided vdc uuid')
- view_org.add_argument('-u', '--uuid', default=False, action='store_true', help='view org based on uuid')
-
- # upload image action
- image_sub = parser_subparsers.add_parser('image')
- image_subparsers = image_sub.add_subparsers(dest='action')
- upload_parser = image_subparsers.add_parser('upload')
- upload_parser.add_argument('image', default=False, action='store', help='- valid path to OVF image ')
- upload_parser.add_argument('catalog', default=False, action='store_true', help='- catalog name')
-
- # boot vm action
- boot_parser = image_subparsers.add_parser('boot')
- boot_parser.add_argument('image', default=False, action='store', help='- Image name')
- boot_parser.add_argument('vmname', default=False, action='store', help='- VM name')
- boot_parser.add_argument('-u', '--uuid', default=False, action='store_true', help='view org based on uuid')
-
- namespace = parser.parse_args()
- # put command_line args to mapping
- command_line_args = {k: v for k, v in vars(namespace).items() if v}
-
- d = defaults.copy()
- d.update(os.environ)
- d.update(command_line_args)
-
- logger = logging.getLogger('mano.vim.vmware')
- formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
- ch = logging.StreamHandler()
- ch.setLevel(str.upper(d['debug']))
- ch.setFormatter(formatter)
- logger.addHandler(ch)
- logger.setLevel(getattr(logging, str.upper(d['debug'])))
- logger.info(
- "Connecting {} username: {} org: {} vdc: {} ".format(d['vcdhost'], d['vcduser'], d['vcdorg'], d['vcdvdc']))
-
- logger.debug("command: \"{}\" actio: \"{}\"".format(d['command'], d['action']))
-
- # main entry point.
- vmwarecli(namespace=namespace)
+++ /dev/null
-# -*- coding: utf-8 -*-
-##
-# This file is standalone vmware vcloud director util
-# 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: mbayramov@vmware.com
-##
-
-"""
-
-Standalone application that leverage openmano vmware connector work with vCloud director rest api.
-
- - Provides capability to create and delete VDC for specific organization.
- - Create, delete and manage network for specific VDC
- - List deployed VM's , VAPPs, VDSs, Organization
- - View detail information about VM / Vapp , Organization etc
- - Operate with images upload / boot / power on etc
-
- Usage example.
-
- List organization created in vCloud director
- vmwarecli.py -u admin -p qwerty123 -c 172.16.254.206 -U Administrator -P qwerty123 -o test -v TEF list org
-
- List VDC for particular organization
- vmwarecli.py -u admin -p qwerty123 -c 172.16.254.206 -U Administrator -P qwerty123 -o test -v TEF list vdc
-
- Upload image
- python vmwarerecli.py image upload /Users/spyroot/Developer/Openmano/Ro/vnfs/cirros/cirros.ovf
-
- Boot Image
- python vmwarerecli.py -u admin -p qwerty123 -c 172.16.254.206 -o test -v TEF image boot cirros cirros
-
- View vApp
- python vmwarerecli.py -u admin -p qwerty123 -c 172.16.254.206 -o test -v TEF view vapp 90bd2b4e-f782-46cf-b5e2-c3817dcf6633 -u
-
- List VMS
- python vmwarerecli.py -u admin -p qwerty123 -c 172.16.254.206 -o test -v TEF list vms
-
- List VDC in OSM format
- python vmwarerecli.py -u admin -p qwerty123 -c 172.16.254.206 -o test -v TEF list vdc -o
-
-Mustaafa Bayramov
-mbayramov@vmware.com
-"""
-import os
-import argparse
-import traceback
-import uuid
-
-from xml.etree import ElementTree as ET
-
-import sys
-from pyvcloud import Http
-
-import logging
-import vimconn
-import time
-import uuid
-import urllib3
-import requests
-
-from vimconn_vmware import vimconnector
-from requests.packages.urllib3.exceptions import InsecureRequestWarning
-from prettytable import PrettyTable
-
-requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
-
-__author__ = "Mustafa Bayramov"
-__date__ = "$16-Sep-2016 11:09:29$"
-
-
-# TODO move to main vim
-def delete_network_action(vca=None, network_uuid=None):
- """
- Method leverages vCloud director and query network based on network uuid
-
- Args:
- vca - is active VCA connection.
- network_uuid - is a network uuid
-
- Returns:
- The return XML respond
- """
-
- if vca is None or network_uuid is None:
- return None
-
- url_list = [vca.host, '/api/admin/network/', network_uuid]
- vm_list_rest_call = ''.join(url_list)
-
- if not (not vca.vcloud_session or not vca.vcloud_session.organization):
- response = Http.get(url=vm_list_rest_call,
- headers=vca.vcloud_session.get_vcloud_headers(),
- verify=vca.verify,
- logger=vca.logger)
- if response.status_code == requests.codes.ok:
- print response.content
- return response.content
-
- return None
-
-
-def print_vapp(vapp_dict=None):
- """ Method takes vapp_dict and print in tabular format
-
- Args:
- vapp_dict: container vapp object.
-
- Returns:
- The return nothing
- """
-
- # following key available to print
- # {'status': 'POWERED_OFF', 'storageProfileName': '*', 'hardwareVersion': '7', 'vmToolsVersion': '0',
- # 'memoryMB': '384',
- # 'href': 'https://172.16.254.206/api/vAppTemplate/vm-129e22e8-08dc-4cb6-8358-25f635e65d3b',
- # 'isBusy': 'false', 'isDeployed': 'false', 'isInMaintenanceMode': 'false', 'isVAppTemplate': 'true',
- # 'networkName': 'nat', 'isDeleted': 'false', 'catalogName': 'Cirros',
- # 'containerName': 'Cirros Template', # 'container':
- # 'https://172.16.254.206/api/vAppTemplate/vappTemplate-b966453d-c361-4505-9e38-ccef45815e5d',
- # 'name': 'Cirros', 'pvdcHighestSupportedHardwareVersion': '11', 'isPublished': 'false',
- # 'numberOfCpus': '1', 'vdc': 'https://172.16.254.206/api/vdc/a5056f85-418c-4bfd-8041-adb0f48be9d9',
- # 'guestOs': 'Other (32-bit)', 'isVdcEnabled': 'true'}
-
- if vapp_dict is None:
- return
-
- vm_table = PrettyTable(['vm uuid',
- 'vapp name',
- 'vapp uuid',
- 'network name',
- 'storage name',
- 'vcpu', 'memory', 'hw ver','deployed','status'])
- for k in vapp_dict:
- entry = []
- entry.append(k)
- entry.append(vapp_dict[k]['containerName'])
- # vm-b1f5cd4c-2239-4c89-8fdc-a41ff18e0d61
- entry.append(vapp_dict[k]['container'].split('/')[-1:][0][5:])
- entry.append(vapp_dict[k]['networkName'])
- entry.append(vapp_dict[k]['storageProfileName'])
- entry.append(vapp_dict[k]['numberOfCpus'])
- entry.append(vapp_dict[k]['memoryMB'])
- entry.append(vapp_dict[k]['pvdcHighestSupportedHardwareVersion'])
- entry.append(vapp_dict[k]['isDeployed'])
- entry.append(vapp_dict[k]['status'])
-
- vm_table.add_row(entry)
-
- print vm_table
-
-
-def print_org(org_dict=None):
- """ Method takes vapp_dict and print in tabular format
-
- Args:
- org_dict: dictionary of organization where key is org uuid.
-
- Returns:
- The return nothing
- """
-
- if org_dict is None:
- return
-
- org_table = PrettyTable(['org uuid', 'name'])
- for k in org_dict:
- entry = [k, org_dict[k]]
- org_table.add_row(entry)
-
- print org_table
-
-
-def print_vm_list(vm_dict=None):
- """ Method takes vapp_dict and print in tabular format
-
- Args:
- vm_dict: dictionary of organization where key is org uuid.
-
- Returns:
- The return nothing
- """
- if vm_dict is None:
- return
-
- vm_table = PrettyTable(
- ['vm uuid', 'vm name', 'vapp uuid', 'vdc uuid', 'network name', 'is deployed', 'vcpu', 'memory', 'status'])
-
- try:
- for k in vm_dict:
- entry = []
- entry.append(k)
- entry.append(vm_dict[k]['name'])
- entry.append(vm_dict[k]['container'].split('/')[-1:][0][5:])
- entry.append(vm_dict[k]['vdc'].split('/')[-1:][0])
- entry.append(vm_dict[k]['networkName'])
- entry.append(vm_dict[k]['isDeployed'])
- entry.append(vm_dict[k]['numberOfCpus'])
- entry.append(vm_dict[k]['memoryMB'])
- entry.append(vm_dict[k]['status'])
- vm_table.add_row(entry)
- print vm_table
- except KeyError:
- logger.error("wrong key {}".format(KeyError.message))
- pass
-
-
-def print_vdc_list(org_dict=None):
- """ Method takes vapp_dict and print in tabular format
-
- Args:
- org_dict: dictionary of organization where key is org uuid.
-
- Returns:
- The return nothing
- """
- if org_dict is None:
- return
- try:
- vdcs_dict = {}
- if org_dict.has_key('vdcs'):
- vdcs_dict = org_dict['vdcs']
- vdc_table = PrettyTable(['vdc uuid', 'vdc name'])
- for k in vdcs_dict:
- entry = [k, vdcs_dict[k]]
- vdc_table.add_row(entry)
-
- print vdc_table
- except KeyError:
- logger.error("wrong key {}".format(KeyError.message))
- logger.logger.debug(traceback.format_exc())
-
-
-def print_network_list(org_dict=None):
- """ Method print network list.
-
- Args:
- org_dict: dictionary of organization that contain key networks with a list of all
- network for for specific VDC
-
- Returns:
- The return nothing
- """
- if org_dict is None:
- return
- try:
- network_dict = {}
- if org_dict.has_key('networks'):
- network_dict = org_dict['networks']
- network_table = PrettyTable(['network uuid', 'network name'])
- for k in network_dict:
- entry = [k, network_dict[k]]
- network_table.add_row(entry)
-
- print network_table
-
- except KeyError:
- logger.error("wrong key {}".format(KeyError.message))
- logger.logger.debug(traceback.format_exc())
-
-
-def print_org_details(org_dict=None):
- """ Method takes vapp_dict and print in tabular format
-
- Args:
- org_dict: dictionary of organization where key is org uuid.
-
- Returns:
- The return nothing
- """
- if org_dict is None:
- return
- try:
- catalogs_dict = {}
-
- print_vdc_list(org_dict=org_dict)
- print_network_list(org_dict=org_dict)
-
- if org_dict.has_key('catalogs'):
- catalogs_dict = org_dict['catalogs']
-
- catalog_table = PrettyTable(['catalog uuid', 'catalog name'])
- for k in catalogs_dict:
- entry = [k, catalogs_dict[k]]
- catalog_table.add_row(entry)
-
- print catalog_table
-
- except KeyError:
- logger.error("wrong key {}".format(KeyError.message))
- logger.logger.debug(traceback.format_exc())
-
-
-def delete_actions(vim=None, action=None, namespace=None):
- if action == 'network' or namespace.action == 'network':
- logger.debug("Requesting delete for network {}".format(namespace.network_name))
- network_uuid = namespace.network_name
- # if request name based we need find UUID
- # TODO optimize it or move to external function
- if not namespace.uuid:
- org_dict = vim.get_org_list()
- for org in org_dict:
- org_net = vim.get_org(org)['networks']
- for network in org_net:
- if org_net[network] == namespace.network_name:
- network_uuid = network
-
- vim.delete_network_action(network_uuid=network_uuid)
-
-
-def list_actions(vim=None, action=None, namespace=None):
- """ Method provide list object from VDC action
-
- Args:
- vim - is vcloud director vim connector.
- action - is action for list ( vdc / org etc)
- namespace - must contain VDC / Org information.
-
- Returns:
- The return nothing
- """
-
- org_id = None
- myorgs = vim.get_org_list()
- for org in myorgs:
- if myorgs[org] == namespace.vcdorg:
- org_id = org
- break
- else:
- print(" Invalid organization.")
- return
-
- if action == 'vms' or namespace.action == 'vms':
- vm_dict = vim.get_vm_list(vdc_name=namespace.vcdvdc)
- print_vm_list(vm_dict=vm_dict)
- elif action == 'vapps' or namespace.action == 'vapps':
- vapp_dict = vim.get_vapp_list(vdc_name=namespace.vcdvdc)
- print_vapp(vapp_dict=vapp_dict)
- elif action == 'networks' or namespace.action == 'networks':
- if namespace.osm:
- osm_print(vim.get_network_list(filter_dict={}))
- else:
- print_network_list(vim.get_org(org_uuid=org_id))
- elif action == 'vdc' or namespace.action == 'vdc':
- if namespace.osm:
- osm_print(vim.get_tenant_list(filter_dict=None))
- else:
- print_vdc_list(vim.get_org(org_uuid=org_id))
- elif action == 'org' or namespace.action == 'org':
- print_org(org_dict=vim.get_org_list())
- else:
- return None
-
-
-def print_network_details(network_dict=None):
- try:
- network_table = PrettyTable(network_dict.keys())
- entry = [network_dict.values()]
- network_table.add_row(entry[0])
- print network_table
- except KeyError:
- logger.error("wrong key {}".format(KeyError.message))
- logger.logger.debug(traceback.format_exc())
-
-
-def osm_print(generic_dict=None):
-
- try:
- for element in generic_dict:
- table = PrettyTable(element.keys())
- entry = [element.values()]
- table.add_row(entry[0])
- print table
- except KeyError:
- logger.error("wrong key {}".format(KeyError.message))
- logger.logger.debug(traceback.format_exc())
-
-
-def view_actions(vim=None, action=None, namespace=None):
- org_id = None
- orgs = vim.get_org_list()
- for org in orgs:
- if orgs[org] == namespace.vcdorg:
- org_id = org
- break
- else:
- print(" Invalid organization.")
- return
-
- myorg = vim.get_org(org_uuid=org_id)
-
- # view org
- if action == 'org' or namespace.action == 'org':
- org_id = None
- orgs = vim.get_org_list()
- if namespace.uuid:
- if namespace.org_name in orgs:
- org_id = namespace.org_name
- else:
- # we need find UUID based on name provided
- for org in orgs:
- if orgs[org] == namespace.org_name:
- org_id = org
- break
-
- logger.debug("Requesting view for orgs {}".format(org_id))
- print_org_details(vim.get_org(org_uuid=org_id))
-
- # view vapp action
- if action == 'vapp' or namespace.action == 'vapp':
- if namespace.vapp_name is not None and namespace.uuid:
- logger.debug("Requesting vapp {} for vdc {}".format(namespace.vapp_name, namespace.vcdvdc))
- vapp_dict = {}
- vapp_uuid = namespace.vapp_name
- # if request based on just name we need get UUID
- if not namespace.uuid:
- vapp_uuid = vim.get_vappid(vdc=namespace.vcdvdc, vapp_name=namespace.vapp_name)
- if vapp_uuid is None:
- print("Can't find vapp by given name {}".format(namespace.vapp_name))
- return
-
- print " namespace {}".format(namespace)
- if vapp_dict is not None and namespace.osm:
- vm_info_dict = vim.get_vminstance(vim_vm_uuid=vapp_uuid)
- print vm_info_dict
- if vapp_dict is not None and namespace.osm != True:
- vapp_dict = vim.get_vapp(vdc_name=namespace.vcdvdc, vapp_name=vapp_uuid, isuuid=True)
- print_vapp(vapp_dict=vapp_dict)
-
- # view network
- if action == 'network' or namespace.action == 'network':
- logger.debug("Requesting view for network {}".format(namespace.network_name))
- network_uuid = namespace.network_name
- # if request name based we need find UUID
- # TODO optimize it or move to external function
- if not namespace.uuid:
- if not myorg.has_key('networks'):
- print("Network {} is undefined in vcloud director for org {} vdc {}".format(namespace.network_name,
- vim.name,
- vim.tenant_name))
- return
-
- my_org_net = myorg['networks']
- for network in my_org_net:
- if my_org_net[network] == namespace.network_name:
- network_uuid = network
- break
-
- print print_network_details(network_dict=vim.get_vcd_network(network_uuid=network_uuid))
-
-
-def create_actions(vim=None, action=None, namespace=None):
- """Method gets provider vdc view from vcloud director
-
- Args:
- vim - is Cloud director vim connector
- action - action for create ( network / vdc etc)
-
- Returns:
- The return xml content of respond or None
- """
- if action == 'network' or namespace.action == 'network':
- logger.debug("Creating a network in vcloud director".format(namespace.network_name))
- network_uuid = vim.create_network(namespace.network_name)
- if network_uuid is not None:
- print ("Crated new network {} and uuid: {}".format(namespace.network_name, network_uuid))
- else:
- print ("Failed create a new network {}".format(namespace.network_name))
- elif action == 'vdc' or namespace.action == 'vdc':
- logger.debug("Creating a new vdc in vcloud director.".format(namespace.vdc_name))
- vdc_uuid = vim.create_vdc(namespace.vdc_name)
- if vdc_uuid is not None:
- print ("Crated new vdc {} and uuid: {}".format(namespace.vdc_name, vdc_uuid))
- else:
- print ("Failed create a new vdc {}".format(namespace.vdc_name))
- else:
- return None
-
-
-def validate_uuid4(uuid_string):
- """Function validate that string contain valid uuid4
-
- Args:
- uuid_string - valid UUID string
-
- Returns:
- The return true if string contain valid UUID format
- """
- try:
- val = uuid.UUID(uuid_string, version=4)
- except ValueError:
- return False
- return True
-
-
-def upload_image(vim=None, image_file=None):
- """Function upload image to vcloud director
-
- Args:
- image_file - valid UUID string
-
- Returns:
- The return true if image uploaded correctly
- """
- try:
- catalog_uuid = vim.get_image_id_from_path(path=image_file, progress=True)
- if catalog_uuid is not None and validate_uuid4(catalog_uuid):
- print("Image uploaded and uuid {}".format(catalog_uuid))
- return True
- except vimconn.vimconnException as upload_exception:
- print("Failed uploaded {} image".format(image_file))
- print("Error Reason: {}".format(upload_exception.message))
- return False
-
-
-def boot_image(vim=None, image_name=None, vm_name=None):
- """ Function boot image that resided in vcloud director.
- The image name can be UUID of name.
-
- Args:
- vim - vim connector
- image_name - image identified by UUID or text string.
- vm_name - vmname
-
-
- Returns:
- The return true if image uploaded correctly
- """
-
- vim_catalog = None
- try:
- catalogs = vim.vca.get_catalogs()
- if not validate_uuid4(image_name):
- vim_catalog = vim.get_catalogid(catalog_name=image_name, catalogs=catalogs)
- if vim_catalog is None:
- return None
- else:
- vim_catalog = vim.get_catalogid(catalog_name=image_name, catalogs=catalogs)
- if vim_catalog is None:
- return None
-
- print (" Booting {} image id {} ".format(vm_name, vim_catalog))
- vm_uuid = vim.new_vminstance(name=vm_name, image_id=vim_catalog)
- if vm_uuid is not None and validate_uuid4(vm_uuid):
- print("Image booted and vm uuid {}".format(vm_uuid))
- vapp_dict = vim.get_vapp(vdc_name=namespace.vcdvdc, vapp_name=vm_uuid, isuuid=True)
- if vapp_dict is not None:
- print_vapp(vapp_dict=vapp_dict)
- return True
- except vimconn.vimconnNotFoundException as notFound:
- print("Failed boot {} image".format(image_name))
- print(notFound.message)
- except vimconn.vimconnException as vimconError:
- print("Failed boot {} image".format(image_name))
- print(vimconError.message)
- except:
- print("Failed boot {} image".format(image_name))
-
-
- return False
-
-
-def image_action(vim=None, action=None, namespace=None):
- """ Function present set of action to manipulate with image.
- - upload image
- - boot image.
- - delete image ( not yet done )
-
- Args:
- vim - vcloud director connector
- action - string (upload/boot etc)
- namespace - contain other attributes image name etc
-
- Returns:
- The return nothing
- """
-
- if action == 'upload' or namespace.action == 'upload':
- upload_image(vim=vim, image_file=namespace.image)
- elif action == 'boot' or namespace.action == 'boot':
- boot_image(vim=vim, image_name=namespace.image, vm_name=namespace.vmname)
- else:
- return None
-
-
-def vmwarecli(command=None, action=None, namespace=None):
- logger.debug("Namespace {}".format(namespace))
- urllib3.disable_warnings()
-
- vcduser = None
- vcdpasword = None
- vcdhost = None
- vcdorg = None
-
- if hasattr(__builtins__, 'raw_input'):
- input = raw_input
-
- if namespace.vcdvdc is None:
- while True:
- vcduser = input("Enter vcd username: ")
- if vcduser is not None and len(vcduser) > 0:
- break
- else:
- vcduser = namespace.vcduser
-
- if namespace.vcdpassword is None:
- while True:
- vcdpasword = input("Please enter vcd password: ")
- if vcdpasword is not None and len(vcdpasword) > 0:
- break
- else:
- vcdpasword = namespace.vcdpassword
-
- if namespace.vcdhost is None:
- while True:
- vcdhost = input("Please enter vcd host name or ip: ")
- if vcdhost is not None and len(vcdhost) > 0:
- break
- else:
- vcdhost = namespace.vcdhost
-
- if namespace.vcdorg is None:
- while True:
- vcdorg = input("Please enter vcd organization name: ")
- if vcdorg is not None and len(vcdorg) > 0:
- break
- else:
- vcdorg = namespace.vcdorg
-
- try:
- vim = vimconnector(uuid=None,
- name=vcdorg,
- tenant_id=None,
- tenant_name=namespace.vcdvdc,
- url=vcdhost,
- url_admin=vcdhost,
- user=vcduser,
- passwd=vcdpasword,
- log_level="DEBUG",
- config={'admin_username': namespace.vcdamdin, 'admin_password': namespace.vcdadminpassword})
- vim.vca = vim.connect()
-
- except vimconn.vimconnConnectionException:
- print("Failed connect to vcloud director. Please check credential and hostname.")
- return
-
- # list
- if command == 'list' or namespace.command == 'list':
- logger.debug("Client requested list action")
- # route request to list actions
- list_actions(vim=vim, action=action, namespace=namespace)
-
- # view action
- if command == 'view' or namespace.command == 'view':
- logger.debug("Client requested view action")
- view_actions(vim=vim, action=action, namespace=namespace)
-
- # delete action
- if command == 'delete' or namespace.command == 'delete':
- logger.debug("Client requested delete action")
- delete_actions(vim=vim, action=action, namespace=namespace)
-
- # create action
- if command == 'create' or namespace.command == 'create':
- logger.debug("Client requested create action")
- create_actions(vim=vim, action=action, namespace=namespace)
-
- # image action
- if command == 'image' or namespace.command == 'image':
- logger.debug("Client requested create action")
- image_action(vim=vim, action=action, namespace=namespace)
-
-
-if __name__ == '__main__':
- defaults = {'vcdvdc': 'default',
- 'vcduser': 'admin',
- 'vcdpassword': 'admin',
- 'vcdhost': 'https://localhost',
- 'vcdorg': 'default',
- 'debug': 'INFO'}
-
- parser = argparse.ArgumentParser()
- parser.add_argument('-u', '--vcduser', help='vcloud director username', type=str)
- parser.add_argument('-p', '--vcdpassword', help='vcloud director password', type=str)
- parser.add_argument('-U', '--vcdamdin', help='vcloud director password', type=str)
- parser.add_argument('-P', '--vcdadminpassword', help='vcloud director password', type=str)
- parser.add_argument('-c', '--vcdhost', help='vcloud director host', type=str)
- parser.add_argument('-o', '--vcdorg', help='vcloud director org', type=str)
- parser.add_argument('-v', '--vcdvdc', help='vcloud director vdc', type=str)
- parser.add_argument('-d', '--debug', help='debug level', type=int)
-
- parser_subparsers = parser.add_subparsers(help='commands', dest='command')
- sub = parser_subparsers.add_parser('list', help='List objects (VMs, vApps, networks)')
- sub_subparsers = sub.add_subparsers(dest='action')
-
- list_vms = sub_subparsers.add_parser('vms', help='list - all vm deployed in vCloud director')
- list_vapps = sub_subparsers.add_parser('vapps', help='list - all vapps deployed in vCloud director')
- list_network = sub_subparsers.add_parser('networks', help='list - all networks deployed')
- list_network.add_argument('-o', '--osm', default=False, action='store_true', help='provide view in OSM format')
-
- #list vdc
- list_vdc = sub_subparsers.add_parser('vdc', help='list - list all vdc for organization accessible to you')
- list_vdc.add_argument('-o', '--osm', default=False, action='store_true', help='provide view in OSM format')
-
- list_org = sub_subparsers.add_parser('org', help='list - list of organizations accessible to you.')
-
- create_sub = parser_subparsers.add_parser('create')
- create_sub_subparsers = create_sub.add_subparsers(dest='action')
- create_vms = create_sub_subparsers.add_parser('vms')
- create_vapp = create_sub_subparsers.add_parser('vapp')
- create_vapp.add_argument('uuid')
-
- # add network
- create_network = create_sub_subparsers.add_parser('network')
- create_network.add_argument('network_name', action='store', help='create a network for a vdc')
-
- # add VDC
- create_vdc = create_sub_subparsers.add_parser('vdc')
- create_vdc.add_argument('vdc_name', action='store', help='create a new VDC for org')
-
- delete_sub = parser_subparsers.add_parser('delete')
- del_sub_subparsers = delete_sub.add_subparsers(dest='action')
- del_vms = del_sub_subparsers.add_parser('vms')
- del_vapp = del_sub_subparsers.add_parser('vapp')
- del_vapp.add_argument('uuid', help='view vapp based on UUID')
-
- # delete network
- del_network = del_sub_subparsers.add_parser('network')
- del_network.add_argument('network_name', action='store',
- help='- delete network for vcloud director by provided name')
- del_network.add_argument('-u', '--uuid', default=False, action='store_true',
- help='delete network for vcloud director by provided uuid')
-
- # delete vdc
- del_vdc = del_sub_subparsers.add_parser('vdc')
-
- view_sub = parser_subparsers.add_parser('view')
- view_sub_subparsers = view_sub.add_subparsers(dest='action')
-
- view_vms_parser = view_sub_subparsers.add_parser('vms')
- view_vms_parser.add_argument('uuid', default=False, action='store_true',
- help='- View VM for specific uuid in vcloud director')
- view_vms_parser.add_argument('name', default=False, action='store_true',
- help='- View VM for specific vapp name in vcloud director')
-
- # view vapp
- view_vapp_parser = view_sub_subparsers.add_parser('vapp')
- view_vapp_parser.add_argument('vapp_name', action='store',
- help='- view vapp for specific vapp name in vcloud director')
- view_vapp_parser.add_argument('-u', '--uuid', default=False, action='store_true', help='view vapp based on uuid')
- view_vapp_parser.add_argument('-o', '--osm', default=False, action='store_true', help='provide view in OSM format')
-
- # view network
- view_network = view_sub_subparsers.add_parser('network')
- view_network.add_argument('network_name', action='store',
- help='- view network for specific network name in vcloud director')
- view_network.add_argument('-u', '--uuid', default=False, action='store_true', help='view network based on uuid')
-
- # view VDC command and actions
- view_vdc = view_sub_subparsers.add_parser('vdc')
- view_vdc.add_argument('vdc_name', action='store',
- help='- View VDC based and action based on provided vdc uuid')
- view_vdc.add_argument('-u', '--uuid', default=False, action='store_true', help='view vdc based on uuid')
-
- # view organization command and actions
- view_org = view_sub_subparsers.add_parser('org')
- view_org.add_argument('org_name', action='store',
- help='- View VDC based and action based on provided vdc uuid')
- view_org.add_argument('-u', '--uuid', default=False, action='store_true', help='view org based on uuid')
-
- # upload image action
- image_sub = parser_subparsers.add_parser('image')
- image_subparsers = image_sub.add_subparsers(dest='action')
- upload_parser = image_subparsers.add_parser('upload')
- upload_parser.add_argument('image', default=False, action='store', help='- valid path to OVF image ')
- upload_parser.add_argument('catalog', default=False, action='store_true', help='- catalog name')
-
- # boot vm action
- boot_parser = image_subparsers.add_parser('boot')
- boot_parser.add_argument('image', default=False, action='store', help='- Image name')
- boot_parser.add_argument('vmname', default=False, action='store', help='- VM name')
- boot_parser.add_argument('-u', '--uuid', default=False, action='store_true', help='view org based on uuid')
-
- namespace = parser.parse_args()
- # put command_line args to mapping
- command_line_args = {k: v for k, v in vars(namespace).items() if v}
-
- d = defaults.copy()
- d.update(os.environ)
- d.update(command_line_args)
-
- logger = logging.getLogger('mano.vim.vmware')
- formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
- ch = logging.StreamHandler()
- ch.setLevel(str.upper(d['debug']))
- ch.setFormatter(formatter)
- logger.addHandler(ch)
- logger.setLevel(getattr(logging, str.upper(d['debug'])))
- logger.info(
- "Connecting {} username: {} org: {} vdc: {} ".format(d['vcdhost'], d['vcduser'], d['vcdorg'], d['vcdvdc']))
-
- logger.debug("command: \"{}\" actio: \"{}\"".format(d['command'], d['action']))
-
- # main entry point.
- vmwarecli(namespace=namespace)