Exemple #1
0
    def __init__(self, host, login, passwd):
        """
        Initializes the SDK
        """
        self._host = host
        self._username = login
        self._password = passwd
        self._sessionID = None
        self._check_session = True

        self._cache = ObjectCache()
        self._cache.setduration(weeks=1)

        self._client = Client('https://%s/sdk/vimService?wsdl' % host,
                              cache=self._cache,
                              cachingpolicy=1)
        self._client.set_options(location='https://%s/sdk' % host,
                                 plugins=[ValueExtender()])

        service_reference = self._build_property('ServiceInstance')
        self._serviceContent = self._client.service.RetrieveServiceContent(
            service_reference)

        # In case of an ESXi host, this would be 'HostAgent'
        self._is_vcenter = self._serviceContent.about.apiType == 'VirtualCenter'
        if not self._is_vcenter:
            # pylint: disable=line-too-long
            self._login()
            self._esxHost = self._get_object(
                self._serviceContent.rootFolder,
                prop_type='HostSystem',
                traversal={
                    'name': 'FolderTraversalSpec',
                    'type': 'Folder',
                    'path': 'childEntity',
                    'traversal': {
                        'name': 'DatacenterTraversalSpec',
                        'type': 'Datacenter',
                        'path': 'hostFolder',
                        'traversal': {
                            'name': 'DFolderTraversalSpec',
                            'type': 'Folder',
                            'path': 'childEntity',
                            'traversal': {
                                'name': 'ComputeResourceTravelSpec',  # noqa
                                'type': 'ComputeResource',
                                'path': 'host'
                            }
                        }
                    }
                },
                properties=['name']).obj_identifier
            # pylint: enable=line-too-long
        else:
            self._esxHost = self._get_vcenter_hosts(
            )[0].obj_identifier  # TODO?
Exemple #2
0
    def __init__(self, host, login, passwd):
        """
        Initializes the SDK
        """
        self._host = host
        self._username = login
        self._password = passwd
        self._sessionID = None
        self._check_session = True

        self._cache = ObjectCache()
        self._cache.setduration(weeks=1)

        self._client = Client('https://{0}/sdk/vimService?wsdl'.format(host),
                              cache=self._cache,
                              cachingpolicy=1)
        self._client.set_options(location='https://{0}/sdk'.format(host),
                                 plugins=[ValueExtender()])

        service_reference = self._build_property('ServiceInstance')
        self._serviceContent = self._client.service.RetrieveServiceContent(
            service_reference)

        # In case of an ESXi host, this would be 'HostAgent'
        self.is_vcenter = self._serviceContent.about.apiType == 'VirtualCenter'
        if not self.is_vcenter:
            self._login()
            self._esxHost = self._get_object(
                self._serviceContent.rootFolder,
                prop_type='HostSystem',
                traversal={
                    'name': 'FolderTraversalSpec',
                    'type': 'Folder',
                    'path': 'childEntity',
                    'traversal': {
                        'name': 'DatacenterTraversalSpec',
                        'type': 'Datacenter',
                        'path': 'hostFolder',
                        'traversal': {
                            'name': 'DFolderTraversalSpec',
                            'type': 'Folder',
                            'path': 'childEntity',
                            'traversal': {
                                'name': 'ComputeResourceTravelSpec',
                                'type': 'ComputeResource',
                                'path': 'host'
                            }
                        }
                    }
                },
                properties=['name']).obj_identifier
        else:
            # @TODO: We need to extend all calls to specify the ESXi host where the action needs to be executed.
            # We cannot just assume an ESXi host here, as this is important for certain calls like creating a VM.
            self._esxHost = None
Exemple #3
0
    def _setup(self):
        plugings = []
        cache_conf = NoCache()
        if Configuration.debug:
            if Configuration.log_file is not None:
                logging.basicConfig(filename=Configuration.log_file, level=Configuration.log_level,
                                    format=utils.get_log_format())
            else:
                logging.basicConfig(level=Configuration.log_level,
                                    format=utils.get_log_format())

            if Configuration.log_level == logging.DEBUG and Configuration.environment ==  constants.PRODUCTION_ENV:
                raise LogException

            plugings.append(LogPlugin())


        s = requests.Session()
        s.mount('file://', FileAdapter())
        if Configuration.proxy_url:
            s.proxies = utils.get_builded_proxy_url(Configuration.proxy_url, Configuration.proxy_port)
            if Configuration.proxy_user:
                s.auth= HTTPProxyAuth(Configuration.proxy_user, Configuration.proxy_pass)

        if Configuration.certificate and Configuration.c_key:
            s.cert=(Configuration.certificate, Configuration.c_key)
        else:
            s.verify = Configuration.certificate

        t = RequestsTransport(s, timeout=Configuration.timeout)

        if Configuration.cache:
            cache_conf = ObjectCache(location=Configuration.cache_location, seconds=Configuration.cache_duration)

        self._client = client.Client(Configuration.get_wsdl().strip(), plugins=plugings, transport=t, cache=cache_conf)
Exemple #4
0
    def __init__(self, name, url, verbose=True, cache=False):
        """.. rubric:: Constructor

        :param str name: a name e.g. Kegg, Reactome, ...
        :param str url: the URL of the WSDL service
        :param bool verbose: prints informative messages

        The :attr:`serv` give  access to all WSDL functionalities of the service.

        The :attr:`methods` is an alias to self.serv.methods and returns
        the list of functionalities.

        """
        super(WSDLService, self).__init__(name, url, verbose=verbose)

        self.logging.info("Initialising %s service (WSDL)" % self.name)
        self.CACHING = cache

        try:
            #: attribute to access to the methods provided by this WSDL service
            from suds.client import Client
            from suds.cache import ObjectCache
            oc = ObjectCache(self.settings.user_config_dir, days=0)
            if self.CACHING is True:
                self.suds = Client(self.url, cache=oc, cachingpolicy=1)
            else:
                self.suds = Client(self.url)
            # reference to the service
            self.serv = self.suds.service
            self._update_settings()
        except Exception:
            self.logging.error("Could not connect to the service %s " %
                               self.url)
            raise Exception
Exemple #5
0
def get_client(hostname,
               wsdl_name,
               username='******',
               password='******',
               cachedir=None,
               verify=False,
               timeout=90,
               port=443):
    """Returns and instance of suds.client.Client.

    A separate client is used for each iControl WSDL/Namespace (e.g.
    "LocalLB.Pool").

    This function allows any suds exceptions to propagate up to the caller.

    @param hostname: The IP address or hostname of the BIGIP.
    @param wsdl_name: The iControl namespace (e.g. "LocalLB.Pool")
    @param username: The admin username on the BIGIP.
    @param password: The admin password on the BIGIP.
    @param cachedir: The directory to cache wsdls in. None indicates
        that caching should be disabled.
    @param verify: When True, performs SSL certificate validation in
        Python / urllib2 versions that support it (v2.7.9 and newer)
    @param timeout: The time to wait (in seconds) before timing out
        the connection to the URL
    """
    url = 'https://%s:%s/iControl/iControlPortal.cgi?WSDL=%s' % (
        hostname, port, wsdl_name)
    imp = Import('http://schemas.xmlsoap.org/soap/encoding/')
    imp.filter.add('urn:iControl')

    if cachedir is not None:
        cachedir = ObjectCache(location=os.path.expanduser(cachedir), days=1)

    doctor = ImportDoctor(imp)
    if verify:
        client = Client(url,
                        doctor=doctor,
                        username=username,
                        password=password,
                        cache=cachedir,
                        timeout=timeout)
    else:
        transport = HTTPSTransportNoVerify(username=username,
                                           password=password,
                                           timeout=timeout)
        client = Client(url,
                        doctor=doctor,
                        username=username,
                        password=password,
                        cache=cachedir,
                        transport=transport,
                        timeout=timeout)

    # Without this, subsequent requests will use the actual hostname of the
    # BIGIP, which is often times invalid.
    client.set_options(location=url.split('?')[0])
    client.factory.separator('_')
    return client
Exemple #6
0
    def __init__(self,
                 xaddr,
                 user,
                 passwd,
                 url,
                 cache_location='/tmp/suds',
                 cache_duration=None,
                 encrypt=True,
                 daemon=False,
                 ws_client=None):

        if not os.path.isfile(url):
            raise ONVIFError('%s doesn`t exist!' % url)

        # Create cache object
        # NOTE: if cache_location is specified,
        # onvif must has the permission to access it.
        cache = ObjectCache(location=cache_location)
        # cache_duration: cache will expire in `cache_duration` days
        if cache_duration is not None:
            cache.setduration(days=cache_duration)

        # Convert pathname to url
        self.url = urlparse.urljoin('file:', urllib.pathname2url(url))
        self.xaddr = xaddr
        # Create soap client
        if not ws_client:
            self.ws_client = Client(url=self.url,
                                    location=self.xaddr,
                                    cache=cache)
        else:
            self.ws_client = ws_client
            self.ws_client.set_options(location=self.xaddr)

        # Set soap header for authentication
        self.user = user
        self.passwd = passwd
        # Indicate wether password digest is needed
        self.encrypt = encrypt

        self.daemon = daemon

        self.set_wsse()

        # Method to create type instance of service method defined in WSDL
        self.create_type = self.ws_client.factory.create
Exemple #7
0
def _get_client(wsdl_url, suds_cache=("default",), suds_timeout=None, user_cache=False):
    """
    Open and re-use (persist) a suds.client.Client instance _suds_client throughout
    the session, to minimize WOF server impact and improve performance.  _suds_client
    is global in scope.

    Parameters
    ----------
    wsdl_url : str
        URL of a service's web service definition language (WSDL) description.
        All WaterOneFlow services publish a WSDL description and this url is the
        entry point to the service.
    suds_cache : ``None`` or tuple
        suds client local cache duration for WSDL description and client object.
        Pass a cache duration tuple like ('days', 3) to set a custom duration.
        Duration may be in months, weeks, days, hours, or seconds.
        If unspecified, the suds default (1 day) will be used.
        Use ``None`` to turn off caching.
    suds_timeout : int or float
        suds SOAP URL open timeout (seconds).
        If unspecified, the suds default (90 seconds) will be used.
    user_cache : bool
        If False (default), use the system temp location to store cache WSDL and
        other files. Use the default user ulmo directory if True.

    Returns
    -------
    _suds_client : suds Client
        Newly or previously instantiated (reused) suds Client object.
    """
    global _suds_client

    # Handle new or changed client request (create new client)
    if _suds_client is None or _suds_client.wsdl.url != wsdl_url or not suds_timeout is None:
        if user_cache:
            cache_dir = os.path.join(util.get_ulmo_dir(), 'suds')
            util.mkdir_if_doesnt_exist(cache_dir)
            _suds_client = suds.client.Client(wsdl_url, cache=ObjectCache(location=cache_dir))
        else:
            _suds_client = suds.client.Client(wsdl_url)

        if suds_cache is None:
            _suds_client.set_options(cache=None)
        else:
            cache = _suds_client.options.cache
            # could add some error catching ...
            if suds_cache[0] == "default":
                cache.setduration(days=1)
            else:
                cache.setduration(**dict([suds_cache]))

        if not suds_timeout is None:
            _suds_client.set_options(timeout=suds_timeout)

    return _suds_client
Exemple #8
0
def get_ewsclient():
    ews_domain, ews_user, ews_pass = \
        map(os.environ.get, ('EWS_DOMAIN', 'EWS_USER', 'EWS_PASS'))
    transport = WindowsHttpAuthenticated(username=ews_user, password=ews_pass)
    uri = "https://%s/EWS/Services.wsdl" % ews_domain
    # Long cache to avoid w3c xml.xsd lifetime issue:
    client = Client(uri,
                    transport=transport,
                    cache=ObjectCache(weeks=52),
                    plugins=[ewsclient.AddService()])
    return client
Exemple #9
0
 def __init__(self, use_local_wsdl=False, suds_debug=False):
     if use_local_wsdl:
         wsdl = os.path.join(os.path.dirname(__file__), 'harmony.wsdl')
         url = 'file://' + wsdl
         cache = None
     else:
         url = 'https://congruity.sourceforge.io/congruity/harmony.wsdl'
         cache = ObjectCache(hours=4)
     if suds_debug:
         logging.basicConfig(level=logging.INFO)
         logging.getLogger('suds.transport').setLevel(logging.DEBUG)
     self.client = Client(url, cache=cache, plugins=[MHPlugin()])
    def __init__(self):
        merchant_settings = getattr(settings, "MERCHANT_SETTINGS")
        if not merchant_settings or not merchant_settings.get("paylane"):
            raise GatewayNotConfigured("The '%s' gateway is not correctly "
                                       "configured." % self.display_name)
        paylane_settings = merchant_settings["paylane"]
        wsdl = paylane_settings.get('WSDL', 'https://direct.paylane.com/wsdl/production/Direct.wsdl')
        wsdl_cache = paylane_settings.get('SUDS_CACHE_DIR', '/tmp/suds')
        username = paylane_settings.get('USERNAME', '')
        password = paylane_settings.get('PASSWORD', '')

        self.client = Client(wsdl, username=username, password=password, cache=ObjectCache(location=wsdl_cache, days=15))
    def __init__(self, username, password, debug=False):

        # suds has schema cache by default, here you can set manually
        oc = ObjectCache()
        oc.setduration(days=1)
        oc.setlocation("./cache")

        self.debug = debug
        if debug:
            logging.getLogger("suds.server").setLevel(logging.DEBUG)
            logging.getLogger("suds.client").setLevel(logging.DEBUG)
            logging.getLogger("suds.transport").setLevel(logging.DEBUG)
        else:
            logging.getLogger("suds.client").setLevel(logging.CRITICAL)  # hide soap faults

        try:
            self.client = suds.client.Client(url=self.url, username=username, password=password, cache=None)
        except suds.transport.TransportError as (err):
            raise Exception(
                "Authentication error: Invalid username or password."
                if err.httpcode == 401
                else "Unknown initialization error: %s" % str(err)
            )
    def __init__(self, host, login, passwd):
        """
        Initializes the SDK
        """
        self._logger = LogHandler.get('extensions', name='vmware sdk')
        self._host = host
        self._username = login
        self._password = passwd
        self._sessionID = None
        self._check_session = True

        self._cache = ObjectCache()
        self._cache.setduration(weeks=1)

        self._client = Client('https://{0}/sdk/vimService?wsdl'.format(host),
                              cache=self._cache,
                              cachingpolicy=1)
        self._client.set_options(location='https://{0}/sdk'.format(host),
                                 plugins=[ValueExtender()])

        service_reference = self._build_property('ServiceInstance')
        self._serviceContent = self._client.service.RetrieveServiceContent(
            service_reference
        )

        # In case of an ESXi host, this would be 'HostAgent'
        self.is_vcenter = self._serviceContent.about.apiType == 'VirtualCenter'
        if not self.is_vcenter:
            self._login()
            self._esxHost = self._get_object(
                self._serviceContent.rootFolder,
                prop_type='HostSystem',
                traversal={'name': 'FolderTraversalSpec',
                           'type': 'Folder',
                           'path': 'childEntity',
                           'traversal': {'name': 'DatacenterTraversalSpec',
                                         'type': 'Datacenter',
                                         'path': 'hostFolder',
                                         'traversal': {'name': 'DFolderTraversalSpec',
                                                       'type': 'Folder',
                                                       'path': 'childEntity',
                                                       'traversal': {'name': 'ComputeResourceTravelSpec',
                                                                     'type': 'ComputeResource',
                                                                     'path': 'host'}}}},
                properties=['name']
            ).obj_identifier
        else:
            # @TODO: We need to extend all calls to specify the ESXi host where the action needs to be executed.
            # We cannot just assume an ESXi host here, as this is important for certain calls like creating a VM.
            self._esxHost = None
Exemple #13
0
    def __init__(self, host, login, passwd):
        """
        Initializes the SDK
        """
        self._host = host
        self._username = login
        self._password = passwd
        self._sessionID = None
        self._check_session = True

        self._cache = ObjectCache()
        self._cache.setduration(weeks=1)

        self._client = Client('https://%s/sdk/vimService?wsdl' % host,
                              cache=self._cache,
                              cachingpolicy=1)
        self._client.set_options(location='https://%s/sdk' % host,
                                 plugins=[ValueExtender()])

        service_reference = self._build_property('ServiceInstance')
        self._serviceContent = self._client.service.RetrieveServiceContent(
            service_reference)

        # In case of an ESXi host, this would be 'HostAgent'
        self._is_vcenter = self._serviceContent.about.apiType == 'VirtualCenter'
        if not self._is_vcenter:
            # pylint: disable=line-too-long
            self._login()
            self._esxHost = self._get_object(
                self._serviceContent.rootFolder,
                prop_type='HostSystem',
                traversal={'name': 'FolderTraversalSpec',
                           'type': 'Folder',
                           'path': 'childEntity',
                           'traversal': {'name': 'DatacenterTraversalSpec',
                                         'type': 'Datacenter',
                                         'path': 'hostFolder',
                                         'traversal': {'name': 'DFolderTraversalSpec',
                                                       'type': 'Folder',
                                                       'path': 'childEntity',
                                                       'traversal': {'name': 'ComputeResourceTravelSpec',  # noqa
                                                                     'type': 'ComputeResource',
                                                                     'path': 'host'}}}},
                properties=['name']
            ).obj_identifier
            # pylint: enable=line-too-long
        else:
            self._esxHost = None
Exemple #14
0
def get_services(bbox=None, user_cache=False):
    """Retrieves a list of services.

    Parameters
    ----------
    bbox : ``None`` or 4-tuple
        Optional argument for a bounding box that covers the area you want to
        look for services in. This should be a tuple containing (min_longitude,
        min_latitude, max_longitude, and max_latitude) with these values in
        decimal degrees. If not provided then the full set of services will be
        queried from HIS Central.
    user_cache : bool
        If False (default), use the system temp location to store cache WSDL and
        other files. Use the default user ulmo directory if True.

    Returns
    -------
    services_dicts : list
        A list of dicts that each contain information on an individual service.
    """
    if user_cache:
        cache_dir = os.path.join(util.get_ulmo_dir(), 'suds')
        util.mkdir_if_doesnt_exist(cache_dir)
        suds_client = suds.client.Client(HIS_CENTRAL_WSDL_URL,
                                          cache=ObjectCache(location=cache_dir))
    else:
        suds_client = suds.client.Client(HIS_CENTRAL_WSDL_URL)

    if bbox is None:
        services = suds_client.service.GetWaterOneFlowServiceInfo()
    else:
        x_min, y_min, x_max, y_max = bbox
        services = suds_client.service.GetServicesInBox2(
            xmin=x_min, ymin=y_min, xmax=x_max, ymax=y_max)

    services = [
        _service_dict(service_info)
        for service_info in services.ServiceInfo
    ]
    return services
