Initial commit for vmware vimconnector
authorbayramov <mbayramov@vmware.com>
Thu, 8 Sep 2016 08:42:46 +0000 (01:42 -0700)
committerbayramov <mbayramov@vmware.com>
Thu, 8 Sep 2016 08:42:46 +0000 (01:42 -0700)
Change-Id: Iaf3be45750a17f5af2fb355d9305db50002c237b
Signed-off-by: bayramov <mbayramov@vmware.com>
vimconn_vmware.py [new file with mode: 0644]

diff --git a/vimconn_vmware.py b/vimconn_vmware.py
new file mode 100644 (file)
index 0000000..20db81e
--- /dev/null
@@ -0,0 +1,789 @@
+# -*- 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('mano.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" )
+