| # -*- 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 |
| ''' |
| import os |
| import requests |
| |
| |
| from xml.etree import ElementTree as ET |
| |
| 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 |
| |
| import logging |
| import json |
| import vimconn |
| import time |
| import uuid |
| import httplib |
| |
| |
| __author__="Mustafa Bayramov" |
| __date__ ="$26-Aug-2016 11:09:29$" |
| |
| |
| #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 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) |
| |
| |
| flavorlist = {} |
| |
| class vimconnector(): |
| '''Vmware VIM Connector base class |
| ''' |
| def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level="ERROR", config={}): |
| 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.vmware') |
| |
| # formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') |
| # ch = logging.StreamHandler() |
| # ch.setLevel(log_level) |
| # ch.setFormatter(formatter) |
| # self.logger.addHandler(ch) |
| # self.logger.setLevel( getattr(logging, log_level)) |
| |
| # self.logger = logging.getLogger('mano.vim.vmware') |
| |
| self.logger.debug("Vmware tenant from VIM filter: '%s'", user) |
| self.logger.debug("Vmware tenant from VIM filter: '%s'", passwd) |
| |
| if not url: |
| raise TypeError, 'url param can not be NoneType' |
| |
| if not self.url_admin: #try to use normal url |
| self.url_admin = self.url |
| |
| self.vcaversion = '5.6' |
| |
| print "Calling constructor with following paramters" |
| print " UUID: {} ".format(uuid) |
| print " name: {} ".format(name) |
| print " tenant_id: {} ".format(tenant_id) |
| print " tenant_name: {} ".format(tenant_name) |
| print " url: {} ".format(url) |
| print " url_admin: {} ".format(url_admin) |
| print " user: {} ".format(user) |
| print " passwd: {} ".format(passwd) |
| print " debug: {} ".format(log_level) |
| |
| 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 connect(self): |
| |
| service_type = 'standalone' |
| version = '5.6' |
| |
| self.logger.debug("Logging in to a VCA '%s'", self.name) |
| |
| vca = VCA(host=self.url, username=self.user, service_type=service_type, version=version, verify=False, log=True) |
| result = vca.login(password=self.passwd, org=self.name) |
| if not result: |
| raise KeyError("Can't connect to a vCloud director.") |
| result = vca.login(token=vca.token, org=self.name, org_url=vca.vcloud_session.org_url) |
| if result is True: |
| self.logger.debug("Successfully logged to a VCA '%s'", self.name) |
| |
| # vca = VCA(host='172.16.254.206', username=self.user, service_type='standalone', version='5.6', verify=False, log=True) |
| # vca.login(password=self.passwd, org=self.name) |
| # vca.login(token=vca.token, org=self.name, org_url=vca.vcloud_session.org_url) |
| |
| # if not result: |
| # result = vca.login(token=vca.token, org=self.name, org_url=vca.vcloud_session.org_url) |
| # if not result: |
| # raise KeyError("Can't connect to a vcloud director") |
| # else: |
| # print "Logged to VCA via existing token" |
| # else: |
| # print "Logged to VCA" |
| |
| return vca |
| |
| |
| def new_tenant(self,tenant_name,tenant_description): |
| '''Adds a new tenant to VIM with this name and description, |
| returns the tenant identifier''' |
| raise vimconnNotImplemented( "Should have implemented this" ) |
| |
| def delete_tenant(self,tenant_id,): |
| '''Delete a tenant from VIM''' |
| '''Returns the tenant identifier''' |
| raise 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>, ...}, ...] |
| ''' |
| raise vimconnNotImplemented( "Should have implemented this" ) |
| |
| 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'. TODO: this need to be revised |
| ip_profile is a dict containing the IP parameters of the network |
| shared is a boolean |
| Returns the network identifier''' |
| |
| self.logger.debug("Vmware tenant from VIM filter: '%s'", net_name) |
| self.logger.debug("Vmware tenant from VIM filter: '%s'", net_type) |
| self.logger.debug("Vmware tenant from VIM filter: '%s'", ip_profile) |
| self.logger.debug("Vmware tenant from VIM filter: '%s'", shared) |
| |
| raise vimconnNotImplemented( "Should have implemented this" ) |
| |
| 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: |
| [{<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") |
| |
| vdc = vca.get_vdc(self.tenant_name) |
| vdcid = vdc.get_id().split(":") |
| |
| networks = vca.get_networks(vdc.get_name()) |
| network_list = [] |
| for network in networks: |
| filter_dict = {} |
| netid = network.get_id().split(":") |
| self.logger.debug ("Adding {} to a list".format(netid[3])) |
| self.logger.debug ("VDC ID {} to a list".format(vdcid[3])) |
| self.logger.debug ("Network {} to a list".format(network.get_name())) |
| |
| filter_dict["name"] = network.get_name() |
| filter_dict["id"] = netid[3] |
| filter_dict["shared"] = network.get_IsShared() |
| filter_dict["tenant_id"] = vdcid[3] |
| 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("Returning {}".format(network_list)) |
| return network_list |
| |
| def get_network(self, net_id): |
| '''Obtain network details of net_id VIM network' |
| Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]''' |
| raise vimconnNotImplemented( "Should have implemented this" ) |
| |
| def delete_network(self, net_id): |
| '''Deletes a tenant network from VIM, provide the network id. |
| Returns the network identifier or raise an exception''' |
| raise vimconnNotImplemented( "Should have implemented this" ) |
| |
| 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) |
| |
| ''' |
| 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 } #TODO to concrete |
| ''' |
| |
| print " get_flavor contains {}".format(flavor_id) |
| |
| vca = self.connect() |
| if not vca: |
| raise vimconn.vimconnConnectionException("self.connect() is failed.") |
| |
| 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''' |
| |
| flavor_uuid = uuid.uuid4() |
| flavorlist[flavor_uuid] = flavor_data |
| |
| print " new_flavor contains {}".format(flavor_data) |
| print " flavor list contains {}".format(flavorlist) |
| |
| return flavor_uuid |
| |
| 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: |
| 200, image-id if the image is created |
| <0, message if there is an error |
| ''' |
| |
| print " ################################################################### " |
| print " new_image contains {}".format(image_dict) |
| print " ################################################################### " |
| |
| |
| def delete_image(self, image_id): |
| '''Deletes a tenant image from VIM''' |
| '''Returns the HTTP response code and a message indicating details of the success or fail''' |
| |
| print " ################################################################### " |
| print " delete_image contains {}".format(image_id) |
| print " ################################################################### " |
| |
| raise vimconnNotImplemented( "Should have implemented this" ) |
| |
| def catalog_exists(self, catalog_name, catalogs): |
| for catalog in catalogs: |
| if catalog.name == catalog_name: |
| return True |
| return False |
| |
| def create_vimcatalog(self, vca, catalog_name): |
| """Create Catalog entry in VIM""" |
| task = vca.create_catalog(catalog_name, catalog_name) |
| result = vca.block_until_completed(task) |
| if not result: |
| return False |
| catalogs = vca.get_catalogs() |
| return self.catalog_exists(catalog_name, catalogs) |
| |
| |
| def upload_ovf(self, vca, catalog_name, item_name, media_file_name, description='', display_progress=False, |
| chunk_bytes=128 * 1024): |
| """ |
| Uploads a OVF file to a vCloud catalog |
| |
| :param catalog_name: (str): The name of the catalog to upload the media. |
| :param item_name: (str): The name of the media file in the catalog. |
| :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) |
| statinfo.st_size |
| |
| # 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 Template" xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"><Description>%s vApp Template</Description></UploadVAppTemplateParams> |
| """ % (escape(item_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 = ET.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 |
| |
| time.sleep(5) |
| |
| self.logger.debug("Failed create vApp template for catalog name {} and image {}". |
| format(catalog_name, media_file_name)) |
| |
| # uploading VMDK file |
| # check status of OVF upload |
| 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) |
| link = filter(lambda link: link.get_rel() == 'upload:default', media.get_Files().get_File()[0].get_Link())[0] |
| |
| # The OVF file and VMDK must be in a same directory |
| head, tail = os.path.split(media_file_name) |
| filevmdk = head + '/' + os.path.basename(link.get_href()) |
| |
| os.path.isfile(filevmdk) |
| statinfo = os.stat(filevmdk) |
| |
| # TODO debug output remove it |
| #print media.get_Files().get_File()[0].get_Link()[0].get_href() |
| #print media.get_Files().get_File()[1].get_Link()[0].get_href() |
| #print link.get_href() |
| |
| # in case first element is pointer to OVF. |
| hrefvmdk = link.get_href().replace("descriptor.ovf","Cirros-disk1.vmdk") |
| |
| f = open(filevmdk, 'rb') |
| bytes_transferred = 0 |
| 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) |
| self.logger.debug('transferred %s of %s bytes' % (str(bytes_transferred), |
| str(statinfo.st_size))) |
| else: |
| self.logger.debug('file upload failed with error: [%s] %s' % (response.status_code, |
| response.content)) |
| return False |
| f.close() |
| 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, catalog_name, media_name, medial_file_name): |
| """Upload media file""" |
| return self.upload_ovf(vca, catalog_name, media_name.split(".")[0], medial_file_name, medial_file_name, True) |
| |
| def get_catalogid(self, catalog_name, catalogs): |
| for catalog in catalogs: |
| if catalog.name == catalog_name: |
| print catalog.name |
| catalog_id = catalog.get_id().split(":") |
| return catalog_id[3] |
| return None |
| |
| def get_catalogbyid(self, catalog_id, catalogs): |
| for catalog in catalogs: |
| catalogid = catalog.get_id().split(":")[3] |
| if catalogid == catalog_id: |
| return catalog.name |
| return None |
| |
| def get_image_id_from_path(self, path): |
| '''Get the image id from image path in the VIM database''' |
| '''Returns: |
| 0,"Image not found" if there are no images with that path |
| 1,image-id if there is one image with that path |
| <0,message if there was an error (Image not found, error contacting VIM, more than 1 image with that path, etc.) |
| ''' |
| |
| vca = self.connect() |
| if not vca: |
| raise vimconn.vimconnConnectionException("self.connect() is failed") |
| |
| self.logger.debug("get_image_id_from_path path {}".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 {}".format(file_extension)) |
| return -1, "Wrong container. vCloud director supports only OVF." |
| catalog_name = os.path.splitext(filename)[0] |
| |
| self.logger.debug("File name {} Catalog Name {} file path {}".format(filename, catalog_name, path)) |
| self.logger.debug("Catalog name {}".format(catalog_name)) |
| |
| catalogs = vca.get_catalogs() |
| if len(catalogs) == 0: |
| self.logger.info("Creating new catalog entry {} in vcloud director".format(catalog_name)) |
| result = self.create_vimcatalog(vca, catalog_name) |
| if not result: |
| return -1, "Failed create new catalog {} ".format(catalog_name) |
| result = self.upload_vimimage(vca, catalog_name, filename, path) |
| if not result: |
| return -1, "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_name: |
| self.logger.debug("Found existing catalog entry for {} catalog id {}".format(catalog_name, self.get_catalogid(catalog_name, catalogs))) |
| return self.get_catalogid(catalog_name, vca.get_catalogs()) |
| |
| # if we didn't find existing catalog we create a new one. |
| self.logger.debug("Creating new catalog entry".format(catalog_name)) |
| result = self.create_vimcatalog(vca, catalog_name) |
| if not result: |
| return -1, "Failed create new catalog {} ".format(catalog_name) |
| result = self.upload_vimimage(vca, catalog_name, filename, path) |
| if not result: |
| return -1, "Failed create vApp template for catalog {} ".format(catalog_name) |
| |
| return self.get_catalogid(catalog_name, vca.get_catalogs()) |
| |
| def get_vappid(self, vdc, vapp_name): |
| """ Take vdc object and vApp name and returns vapp uuid or 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: |
| return None |
| return None |
| |
| def get_vappbyid(self, vdc, vapp_id): |
| refs = filter(lambda ref: ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml', |
| vdc.ResourceEntities.ResourceEntity) |
| for ref in refs: |
| print ref.href |
| |
| if len(refs) == 1: |
| return refs[0].href.split("vapp")[1][1:] |
| |
| def new_vminstance(self,name,description,start,image_id,flavor_id,net_list,cloud_config=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.") |
| |
| # TODO following attribute need be featched from flavor / OVF file must contain same data. |
| # task = self.vca.create_vapp(vdc_name, vapp_name, template, catalog, |
| # vm_name=vm_name, |
| # vm_cpus=cpu, |
| # vm_memory=memory) |
| # |
| |
| catalogs = vca.get_catalogs() |
| #image upload creates template name as catalog name space Template. |
| templateName = self.get_catalogbyid(image_id, catalogs) + ' Template' |
| task = vca.create_vapp(self.tenant_name, name, templateName, self.get_catalogbyid(image_id,catalogs), vm_name=name) |
| if task is False: |
| return -1, " Failed deploy vApp {}".format(name) |
| |
| result = vca.block_until_completed(task) |
| if result: |
| vappID = self.get_vappid(vca.get_vdc(self.tenant_name), name) |
| if vappID is None: |
| return -1, " Failed featch UUID for vApp {}".format(name) |
| else: |
| return vappID |
| |
| return -1, " Failed create vApp {}".format(name) |
| |
| def get_vminstance(self,vm_id): |
| '''Returns the VM instance information from VIM''' |
| |
| vca = self.connect() |
| if not vca: |
| raise vimconn.vimconnConnectionException("self.connect() is failed.") |
| |
| |
| raise vimconnNotImplemented( "Should have implemented this" ) |
| |
| def delete_vminstance(self, vm_id): |
| '''Removes a VM instance from VIM''' |
| '''Returns the instance identifier''' |
| |
| print " ###### {} ".format(vm_id) |
| |
| vca = self.connect() |
| if not vca: |
| raise vimconn.vimconnConnectionException("self.connect() is failed.") |
| |
| thevdc = vca.get_vdc(self.tenant_name) |
| self.get_vappid(vca.get_vdc(self.tenant_name), name) |
| |
| |
| |
| |
| 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 |
| ''' |
| 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" ) |
| |