Exemple #15
0
def get_client(hostname,
               wsdl_name,
               username='******',
               password='******',
               cachedir=None):
    """Returns and instance of suds.client.Client.

    A separate client is used for each iControl WSDL/Namespace (e.g.
    "LocalLB.Pool").

    This function allows any suds exceptions to propagate up to the caller.

    @param hostname: The IP address or hostname of the BIGIP.
    @param wsdl_name: The iControl namespace (e.g. "LocalLB.Pool")
    @param username: The admin username on the BIGIP.
    @param password: The admin password on the BIGIP.
    @param cachedir: The directory to cache wsdls in. None indicates
        that caching should be disabled.
    """
    url = 'https://%s/iControl/iControlPortal.cgi?WSDL=%s' % (hostname,
                                                              wsdl_name)
    imp = Import('http://schemas.xmlsoap.org/soap/encoding/')
    imp.filter.add('urn:iControl')

    if cachedir is not None:
        cachedir = ObjectCache(location=os.path.expanduser(cachedir), days=1)

    doctor = ImportDoctor(imp)
    client = Client(url,
                    doctor=doctor,
                    username=username,
                    password=password,
                    cache=cachedir)

    # Without this, subsequent requests will use the actual hostname of the
    # BIGIP, which is often times invalid.
    client.set_options(location=url.split('?')[0])
    client.factory.separator('_')
    return client
Exemple #16
0
    def __init__(self,
                 service,
                 version,
                 authorization_data=None,
                 environment='production',
                 **suds_options):
        """ Initializes a new instance of this class.

        :param service: The service name.
        :type service: str
        :param authorization_data: (optional) The authorization data, if not provided, cannot call operations on service
        :type authorization_data: AuthorizationData or None
        :param environment: (optional) The environment name, can only be 'production' or 'sandbox', the default is 'production'
        :type environment: str
        :param version: to specify the service version
        :param suds_options: The suds options need to pass to suds client
        """

        self._input_service = service
        self._input_environment = environment
        self._authorization_data = authorization_data
        self._refresh_oauth_tokens_automatically = True
        self._version = ServiceClient._format_version(version)

        # TODO This is a temp fix for set default suds temp folder with user info, suds development branch has already fixed it.
        if 'cache' not in suds_options:
            location = path.join(gettempdir(), 'suds', getuser())
            suds_options['cache'] = ObjectCache(location, days=1)
        # set cachingpolicy to 1 to reuse WSDL cache files, otherwise will only reuse XML files
        if 'cachingpolicy' not in suds_options:
            suds_options['cachingpolicy'] = 1
        self._options = suds_options

        self._service = ServiceClient._format_service(service)
        self._environment = ServiceClient._format_environment(environment)

        self.hp = HeaderPlugin()
        suds_options['plugins'] = [self.hp]
        self._soap_client = Client(self.service_url, **suds_options)
Exemple #17
0
 def __init__(self, url, **kwargs):
     """
     @param url: The URL for the WSDL.
     @type url: str
     @param kwargs: keyword arguments.
     @see: L{Options}
     """
     options = Options()
     options.transport = HttpAuthenticated()
     self.options = options
     options.cache = ObjectCache(days=1)
     self.set_options(**kwargs)
     reader = DefinitionsReader(options, Definitions)
     self.wsdl = reader.open(url)
     plugins = PluginContainer(options.plugins)
     plugins.init.initialized(wsdl=self.wsdl)
     self.factory = Factory(self.wsdl)
     self.service = ServiceSelector(self, self.wsdl.services)
     self.sd = []
     for s in self.wsdl.services:
         sd = ServiceDefinition(self.wsdl, s)
         self.sd.append(sd)
     self.messages = dict(tx=None, rx=None)
Exemple #18
0
class Sdk(object):
    """
    This class contains all SDK related methods
    """
    def __init__(self, host, login, passwd):
        """
        Initializes the SDK
        """
        self._host = host
        self._username = login
        self._password = passwd
        self._sessionID = None
        self._check_session = True

        self._cache = ObjectCache()
        self._cache.setduration(weeks=1)

        self._client = Client('https://{0}/sdk/vimService?wsdl'.format(host),
                              cache=self._cache,
                              cachingpolicy=1)
        self._client.set_options(location='https://{0}/sdk'.format(host),
                                 plugins=[ValueExtender()])

        service_reference = self._build_property('ServiceInstance')
        self._serviceContent = self._client.service.RetrieveServiceContent(
            service_reference)

        # In case of an ESXi host, this would be 'HostAgent'
        self.is_vcenter = self._serviceContent.about.apiType == 'VirtualCenter'
        if not self.is_vcenter:
            self._login()
            self._esxHost = self._get_object(
                self._serviceContent.rootFolder,
                prop_type='HostSystem',
                traversal={
                    'name': 'FolderTraversalSpec',
                    'type': 'Folder',
                    'path': 'childEntity',
                    'traversal': {
                        'name': 'DatacenterTraversalSpec',
                        'type': 'Datacenter',
                        'path': 'hostFolder',
                        'traversal': {
                            'name': 'DFolderTraversalSpec',
                            'type': 'Folder',
                            'path': 'childEntity',
                            'traversal': {
                                'name': 'ComputeResourceTravelSpec',
                                'type': 'ComputeResource',
                                'path': 'host'
                            }
                        }
                    }
                },
                properties=['name']).obj_identifier
        else:
            # @TODO: We need to extend all calls to specify the ESXi host where the action needs to be executed.
            # We cannot just assume an ESXi host here, as this is important for certain calls like creating a VM.
            self._esxHost = None

    @authenticated(force=True)
    def _get_vcenter_hosts(self):
        """
        reload vCenter info (host name and summary)
        """
        if not self.is_vcenter:
            raise RuntimeError('Must be connected to a vCenter Server API.')
        return self._get_object(self._serviceContent.rootFolder,
                                prop_type='HostSystem',
                                traversal={
                                    'name': 'FolderTraversalSpec',
                                    'type': 'Folder',
                                    'path': 'childEntity',
                                    'traversal': {
                                        'name': 'DatacenterTraversalSpec',
                                        'type': 'Datacenter',
                                        'path': 'hostFolder',
                                        'traversal': {
                                            'name': 'DFolderTraversalSpec',
                                            'type': 'Folder',
                                            'path': 'childEntity',
                                            'traversal': {
                                                'name':
                                                'ComputeResourceTravelSpec',
                                                'type': 'ComputeResource',
                                                'path': 'host'
                                            }
                                        }
                                    }
                                },
                                properties=[
                                    'name', 'summary.runtime',
                                    'config.virtualNicManagerInfo.netConfig'
                                ],
                                as_list=True)

    def get_host_status_by_ip(self, host_ip):
        """
        Return host status by ip, from vcenter info
        Must be connected to a vcenter server api
        """
        host = self._get_host_info_by_ip(host_ip)
        return host.summary.runtime.powerState

    def get_host_status_by_pk(self, pk):
        """
        Return host status by pk, from vcenter info
        Must be connected to a vcenter server api
        """
        host = self._get_host_info_by_pk(pk)
        return host.summary.runtime.powerState

    def get_host_primary_key(self, host_ip):
        """
        Return host primary key, based on current ip
        Must be connected to a vcenter server api
        """
        host = self._get_host_info_by_ip(host_ip)
        return host.obj_identifier.value

    def _get_host_info_by_ip(self, host_ip):
        """
        Return HostSystem object by ip, from vcenter info
        Must be connected to a vcenter server api
        """
        datacenter_info = self._get_vcenter_hosts()
        for host in datacenter_info:
            for nic in host.config.virtualNicManagerInfo.netConfig.VirtualNicManagerNetConfig:
                if nic.nicType == 'management':
                    for vnic in nic.candidateVnic:
                        if vnic.spec.ip.ipAddress == host_ip:
                            return host
        raise RuntimeError(
            'Host with ip {0} not found in datacenter info'.format(host_ip))

    def _get_host_info_by_pk(self, pk):
        """
        Return HostSystem object by pk, from vcenter info
        Must be connected to a vcenter server api
        """
        datacenter_info = self._get_vcenter_hosts()
        for host in datacenter_info:
            if host.obj_identifier.value == pk:
                return host

    def get_hosts(self):
        """
        Gets a neutral list of all hosts available
        """
        host_data = self._get_vcenter_hosts()
        host_data = [] if host_data is None else host_data
        hosts = {}
        for host in host_data:
            ips = []
            for nic in host.config.virtualNicManagerInfo.netConfig.VirtualNicManagerNetConfig:
                if nic.nicType == 'management':
                    for vnic in nic.candidateVnic:
                        ips.append(vnic.spec.ip.ipAddress)
            hosts[host.obj_identifier.value] = {'name': host.name, 'ips': ips}
        return hosts

    def test_connection(self):
        """
        Tests the connection
        """
        self._login()
        return True

    def list_hosts_in_datacenter(self):
        """
        return a list of registered host names in vCenter
        must be connected to a vcenter server api
        """
        datacenter_info = self._get_vcenter_hosts()
        return [host.name for host in datacenter_info]

    def validate_result(self, result, message=None):
        """
        Validates a given result. Returning True if the task succeeded, raising an error if not
        """
        if hasattr(result, '_type') and result._type == 'Task':
            return self.validate_result(self.get_task_info(result), message)
        elif hasattr(result, 'info'):
            if result.info.state == 'success':
                return True
            elif result.info.state == 'error':
                error = result.info.error.localizedMessage
                raise Exception((
                    '{0}: {1}'.format(message, error)) if message else error)
        raise Exception(('{0}: {1}'.format(message, 'Unexpected result')
                         ) if message else 'Unexpected result')

    @authenticated()
    def get_task_info(self, task):
        """
        Loads the task details
        """
        return self._get_object(task)

    @authenticated()
    def get_vm_ip_information(self):
        """
        Get the IP information for all vms on a given esxi host
        """
        esxhost = self._validate_host(None)
        configuration = []
        for vm in self._get_object(
                esxhost,
                prop_type='VirtualMachine',
                traversal={
                    'name': 'HostSystemTraversalSpec',
                    'type': 'HostSystem',
                    'path': 'vm'
                },
                properties=['name', 'guest.net', 'config.files'],
                as_list=True):
            vmi = {
                'id': str(vm.obj_identifier.value),
                'vmxpath': str(vm.config.files.vmPathName),
                'name': str(vm.name),
                'net': []
            }
            if vm.guest.net:
                for net in vm.guest.net[0]:
                    vmi['net'].append({
                        'mac':
                        str(net.macAddress),
                        'ipaddresses':
                        [str(i.ipAddress) for i in net.ipConfig.ipAddress]
                    })
            configuration.append(vmi)
        return configuration

    @authenticated()
    def exists(self, name=None, key=None):
        """
        Checks whether a vm with a given name or key exists on a given esxi host
        """
        esxhost = self._validate_host(self._esxHost)
        if name is not None or key is not None:
            try:
                if name is not None:
                    vms = [
                        vm for vm in self._get_object(
                            esxhost,
                            prop_type='VirtualMachine',
                            traversal={
                                'name': 'HostSystemTraversalSpec',
                                'type': 'HostSystem',
                                'path': 'vm'
                            },
                            properties=['name'],
                            as_list=True) if vm.name == name
                    ]
                    if len(vms) == 0:
                        return None
                    else:
                        return vms[0].obj_identifier
                if key is not None:
                    return self._get_object(Sdk._build_property(
                        'VirtualMachine', key),
                                            properties=['name']).obj_identifier
            except:
                return None
        else:
            raise Exception('A name or key should be passed.')

    @authenticated()
    def get_vm(self, key):
        """
        Retreives a vm object, based on its key
        """
        vmid = self.exists(key=key)
        if vmid is None:
            raise RuntimeError(
                'Virtual Machine with key {} could not be found.'.format(key))
        vm = self._get_object(vmid)
        return vm

    @authenticated()
    def get_vm_device_info(self, key):
        """
        Return a vm config, based on its key
        """
        vm = self.get_vm(key)
        filename = vm.config.files.vmPathName
        regex = '\[([^\]]+)\]\s(.+)'
        match = re.search(regex, filename)
        disks = self._get_vmachine_vdisks(vm)
        return {
            'file_name': match.group(2),
            'host_name': vm.name,
            'vpool_name': match.group(1),
            'disks': disks
        }

    @authenticated()
    def get_all_vms(self):
        """
        Get all vMachines on all esxhosts registered to this vCenter
        :return: list
        """
        if not self.is_vcenter:
            raise RuntimeError('Must be connected to a vCenter Server API.')
        hosts = self._get_vcenter_hosts()
        guests = []
        for host in hosts:
            esxhost = self._validate_host(host.obj_identifier)
            vms = self._get_object(esxhost,
                                   prop_type='VirtualMachine',
                                   traversal={
                                       'name': 'HostSystemTraversalSpec',
                                       'type': 'HostSystem',
                                       'path': 'vm'
                                   },
                                   properties=['name', 'config'],
                                   as_list=True)
            for vm in vms:
                guests.append({
                    'id': vm.obj_identifier.value,
                    'name': vm.name,
                    'instance_name': vm.name
                })
        return guests

    def _get_vmachine_vdisks(self, vm_object):
        disks = []
        regex = '\[([^\]]+)\]\s(.+)'
        disk_type = type(self._client.factory.create('ns0:VirtualDisk'))
        for device in vm_object.config.hardware.device:
            if isinstance(device, disk_type):
                backingfile = device.backing.fileName
                match = re.search(regex, backingfile)
                if match:
                    disks.append({
                        'filename': match.group(2),
                        'datastore': match.group(1),
                        'name': device.deviceInfo.label
                    })
        return disks

    @authenticated()
    def get_all_vdisks(self):
        """
        Get all vDisks on all esxhosts registered to this vCenter
        # similar to cinder.volumes.list()
        :return: list
        """
        if not self.is_vcenter:
            raise RuntimeError('Must be connected to a vCenter Server API.')
        hosts = self._get_vcenter_hosts()

        disks = []
        for host in hosts:
            esxhost = self._validate_host(host.obj_identifier)
            vms = self._get_object(esxhost,
                                   prop_type='VirtualMachine',
                                   traversal={
                                       'name': 'HostSystemTraversalSpec',
                                       'type': 'HostSystem',
                                       'path': 'vm'
                                   },
                                   properties=['name', 'config'],
                                   as_list=True)
            for vm in vms:
                disks.extend(self._get_vmachine_vdisks(vm))
        return disks

    @authenticated()
    def get_vms(self, ip, mountpoint):
        """
        Get all vMachines using a given nfs share
        """
        esxhost = self._validate_host(None)
        datastore = self.get_datastore(ip, mountpoint)
        filtered_vms = []
        vms = self._get_object(esxhost,
                               prop_type='VirtualMachine',
                               traversal={
                                   'name': 'HostSystemTraversalSpec',
                                   'type': 'HostSystem',
                                   'path': 'vm'
                               },
                               properties=['name', 'config'],
                               as_list=True)
        for vm in vms:
            mapping = self._get_vm_datastore_mapping(vm)
            if datastore.name in mapping:
                filtered_vms.append(vm)
        return filtered_vms

    @authenticated()
    def set_disk_mode(self, vmid, disks, mode, wait=True):
        """
        Sets the disk mode for a set of disks
        """
        config = self._client.factory.create('ns0:VirtualMachineConfigSpec')
        config.deviceChange = []

        disk_type = type(self._client.factory.create('ns0:VirtualDisk'))

        vmid = self.exists(key=vmid)
        vm = self._get_object(vmid)
        for device in vm.config.hardware.devices:
            if type(device) == disk_type and hasattr(
                    device, 'backing') and device.backing.fileName in disks:
                backing = self._client.factory.create(
                    'ns0:VirtualDiskFlatVer2BackingInfo')
                backing.diskMode = mode
                device = self._client.factory.create('ns0:VirtualDisk')
                device.backing = backing
                disk_spec = self._client.factory.create(
                    'ns0:VirtualDeviceConfigSpec')
                disk_spec.operation = 'edit'
                disk_spec.fileOperation = None
                disk_spec.device = device
                config.deviceChange.append(disk_spec)

        task = self._client.service.ReconfigVM_Task(vm.obj_identifier, config)

        if wait:
            self.wait_for_task(task)
        return task

    @staticmethod
    def _create_disk(factory, key, disk, unit, datastore):
        """
        Creates a disk spec for a given backing device
        Example for parameter disk: {'name': diskname, 'backingdevice': 'disk-flat.vmdk'}
        """
        device_info = factory.create('ns0:Description')
        device_info.label = disk['name']
        device_info.summary = 'Disk {0}'.format(disk['name'])
        backing = factory.create('ns0:VirtualDiskFlatVer2BackingInfo')
        backing.diskMode = 'persistent'
        backing.fileName = '[{0}] {1}'.format(datastore.name,
                                              disk['backingdevice'])
        backing.thinProvisioned = True
        device = factory.create('ns0:VirtualDisk')
        device.controllerKey = key
        device.key = -200 - unit
        device.unitNumber = unit
        device.deviceInfo = device_info
        device.backing = backing
        disk_spec = factory.create('ns0:VirtualDeviceConfigSpec')
        disk_spec.operation = 'add'
        disk_spec.fileOperation = None
        disk_spec.device = device
        return disk_spec

    @staticmethod
    def _create_file_info(factory, datastore):
        """
        Creates a file info object
        """
        file_info = factory.create('ns0:VirtualMachineFileInfo')
        file_info.vmPathName = '[{0}]'.format(datastore)
        return file_info

    @staticmethod
    def _create_nic(factory, device_type, device_label, device_summary,
                    network, unit):
        """
        Creates a NIC spec
        """
        device_info = factory.create('ns0:Description')
        device_info.label = device_label
        device_info.summary = device_summary
        backing = factory.create('ns0:VirtualEthernetCardNetworkBackingInfo')
        backing.deviceName = network
        device = factory.create('ns0:{0}'.format(device_type))
        device.addressType = 'Generated'
        device.wakeOnLanEnabled = True
        device.controllerKey = 100  # PCI Controller
        device.key = -300 - unit
        device.unitNumber = unit
        device.backing = backing
        device.deviceInfo = device_info
        nic_spec = factory.create('ns0:VirtualDeviceConfigSpec')
        nic_spec.operation = 'add'
        nic_spec.fileOperation = None
        nic_spec.device = device
        return nic_spec

    def _create_disk_controller(self, factory, key):
        """
        Create a disk controller
        """
        device_info = self._client.factory.create('ns0:Description')
        device_info.label = 'SCSI controller 0'
        device_info.summary = 'LSI Logic SAS'
        controller = factory.create('ns0:VirtualLsiLogicSASController')
        controller.busNumber = 0
        controller.key = key
        controller.sharedBus = 'noSharing'
        controller.deviceInfo = device_info
        controller_spec = factory.create('ns0:VirtualDeviceConfigSpec')
        controller_spec.operation = 'add'
        controller_spec.fileOperation = None
        controller_spec.device = controller
        return controller_spec

    @staticmethod
    def _create_option_value(factory, key, value):
        """
        Create option values
        """
        option = factory.create('ns0:OptionValue')
        option.key = key
        option.value = value
        return option

    @authenticated()
    def copy_file(self, source, destination, wait=True):
        """
        Copies a file on the datastore
        """
        task = self._client.service.CopyDatastoreFile_Task(
            _this=self._serviceContent.fileManager,
            sourceName=source,
            destinationName=destination)

        if wait:
            self.wait_for_task(task)
        return task

    @authenticated()
    def create_vm_from_template(self,
                                name,
                                source_vm,
                                disks,
                                ip,
                                mountpoint,
                                wait=True):
        """
        Create a vm based on an existing vtemplate on specified tgt hypervisor
        Raises RuntimeError if datastore is not available at (ip, mountpoint)
        """

        esxhost = self._validate_host(None)
        host_data = self._get_host_data(esxhost)

        datastore = self.get_datastore(ip, mountpoint)
        # Build basic config information
        config = self._client.factory.create('ns0:VirtualMachineConfigSpec')
        config.name = name
        config.numCPUs = source_vm.config.hardware.numCPU
        config.memoryMB = source_vm.config.hardware.memoryMB
        config.guestId = source_vm.config.guestId
        config.deviceChange = []
        config.extraConfig = []
        config.files = Sdk._create_file_info(self._client.factory,
                                             datastore.name)

        disk_controller_key = -101
        config.deviceChange.append(
            self._create_disk_controller(self._client.factory,
                                         disk_controller_key))

        # Add disk devices
        for disk in disks:
            config.deviceChange.append(
                Sdk._create_disk(self._client.factory, disk_controller_key,
                                 disk, disks.index(disk), datastore))

        # Add network
        nw_type = type(
            self._client.factory.create(
                'ns0:VirtualEthernetCardNetworkBackingInfo'))
        for device in source_vm.config.hardware.device:
            if hasattr(device, 'backing') and type(device.backing) == nw_type:
                config.deviceChange.append(
                    Sdk._create_nic(self._client.factory,
                                    device.__class__.__name__,
                                    device.deviceInfo.label,
                                    device.deviceInfo.summary,
                                    device.backing.deviceName,
                                    device.unitNumber))

        # Copy additional properties
        extraconfigstoskip = ['nvram']
        for item in source_vm.config.extraConfig:
            if item.key not in extraconfigstoskip:
                config.extraConfig.append(
                    Sdk._create_option_value(self._client.factory, item.key,
                                             item.value))

        task = self._client.service.CreateVM_Task(
            host_data['folder'],
            config=config,
            pool=host_data['resourcePool'],
            host=host_data['host'])

        if wait:
            self.wait_for_task(task)
        return task

    @authenticated()
    def clone_vm(self, vmid, name, disks, wait=True):
        """
        Clone a existing VM configuration

        @param vmid: unique id of the vm
        @param name: name of the clone vm
        @param disks: list of disks to use in vm configuration
        @param wait: wait for task to complete or not (True/False)
        """

        esxhost = self._validate_host(None)
        host_data = self._get_host_data(esxhost)

        source_vm_object = self.exists(key=vmid)
        if not source_vm_object:
            raise Exception('VM with key reference {0} not found'.format(vmid))
        source_vm = self._get_object(source_vm_object)
        datastore = self._get_object(source_vm.datastore[0][0])

        # Build basic config information
        config = self._client.factory.create('ns0:VirtualMachineConfigSpec')
        config.name = name
        config.numCPUs = source_vm.config.hardware.numCPU
        config.memoryMB = source_vm.config.hardware.memoryMB
        config.guestId = source_vm.config.guestId
        config.deviceChange = []
        config.extraConfig = []
        config.files = Sdk._create_file_info(self._client.factory,
                                             datastore.name)

        disk_controller_key = -101
        config.deviceChange.append(
            self._create_disk_controller(self._client.factory,
                                         disk_controller_key))

        # Add disk devices
        for disk in disks:
            config.deviceChange.append(
                Sdk._create_disk(self._client.factory, disk_controller_key,
                                 disk, disks.index(disk), datastore))
            self.copy_file(
                '[{0}] {1}.vmdk'.format(
                    datastore.name,
                    disk['name'].split('_')[-1].replace('-clone', '')),
                '[{0}] {1}'.format(datastore.name, disk['backingdevice']))

        # Add network
        nw_type = type(
            self._client.factory.create(
                'ns0:VirtualEthernetCardNetworkBackingInfo'))
        for device in source_vm.config.hardware.device:
            if hasattr(device, 'backing') and type(device.backing) == nw_type:
                config.deviceChange.append(
                    Sdk._create_nic(self._client.factory,
                                    device.__class__.__name__,
                                    device.deviceInfo.label,
                                    device.deviceInfo.summary,
                                    device.backing.deviceName,
                                    device.unitNumber))

        # Copy additional properties
        extraconfigstoskip = ['nvram']
        for item in source_vm.config.extraConfig:
            if item.key not in extraconfigstoskip:
                config.extraConfig.append(
                    Sdk._create_option_value(self._client.factory, item.key,
                                             item.value))

        task = self._client.service.CreateVM_Task(
            host_data['folder'],
            config=config,
            pool=host_data['resourcePool'],
            host=host_data['host'])
        if wait:
            self.wait_for_task(task)
        return task

    @authenticated()
    def get_datastore(self, ip, mountpoint, host=None):
        """
        @param ip : hypervisor ip to query for datastore presence
        @param mountpoint: nfs mountpoint on hypervisor
        @rtype: sdk datastore object
        @return: object when found else None
        """

        datastore = None
        if host is None:
            host = self._esxHost
        esxhost = self._validate_host(host)
        host_system = self._get_object(esxhost, properties=['datastore'])
        for store in host_system.datastore[0]:
            store = self._get_object(store)
            if not store.summary.accessible:
                logger.warning(
                    'Datastore {0} is not accessible, skipping'.format(
                        store.name))
            if hasattr(store.info, 'nas'):
                if store.info.nas.remoteHost == ip and store.info.nas.remotePath == mountpoint:
                    datastore = store

        return datastore

    @authenticated()
    def is_datastore_available(self, ip, mountpoint):
        """
        @param ip : hypervisor ip to query for datastore presence
        @param mountpoint: nfs mountpoint on hypervisor
        @rtype: boolean
        @return: True | False
        """

        if self.get_datastore(ip, mountpoint):
            return True
        else:
            return False

    def make_agnostic_config(self, vm_object, host=None):
        regex = '\[([^\]]+)\]\s(.+)'
        match = re.search(regex, vm_object.config.files.vmPathName)
        if host is None:
            host = self._esxHost
        esxhost = self._validate_host(host)

        config = {
            'name': vm_object.config.name,
            'id': vm_object.obj_identifier.value,
            'backing': {
                'filename': match.group(2),
                'datastore': match.group(1)
            },
            'disks': [],
            'datastores': {}
        }

        for device in vm_object.config.hardware.device:
            if device.__class__.__name__ == 'VirtualDisk':
                if device.backing is not None and device.backing.fileName is not None:
                    backingfile = device.backing.fileName
                    match = re.search(regex, backingfile)
                    if match:
                        filename = match.group(2)
                        backingfile = filename.replace('.vmdk', '-flat.vmdk')
                        backingfile = '/{0}'.format(backingfile.strip('/'))
                        config['disks'].append({
                            'filename': filename,
                            'backingfilename': backingfile,
                            'datastore': match.group(1),
                            'name': device.deviceInfo.label,
                            'order': device.unitNumber
                        })

        host_system = self._get_object(esxhost, properties=['datastore'])
        for store in host_system.datastore[0]:
            store = self._get_object(store)
            if hasattr(store.info, 'nas'):
                config['datastores'][store.info.name] = '{}:{}'.format(
                    store.info.nas.remoteHost, store.info.nas.remotePath)

        return config

    @authenticated()
    def delete_vm(self,
                  vmid,
                  storagedriver_mountpoint,
                  storagedriver_storage_ip,
                  devicename,
                  wait=False):
        """
        Delete a given vm
        """
        machine = None
        task = None
        if vmid:
            try:
                machine = Sdk._build_property('VirtualMachine', vmid)
            except Exception as ex:
                logger.error(
                    'SDK domain retrieve failed by vmid: {}'.format(ex))
        elif storagedriver_mountpoint and storagedriver_storage_ip and devicename:
            try:
                machine_info = self.get_nfs_datastore_object(
                    storagedriver_storage_ip, storagedriver_mountpoint,
                    devicename)[0]
                machine = Sdk._build_property(
                    'VirtualMachine', machine_info.obj_identifier.value)
            except Exception as ex:
                logger.error(
                    'SDK domain retrieve failed by nfs datastore info: {}'.
                    format(ex))
        if machine:
            task = self._client.service.Destroy_Task(machine)
            if wait:
                self.wait_for_task(task)

        if storagedriver_mountpoint and devicename:
            vmx_path = os.path.join(storagedriver_mountpoint, devicename)
            if os.path.exists(vmx_path):
                dir_name = os.path.dirname(vmx_path)
                logger.debug('Removing leftover files in {0}'.format(dir_name))
                try:
                    shutil.rmtree(dir_name)
                    logger.debug('Removed dir tree {}'.format(dir_name))
                except Exception as exception:
                    logger.error(
                        'Failed to remove dir tree {0}. Reason: {1}'.format(
                            dir_name, str(exception)))
        return task

    @authenticated()
    def get_power_state(self, vmid):
        """
        Get the power state of a given vm
        """
        return self._get_object(Sdk._build_property('VirtualMachine', vmid),
                                properties=['runtime.powerState'
                                            ]).runtime.powerState

    @authenticated()
    def mount_nfs_datastore(self, name, remote_host, remote_path):
        """
        Mounts a given NFS export as a datastore
        """
        esxhost = self._validate_host(None)
        host = self._get_object(esxhost,
                                properties=[
                                    'datastore', 'name', 'configManager',
                                    'configManager.datastoreSystem'
                                ])
        for store in host.datastore[0]:
            store = self._get_object(store)
            if hasattr(store.info, 'nas'):
                if store.info.name == name:
                    if store.info.nas.remoteHost == remote_host and store.info.nas.remotePath == remote_path:
                        # We'll remove this store, as it's identical to the once we'll add,
                        # forcing a refresh
                        self._client.service.RemoveDatastore(
                            host.configManager.datastoreSystem,
                            store.obj_identifier)
                        break
                    else:
                        raise RuntimeError(
                            'A datastore {0} already exists, pointing to {1}:{2}'
                            .format(name, store.info.nas.remoteHost,
                                    store.info.nas.remotePath))
        spec = self._client.factory.create('ns0:HostNasVolumeSpec')
        spec.accessMode = 'readWrite'
        spec.localPath = name
        spec.remoteHost = remote_host
        spec.remotePath = remote_path
        spec.type = 'nfs'
        return self._client.service.CreateNasDatastore(
            host.configManager.datastoreSystem, spec)

    @authenticated()
    def wait_for_task(self, task):
        """
        Wait for a task to be completed
        """
        state = self.get_task_info(task).info.state
        while state in ['running', 'queued']:
            sleep(1)
            state = self.get_task_info(task).info.state

    @authenticated()
    def get_nfs_datastore_object(self, ip, mountpoint, filename, host=None):
        """
        ip : "10.130.12.200", string
        mountpoint: "/srv/volumefs", string
        filename: "cfovs001/vhd0(-flat).vmdk"
        identify nfs datastore on this esx host based on ip and mount
        check if filename is present on datastore
        if file is .vmdk return VirtualDisk object for corresponding virtual disk
        if file is .vmx return VirtualMachineConfigInfo for corresponding vm

        @rtype: tuple
        @return: A tuple. First item: vm config, second item: Device if a vmdk was given
        """

        filename = filename.replace(
            '-flat.vmdk', '.vmdk')  # Support both -flat.vmdk and .vmdk
        if not filename.endswith('.vmdk') and not filename.endswith('.vmx'):
            raise ValueError('Unexpected filetype')
        if host is None:
            host = self._esxHost
        esxhost = self._validate_host(host)

        datastore = self.get_datastore(ip, mountpoint, host=esxhost)
        if not datastore:
            raise RuntimeError('Could not find datastore')

        vms = self._get_object(esxhost,
                               prop_type='VirtualMachine',
                               traversal={
                                   'name': 'HostSystemTraversalSpec',
                                   'type': 'HostSystem',
                                   'path': 'vm'
                               },
                               properties=['name', 'config'],
                               as_list=True)
        if not vms:
            raise RuntimeError('No vMachines found')
        for vm in vms:
            mapping = self._get_vm_datastore_mapping(vm)
            if datastore.name in mapping:
                if filename in mapping[datastore.name]:
                    return vm, mapping[datastore.name][filename]
        raise RuntimeError(
            'Could not locate given file on the given datastore')

    def file_exists(self, ip, mountpoint, filename):
        try:
            self.get_nfs_datastore_object(ip, mountpoint, filename)
            return True
        except Exception, ex:
            logger.debug('File does not exist: {0}'.format(ex))
            return False
