- RO descriptor now support "availability_zone" tag in the VNFC descriptor.
- RO database extend table vms table to include availability_zone.
- In case a VNF requires more zones than available in the datacenter, an error message of instantiation failure will be issued and the usual rollback procedure will start.
- In case a VDU does not include availability tag, it will be treated as normal, following the default scheduling policy in the VIM.
Change-Id: I335a0db6fa232953f655a598351dd7e7bbb97785
Signed-off-by: mirabal <leonardo.mirabal@altran.com>
DBNAME="mano_db"
QUIET_MODE=""
#TODO update it with the last database version
-LAST_DB_VERSION=22
+LAST_DB_VERSION=23
# Detect paths
MYSQL=$(which mysql)
#[ $OPENMANO_VER_NUM -ge 5009 ] && DB_VERSION=20 #0.5.9 => 20
#[ $OPENMANO_VER_NUM -ge 5015 ] && DB_VERSION=21 #0.5.15 => 21
#[ $OPENMANO_VER_NUM -ge 5016 ] && DB_VERSION=22 #0.5.16 => 22
+#[ $OPENMANO_VER_NUM -ge 5019 ] && DB_VERSION=23 #0.5.20 => 23
#TODO ... put next versions here
function upgrade_to_1(){
echo "DELETE FROM schema_version WHERE version_int='22';" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
}
+function upgrade_to_23(){
+ # echo " upgrade database from version 0.22 to version 0.23"
+ echo " add column 'availability_zone' at table 'vms'"
+ echo "ALTER TABLE mano_db.vms ADD COLUMN availability_zone VARCHAR(255) NULL AFTER modified_at;" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+ echo "INSERT INTO schema_version (version_int, version, openmano_ver, comments, date) VALUES (23, '0.23', '0.5.20', 'Changed type of ram in flavors from SMALLINT to MEDIUMINT', '2017-08-29');" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+}
+function downgrade_from_23(){
+ # echo " downgrade database from version 0.23 to version 0.22"
+ echo " remove column 'availability_zone' from table 'vms'"
+ echo "ALTER TABLE mano_db.vms DROP COLUMN availability_zone;" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+ echo "DELETE FROM schema_version WHERE version_int='23';" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+}
+
function upgrade_to_X(){
echo " change 'datacenter_nets'"
echo "ALTER TABLE datacenter_nets ADD COLUMN vim_tenant_id VARCHAR(36) NOT NULL AFTER datacenter_id, DROP INDEX name_datacenter_id, ADD UNIQUE INDEX name_datacenter_id (name, datacenter_id, vim_tenant_id);" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
__author__ = "Alfonso Tierno, Gerardo Garcia, Pablo Montes"
__date__ = "$26-aug-2014 11:09:29$"
-__version__ = "0.5.19-r528"
+__version__ = "0.5.20-r529"
version_date = "Aug 2017"
-database_version = 22 # expected database schema version
+database_version = 23 # expected database schema version
global global_config
global logger
+
class LoadConfigurationException(Exception):
pass
for vnfc in vnf_descriptor['vnf']['VNFC']:
VNFCitem={}
VNFCitem["name"] = vnfc['name']
+ VNFCitem["availability_zone"] = vnfc.get('availability_zone')
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"])
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"])
#myvim.new_vminstance(self,vimURI,tenant_id,name,description,image_id,flavor_id,net_dict)
i = 0
for sce_vnf in scenarioDict['vnfs']:
+ nfv_availability_zones = []
+ for vm in sce_vnf['vms']:
+ vm_av = vm.get('availability_zone')
+ if vm_av and vm_av not in nfv_availability_zones:
+ nfv_availability_zones.append(vm_av)
for vm in sce_vnf['vms']:
i += 1
myVMDict = {}
#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'])
+
+ if 'availability_zone' in myVMDict:
+ counter_availability_zone = nfv_availability_zones.index(myVMDict['availability_zone'])
+ else:
+ counter_availability_zone = None
+
+ vm_id = myvim.new_vminstance(myVMDict['name'], myVMDict['description'], myVMDict.get('start', None),
+ myVMDict['imageRef'], myVMDict['flavorRef'], myVMDict['networks'],
+ availavility_zone_index=counter_availability_zone,
+ nfv_availability_zones=nfv_availability_zones)
#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})
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 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"):
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)
+
sce_vnf_list = sorted(scenarioDict['vnfs'], key=lambda k: k['name'])
#for sce_vnf in scenarioDict['vnfs']:
for sce_vnf in sce_vnf_list:
+ nfv_availability_zones = []
+ for vm in sce_vnf['vms']:
+ vm_av = vm.get('availability_zone')
+ if vm_av and vm_av not in nfv_availability_zones:
+ nfv_availability_zones.append(vm_av)
+
+ # check if there is enough availability zones available at vim level.
+ if myvims[datacenter_id].availability_zone:
+ vim_availability_zones = myvims[datacenter_id].availability_zone
+ nfv_availability_zones_num = len(vim_availability_zones)
+ if len(nfv_availability_zones) > nfv_availability_zones_num:
+ raise NfvoException('No enough availablity zones for this deployment', HTTP_Bad_Request)
+
if sce_vnf.get("datacenter"):
vim = myvims[ sce_vnf["datacenter"] ]
myvim_thread_id = myvim_threads_id[ sce_vnf["datacenter"] ]
vim = myvims[ default_datacenter_id ]
myvim_thread_id = myvim_threads_id[ default_datacenter_id ]
datacenter_id = default_datacenter_id
- sce_vnf["datacenter_id"] = datacenter_id
+ sce_vnf["datacenter_id"] = datacenter_id
i = 0
+
for vm in sce_vnf['vms']:
i += 1
myVMDict = {}
vm['vim_flavor_id'] = flavor_id
myVMDict['imageRef'] = vm['vim_image_id']
myVMDict['flavorRef'] = vm['vim_flavor_id']
+ myVMDict['availability_zone'] = vm.get('availability_zone')
myVMDict['networks'] = []
task_depends = {}
#TODO ALF. connect_mgmt_interfaces. Connect management interfaces if this is true
cloud_config_vm = unify_cloud_config(vm["boot_data"], cloud_config)
else:
cloud_config_vm = cloud_config
+
+ if 'availability_zone' in myVMDict and myVMDict.get('availability_zone'):
+ counter_availability_zone = nfv_availability_zones.index(myVMDict['availability_zone'])
+ else:
+ counter_availability_zone = None
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)
+ cloud_config_vm, myVMDict['disks'], counter_availability_zone,
+ nfv_availability_zones), depends=task_depends)
instance_tasks[task["id"]] = task
tasks_to_launch[myvim_thread_id].append(task)
vm_id = task["id"]
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"
+ cmd = "SELECT vms.uuid as uuid, flavor_id, image_id, vms.name as name," \
+ " vms.description as description, vms.boot_data as boot_data," \
+ " vms.availability_zone as availability_zone" \
+ " 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()
"properties":{
"name": name_schema,
"description": description_schema,
- "VNFC image": {"oneOf": [path_schema, http_schema]},
"image name": name_schema,
+ "availability_zone": name_schema,
+ "VNFC image": {"oneOf": [path_schema, http_schema]},
"image checksum": checksum_schema,
"image metadata": metadata_schema,
#"cloud-config": cloud_config_schema, #common for all vnfs in the scenario
"properties":{
"name": name_schema,
"description": description_schema,
+
"class": nameshort_schema,
"public": {"type" : "boolean"},
"physical": {"type" : "boolean"},
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
self.user = user
self.passwd = passwd
self.config = config
+ self.availability_zone = None
self.logger = logging.getLogger('openmano.vim')
if log_level:
self.logger.setLevel( getattr(logging, log_level) )
"""
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):
+ def new_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None, disk_list=None,
+ availavility_zone_index=None, nfv_availability_zones=None):
"""Adds a VM instance to VIM
Params:
'start': (boolean) indicates if VM must start or created in pause mode.
'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
+ availavility_zone_index: Index of nfv_availability_zones to use for this this VM
+ nfv_availability_zones: list of availability zones given by user in the VNFC descriptor. Ignore if
+ availability_zone_index is None
Returns the instance identifier or raises an exception on error
"""
raise vimconnNotImplemented( "Should have implemented this" )
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):
if not url:
raise TypeError, 'url param can not be NoneType'
self.persistent_info = persistent_info
+ self.availability_zone = persistent_info.get('availability_zone', None)
self.session = persistent_info.get('session', {'reload_client': True})
self.nova = self.session.get('nova')
self.neutron = self.session.get('neutron')
self.glancev1 = self.session['glancev1'] = glClient.Client('1', session=sess)
self.session['reload_client'] = False
self.persistent_info['session'] = self.session
+ # add availablity zone info inside self.persistent_info
+ self._set_availablity_zones()
+ self.persistent_info['availability_zone'] = self.availability_zone
def __net_os2mano(self, net_list_dict):
'''Transform the net openstack format to mano format
raise vimconn.vimconnException('Timeout waiting for instance ' + vm_id + ' to get ' + status,
http_code=vimconn.HTTP_Request_Timeout)
- def new_vminstance(self,name,description,start,image_id,flavor_id,net_list,cloud_config=None,disk_list=None):
+ def _get_openstack_availablity_zones(self):
+ """
+ Get from openstack availability zones available
+ :return:
+ """
+ try:
+ openstack_availability_zone = self.nova.availability_zones.list()
+ openstack_availability_zone = [str(zone.zoneName) for zone in openstack_availability_zone
+ if zone.zoneName != 'internal']
+ return openstack_availability_zone
+ except Exception as e:
+ return None
+
+ def _set_availablity_zones(self):
+ """
+ Set vim availablity zone
+ :return:
+ """
+
+ if 'availability_zone' in self.config:
+ vim_availability_zones = self.config.get('availability_zone')
+ if isinstance(vim_availability_zones, str):
+ self.availability_zone = [vim_availability_zones]
+ elif isinstance(vim_availability_zones, list):
+ self.availability_zone = vim_availability_zones
+ else:
+ self.availability_zone = self._get_openstack_availablity_zones()
+
+ def _get_vm_availavility_zone(self, availavility_zone_index, nfv_availability_zones):
+ """
+ Return a list with all availability zones create during datacenter attach.
+ :return: List with availability zones
+ """
+ openstack_avilability_zone = self.availability_zone
+
+ # check if VIM offer enough availability zones describe in the VNFC
+ if self.availability_zone and availavility_zone_index is not None \
+ and 0 <= len(nfv_availability_zones) <= len(self.availability_zone):
+
+ if nfv_availability_zones:
+ vnf_azone = nfv_availability_zones[availavility_zone_index]
+ zones_available = []
+
+ for nfv_zone in nfv_availability_zones:
+ for vim_zone in openstack_avilability_zone:
+ if nfv_zone is vim_zone:
+ zones_available.append(nfv_zone)
+
+ if len(zones_available) == len(openstack_avilability_zone) and vnf_azone in openstack_avilability_zone:
+ return vnf_azone
+ else:
+ return openstack_avilability_zone[availavility_zone_index]
+ else:
+ raise vimconn.vimconnConflictException("No enough availablity zones for this deployment")
+ return None
+
+ def new_vminstance(self, name, description, start, image_id, flavor_id, net_list,cloud_config=None,disk_list=None,
+ availavility_zone_index=None, nfv_availability_zones=None):
'''Adds a VM instance to VIM
Params:
start: indicates if VM must start or boot in pause mode. Ignored
type: 'virtual', 'PF', 'VF', 'VFnotShared'
vim_id: filled/added by this function
floating_ip: True/False (or it can be None)
+ '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
+ availavility_zone_index:counter for instance order in vim availability_zones availables
+ nfv_availability_zones: Lost given by user in the VNFC descriptor.
#TODO ip, security groups
Returns the instance identifier
'''
raise vimconn.vimconnException('Timeout creating volumes for instance ' + name,
http_code=vimconn.HTTP_Request_Timeout)
-
- self.logger.debug("nova.servers.create({}, {}, {}, nics={}, meta={}, security_groups={}," \
- "availability_zone={}, key_name={}, userdata={}, config_drive={}, " \
- "block_device_mapping={})".format(name, image_id, flavor_id, net_list_vim,
- metadata, security_groups, self.config.get('availability_zone'),
- self.config.get('keypair'), userdata, config_drive, block_device_mapping))
+ # get availability Zone
+ vm_av_zone = self._get_vm_availavility_zone(availavility_zone_index, nfv_availability_zones)
+
+ self.logger.debug("nova.servers.create({}, {}, {}, nics={}, meta={}, security_groups={}, "
+ "availability_zone={}, key_name={}, userdata={}, config_drive={}, "
+ "block_device_mapping={})".format(name, image_id, flavor_id, net_list_vim, metadata,
+ security_groups, vm_av_zone, self.config.get('keypair'),
+ userdata, config_drive, block_device_mapping))
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'),
+ availability_zone=vm_av_zone,
key_name=self.config.get('keypair'),
userdata=userdata,
config_drive=config_drive,
--- /dev/null
+##
+# 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
+##
+---
+schema_version: 2
+scenario:
+ name: simple_ha
+ description: Simple network scenario consisting of two VNF connected to an external network
+ vnfs:
+ linux1: # vnf/net name in the scenario
+ vnf_name: linux_test_2vms # VNF name as introduced in OPENMANO DB
+ networks:
+ mgmt: # provide a name for this net or connection
+ external: true
+ interfaces:
+ - linux1: control0 # Node and its interface
+ - linux1: control1 # Node and its interface
+
+
+
+
--- /dev/null
+##
+# 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
+##
+---
+
+vnf:
+ name: linux_test_2vms
+ description: Single-VM VNF with a traditional cloud VM based on generic Linux OS
+ external-connections:
+ - name: control0
+ type: mgmt # "mgmt" (autoconnect to management net), "bridge", "data"
+ VNFC: linux-VM-HA-A # Virtual Machine this interface belongs to
+ local_iface_name: eth0 # interface name inside this Virtual Machine (must be defined in the VNFC section)
+ description: Management interface 0
+ - name: control1
+ type: mgmt # "mgmt" (autoconnect to management net), "bridge", "data"
+ VNFC: linux-VM-HA-B # Virtual Machine this interface belongs to
+ local_iface_name: eth0 # interface name inside this Virtual Machine (must be defined in the VNFC section)
+ description: Management interface 1
+ VNFC:
+ - name: linux-VM-HA-A
+ description: Generic Linux Virtual Machine
+ availability_zone: A # availanility zone A
+ #Copy the image to a compute path and edit this path
+ image name: TestVM
+ vcpus: 1 # Only for traditional cloud VMs. Number of virtual CPUs (oversubscription is allowed).
+ ram: 1024 # Only for traditional cloud VMs. Memory in MBytes (not from hugepages, oversubscription is allowed)
+ disk: 10
+ bridge-ifaces:
+ - name: eth0
+ vpci: "0000:00:11.0"
+ numas: []
+ - name: linux-VM-HA-B
+ description: Generic Linux Virtual Machine
+ availability_zone: B # availanility zone B
+ #Copy the image to a compute path and edit this path
+ image name: TestVM
+ vcpus: 1 # Only for traditional cloud VMs. Number of virtual CPUs (oversubscription is allowed).
+ ram: 1024 # Only for traditional cloud VMs. Memory in MBytes (not from hugepages, oversubscription is allowed)
+ disk: 10
+ bridge-ifaces:
+ - name: eth0
+ vpci: "0000:00:12.0"
+ numas: []