Exemple #19
0
from odoo.tools import float_round, DEFAULT_SERVER_DATE_FORMAT
from odoo.tools.float_utils import float_compare, float_repr
from odoo.tools.translate import _
from odoo.exceptions import UserError
from base64 import b64decode
import os

_logger = logging.getLogger(__name__)
try:
    from suds.client import Client
    from suds.wsse import Security
    from .wsse.suds import WssePlugin
    from suds.transport.https import HttpTransport
    from suds.cache import ObjectCache
    cache_path = "/tmp/{0}-suds".format(os.getuid())
    cache = ObjectCache(cache_path)
except Exception as e:
    _logger.warning("No Load suds or wsse: %s" % str(e))

URLS = {
    'integ':
    'https://webpay3gint.transbank.cl/WSWebpayTransaction/cxf/WSWebpayService?wsdl',
    'test':
    'https://webpay3gint.transbank.cl/WSWebpayTransaction/cxf/WSWebpayService?wsdl',
    'prod':
    'https://webpay3g.transbank.cl/WSWebpayTransaction/cxf/WSWebpayService?wsdl',
}


class PaymentAcquirerWebpay(models.Model):
    _inherit = 'payment.acquirer'
Exemple #20
0
class Sdk(object):
    """
    This class contains all SDK related methods
    """
    def __init__(self, host, login, passwd):
        """
        Initializes the SDK
        """
        self._host = host
        self._username = login
        self._password = passwd
        self._sessionID = None
        self._check_session = True

        self._cache = ObjectCache()
        self._cache.setduration(weeks=1)

        self._client = Client('https://%s/sdk/vimService?wsdl' % host,
                              cache=self._cache,
                              cachingpolicy=1)
        self._client.set_options(location='https://%s/sdk' % host,
                                 plugins=[ValueExtender()])

        service_reference = self._build_property('ServiceInstance')
        self._serviceContent = self._client.service.RetrieveServiceContent(
            service_reference)

        # In case of an ESXi host, this would be 'HostAgent'
        self._is_vcenter = self._serviceContent.about.apiType == 'VirtualCenter'
        if not self._is_vcenter:
            # pylint: disable=line-too-long
            self._login()
            self._esxHost = self._get_object(
                self._serviceContent.rootFolder,
                prop_type='HostSystem',
                traversal={
                    'name': 'FolderTraversalSpec',
                    'type': 'Folder',
                    'path': 'childEntity',
                    'traversal': {
                        'name': 'DatacenterTraversalSpec',
                        'type': 'Datacenter',
                        'path': 'hostFolder',
                        'traversal': {
                            'name': 'DFolderTraversalSpec',
                            'type': 'Folder',
                            'path': 'childEntity',
                            'traversal': {
                                'name': 'ComputeResourceTravelSpec',  # noqa
                                'type': 'ComputeResource',
                                'path': 'host'
                            }
                        }
                    }
                },
                properties=['name']).obj_identifier
            # pylint: enable=line-too-long
        else:
            self._esxHost = None

    @authenticated(force=True)
    def _get_vcenter_hosts(self):
        """
        reload vCenter info (host name and summary)
        """
        if not self._is_vcenter:
            raise RuntimeError('Must be connected to a vCenter Server API.')
        datacenter_info = self._get_object(
            self._serviceContent.rootFolder,
            prop_type='HostSystem',
            traversal={
                'name': 'FolderTraversalSpec',
                'type': 'Folder',
                'path': 'childEntity',
                'traversal': {
                    'name': 'DatacenterTraversalSpec',
                    'type': 'Datacenter',
                    'path': 'hostFolder',
                    'traversal': {
                        'name': 'DFolderTraversalSpec',
                        'type': 'Folder',
                        'path': 'childEntity',
                        'traversal': {
                            'name': 'ComputeResourceTravelSpec',  # noqa
                            'type': 'ComputeResource',
                            'path': 'host'
                        }
                    }
                }
            },
            properties=[
                'name', 'summary.runtime',
                'config.virtualNicManagerInfo.netConfig'
            ])
        return datacenter_info

    def get_host_status_by_ip(self, host_ip):
        """
        Return host status by ip, from vcenter info
        Must be connected to a vcenter server api
        """
        host = self._get_host_info_by_ip(host_ip)
        return host.summary.runtime.powerState

    def get_host_status_by_pk(self, pk):
        """
        Return host status by pk, from vcenter info
        Must be connected to a vcenter server api
        """
        host = self._get_host_info_by_pk(pk)
        return host.summary.runtime.powerState

    def get_host_primary_key(self, host_ip):
        """
        Return host primary key, based on current ip
        Must be connected to a vcenter server api
        """
        host = self._get_host_info_by_ip(host_ip)
        return host.obj_identifier.value

    def _get_host_info_by_ip(self, host_ip):
        """
        Return HostSystem object by ip, from vcenter info
        Must be connected to a vcenter server api
        """
        datacenter_info = self._get_vcenter_hosts()
        for host in datacenter_info:
            for nic in host.config.virtualNicManagerInfo.netConfig.VirtualNicManagerNetConfig:
                if nic.nicType == 'management':
                    for vnic in nic.candidateVnic:
                        if vnic.spec.ip.ipAddress == host_ip:
                            return host
        raise RuntimeError(
            'Host with ip {0} not found in datacenter info'.format(host_ip))

    def _get_host_info_by_pk(self, pk):
        """
        Return HostSystem object by pk, from vcenter info
        Must be connected to a vcenter server api
        """
        datacenter_info = self._get_vcenter_hosts()
        for host in datacenter_info:
            if host.obj_identifier.value == pk:
                return host

    def get_hosts(self):
        """
        Gets a neutral list of all hosts available
        """
        host_data = self._get_vcenter_hosts()
        host_data = [] if host_data is None else host_data
        hosts = {}
        for host in host_data:
            ips = []
            for nic in host.config.virtualNicManagerInfo.netConfig.VirtualNicManagerNetConfig:
                if nic.nicType == 'management':
                    for vnic in nic.candidateVnic:
                        ips.append(vnic.spec.ip.ipAddress)
            hosts[host.obj_identifier.value] = {'name': host.name, 'ips': ips}
        return hosts

    def test_connection(self):
        """
        Tests the connection
        """
        self._login()
        return True

    def list_hosts_in_datacenter(self):
        """
        return a list of registered host names in vCenter
        must be connected to a vcenter server api
        """
        datacenter_info = self._get_vcenter_hosts()
        return [host.name for host in datacenter_info]

    def validate_result(self, result, message=None):
        """
        Validates a given result. Returning True if the task succeeded, raising an error if not
        """
        if hasattr(result, '_type') and result._type == 'Task':
            return self.validate_result(self.get_task_info(result), message)
        elif hasattr(result, 'info'):
            if result.info.state == 'success':
                return True
            elif result.info.state == 'error':
                error = result.info.error.localizedMessage
                raise Exception(('%s: %s' %
                                 (message, error)) if message else error)
        raise Exception(('%s: %s' % (message, 'Unexpected result')
                         ) if message else 'Unexpected result')

    @authenticated()
    def get_task_info(self, task):
        """
        Loads the task details
        """
        return self._get_object(task)

    @authenticated()
    def get_vm_ip_information(self):
        """
        Get the IP information for all vms on a given esxi host
        """
        esxhost = self._validate_host(None)
        configuration = []
        for vm in self._get_object(
                esxhost,
                prop_type='VirtualMachine',
                traversal={
                    'name': 'HostSystemTraversalSpec',
                    'type': 'HostSystem',
                    'path': 'vm'
                },
                properties=['name', 'guest.net', 'config.files']):
            vmi = {
                'id': str(vm.obj_identifier.value),
                'vmxpath': str(vm.config.files.vmPathName),
                'name': str(vm.name),
                'net': []
            }
            if vm.guest.net:
                for net in vm.guest.net[0]:
                    vmi['net'].append({
                        'mac':
                        str(net.macAddress),
                        'ipaddresses':
                        [str(i.ipAddress) for i in net.ipConfig.ipAddress]
                    })
            configuration.append(vmi)
        return configuration

    @authenticated()
    def exists(self, name=None, key=None):
        """
        Checks whether a vm with a given name or key exists on a given esxi host
        """
        esxhost = self._validate_host(None)
        if name is not None or key is not None:
            try:
                if name is not None:
                    vms = [
                        vm for vm in self._get_object(
                            esxhost,
                            prop_type='VirtualMachine',
                            traversal={
                                'name': 'HostSystemTraversalSpec',
                                'type': 'HostSystem',
                                'path': 'vm'
                            },
                            properties=['name']) if vm.name == name
                    ]
                    if len(vms) == 0:
                        return None
                    else:
                        return vms[0].obj_identifier
                if key is not None:
                    return self._get_object(self._build_property(
                        'VirtualMachine', key),
                                            properties=['name']).obj_identifier
            except:
                return None
        else:
            raise Exception('A name or key should be passed.')

    @authenticated()
    def get_vm(self, key):
        """
        Retreives a vm object, based on its key
        """
        vmid = self.exists(key=key)
        if vmid is None:
            raise RuntimeError(
                'Virtual Machine with key {} could not be found.'.format(key))
        vm = self._get_object(vmid)
        return vm

    @authenticated()
    def get_vms(self, ip, mountpoint):
        """
        Get all vMachines using a given nfs share
        """
        esxhost = self._validate_host(None)
        datastore = self.get_datastore(ip, mountpoint)
        filtered_vms = []
        vms = self._get_object(esxhost,
                               prop_type='VirtualMachine',
                               traversal={
                                   'name': 'HostSystemTraversalSpec',
                                   'type': 'HostSystem',
                                   'path': 'vm'
                               },
                               properties=['name', 'config'])
        for vm in vms:
            mapping = self._get_vm_datastore_mapping(vm)
            if datastore.name in mapping:
                filtered_vms.append(vm)
        return filtered_vms

    @authenticated()
    def set_disk_mode(self, vmid, disks, mode, wait=True):
        """
        Sets the disk mode for a set of disks
        """
        config = self._client.factory.create('ns0:VirtualMachineConfigSpec')
        config.deviceChange = []

        disk_type = type(self._client.factory.create('ns0:VirtualDisk'))

        vmid = self.exists(key=vmid)
        vm = self._get_object(vmid)
        for device in vm.config.hardware.devices:
            if type(device) == disk_type and hasattr(device, 'backing') \
                    and device.backing.fileName in disks:
                backing = self._client.factory.create(
                    'ns0:VirtualDiskFlatVer2BackingInfo')
                backing.diskMode = mode
                device = self._client.factory.create('ns0:VirtualDisk')
                device.backing = backing
                diskSpec = self._client.factory.create(
                    'ns0:VirtualDeviceConfigSpec')
                diskSpec.operation = 'edit'
                diskSpec.fileOperation = None
                diskSpec.device = device
                config.deviceChange.append(diskSpec)

        task = self._client.service.ReconfigVM_Task(vm.obj_identifier, config)

        if wait:
            self.wait_for_task(task)
        return task

    def _create_disk(self, factory, key, disk, unit, datastore):
        """
        Creates a disk spec for a given backing device
        Example for parameter disk: {'name': diskname, 'backingdevice': 'disk-flat.vmdk'}
        """
        deviceInfo = factory.create('ns0:Description')
        deviceInfo.label = disk['name']
        deviceInfo.summary = 'Disk %s' % disk['name']
        backing = factory.create('ns0:VirtualDiskFlatVer2BackingInfo')
        backing.diskMode = 'persistent'
        backing.fileName = '[%s] %s' % (datastore.name, disk['backingdevice'])
        backing.thinProvisioned = True
        device = factory.create('ns0:VirtualDisk')
        device.controllerKey = key
        device.key = -200 - unit
        device.unitNumber = unit
        device.deviceInfo = deviceInfo
        device.backing = backing
        diskSpec = factory.create('ns0:VirtualDeviceConfigSpec')
        diskSpec.operation = 'add'
        diskSpec.fileOperation = None
        diskSpec.device = device
        return diskSpec

    def _create_file_info(self, factory, datastore):
        """
        Creates a file info object
        """
        fileInfo = factory.create('ns0:VirtualMachineFileInfo')
        fileInfo.vmPathName = '[%s]' % datastore
        return fileInfo

    def _create_nic(self, factory, device_type, device_label, device_summary,
                    network, unit):
        """
        Creates a NIC spec
        """
        deviceInfo = factory.create('ns0:Description')
        deviceInfo.label = device_label
        deviceInfo.summary = device_summary
        backing = factory.create('ns0:VirtualEthernetCardNetworkBackingInfo')
        backing.deviceName = network
        device = factory.create('ns0:%s' % device_type)
        device.addressType = 'Generated'
        device.wakeOnLanEnabled = True
        device.controllerKey = 100  # PCI Controller
        device.key = -300 - unit
        device.unitNumber = unit
        device.backing = backing
        device.deviceInfo = deviceInfo
        nicSpec = factory.create('ns0:VirtualDeviceConfigSpec')
        nicSpec.operation = 'add'
        nicSpec.fileOperation = None
        nicSpec.device = device
        return nicSpec

    def _create_disk_controller(self, factory, key):
        """
        Create a disk controller
        """
        deviceInfo = self._client.factory.create('ns0:Description')
        deviceInfo.label = 'SCSI controller 0'
        deviceInfo.summary = 'LSI Logic SAS'
        controller = factory.create('ns0:VirtualLsiLogicSASController')
        controller.busNumber = 0
        controller.key = key
        controller.sharedBus = 'noSharing'
        controller.deviceInfo = deviceInfo
        controllerSpec = factory.create('ns0:VirtualDeviceConfigSpec')
        controllerSpec.operation = 'add'
        controllerSpec.fileOperation = None
        controllerSpec.device = controller
        return controllerSpec

    def _create_option_value(self, factory, key, value):
        """
        Create option values
        """
        option = factory.create('ns0:OptionValue')
        option.key = key
        option.value = value
        return option

    @authenticated()
    def copy_file(self, source, destination, wait=True):
        """
        Copies a file on the datastore
        """
        task = self._client.service.CopyDatastoreFile_Task(
            _this=self._serviceContent.fileManager,
            sourceName=source,
            destinationName=destination)

        if wait:
            self.wait_for_task(task)
        return task

    @authenticated()
    def create_vm_from_template(self,
                                name,
                                source_vm,
                                disks,
                                ip,
                                mountpoint,
                                wait=True):
        """
        Create a vm based on an existing vtemplate on specified tgt hypervisor
        Raises RuntimeError if datastore is not available at (ip, mountpoint)
        """

        esxhost = self._validate_host(None)
        host_data = self._get_host_data(esxhost)

        datastore = self.get_datastore(ip, mountpoint)
        # Build basic config information
        config = self._client.factory.create('ns0:VirtualMachineConfigSpec')
        config.name = name
        config.numCPUs = source_vm.config.hardware.numCPU
        config.memoryMB = source_vm.config.hardware.memoryMB
        config.guestId = source_vm.config.guestId
        config.deviceChange = []
        config.extraConfig = []
        config.files = self._create_file_info(self._client.factory,
                                              datastore.name)

        disk_controller_key = -101
        config.deviceChange.append(
            self._create_disk_controller(self._client.factory,
                                         disk_controller_key))

        # Add disk devices
        for disk in disks:
            config.deviceChange.append(
                self._create_disk(self._client.factory, disk_controller_key,
                                  disk, disks.index(disk), datastore))

        # Add network
        nw_type = type(
            self._client.factory.create(
                'ns0:VirtualEthernetCardNetworkBackingInfo'))
        for device in source_vm.config.hardware.device:
            if hasattr(device, 'backing') and type(device.backing) == nw_type:
                config.deviceChange.append(
                    self._create_nic(self._client.factory,
                                     device.__class__.__name__,
                                     device.deviceInfo.label,
                                     device.deviceInfo.summary,
                                     device.backing.deviceName,
                                     device.unitNumber))

        # Copy additional properties
        extraconfigstoskip = ['nvram']
        for item in source_vm.config.extraConfig:
            if not item.key in extraconfigstoskip:
                config.extraConfig.append(
                    self._create_option_value(self._client.factory, item.key,
                                              item.value))

        task = self._client.service.CreateVM_Task(
            host_data['folder'],
            config=config,
            pool=host_data['resourcePool'],
            host=host_data['host'])

        if wait:
            self.wait_for_task(task)
        return task

    @authenticated()
    def clone_vm(self, vmid, name, disks, wait=True):
        """
        Clone a existing VM configuration

        @param vmid: unique id of the vm
        @param name: name of the clone vm
        @param disks: list of disks to use in vm configuration
        @param kvmport: kvm port for the clone vm
        @param esxhost: esx host identifier on which to clone the vm
        @param wait: wait for task to complete or not (True/False)
        """

        esxhost = self._validate_host(None)
        host_data = self._get_host_data(esxhost)

        source_vm_object = self.exists(key=vmid)
        if not source_vm_object:
            raise Exception('VM with key reference %s not found' % vmid)
        source_vm = self._get_object(source_vm_object)
        datastore = self._get_object(source_vm.datastore[0][0])

        # Build basic config information
        config = self._client.factory.create('ns0:VirtualMachineConfigSpec')
        config.name = name
        config.numCPUs = source_vm.config.hardware.numCPU
        config.memoryMB = source_vm.config.hardware.memoryMB
        config.guestId = source_vm.config.guestId
        config.deviceChange = []
        config.extraConfig = []
        config.files = self._create_file_info(self._client.factory,
                                              datastore.name)

        disk_controller_key = -101
        config.deviceChange.append(
            self._create_disk_controller(self._client.factory,
                                         disk_controller_key))

        # Add disk devices
        for disk in disks:
            config.deviceChange.append(
                self._create_disk(self._client.factory, disk_controller_key,
                                  disk, disks.index(disk), datastore))
            self.copy_file(
                '[{0}] {1}'.format(
                    datastore.name, '%s.vmdk' %
                    disk['name'].split('_')[-1].replace('-clone', '')),
                '[{0}] {1}'.format(datastore.name, disk['backingdevice']))

        # Add network
        nw_type = type(
            self._client.factory.create(
                'ns0:VirtualEthernetCardNetworkBackingInfo'))
        for device in source_vm.config.hardware.device:
            if hasattr(device, 'backing') and type(device.backing) == nw_type:
                config.deviceChange.append(
                    self._create_nic(self._client.factory,
                                     device.__class__.__name__,
                                     device.deviceInfo.label,
                                     device.deviceInfo.summary,
                                     device.backing.deviceName,
                                     device.unitNumber))

        # Copy additional properties
        extraconfigstoskip = ['nvram']
        for item in source_vm.config.extraConfig:
            if not item.key in extraconfigstoskip:
                config.extraConfig.append(
                    self._create_option_value(self._client.factory, item.key,
                                              item.value))

        task = self._client.service.CreateVM_Task(
            host_data['folder'],
            config=config,
            pool=host_data['resourcePool'],
            host=host_data['host'])
        if wait:
            self.wait_for_task(task)
        return task

    @authenticated()
    def get_vm(self, key):
        vmid = self.exists(key=key)
        if vmid is None:
            raise RuntimeError(
                'Virtual Machine with key {} could not be found.'.format(key))
        vm = self._get_object(vmid)
        return vm

    @authenticated()
    def get_datastore(self, ip, mountpoint):
        """
        @param ip : hypervisor ip to query for datastore presence
        @param mountpoint: nfs mountpoint on hypervisor
        @rtype: sdk datastore object
        @return: object when found else None
        """

        datastore = None
        esxhost = self._validate_host(None)
        host_system = self._get_object(esxhost, properties=['datastore'])
        for store in host_system.datastore[0]:
            store = self._get_object(store)
            if not store.summary.accessible:
                raise RuntimeError(
                    'Datastore {0} is not accessible at mountpoint {1}'.format(
                        store.name, mountpoint))
            if hasattr(store.info, 'nas'):
                if store.info.nas.remoteHost == ip and store.info.nas.remotePath == mountpoint:
                    datastore = store

        return datastore

    @authenticated()
    def is_datastore_available(self, ip, mountpoint):
        """
        @param ip : hypervisor ip to query for datastore presence
        @param mountpoint: nfs mountpoint on hypervisor
        @rtype: boolean
        @return: True | False
        """

        if self.get_datastore(ip, mountpoint):
            return True
        else:
            return False

    def make_agnostic_config(self, vm_object):
        regex = '\[([^\]]+)\]\s(.+)'
        match = re.search(regex, vm_object.config.files.vmPathName)
        esxhost = self._validate_host(None)

        config = {
            'name': vm_object.config.name,
            'id': vm_object.obj_identifier.value,
            'backing': {
                'filename': match.group(2),
                'datastore': match.group(1)
            },
            'disks': [],
            'datastores': {}
        }

        for device in vm_object.config.hardware.device:
            if device.__class__.__name__ == 'VirtualDisk':
                if device.backing is not None and \
                        device.backing.fileName is not None:
                    backingfile = device.backing.fileName
                    match = re.search(regex, backingfile)
                    if match:
                        filename = match.group(2)
                        backingfile = filename.replace('.vmdk', '-flat.vmdk')
                        config['disks'].append({
                            'filename': filename,
                            'backingfilename': backingfile,
                            'datastore': match.group(1),
                            'name': device.deviceInfo.label,
                            'order': device.unitNumber
                        })

        host_system = self._get_object(esxhost, properties=['datastore'])
        for store in host_system.datastore[0]:
            store = self._get_object(store)
            if hasattr(store.info, 'nas'):
                config['datastores'][store.info.name] = '{}:{}'.format(
                    store.info.nas.remoteHost, store.info.nas.remotePath)

        return config

    @authenticated()
    def delete_vm(self,
                  vmid,
                  storagedriver_mountpoint,
                  storagedriver_storage_ip,
                  devicename,
                  wait=False):
        """
        Delete a given vm
        """
        machine = None
        if vmid:
            try:
                machine = self._build_property('VirtualMachine', vmid)
            except Exception as ex:
                logger.error(
                    'SDK domain retrieve failed by vmid: {}'.format(ex))
        elif storagedriver_mountpoint and storagedriver_storage_ip and devicename:
            try:
                machine_info = self.get_nfs_datastore_object(
                    storagedriver_storage_ip, storagedriver_mountpoint,
                    devicename)[0]
                machine = self._build_property(
                    'VirtualMachine', machine_info.obj_identifier.value)
            except Exception as ex:
                logger.error(
                    'SDK domain retrieve failed by nfs datastore info: {}'.
                    format(ex))
        if machine:
            task = self._client.service.Destroy_Task(machine)
            if wait:
                self.wait_for_task(task)

        if storagedriver_mountpoint and devicename:
            vmx_path = os.path.join(storagedriver_mountpoint, devicename)
            if os.path.exists(vmx_path):
                dir_name = os.path.dirname(vmx_path)
                logger.debug('Removing leftover files in {0}'.format(dir_name))
                try:
                    shutil.rmtree(dir_name)
                    logger.debug('Removed dir tree {}'.format(dir_name))
                except Exception as exception:
                    logger.error(
                        'Failed to remove dir tree {0}. Reason: {1}'.format(
                            dir_name, str(exception)))
        return task

    @authenticated()
    def get_power_state(self, vmid):
        """
        Get the power state of a given vm
        """
        return self._get_object(self._build_property('VirtualMachine', vmid),
                                properties=['runtime.powerState'
                                            ]).runtime.powerState

    @authenticated()
    def mount_nfs_datastore(self, name, remote_host, remote_path):
        """
        Mounts a given NFS export as a datastore
        """
        esxhost = self._validate_host(None)
        host = self._get_object(esxhost,
                                properties=[
                                    'datastore', 'name', 'configManager',
                                    'configManager.datastoreSystem'
                                ])
        for store in host.datastore[0]:
            store = self._get_object(store)
            if hasattr(store.info, 'nas'):
                if store.info.name == name:
                    if store.info.nas.remoteHost == remote_host and \
                            store.info.nas.remotePath == remote_path:
                        # We'll remove this store, as it's identical to the once we'll add,
                        # forcing a refresh
                        self._client.service.RemoveDatastore(
                            host.configManager.datastoreSystem,
                            store.obj_identifier)
                        break
                    else:
                        raise RuntimeError(
                            'A datastore {0} already exists, pointing to {1}:{2}'
                            .format(name, store.info.nas.remoteHost,
                                    store.info.nas.remotePath))
        spec = self._client.factory.create('ns0:HostNasVolumeSpec')
        spec.accessMode = 'readWrite'
        spec.localPath = name
        spec.remoteHost = remote_host
        spec.remotePath = remote_path
        spec.type = 'nfs'
        return self._client.service.CreateNasDatastore(
            host.configManager.datastoreSystem, spec)

    @authenticated()
    def wait_for_task(self, task):
        """
        Wait for a task to be completed
        """
        state = self.get_task_info(task).info.state
        while state in ['running', 'queued']:
            sleep(1)
            state = self.get_task_info(task).info.state

    @authenticated()
    def get_nfs_datastore_object(self, ip, mountpoint, filename):
        """
        ip : "10.130.12.200", string
        mountpoint: "/srv/volumefs", string
        filename: "cfovs001/vhd0(-flat).vmdk"
        identify nfs datastore on this esx host based on ip and mount
        check if filename is present on datastore
        if file is .vmdk return VirtualDisk object for corresponding virtual disk
        if file is .vmx return VirtualMachineConfigInfo for corresponding vm

        @rtype: tuple
        @return: A tuple. First item: vm config, second item: Device if a vmdk was given
        """

        filename = filename.replace(
            '-flat.vmdk', '.vmdk')  # Support both -flat.vmdk and .vmdk
        if not filename.endswith('.vmdk') and not filename.endswith('.vmx'):
            raise ValueError('Unexpected filetype')
        esxhost = self._validate_host(None)

        datastore = self.get_datastore(ip, mountpoint)
        if not datastore:
            raise RuntimeError('Could not find datastore')

        vms = self._get_object(esxhost,
                               prop_type='VirtualMachine',
                               traversal={
                                   'name': 'HostSystemTraversalSpec',
                                   'type': 'HostSystem',
                                   'path': 'vm'
                               },
                               properties=['name', 'config'])
        if not vms:
            raise RuntimeError('No vMachines found')
        for vm in vms:
            mapping = self._get_vm_datastore_mapping(vm)
            if datastore.name in mapping:
                if filename in mapping[datastore.name]:
                    return vm, mapping[datastore.name][filename]
        raise RuntimeError(
            'Could not locate given file on the given datastore')

    def _get_vm_datastore_mapping(self, vm):
        """
        Creates a datastore mapping for a vm's devices
        Structure
            {<datastore name>: {<backing filename>: <device>,
                                <backing filename>: <device>},
             <datastore name>: {<backing filename>: <device>}}
        Example
            {'datastore A': {'/machine1/machine1.vmx': <device>,
                             '/machine1/disk1.vmdk': <device>},
             'datastore B': {'/machine1/disk2.vmdk': <device>}}
        """
        def extract_names(backingfile, given_mapping, metadata=None):
            match = re.search('\[([^\]]+)\]\s(.+)', backingfile)
            if match:
                datastore_name = match.group(1)
                filename = match.group(2)
                if datastore_name not in mapping:
                    given_mapping[datastore_name] = {}
                given_mapping[datastore_name][filename] = metadata
            return given_mapping

        virtual_disk_type = self._client.factory.create('ns0:VirtualDisk')
        flat_type = self._client.factory.create(
            'ns0:VirtualDiskFlatVer2BackingInfo')

        mapping = {}
        for device in vm.config.hardware.device:
            if isinstance(device, type(virtual_disk_type)):
                if device.backing is not None and isinstance(
                        device.backing, type(flat_type)):
                    mapping = extract_names(device.backing.fileName, mapping,
                                            device)
        mapping = extract_names(vm.config.files.vmPathName, mapping)
        return mapping

    def _get_host_data(self, esxhost):
        """
        Get host data for a given esxhost
        """
        hostobject = self._get_object(
            esxhost, properties=['parent', 'datastore', 'network'])
        datastore = self._get_object(hostobject.datastore[0][0],
                                     properties=['info']).info
        computeresource = self._get_object(
            hostobject.parent, properties=['resourcePool', 'parent'])
        datacenter = self._get_object(computeresource.parent,
                                      properties=['parent']).parent
        vm_folder = self._get_object(datacenter,
                                     properties=['vmFolder']).vmFolder

        return {
            'host': esxhost,
            'computeResource': computeresource,
            'resourcePool': computeresource.resourcePool,
            'datacenter': datacenter,
            'folder': vm_folder,
            'datastore': datastore,
            'network': hostobject.network[0]
        }

    def _get_host_iqn_mapping(self, esxhost, rescan=False):
        """
        Get the IQN mapping for a given esx host, optionally rescanning the host
        """
        # pylint: disable=line-too-long
        regex = re.compile(
            '^key-vim.host.PlugStoreTopology.Path-iqn.+?,(?P<iqn>iqn.*?),t,1-(?P<eui>eui.+)$'
        )  # noqa
        # pylint: enable=line-too-long

        hostobject = self._get_object(
            esxhost, properties=['configManager.storageSystem'])
        stg_ssystem = self._get_object(
            hostobject.configManager.storageSystem,
            properties=[
                'storageDeviceInfo',
                'storageDeviceInfo.plugStoreTopology.device'
            ])
        if rescan:
            # Force a rescan of the vmfs
            self._client.service.RescanVmfs(stg_ssystem.obj_identifier)
            stg_ssystem = self._get_object(
                hostobject.configManager.storageSystem,
                properties=[
                    'storageDeviceInfo',
                    'storageDeviceInfo.plugStoreTopology.device'
                ])

        device_info_mapping = {}
        for disk in stg_ssystem.storageDeviceInfo.scsiLun:
            device_info_mapping[disk.key] = disk.uuid

        iqn_mapping = {}
        for device in stg_ssystem.storageDeviceInfo.plugStoreTopology\
                                 .device.HostPlugStoreTopologyDevice:
            for path in device.path:
                match = regex.search(path)
                if match:
                    groups = match.groupdict()
                    iqn_mapping[groups['iqn']] = {
                        'eui': groups['eui'],
                        'lun': device.lun,
                        'uuid': device_info_mapping[device.lun]
                    }

        return iqn_mapping

    def _get_object(self,
                    key_object,
                    prop_type=None,
                    traversal=None,
                    properties=None):
        """
        Gets an object based on a given set of query parameters. Only the requested properties
        will be loaded. If no properties are specified, all will be loaded
        """
        object_spec = self._client.factory.create('ns0:ObjectSpec')
        object_spec.obj = key_object

        property_spec = self._client.factory.create('ns0:PropertySpec')
        property_spec.type = key_object._type if prop_type is None else prop_type
        if properties is None:
            property_spec.all = True
        else:
            property_spec.all = False
            property_spec.pathSet = properties

        if traversal is not None:
            select_set_ptr = object_spec
            while True:
                select_set_ptr.selectSet = self._client.factory.create(
                    'ns0:TraversalSpec')
                select_set_ptr.selectSet.name = traversal['name']
                select_set_ptr.selectSet.type = traversal['type']
                select_set_ptr.selectSet.path = traversal['path']
                if 'traversal' in traversal:
                    traversal = traversal['traversal']
                    select_set_ptr = select_set_ptr.selectSet
                else:
                    break

        property_filter_spec = self._client.factory.create(
            'ns0:PropertyFilterSpec')
        property_filter_spec.objectSet = [object_spec]
        property_filter_spec.propSet = [property_spec]

        found_objects = self._client.service.RetrieveProperties(
            self._serviceContent.propertyCollector, [property_filter_spec])

        if len(found_objects) > 0:
            for item in found_objects:
                item.obj_identifier = item.obj
                del item.obj

                if hasattr(item, 'missingSet'):
                    for missing_item in item.missingSet:
                        if missing_item.fault.fault.__class__.__name__ == 'NotAuthenticated':
                            raise NotAuthenticatedException()

                for propSet in item.propSet:
                    if '.' in propSet.name:
                        working_item = item
                        path = str(propSet.name).split('.')
                        part_counter = 0
                        for part in path:
                            part_counter += 1
                            if part_counter < len(path):
                                if not part in working_item.__dict__:
                                    setattr(working_item, part,
                                            type(part, (), {})())
                                working_item = working_item.__dict__[part]
                            else:
                                setattr(working_item, part, propSet.val)
                    else:
                        setattr(item, propSet.name, propSet.val)
                del item.propSet
            if len(found_objects) == 1:
                return found_objects[0]
            else:
                return found_objects

        return None

    def _build_property(self, property_name, value=None):
        """
        Create a property object with given name and value
        """
        new_property = Property(property_name)
        new_property._type = property_name
        if value is not None:
            new_property.value = value
        return new_property

    def _validate_host(self, host):
        """
        Validates wheteher a given host is valid
        """
        if host is None:
            if self._is_vcenter:
                raise Exception(
                    'A HostSystem reference is mandatory for a vCenter Server')
            else:
                return self._esxHost
        else:
            if hasattr(host, '_type') and host._type == 'HostSystem':
                return self._get_object(host,
                                        properties=['name']).obj_identifier
            else:
                return self._get_object(self._build_property(
                    'HostSystem', host),
                                        properties=['name']).obj_identifier

    def _login(self):
        """
        Executes a logout (to make sure we're logged out), and logs in again
        """
        self._logout()
        self._sessionID = self._client.service.Login(
            self._serviceContent.sessionManager, self._username,
            self._password, None).key

    def _logout(self):
        """
        Logs out the current session
        """
        try:
            self._client.service.Logout(self._serviceContent.sessionManager)
        except:
            pass
Exemple #21
0
 def build_object_cache(self):
     cache = ObjectCache()
     cache.setduration(seconds=config.get('soap.client_timeout'))
     cache.setlocation(config.get('soap.cache_location'))
     return cache
Exemple #22
0
 def activate_cache(self):
     oc = ObjectCache()
     oc.setduration(days=365)
Exemple #23
0
 def build_object_cache(self):
     cache = ObjectCache()
     cache.setduration(seconds=config.get('soap.client_timeout'))
     cache.setlocation(config.get('soap.cache_location'))
     return cache
Exemple #24
0
def clear_suds_cache(wsdl):
    for type in ("wsdl", "document"):
        cacheId = Reader(Options()).mangle(wsdl, type)
        DocumentCache().purge(cacheId)
        ObjectCache().purge(cacheId)
        FileCache().purge(cacheId)
Exemple #25
0
class Sdk(object):
    """
    This class contains all SDK related methods
    """

    def __init__(self, host, login, passwd):
        """
        Initializes the SDK
        """
        self._host = host
        self._username = login
        self._password = passwd
        self._sessionID = None
        self._check_session = True

        self._cache = ObjectCache()
        self._cache.setduration(weeks=1)

        self._client = Client('https://%s/sdk/vimService?wsdl' % host,
                              cache=self._cache,
                              cachingpolicy=1)
        self._client.set_options(location='https://%s/sdk' % host,
                                 plugins=[ValueExtender()])

        service_reference = self._build_property('ServiceInstance')
        self._serviceContent = self._client.service.RetrieveServiceContent(
            service_reference)

        # In case of an ESXi host, this would be 'HostAgent'
        self._is_vcenter = self._serviceContent.about.apiType == 'VirtualCenter'
        if not self._is_vcenter:
            # pylint: disable=line-too-long
            self._login()
            self._esxHost = self._get_object(
                self._serviceContent.rootFolder,
                prop_type='HostSystem',
                traversal={'name': 'FolderTraversalSpec',
                           'type': 'Folder',
                           'path': 'childEntity',
                           'traversal': {'name': 'DatacenterTraversalSpec',
                                         'type': 'Datacenter',
                                         'path': 'hostFolder',
                                         'traversal': {'name': 'DFolderTraversalSpec',
                                                       'type': 'Folder',
                                                       'path': 'childEntity',
                                                       'traversal': {'name': 'ComputeResourceTravelSpec',  # noqa
                                                                     'type': 'ComputeResource',
                                                                     'path': 'host'}}}},
                properties=['name']
            ).obj_identifier
            # pylint: enable=line-too-long
        else:
            self._esxHost = None

    @authenticated(force=True)
    def _get_vcenter_hosts(self):
        """
        reload vCenter info (host name and summary)
        """
        if not self._is_vcenter:
            raise RuntimeError('Must be connected to a vCenter Server API.')
        datacenter_info = self._get_object(
            self._serviceContent.rootFolder,
            prop_type='HostSystem',
            traversal={'name': 'FolderTraversalSpec',
                       'type': 'Folder',
                       'path': 'childEntity',
                       'traversal': {'name': 'DatacenterTraversalSpec',
                                     'type': 'Datacenter',
                                     'path': 'hostFolder',
                                     'traversal': {'name': 'DFolderTraversalSpec',
                                                   'type': 'Folder',
                                                   'path': 'childEntity',
                                                   'traversal': {'name': 'ComputeResourceTravelSpec',  # noqa
                                                                 'type': 'ComputeResource',
                                                                 'path': 'host'}}}},
            properties=['name', 'summary.runtime', 'config.virtualNicManagerInfo.netConfig']
        )
        return datacenter_info

    def get_host_status_by_ip(self, host_ip):
        """
        Return host status by ip, from vcenter info
        Must be connected to a vcenter server api
        """
        host = self._get_host_info_by_ip(host_ip)
        return host.summary.runtime.powerState

    def get_host_status_by_pk(self, pk):
        """
        Return host status by pk, from vcenter info
        Must be connected to a vcenter server api
        """
        host = self._get_host_info_by_pk(pk)
        return host.summary.runtime.powerState

    def get_host_primary_key(self, host_ip):
        """
        Return host primary key, based on current ip
        Must be connected to a vcenter server api
        """
        host = self._get_host_info_by_ip(host_ip)
        return host.obj_identifier.value

    def _get_host_info_by_ip(self, host_ip):
        """
        Return HostSystem object by ip, from vcenter info
        Must be connected to a vcenter server api
        """
        datacenter_info = self._get_vcenter_hosts()
        for host in datacenter_info:
            for nic in host.config.virtualNicManagerInfo.netConfig.VirtualNicManagerNetConfig:
                if nic.nicType == 'management':
                    for vnic in nic.candidateVnic:
                        if vnic.spec.ip.ipAddress == host_ip:
                            return host
        raise RuntimeError('Host with ip {0} not found in datacenter info'.format(host_ip))

    def _get_host_info_by_pk(self, pk):
        """
        Return HostSystem object by pk, from vcenter info
        Must be connected to a vcenter server api
        """
        datacenter_info = self._get_vcenter_hosts()
        for host in datacenter_info:
            if host.obj_identifier.value == pk:
                return host

    def get_hosts(self):
        """
        Gets a neutral list of all hosts available
        """
        host_data = self._get_vcenter_hosts()
        host_data = [] if host_data is None else host_data
        hosts = {}
        for host in host_data:
            ips = []
            for nic in host.config.virtualNicManagerInfo.netConfig.VirtualNicManagerNetConfig:
                if nic.nicType == 'management':
                    for vnic in nic.candidateVnic:
                        ips.append(vnic.spec.ip.ipAddress)
            hosts[host.obj_identifier.value] = {'name': host.name,
                                                'ips': ips}
        return hosts

    def test_connection(self):
        """
        Tests the connection
        """
        self._login()
        return True

    def list_hosts_in_datacenter(self):
        """
        return a list of registered host names in vCenter
        must be connected to a vcenter server api
        """
        datacenter_info = self._get_vcenter_hosts()
        return [host.name for host in datacenter_info]

    def validate_result(self, result, message=None):
        """
        Validates a given result. Returning True if the task succeeded, raising an error if not
        """
        if hasattr(result, '_type') and result._type == 'Task':
            return self.validate_result(self.get_task_info(result), message)
        elif hasattr(result, 'info'):
            if result.info.state == 'success':
                return True
            elif result.info.state == 'error':
                error = result.info.error.localizedMessage
                raise Exception(('%s: %s' % (message, error)) if message else error)
        raise Exception(('%s: %s' % (message, 'Unexpected result'))
                        if message else 'Unexpected result')

    @authenticated()
    def get_task_info(self, task):
        """
        Loads the task details
        """
        return self._get_object(task)

    @authenticated()
    def get_vm_ip_information(self):
        """
        Get the IP information for all vms on a given esxi host
        """
        esxhost = self._validate_host(None)
        configuration = []
        for vm in self._get_object(esxhost,
                                   prop_type='VirtualMachine',
                                   traversal={
                                       'name': 'HostSystemTraversalSpec',
                                       'type': 'HostSystem',
                                       'path': 'vm'},
                                   properties=['name', 'guest.net', 'config.files']):
            vmi = {'id': str(vm.obj_identifier.value),
                   'vmxpath': str(vm.config.files.vmPathName),
                   'name': str(vm.name),
                   'net': []}
            if vm.guest.net:
                for net in vm.guest.net[0]:
                    vmi['net'].append({'mac': str(net.macAddress),
                                       'ipaddresses': [str(i.ipAddress)
                                                       for i in net.ipConfig.ipAddress]})
            configuration.append(vmi)
        return configuration

    @authenticated()
    def exists(self, name=None, key=None):
        """
        Checks whether a vm with a given name or key exists on a given esxi host
        """
        esxhost = self._validate_host(None)
        if name is not None or key is not None:
            try:
                if name is not None:
                    vms = [vm for vm in
                           self._get_object(esxhost,
                                            prop_type='VirtualMachine',
                                            traversal={'name': 'HostSystemTraversalSpec',
                                                       'type': 'HostSystem',
                                                       'path': 'vm'},
                                            properties=['name']) if vm.name == name]
                    if len(vms) == 0:
                        return None
                    else:
                        return vms[0].obj_identifier
                if key is not None:
                    return self._get_object(
                        self._build_property('VirtualMachine', key),
                        properties=['name']).obj_identifier
            except:
                return None
        else:
            raise Exception('A name or key should be passed.')

    @authenticated()
    def get_vm(self, key):
        """
        Retreives a vm object, based on its key
        """
        vmid = self.exists(key=key)
        if vmid is None:
            raise RuntimeError('Virtual Machine with key {} could not be found.'.format(key))
        vm = self._get_object(vmid)
        return vm

    @authenticated()
    def get_vms(self, ip, mountpoint):
        """
        Get all vMachines using a given nfs share
        """
        esxhost = self._validate_host(None)
        datastore = self.get_datastore(ip, mountpoint)
        filtered_vms = []
        vms = self._get_object(esxhost,
                               prop_type='VirtualMachine',
                               traversal={'name': 'HostSystemTraversalSpec',
                                          'type': 'HostSystem',
                                          'path': 'vm'},
                               properties=['name', 'config'])
        for vm in vms:
            mapping = self._get_vm_datastore_mapping(vm)
            if datastore.name in mapping:
                filtered_vms.append(vm)
        return filtered_vms

    @authenticated()
    def set_disk_mode(self, vmid, disks, mode, wait=True):
        """
        Sets the disk mode for a set of disks
        """
        config = self._client.factory.create('ns0:VirtualMachineConfigSpec')
        config.deviceChange = []

        disk_type = type(self._client.factory.create('ns0:VirtualDisk'))

        vmid = self.exists(key=vmid)
        vm = self._get_object(vmid)
        for device in vm.config.hardware.devices:
            if type(device) == disk_type and hasattr(device, 'backing') \
                    and device.backing.fileName in disks:
                backing = self._client.factory.create(
                    'ns0:VirtualDiskFlatVer2BackingInfo')
                backing.diskMode = mode
                device = self._client.factory.create('ns0:VirtualDisk')
                device.backing = backing
                diskSpec = self._client.factory.create(
                    'ns0:VirtualDeviceConfigSpec')
                diskSpec.operation = 'edit'
                diskSpec.fileOperation = None
                diskSpec.device = device
                config.deviceChange.append(diskSpec)

        task = self._client.service.ReconfigVM_Task(vm.obj_identifier, config)

        if wait:
            self.wait_for_task(task)
        return task

    def _create_disk(self, factory, key, disk, unit, datastore):
        """
        Creates a disk spec for a given backing device
        Example for parameter disk: {'name': diskname, 'backingdevice': 'disk-flat.vmdk'}
        """
        deviceInfo = factory.create('ns0:Description')
        deviceInfo.label = disk['name']
        deviceInfo.summary = 'Disk %s' % disk['name']
        backing = factory.create('ns0:VirtualDiskFlatVer2BackingInfo')
        backing.diskMode = 'persistent'
        backing.fileName = '[%s] %s' % (datastore.name, disk['backingdevice'])
        backing.thinProvisioned = True
        device = factory.create('ns0:VirtualDisk')
        device.controllerKey = key
        device.key = -200 - unit
        device.unitNumber = unit
        device.deviceInfo = deviceInfo
        device.backing = backing
        diskSpec = factory.create('ns0:VirtualDeviceConfigSpec')
        diskSpec.operation = 'add'
        diskSpec.fileOperation = None
        diskSpec.device = device
        return diskSpec

    def _create_file_info(self, factory, datastore):
        """
        Creates a file info object
        """
        fileInfo = factory.create('ns0:VirtualMachineFileInfo')
        fileInfo.vmPathName = '[%s]' % datastore
        return fileInfo

    def _create_nic(self, factory, device_type, device_label, device_summary, network, unit):
        """
        Creates a NIC spec
        """
        deviceInfo = factory.create('ns0:Description')
        deviceInfo.label = device_label
        deviceInfo.summary = device_summary
        backing = factory.create('ns0:VirtualEthernetCardNetworkBackingInfo')
        backing.deviceName = network
        device = factory.create('ns0:%s' % device_type)
        device.addressType = 'Generated'
        device.wakeOnLanEnabled = True
        device.controllerKey = 100  # PCI Controller
        device.key = -300 - unit
        device.unitNumber = unit
        device.backing = backing
        device.deviceInfo = deviceInfo
        nicSpec = factory.create('ns0:VirtualDeviceConfigSpec')
        nicSpec.operation = 'add'
        nicSpec.fileOperation = None
        nicSpec.device = device
        return nicSpec

    def _create_disk_controller(self, factory, key):
        """
        Create a disk controller
        """
        deviceInfo = self._client.factory.create('ns0:Description')
        deviceInfo.label = 'SCSI controller 0'
        deviceInfo.summary = 'LSI Logic SAS'
        controller = factory.create('ns0:VirtualLsiLogicSASController')
        controller.busNumber = 0
        controller.key = key
        controller.sharedBus = 'noSharing'
        controller.deviceInfo = deviceInfo
        controllerSpec = factory.create('ns0:VirtualDeviceConfigSpec')
        controllerSpec.operation = 'add'
        controllerSpec.fileOperation = None
        controllerSpec.device = controller
        return controllerSpec

    def _create_option_value(self, factory, key, value):
        """
        Create option values
        """
        option = factory.create('ns0:OptionValue')
        option.key = key
        option.value = value
        return option

    @authenticated()
    def copy_file(self, source, destination, wait=True):
        """
        Copies a file on the datastore
        """
        task = self._client.service.CopyDatastoreFile_Task(
            _this=self._serviceContent.fileManager,
            sourceName=source,
            destinationName=destination)

        if wait:
            self.wait_for_task(task)
        return task

    @authenticated()
    def create_vm_from_template(self, name, source_vm, disks, ip, mountpoint, wait=True):
        """
        Create a vm based on an existing vtemplate on specified tgt hypervisor
        Raises RuntimeError if datastore is not available at (ip, mountpoint)
        """

        esxhost = self._validate_host(None)
        host_data = self._get_host_data(esxhost)

        datastore = self.get_datastore(ip, mountpoint)
        # Build basic config information
        config = self._client.factory.create('ns0:VirtualMachineConfigSpec')
        config.name = name
        config.numCPUs = source_vm.config.hardware.numCPU
        config.memoryMB = source_vm.config.hardware.memoryMB
        config.guestId = source_vm.config.guestId
        config.deviceChange = []
        config.extraConfig = []
        config.files = self._create_file_info(self._client.factory, datastore.name)

        disk_controller_key = -101
        config.deviceChange.append(
            self._create_disk_controller(self._client.factory,
                                         disk_controller_key))

        # Add disk devices
        for disk in disks:
            config.deviceChange.append(
                self._create_disk(self._client.factory, disk_controller_key,
                                  disk, disks.index(disk), datastore))

        # Add network
        nw_type = type(self._client.factory.create('ns0:VirtualEthernetCardNetworkBackingInfo'))
        for device in source_vm.config.hardware.device:
            if hasattr(device, 'backing') and type(device.backing) == nw_type:
                config.deviceChange.append(
                    self._create_nic(self._client.factory,
                                     device.__class__.__name__,
                                     device.deviceInfo.label,
                                     device.deviceInfo.summary,
                                     device.backing.deviceName,
                                     device.unitNumber))

        # Copy additional properties
        extraconfigstoskip = ['nvram']
        for item in source_vm.config.extraConfig:
            if not item.key in extraconfigstoskip:
                config.extraConfig.append(
                    self._create_option_value(self._client.factory,
                                              item.key,
                                              item.value))

        task = self._client.service.CreateVM_Task(host_data['folder'],
                                                  config=config,
                                                  pool=host_data['resourcePool'],
                                                  host=host_data['host'])

        if wait:
            self.wait_for_task(task)
        return task

    @authenticated()
    def clone_vm(self, vmid, name, disks, wait=True):
        """
        Clone a existing VM configuration

        @param vmid: unique id of the vm
        @param name: name of the clone vm
        @param disks: list of disks to use in vm configuration
        @param kvmport: kvm port for the clone vm
        @param esxhost: esx host identifier on which to clone the vm
        @param wait: wait for task to complete or not (True/False)
        """

        esxhost = self._validate_host(None)
        host_data = self._get_host_data(esxhost)

        source_vm_object = self.exists(key=vmid)
        if not source_vm_object:
            raise Exception('VM with key reference %s not found' % vmid)
        source_vm = self._get_object(source_vm_object)
        datastore = self._get_object(source_vm.datastore[0][0])

        # Build basic config information
        config = self._client.factory.create('ns0:VirtualMachineConfigSpec')
        config.name = name
        config.numCPUs = source_vm.config.hardware.numCPU
        config.memoryMB = source_vm.config.hardware.memoryMB
        config.guestId = source_vm.config.guestId
        config.deviceChange = []
        config.extraConfig = []
        config.files = self._create_file_info(
            self._client.factory, datastore.name)

        disk_controller_key = -101
        config.deviceChange.append(
            self._create_disk_controller(self._client.factory,
                                         disk_controller_key))

        # Add disk devices
        for disk in disks:
            config.deviceChange.append(
                self._create_disk(self._client.factory, disk_controller_key,
                                  disk, disks.index(disk), datastore))
            self.copy_file(
                '[{0}] {1}'.format(datastore.name, '%s.vmdk'
                                   % disk['name'].split('_')[-1].replace('-clone', '')),
                '[{0}] {1}'.format(datastore.name, disk['backingdevice']))

        # Add network
        nw_type = type(self._client.factory.create(
            'ns0:VirtualEthernetCardNetworkBackingInfo'))
        for device in source_vm.config.hardware.device:
            if hasattr(device, 'backing') and type(device.backing) == nw_type:
                config.deviceChange.append(
                    self._create_nic(self._client.factory,
                                     device.__class__.__name__,
                                     device.deviceInfo.label,
                                     device.deviceInfo.summary,
                                     device.backing.deviceName,
                                     device.unitNumber))

        # Copy additional properties
        extraconfigstoskip = ['nvram']
        for item in source_vm.config.extraConfig:
            if not item.key in extraconfigstoskip:
                config.extraConfig.append(
                    self._create_option_value(self._client.factory,
                                              item.key,
                                              item.value))

        task = self._client.service.CreateVM_Task(host_data['folder'],
                                                  config=config,
                                                  pool=host_data['resourcePool'],
                                                  host=host_data['host'])
        if wait:
            self.wait_for_task(task)
        return task

    @authenticated()
    def get_vm(self, key):
        vmid = self.exists(key=key)
        if vmid is None:
            raise RuntimeError('Virtual Machine with key {} could not be found.'.format(key))
        vm = self._get_object(vmid)
        return vm

    @authenticated()
    def get_datastore(self, ip, mountpoint):
        """
        @param ip : hypervisor ip to query for datastore presence
        @param mountpoint: nfs mountpoint on hypervisor
        @rtype: sdk datastore object
        @return: object when found else None
        """

        datastore = None
        esxhost = self._validate_host(None)
        host_system = self._get_object(esxhost, properties=['datastore'])
        for store in host_system.datastore[0]:
            store = self._get_object(store)
            if not store.summary.accessible:
                raise RuntimeError('Datastore {0} is not accessible at mountpoint {1}'.format(store.name, mountpoint))
            if hasattr(store.info, 'nas'):
                if store.info.nas.remoteHost == ip and store.info.nas.remotePath == mountpoint:
                    datastore = store

        return datastore

    @authenticated()
    def is_datastore_available(self, ip, mountpoint):
        """
        @param ip : hypervisor ip to query for datastore presence
        @param mountpoint: nfs mountpoint on hypervisor
        @rtype: boolean
        @return: True | False
        """

        if self.get_datastore(ip, mountpoint):
            return True
        else:
            return False

    def make_agnostic_config(self, vm_object):
        regex = '\[([^\]]+)\]\s(.+)'
        match = re.search(regex, vm_object.config.files.vmPathName)
        esxhost = self._validate_host(None)

        config = {'name': vm_object.config.name,
                  'id': vm_object.obj_identifier.value,
                  'backing': {'filename': match.group(2),
                              'datastore': match.group(1)},
                  'disks': [],
                  'datastores': {}}

        for device in vm_object.config.hardware.device:
            if device.__class__.__name__ == 'VirtualDisk':
                if device.backing is not None and \
                        device.backing.fileName is not None:
                    backingfile = device.backing.fileName
                    match = re.search(regex, backingfile)
                    if match:
                        filename = match.group(2)
                        backingfile = filename.replace('.vmdk', '-flat.vmdk')
                        config['disks'].append({'filename': filename,
                                                'backingfilename': backingfile,
                                                'datastore': match.group(1),
                                                'name': device.deviceInfo.label,
                                                'order': device.unitNumber})

        host_system = self._get_object(esxhost, properties=['datastore'])
        for store in host_system.datastore[0]:
            store = self._get_object(store)
            if hasattr(store.info, 'nas'):
                config['datastores'][store.info.name] = '{}:{}'.format(store.info.nas.remoteHost,
                                                                       store.info.nas.remotePath)

        return config

    @authenticated()
    def delete_vm(self, vmid, storagedriver_mountpoint, storagedriver_storage_ip, devicename, wait=False):
        """
        Delete a given vm
        """
        machine = None
        if vmid:
            try:
                machine = self._build_property('VirtualMachine', vmid)
            except Exception as ex:
                logger.error('SDK domain retrieve failed by vmid: {}'.format(ex))
        elif storagedriver_mountpoint and storagedriver_storage_ip and devicename:
            try:
                machine_info = self.get_nfs_datastore_object(storagedriver_storage_ip, storagedriver_mountpoint, devicename)[0]
                machine = self._build_property('VirtualMachine', machine_info.obj_identifier.value)
            except Exception as ex:
                logger.error('SDK domain retrieve failed by nfs datastore info: {}'.format(ex))
        if machine:
            task = self._client.service.Destroy_Task(machine)
            if wait:
                self.wait_for_task(task)

        if storagedriver_mountpoint and devicename:
            vmx_path = os.path.join(storagedriver_mountpoint, devicename)
            if os.path.exists(vmx_path):
                dir_name = os.path.dirname(vmx_path)
                logger.debug('Removing leftover files in {0}'.format(dir_name))
                try:
                    shutil.rmtree(dir_name)
                    logger.debug('Removed dir tree {}'.format(dir_name))
                except Exception as exception:
                    logger.error('Failed to remove dir tree {0}. Reason: {1}'.format(dir_name, str(exception)))
        return task

    @authenticated()
    def get_power_state(self, vmid):
        """
        Get the power state of a given vm
        """
        return self._get_object(self._build_property('VirtualMachine', vmid),
                                properties=['runtime.powerState']).runtime.powerState

    @authenticated()
    def mount_nfs_datastore(self, name, remote_host, remote_path):
        """
        Mounts a given NFS export as a datastore
        """
        esxhost = self._validate_host(None)
        host = self._get_object(esxhost, properties=['datastore',
                                                     'name',
                                                     'configManager',
                                                     'configManager.datastoreSystem'])
        for store in host.datastore[0]:
            store = self._get_object(store)
            if hasattr(store.info, 'nas'):
                if store.info.name == name:
                    if store.info.nas.remoteHost == remote_host and \
                            store.info.nas.remotePath == remote_path:
                        # We'll remove this store, as it's identical to the once we'll add,
                        # forcing a refresh
                        self._client.service.RemoveDatastore(host.configManager.datastoreSystem,
                                                             store.obj_identifier)
                        break
                    else:
                        raise RuntimeError('A datastore {0} already exists, pointing to {1}:{2}'.format(
                            name, store.info.nas.remoteHost, store.info.nas.remotePath
                        ))
        spec = self._client.factory.create('ns0:HostNasVolumeSpec')
        spec.accessMode = 'readWrite'
        spec.localPath = name
        spec.remoteHost = remote_host
        spec.remotePath = remote_path
        spec.type = 'nfs'
        return self._client.service.CreateNasDatastore(host.configManager.datastoreSystem, spec)

    @authenticated()
    def wait_for_task(self, task):
        """
        Wait for a task to be completed
        """
        state = self.get_task_info(task).info.state
        while state in ['running', 'queued']:
            sleep(1)
            state = self.get_task_info(task).info.state

    @authenticated()
    def get_nfs_datastore_object(self, ip, mountpoint, filename):
        """
        ip : "10.130.12.200", string
        mountpoint: "/srv/volumefs", string
        filename: "cfovs001/vhd0(-flat).vmdk"
        identify nfs datastore on this esx host based on ip and mount
        check if filename is present on datastore
        if file is .vmdk return VirtualDisk object for corresponding virtual disk
        if file is .vmx return VirtualMachineConfigInfo for corresponding vm

        @rtype: tuple
        @return: A tuple. First item: vm config, second item: Device if a vmdk was given
        """

        filename = filename.replace('-flat.vmdk', '.vmdk')  # Support both -flat.vmdk and .vmdk
        if not filename.endswith('.vmdk') and not filename.endswith('.vmx'):
            raise ValueError('Unexpected filetype')
        esxhost = self._validate_host(None)

        datastore = self.get_datastore(ip, mountpoint)
        if not datastore:
            raise RuntimeError('Could not find datastore')

        vms = self._get_object(esxhost,
                               prop_type='VirtualMachine',
                               traversal={'name': 'HostSystemTraversalSpec',
                                          'type': 'HostSystem',
                                          'path': 'vm'},
                               properties=['name', 'config'])
        if not vms:
            raise RuntimeError('No vMachines found')
        for vm in vms:
            mapping = self._get_vm_datastore_mapping(vm)
            if datastore.name in mapping:
                if filename in mapping[datastore.name]:
                    return vm, mapping[datastore.name][filename]
        raise RuntimeError('Could not locate given file on the given datastore')

    def _get_vm_datastore_mapping(self, vm):
        """
        Creates a datastore mapping for a vm's devices
        Structure
            {<datastore name>: {<backing filename>: <device>,
                                <backing filename>: <device>},
             <datastore name>: {<backing filename>: <device>}}
        Example
            {'datastore A': {'/machine1/machine1.vmx': <device>,
                             '/machine1/disk1.vmdk': <device>},
             'datastore B': {'/machine1/disk2.vmdk': <device>}}
        """
        def extract_names(backingfile, given_mapping, metadata=None):
            match = re.search('\[([^\]]+)\]\s(.+)', backingfile)
            if match:
                datastore_name = match.group(1)
                filename = match.group(2)
                if datastore_name not in mapping:
                    given_mapping[datastore_name] = {}
                given_mapping[datastore_name][filename] = metadata
            return given_mapping

        virtual_disk_type = self._client.factory.create('ns0:VirtualDisk')
        flat_type = self._client.factory.create('ns0:VirtualDiskFlatVer2BackingInfo')

        mapping = {}
        for device in vm.config.hardware.device:
            if isinstance(device, type(virtual_disk_type)):
                if device.backing is not None and isinstance(device.backing, type(flat_type)):
                    mapping = extract_names(device.backing.fileName, mapping, device)
        mapping = extract_names(vm.config.files.vmPathName, mapping)
        return mapping

    def _get_host_data(self, esxhost):
        """
        Get host data for a given esxhost
        """
        hostobject = self._get_object(
            esxhost, properties=['parent', 'datastore', 'network'])
        datastore = self._get_object(
            hostobject.datastore[0][0], properties=['info']).info
        computeresource = self._get_object(
            hostobject.parent, properties=['resourcePool', 'parent'])
        datacenter = self._get_object(
            computeresource.parent, properties=['parent']).parent
        vm_folder = self._get_object(
            datacenter, properties=['vmFolder']).vmFolder

        return {'host': esxhost,
                'computeResource': computeresource,
                'resourcePool': computeresource.resourcePool,
                'datacenter': datacenter,
                'folder': vm_folder,
                'datastore': datastore,
                'network': hostobject.network[0]}

    def _get_host_iqn_mapping(self, esxhost, rescan=False):
        """
        Get the IQN mapping for a given esx host, optionally rescanning the host
        """
        # pylint: disable=line-too-long
        regex = re.compile('^key-vim.host.PlugStoreTopology.Path-iqn.+?,(?P<iqn>iqn.*?),t,1-(?P<eui>eui.+)$')  # noqa
        # pylint: enable=line-too-long

        hostobject = self._get_object(
            esxhost, properties=['configManager.storageSystem'])
        stg_ssystem = self._get_object(hostobject.configManager.storageSystem,
                                       properties=['storageDeviceInfo',
                                                   'storageDeviceInfo.plugStoreTopology.device'])
        if rescan:
            # Force a rescan of the vmfs
            self._client.service.RescanVmfs(stg_ssystem.obj_identifier)
            stg_ssystem = self._get_object(
                hostobject.configManager.storageSystem,
                properties=['storageDeviceInfo',
                            'storageDeviceInfo.plugStoreTopology.device'])

        device_info_mapping = {}
        for disk in stg_ssystem.storageDeviceInfo.scsiLun:
            device_info_mapping[disk.key] = disk.uuid

        iqn_mapping = {}
        for device in stg_ssystem.storageDeviceInfo.plugStoreTopology\
                                 .device.HostPlugStoreTopologyDevice:
            for path in device.path:
                match = regex.search(path)
                if match:
                    groups = match.groupdict()
                    iqn_mapping[groups['iqn']] = {'eui': groups['eui'],
                                                  'lun': device.lun,
                                                  'uuid': device_info_mapping[device.lun]}

        return iqn_mapping

    def _get_object(self, key_object, prop_type=None, traversal=None, properties=None):
        """
        Gets an object based on a given set of query parameters. Only the requested properties
        will be loaded. If no properties are specified, all will be loaded
        """
        object_spec = self._client.factory.create('ns0:ObjectSpec')
        object_spec.obj = key_object

        property_spec = self._client.factory.create('ns0:PropertySpec')
        property_spec.type = key_object._type if prop_type is None else prop_type
        if properties is None:
            property_spec.all = True
        else:
            property_spec.all = False
            property_spec.pathSet = properties

        if traversal is not None:
            select_set_ptr = object_spec
            while True:
                select_set_ptr.selectSet = self._client.factory.create(
                    'ns0:TraversalSpec')
                select_set_ptr.selectSet.name = traversal['name']
                select_set_ptr.selectSet.type = traversal['type']
                select_set_ptr.selectSet.path = traversal['path']
                if 'traversal' in traversal:
                    traversal = traversal['traversal']
                    select_set_ptr = select_set_ptr.selectSet
                else:
                    break

        property_filter_spec = self._client.factory.create(
            'ns0:PropertyFilterSpec')
        property_filter_spec.objectSet = [object_spec]
        property_filter_spec.propSet = [property_spec]

        found_objects = self._client.service.RetrieveProperties(
            self._serviceContent.propertyCollector,
            [property_filter_spec]
        )

        if len(found_objects) > 0:
            for item in found_objects:
                item.obj_identifier = item.obj
                del item.obj

                if hasattr(item, 'missingSet'):
                    for missing_item in item.missingSet:
                        if missing_item.fault.fault.__class__.__name__ == 'NotAuthenticated':
                            raise NotAuthenticatedException()

                for propSet in item.propSet:
                    if '.' in propSet.name:
                        working_item = item
                        path = str(propSet.name).split('.')
                        part_counter = 0
                        for part in path:
                            part_counter += 1
                            if part_counter < len(path):
                                if not part in working_item.__dict__:
                                    setattr(working_item, part, type(part, (), {})())
                                working_item = working_item.__dict__[part]
                            else:
                                setattr(working_item, part, propSet.val)
                    else:
                        setattr(item, propSet.name, propSet.val)
                del item.propSet
            if len(found_objects) == 1:
                return found_objects[0]
            else:
                return found_objects

        return None

    def _build_property(self, property_name, value=None):
        """
        Create a property object with given name and value
        """
        new_property = Property(property_name)
        new_property._type = property_name
        if value is not None:
            new_property.value = value
        return new_property

    def _validate_host(self, host):
        """
        Validates wheteher a given host is valid
        """
        if host is None:
            if self._is_vcenter:
                raise Exception(
                    'A HostSystem reference is mandatory for a vCenter Server')
            else:
                return self._esxHost
        else:
            if hasattr(host, '_type') and host._type == 'HostSystem':
                return self._get_object(host, properties=['name']).obj_identifier
            else:
                return self._get_object(
                    self._build_property('HostSystem', host),
                    properties=['name']).obj_identifier

    def _login(self):
        """
        Executes a logout (to make sure we're logged out), and logs in again
        """
        self._logout()
        self._sessionID = self._client.service.Login(
            self._serviceContent.sessionManager,
            self._username,
            self._password,
            None
        ).key

    def _logout(self):
        """
        Logs out the current session
        """
        try:
            self._client.service.Logout(self._serviceContent.sessionManager)
        except:
            pass
Exemple #26
0
class Sdk(object):
    """
    This class contains all SDK related methods
    """

    def __init__(self, host, login, passwd):
        """
        Initializes the SDK
        """
        self._host = host
        self._username = login
        self._password = passwd
        self._sessionID = None
        self._check_session = True

        self._cache = ObjectCache()
        self._cache.setduration(weeks=1)

        self._client = Client('https://{0}/sdk/vimService?wsdl'.format(host),
                              cache=self._cache,
                              cachingpolicy=1)
        self._client.set_options(location='https://{0}/sdk'.format(host),
                                 plugins=[ValueExtender()])

        service_reference = self._build_property('ServiceInstance')
        self._serviceContent = self._client.service.RetrieveServiceContent(
            service_reference
        )

        # In case of an ESXi host, this would be 'HostAgent'
        self.is_vcenter = self._serviceContent.about.apiType == 'VirtualCenter'
        if not self.is_vcenter:
            self._login()
            self._esxHost = self._get_object(
                self._serviceContent.rootFolder,
                prop_type='HostSystem',
                traversal={'name': 'FolderTraversalSpec',
                           'type': 'Folder',
                           'path': 'childEntity',
                           'traversal': {'name': 'DatacenterTraversalSpec',
                                         'type': 'Datacenter',
                                         'path': 'hostFolder',
                                         'traversal': {'name': 'DFolderTraversalSpec',
                                                       'type': 'Folder',
                                                       'path': 'childEntity',
                                                       'traversal': {'name': 'ComputeResourceTravelSpec',
                                                                     'type': 'ComputeResource',
                                                                     'path': 'host'}}}},
                properties=['name']
            ).obj_identifier
        else:
            # @TODO: We need to extend all calls to specify the ESXi host where the action needs to be executed.
            # We cannot just assume an ESXi host here, as this is important for certain calls like creating a VM.
            self._esxHost = None

    @authenticated(force=True)
    def _get_vcenter_hosts(self):
        """
        reload vCenter info (host name and summary)
        """
        if not self.is_vcenter:
            raise RuntimeError('Must be connected to a vCenter Server API.')
        return self._get_object(
            self._serviceContent.rootFolder,
            prop_type='HostSystem',
            traversal={'name': 'FolderTraversalSpec',
                       'type': 'Folder',
                       'path': 'childEntity',
                       'traversal': {'name': 'DatacenterTraversalSpec',
                                     'type': 'Datacenter',
                                     'path': 'hostFolder',
                                     'traversal': {'name': 'DFolderTraversalSpec',
                                                   'type': 'Folder',
                                                   'path': 'childEntity',
                                                   'traversal': {'name': 'ComputeResourceTravelSpec',
                                                                 'type': 'ComputeResource',
                                                                 'path': 'host'}}}},
            properties=['name', 'summary.runtime', 'config.virtualNicManagerInfo.netConfig'],
            as_list=True
        )

    def get_host_status_by_ip(self, host_ip):
        """
        Return host status by ip, from vcenter info
        Must be connected to a vcenter server api
        """
        host = self._get_host_info_by_ip(host_ip)
        return host.summary.runtime.powerState

    def get_host_status_by_pk(self, pk):
        """
        Return host status by pk, from vcenter info
        Must be connected to a vcenter server api
        """
        host = self._get_host_info_by_pk(pk)
        return host.summary.runtime.powerState

    def get_host_primary_key(self, host_ip):
        """
        Return host primary key, based on current ip
        Must be connected to a vcenter server api
        """
        host = self._get_host_info_by_ip(host_ip)
        return host.obj_identifier.value

    def _get_host_info_by_ip(self, host_ip):
        """
        Return HostSystem object by ip, from vcenter info
        Must be connected to a vcenter server api
        """
        datacenter_info = self._get_vcenter_hosts()
        for host in datacenter_info:
            for nic in host.config.virtualNicManagerInfo.netConfig.VirtualNicManagerNetConfig:
                if nic.nicType == 'management':
                    for vnic in nic.candidateVnic:
                        if vnic.spec.ip.ipAddress == host_ip:
                            return host
        raise RuntimeError('Host with ip {0} not found in datacenter info'.format(host_ip))

    def _get_host_info_by_pk(self, pk):
        """
        Return HostSystem object by pk, from vcenter info
        Must be connected to a vcenter server api
        """
        datacenter_info = self._get_vcenter_hosts()
        for host in datacenter_info:
            if host.obj_identifier.value == pk:
                return host

    def get_hosts(self):
        """
        Gets a neutral list of all hosts available
        """
        host_data = self._get_vcenter_hosts()
        host_data = [] if host_data is None else host_data
        hosts = {}
        for host in host_data:
            ips = []
            for nic in host.config.virtualNicManagerInfo.netConfig.VirtualNicManagerNetConfig:
                if nic.nicType == 'management':
                    for vnic in nic.candidateVnic:
                        ips.append(vnic.spec.ip.ipAddress)
            hosts[host.obj_identifier.value] = {'name': host.name,
                                                'ips': ips}
        return hosts

    def test_connection(self):
        """
        Tests the connection
        """
        self._login()
        return True

    def list_hosts_in_datacenter(self):
        """
        return a list of registered host names in vCenter
        must be connected to a vcenter server api
        """
        datacenter_info = self._get_vcenter_hosts()
        return [host.name for host in datacenter_info]

    def validate_result(self, result, message=None):
        """
        Validates a given result. Returning True if the task succeeded, raising an error if not
        """
        if hasattr(result, '_type') and result._type == 'Task':
            return self.validate_result(self.get_task_info(result), message)
        elif hasattr(result, 'info'):
            if result.info.state == 'success':
                return True
            elif result.info.state == 'error':
                error = result.info.error.localizedMessage
                raise Exception(('{0}: {1}'.format(message, error)) if message else error)
        raise Exception(('{0}: {1}'.format(message, 'Unexpected result'))
                        if message else 'Unexpected result')

    @authenticated()
    def get_task_info(self, task):
        """
        Loads the task details
        """
        return self._get_object(task)

    @authenticated()
    def get_vm_ip_information(self):
        """
        Get the IP information for all vms on a given esxi host
        """
        esxhost = self._validate_host(None)
        configuration = []
        for vm in self._get_object(esxhost,
                                   prop_type='VirtualMachine',
                                   traversal={
                                       'name': 'HostSystemTraversalSpec',
                                       'type': 'HostSystem',
                                       'path': 'vm'},
                                   properties=['name', 'guest.net', 'config.files'],
                                   as_list=True):
            vmi = {'id': str(vm.obj_identifier.value),
                   'vmxpath': str(vm.config.files.vmPathName),
                   'name': str(vm.name),
                   'net': []}
            if vm.guest.net:
                for net in vm.guest.net[0]:
                    vmi['net'].append({'mac': str(net.macAddress),
                                       'ipaddresses': [str(i.ipAddress)
                                                       for i in net.ipConfig.ipAddress]})
            configuration.append(vmi)
        return configuration

    @authenticated()
    def exists(self, name=None, key=None):
        """
        Checks whether a vm with a given name or key exists on a given esxi host
        """
        esxhost = self._validate_host(self._esxHost)
        if name is not None or key is not None:
            try:
                if name is not None:
                    vms = [vm for vm in
                           self._get_object(esxhost,
                                            prop_type='VirtualMachine',
                                            traversal={'name': 'HostSystemTraversalSpec',
                                                       'type': 'HostSystem',
                                                       'path': 'vm'},
                                            properties=['name'],
                                            as_list=True)
                           if vm.name == name]
                    if len(vms) == 0:
                        return None
                    else:
                        return vms[0].obj_identifier
                if key is not None:
                    return self._get_object(
                        Sdk._build_property('VirtualMachine', key),
                        properties=['name']
                    ).obj_identifier
            except:
                return None
        else:
            raise Exception('A name or key should be passed.')

    @authenticated()
    def get_vm(self, key):
        """
        Retreives a vm object, based on its key
        """
        vmid = self.exists(key=key)
        if vmid is None:
            raise RuntimeError('Virtual Machine with key {} could not be found.'.format(key))
        vm = self._get_object(vmid)
        return vm

    @authenticated()
    def get_vm_device_info(self, key):
        """
        Return a vm config, based on its key
        """
        vm = self.get_vm(key)
        filename = vm.config.files.vmPathName
        regex = '\[([^\]]+)\]\s(.+)'
        match = re.search(regex, filename)
        disks = self._get_vmachine_vdisks(vm)
        return {'file_name': match.group(2),
                'host_name': vm.name,
                'vpool_name': match.group(1),
                'disks': disks}

    @authenticated()
    def get_all_vms(self):
        """
        Get all vMachines on all esxhosts registered to this vCenter
        :return: list
        """
        if not self.is_vcenter:
            raise RuntimeError('Must be connected to a vCenter Server API.')
        hosts = self._get_vcenter_hosts()
        guests = []
        for host in hosts:
            esxhost = self._validate_host(host.obj_identifier)
            vms = self._get_object(esxhost,
                                   prop_type='VirtualMachine',
                                   traversal={'name': 'HostSystemTraversalSpec',
                                              'type': 'HostSystem',
                                              'path': 'vm'},
                                   properties=['name', 'config'],
                                   as_list=True)
            for vm in vms:
                guests.append({'id': vm.obj_identifier.value,
                               'name': vm.name,
                               'instance_name': vm.name})
        return guests

    def _get_vmachine_vdisks(self, vm_object):
        disks = []
        regex = '\[([^\]]+)\]\s(.+)'
        disk_type = type(self._client.factory.create('ns0:VirtualDisk'))
        for device in vm_object.config.hardware.device:
            if isinstance(device, disk_type):
                backingfile = device.backing.fileName
                match = re.search(regex, backingfile)
                if match:
                    disks.append({'filename': match.group(2),
                                  'datastore': match.group(1),
                                  'name': device.deviceInfo.label})
        return disks

    @authenticated()
    def get_all_vdisks(self):
        """
        Get all vDisks on all esxhosts registered to this vCenter
        # similar to cinder.volumes.list()
        :return: list
        """
        if not self.is_vcenter:
            raise RuntimeError('Must be connected to a vCenter Server API.')
        hosts = self._get_vcenter_hosts()

        disks = []
        for host in hosts:
            esxhost = self._validate_host(host.obj_identifier)
            vms = self._get_object(esxhost,
                                   prop_type='VirtualMachine',
                                   traversal={'name': 'HostSystemTraversalSpec',
                                              'type': 'HostSystem',
                                              'path': 'vm'},
                                   properties=['name', 'config'],
                                   as_list=True)
            for vm in vms:
                disks.extend(self._get_vmachine_vdisks(vm))
        return disks

    @authenticated()
    def get_vms(self, ip, mountpoint):
        """
        Get all vMachines using a given nfs share
        """
        esxhost = self._validate_host(None)
        datastore = self.get_datastore(ip, mountpoint)
        filtered_vms = []
        vms = self._get_object(esxhost,
                               prop_type='VirtualMachine',
                               traversal={'name': 'HostSystemTraversalSpec',
                                          'type': 'HostSystem',
                                          'path': 'vm'},
                               properties=['name', 'config'],
                               as_list=True)
        for vm in vms:
            mapping = self._get_vm_datastore_mapping(vm)
            if datastore.name in mapping:
                filtered_vms.append(vm)
        return filtered_vms

    @authenticated()
    def set_disk_mode(self, vmid, disks, mode, wait=True):
        """
        Sets the disk mode for a set of disks
        """
        config = self._client.factory.create('ns0:VirtualMachineConfigSpec')
        config.deviceChange = []

        disk_type = type(self._client.factory.create('ns0:VirtualDisk'))

        vmid = self.exists(key=vmid)
        vm = self._get_object(vmid)
        for device in vm.config.hardware.devices:
            if type(device) == disk_type and hasattr(device, 'backing') and device.backing.fileName in disks:
                backing = self._client.factory.create('ns0:VirtualDiskFlatVer2BackingInfo')
                backing.diskMode = mode
                device = self._client.factory.create('ns0:VirtualDisk')
                device.backing = backing
                disk_spec = self._client.factory.create('ns0:VirtualDeviceConfigSpec')
                disk_spec.operation = 'edit'
                disk_spec.fileOperation = None
                disk_spec.device = device
                config.deviceChange.append(disk_spec)

        task = self._client.service.ReconfigVM_Task(vm.obj_identifier, config)

        if wait:
            self.wait_for_task(task)
        return task

    @staticmethod
    def _create_disk(factory, key, disk, unit, datastore):
        """
        Creates a disk spec for a given backing device
        Example for parameter disk: {'name': diskname, 'backingdevice': 'disk-flat.vmdk'}
        """
        device_info = factory.create('ns0:Description')
        device_info.label = disk['name']
        device_info.summary = 'Disk {0}'.format(disk['name'])
        backing = factory.create('ns0:VirtualDiskFlatVer2BackingInfo')
        backing.diskMode = 'persistent'
        backing.fileName = '[{0}] {1}'.format(datastore.name, disk['backingdevice'])
        backing.thinProvisioned = True
        device = factory.create('ns0:VirtualDisk')
        device.controllerKey = key
        device.key = -200 - unit
        device.unitNumber = unit
        device.deviceInfo = device_info
        device.backing = backing
        disk_spec = factory.create('ns0:VirtualDeviceConfigSpec')
        disk_spec.operation = 'add'
        disk_spec.fileOperation = None
        disk_spec.device = device
        return disk_spec

    @staticmethod
    def _create_file_info(factory, datastore):
        """
        Creates a file info object
        """
        file_info = factory.create('ns0:VirtualMachineFileInfo')
        file_info.vmPathName = '[{0}]'.format(datastore)
        return file_info

    @staticmethod
    def _create_nic(factory, device_type, device_label, device_summary, network, unit):
        """
        Creates a NIC spec
        """
        device_info = factory.create('ns0:Description')
        device_info.label = device_label
        device_info.summary = device_summary
        backing = factory.create('ns0:VirtualEthernetCardNetworkBackingInfo')
        backing.deviceName = network
        device = factory.create('ns0:{0}'.format(device_type))
        device.addressType = 'Generated'
        device.wakeOnLanEnabled = True
        device.controllerKey = 100  # PCI Controller
        device.key = -300 - unit
        device.unitNumber = unit
        device.backing = backing
        device.deviceInfo = device_info
        nic_spec = factory.create('ns0:VirtualDeviceConfigSpec')
        nic_spec.operation = 'add'
        nic_spec.fileOperation = None
        nic_spec.device = device
        return nic_spec

    def _create_disk_controller(self, factory, key):
        """
        Create a disk controller
        """
        device_info = self._client.factory.create('ns0:Description')
        device_info.label = 'SCSI controller 0'
        device_info.summary = 'LSI Logic SAS'
        controller = factory.create('ns0:VirtualLsiLogicSASController')
        controller.busNumber = 0
        controller.key = key
        controller.sharedBus = 'noSharing'
        controller.deviceInfo = device_info
        controller_spec = factory.create('ns0:VirtualDeviceConfigSpec')
        controller_spec.operation = 'add'
        controller_spec.fileOperation = None
        controller_spec.device = controller
        return controller_spec

    @staticmethod
    def _create_option_value(factory, key, value):
        """
        Create option values
        """
        option = factory.create('ns0:OptionValue')
        option.key = key
        option.value = value
        return option

    @authenticated()
    def copy_file(self, source, destination, wait=True):
        """
        Copies a file on the datastore
        """
        task = self._client.service.CopyDatastoreFile_Task(
            _this=self._serviceContent.fileManager,
            sourceName=source,
            destinationName=destination
        )

        if wait:
            self.wait_for_task(task)
        return task

    @authenticated()
    def create_vm_from_template(self, name, source_vm, disks, ip, mountpoint, wait=True):
        """
        Create a vm based on an existing vtemplate on specified tgt hypervisor
        Raises RuntimeError if datastore is not available at (ip, mountpoint)
        """

        esxhost = self._validate_host(None)
        host_data = self._get_host_data(esxhost)

        datastore = self.get_datastore(ip, mountpoint)
        # Build basic config information
        config = self._client.factory.create('ns0:VirtualMachineConfigSpec')
        config.name = name
        config.numCPUs = source_vm.config.hardware.numCPU
        config.memoryMB = source_vm.config.hardware.memoryMB
        config.guestId = source_vm.config.guestId
        config.deviceChange = []
        config.extraConfig = []
        config.files = Sdk._create_file_info(self._client.factory, datastore.name)

        disk_controller_key = -101
        config.deviceChange.append(
            self._create_disk_controller(self._client.factory,
                                         disk_controller_key))

        # Add disk devices
        for disk in disks:
            config.deviceChange.append(
                Sdk._create_disk(self._client.factory, disk_controller_key,
                                 disk, disks.index(disk), datastore))

        # Add network
        nw_type = type(self._client.factory.create('ns0:VirtualEthernetCardNetworkBackingInfo'))
        for device in source_vm.config.hardware.device:
            if hasattr(device, 'backing') and type(device.backing) == nw_type:
                config.deviceChange.append(
                    Sdk._create_nic(self._client.factory,
                                    device.__class__.__name__,
                                    device.deviceInfo.label,
                                    device.deviceInfo.summary,
                                    device.backing.deviceName,
                                    device.unitNumber))

        # Copy additional properties
        extraconfigstoskip = ['nvram']
        for item in source_vm.config.extraConfig:
            if item.key not in extraconfigstoskip:
                config.extraConfig.append(
                    Sdk._create_option_value(self._client.factory,
                                             item.key,
                                             item.value))

        task = self._client.service.CreateVM_Task(host_data['folder'],
                                                  config=config,
                                                  pool=host_data['resourcePool'],
                                                  host=host_data['host'])

        if wait:
            self.wait_for_task(task)
        return task

    @authenticated()
    def clone_vm(self, vmid, name, disks, wait=True):
        """
        Clone a existing VM configuration

        @param vmid: unique id of the vm
        @param name: name of the clone vm
        @param disks: list of disks to use in vm configuration
        @param wait: wait for task to complete or not (True/False)
        """

        esxhost = self._validate_host(None)
        host_data = self._get_host_data(esxhost)

        source_vm_object = self.exists(key=vmid)
        if not source_vm_object:
            raise Exception('VM with key reference {0} not found'.format(vmid))
        source_vm = self._get_object(source_vm_object)
        datastore = self._get_object(source_vm.datastore[0][0])

        # Build basic config information
        config = self._client.factory.create('ns0:VirtualMachineConfigSpec')
        config.name = name
        config.numCPUs = source_vm.config.hardware.numCPU
        config.memoryMB = source_vm.config.hardware.memoryMB
        config.guestId = source_vm.config.guestId
        config.deviceChange = []
        config.extraConfig = []
        config.files = Sdk._create_file_info(self._client.factory, datastore.name)

        disk_controller_key = -101
        config.deviceChange.append(
            self._create_disk_controller(self._client.factory,
                                         disk_controller_key))

        # Add disk devices
        for disk in disks:
            config.deviceChange.append(
                Sdk._create_disk(self._client.factory, disk_controller_key,
                                 disk, disks.index(disk), datastore))
            self.copy_file(
                '[{0}] {1}.vmdk'.format(datastore.name, disk['name'].split('_')[-1].replace('-clone', '')),
                '[{0}] {1}'.format(datastore.name, disk['backingdevice'])
            )

        # Add network
        nw_type = type(self._client.factory.create('ns0:VirtualEthernetCardNetworkBackingInfo'))
        for device in source_vm.config.hardware.device:
            if hasattr(device, 'backing') and type(device.backing) == nw_type:
                config.deviceChange.append(
                    Sdk._create_nic(self._client.factory,
                                    device.__class__.__name__,
                                    device.deviceInfo.label,
                                    device.deviceInfo.summary,
                                    device.backing.deviceName,
                                    device.unitNumber))

        # Copy additional properties
        extraconfigstoskip = ['nvram']
        for item in source_vm.config.extraConfig:
            if item.key not in extraconfigstoskip:
                config.extraConfig.append(
                    Sdk._create_option_value(self._client.factory,
                                             item.key,
                                             item.value)
                )

        task = self._client.service.CreateVM_Task(host_data['folder'],
                                                  config=config,
                                                  pool=host_data['resourcePool'],
                                                  host=host_data['host'])
        if wait:
            self.wait_for_task(task)
        return task

    @authenticated()
    def get_datastore(self, ip, mountpoint, host=None):
        """
        @param ip : hypervisor ip to query for datastore presence
        @param mountpoint: nfs mountpoint on hypervisor
        @rtype: sdk datastore object
        @return: object when found else None
        """

        datastore = None
        if host is None:
            host = self._esxHost
        esxhost = self._validate_host(host)
        host_system = self._get_object(esxhost, properties=['datastore'])
        for store in host_system.datastore[0]:
            store = self._get_object(store)
            if not store.summary.accessible:
                logger.warning('Datastore {0} is not accessible, skipping'.format(store.name))
            if hasattr(store.info, 'nas'):
                if store.info.nas.remoteHost == ip and store.info.nas.remotePath == mountpoint:
                    datastore = store

        return datastore

    @authenticated()
    def is_datastore_available(self, ip, mountpoint):
        """
        @param ip : hypervisor ip to query for datastore presence
        @param mountpoint: nfs mountpoint on hypervisor
        @rtype: boolean
        @return: True | False
        """

        if self.get_datastore(ip, mountpoint):
            return True
        else:
            return False

    def make_agnostic_config(self, vm_object, host=None):
        regex = '\[([^\]]+)\]\s(.+)'
        match = re.search(regex, vm_object.config.files.vmPathName)
        if host is None:
            host = self._esxHost
        esxhost = self._validate_host(host)

        config = {'name': vm_object.config.name,
                  'id': vm_object.obj_identifier.value,
                  'backing': {'filename': match.group(2),
                              'datastore': match.group(1)},
                  'disks': [],
                  'datastores': {}}

        for device in vm_object.config.hardware.device:
            if device.__class__.__name__ == 'VirtualDisk':
                if device.backing is not None and device.backing.fileName is not None:
                    backingfile = device.backing.fileName
                    match = re.search(regex, backingfile)
                    if match:
                        filename = match.group(2)
                        backingfile = filename.replace('.vmdk', '-flat.vmdk')
                        backingfile = '/{0}'.format(backingfile.strip('/'))
                        config['disks'].append({'filename': filename,
                                                'backingfilename': backingfile,
                                                'datastore': match.group(1),
                                                'name': device.deviceInfo.label,
                                                'order': device.unitNumber})

        host_system = self._get_object(esxhost, properties=['datastore'])
        for store in host_system.datastore[0]:
            store = self._get_object(store)
            if hasattr(store.info, 'nas'):
                config['datastores'][store.info.name] = '{}:{}'.format(store.info.nas.remoteHost,
                                                                       store.info.nas.remotePath)

        return config

    @authenticated()
    def delete_vm(self, vmid, storagedriver_mountpoint, storagedriver_storage_ip, devicename, wait=False):
        """
        Delete a given vm
        """
        machine = None
        task = None
        if vmid:
            try:
                machine = Sdk._build_property('VirtualMachine', vmid)
            except Exception as ex:
                logger.error('SDK domain retrieve failed by vmid: {}'.format(ex))
        elif storagedriver_mountpoint and storagedriver_storage_ip and devicename:
            try:
                machine_info = self.get_nfs_datastore_object(storagedriver_storage_ip, storagedriver_mountpoint, devicename)[0]
                machine = Sdk._build_property('VirtualMachine', machine_info.obj_identifier.value)
            except Exception as ex:
                logger.error('SDK domain retrieve failed by nfs datastore info: {}'.format(ex))
        if machine:
            task = self._client.service.Destroy_Task(machine)
            if wait:
                self.wait_for_task(task)

        if storagedriver_mountpoint and devicename:
            vmx_path = os.path.join(storagedriver_mountpoint, devicename)
            if os.path.exists(vmx_path):
                dir_name = os.path.dirname(vmx_path)
                logger.debug('Removing leftover files in {0}'.format(dir_name))
                try:
                    shutil.rmtree(dir_name)
                    logger.debug('Removed dir tree {}'.format(dir_name))
                except Exception as exception:
                    logger.error('Failed to remove dir tree {0}. Reason: {1}'.format(dir_name, str(exception)))
        return task

    @authenticated()
    def get_power_state(self, vmid):
        """
        Get the power state of a given vm
        """
        return self._get_object(Sdk._build_property('VirtualMachine', vmid),
                                properties=['runtime.powerState']).runtime.powerState

    @authenticated()
    def mount_nfs_datastore(self, name, remote_host, remote_path):
        """
        Mounts a given NFS export as a datastore
        """
        esxhost = self._validate_host(None)
        host = self._get_object(esxhost, properties=['datastore',
                                                     'name',
                                                     'configManager',
                                                     'configManager.datastoreSystem'])
        for store in host.datastore[0]:
            store = self._get_object(store)
            if hasattr(store.info, 'nas'):
                if store.info.name == name:
                    if store.info.nas.remoteHost == remote_host and store.info.nas.remotePath == remote_path:
                        # We'll remove this store, as it's identical to the once we'll add,
                        # forcing a refresh
                        self._client.service.RemoveDatastore(host.configManager.datastoreSystem,
                                                             store.obj_identifier)
                        break
                    else:
                        raise RuntimeError('A datastore {0} already exists, pointing to {1}:{2}'.format(
                            name, store.info.nas.remoteHost, store.info.nas.remotePath
                        ))
        spec = self._client.factory.create('ns0:HostNasVolumeSpec')
        spec.accessMode = 'readWrite'
        spec.localPath = name
        spec.remoteHost = remote_host
        spec.remotePath = remote_path
        spec.type = 'nfs'
        return self._client.service.CreateNasDatastore(host.configManager.datastoreSystem, spec)

    @authenticated()
    def wait_for_task(self, task):
        """
        Wait for a task to be completed
        """
        state = self.get_task_info(task).info.state
        while state in ['running', 'queued']:
            sleep(1)
            state = self.get_task_info(task).info.state

    @authenticated()
    def get_nfs_datastore_object(self, ip, mountpoint, filename, host=None):
        """
        ip : "10.130.12.200", string
        mountpoint: "/srv/volumefs", string
        filename: "cfovs001/vhd0(-flat).vmdk"
        identify nfs datastore on this esx host based on ip and mount
        check if filename is present on datastore
        if file is .vmdk return VirtualDisk object for corresponding virtual disk
        if file is .vmx return VirtualMachineConfigInfo for corresponding vm

        @rtype: tuple
        @return: A tuple. First item: vm config, second item: Device if a vmdk was given
        """

        filename = filename.replace('-flat.vmdk', '.vmdk')  # Support both -flat.vmdk and .vmdk
        if not filename.endswith('.vmdk') and not filename.endswith('.vmx'):
            raise ValueError('Unexpected filetype')
        if host is None:
            host = self._esxHost
        esxhost = self._validate_host(host)

        datastore = self.get_datastore(ip, mountpoint, host=esxhost)
        if not datastore:
            raise RuntimeError('Could not find datastore')

        vms = self._get_object(esxhost,
                               prop_type='VirtualMachine',
                               traversal={'name': 'HostSystemTraversalSpec',
                                          'type': 'HostSystem',
                                          'path': 'vm'},
                               properties=['name', 'config'],
                               as_list=True)
        if not vms:
            raise RuntimeError('No vMachines found')
        for vm in vms:
            mapping = self._get_vm_datastore_mapping(vm)
            if datastore.name in mapping:
                if filename in mapping[datastore.name]:
                    return vm, mapping[datastore.name][filename]
        raise RuntimeError('Could not locate given file on the given datastore')

    def file_exists(self, ip, mountpoint, filename):
        try:
            self.get_nfs_datastore_object(ip, mountpoint, filename)
            return True
        except Exception, ex:
            logger.debug('File does not exist: {0}'.format(ex))
            return False
Exemple #27
0
    def __init__(self,
                 hostname=None,
                 username=None,
                 password=None,
                 wsdls=None,
                 directory=None,
                 fromurl=False,
                 debug=False,
                 proto='https',
                 sessions=False,
                 cache=None,
                 **kwargs):

        self.hostname = hostname
        self.username = username
        self.password = password
        self.directory = directory
        self.sessions = sessions
        self.fromurl = fromurl
        self.proto = proto
        self.debug = debug
        self.kw = kwargs
        self.sessionisset = False

        # Setup the object cache
        if cache:
            self.cache = cache
        else:
            self.cache = ObjectCache(days=30)

        if self.debug == True:
            self._set_trace_logging()

        location = '{protocol}://{hostname}{uri}'.format(
            protocol=self.proto, hostname=self.hostname, uri=ICONTROL_URI)

        if self.sessions:
            '''This if block forces System.Session into index 0 in the wsdl list.'''
            if SESSION_WSDL in wsdls:
                self.wsdls = [
                    x for x in wsdls if not x.startswith('System.Session')
                ]
                self.wsdls.insert(0, SESSION_WSDL)
            else:
                self.wsdls = wsdls
                self.wsdls.insert(0, SESSION_WSDL)
        else:
            self.wsdls = wsdls

        self.clients = self._get_clients()

        for client in self.clients:
            self._set_module_attributes(client)
            self._set_interface_attributes(client)
            self._set_interface_sudsclient(client)
            self._set_type_factory(client)
            self._set_interface_methods(client)
            client.factory.separator('_')
            client.set_options(location=location, cache=self.cache)

            if self.sessions:
                if self.sessionisset:
                    pass
                else:
                    self.sessionid = self.get_sessionid()
                self.set_sessionid(self.sessionid.__str__(), client)
Exemple #28
0
def get_cache(configname):
    return ObjectCache(get_cache_path(configname))