summaryrefslogtreecommitdiffstats
path: root/nova/virt/vmwareapi/vim.py
blob: 194b78a1d48532be5dd300316efebe921639bb6c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright (c) 2011 Citrix Systems, Inc.
# Copyright 2011 OpenStack LLC.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

"""
Classes for making VMware VI SOAP calls.
"""

import httplib

try:
    import suds
except ImportError:
    suds = None

from nova.openstack.common import cfg
from nova.virt.vmwareapi import error_util

RESP_NOT_XML_ERROR = 'Response is "text/html", not "text/xml"'
CONN_ABORT_ERROR = 'Software caused connection abort'
ADDRESS_IN_USE_ERROR = 'Address already in use'

vmwareapi_wsdl_loc_opt = cfg.StrOpt('vmwareapi_wsdl_loc',
        default=None,
        help='VIM Service WSDL Location '
             'e.g http://<server>/vimService.wsdl. '
             'Due to a bug in vSphere ESX 4.1 default wsdl. '
             'Refer readme-vmware to setup')

CONF = cfg.CONF
CONF.register_opt(vmwareapi_wsdl_loc_opt)


if suds:

    class VIMMessagePlugin(suds.plugin.MessagePlugin):

        def addAttributeForValue(self, node):
            # suds does not handle AnyType properly.
            # VI SDK requires type attribute to be set when AnyType is used
            if node.name == 'value':
                node.set('xsi:type', 'xsd:string')

        def marshalled(self, context):
            """suds will send the specified soap envelope.
            Provides the plugin with the opportunity to prune empty
            nodes and fixup nodes before sending it to the server.
            """
            # suds builds the entire request object based on the wsdl schema.
            # VI SDK throws server errors if optional SOAP nodes are sent
            # without values, e.g. <test/> as opposed to <test>test</test>
            context.envelope.prune()
            context.envelope.walk(self.addAttributeForValue)


class Vim:
    """The VIM Object."""

    def __init__(self,
                 protocol="https",
                 host="localhost"):
        """
        Creates the necessary Communication interfaces and gets the
        ServiceContent for initiating SOAP transactions.

        protocol: http or https
        host    : ESX IPAddress[:port] or ESX Hostname[:port]
        """
        if not suds:
            raise Exception(_("Unable to import suds."))

        self._protocol = protocol
        self._host_name = host
        wsdl_url = CONF.vmwareapi_wsdl_loc
        if wsdl_url is None:
            raise Exception(_("Must specify vmwareapi_wsdl_loc"))
        # TODO(sateesh): Use this when VMware fixes their faulty wsdl
        #wsdl_url = '%s://%s/sdk/vimService.wsdl' % (self._protocol,
        #        self._host_name)
        url = '%s://%s/sdk' % (self._protocol, self._host_name)
        self.client = suds.client.Client(wsdl_url, location=url,
                            plugins=[VIMMessagePlugin()])
        self._service_content = self.RetrieveServiceContent("ServiceInstance")

    def get_service_content(self):
        """Gets the service content object."""
        return self._service_content

    def __getattr__(self, attr_name):
        """Makes the API calls and gets the result."""
        try:
            return getattr(self, attr_name)
        except AttributeError:

            def vim_request_handler(managed_object, **kwargs):
                """
                Builds the SOAP message and parses the response for fault
                checking and other errors.

                managed_object    : Managed Object Reference or Managed
                                    Object Name
                **kwargs          : Keyword arguments of the call
                """
                # Dynamic handler for VI SDK Calls
                try:
                    request_mo = self._request_managed_object_builder(
                                 managed_object)
                    request = getattr(self.client.service, attr_name)
                    response = request(request_mo, **kwargs)
                    # To check for the faults that are part of the message body
                    # and not returned as Fault object response from the ESX
                    # SOAP server
                    if hasattr(error_util.FaultCheckers,
                                    attr_name.lower() + "_fault_checker"):
                        fault_checker = getattr(error_util.FaultCheckers,
                                    attr_name.lower() + "_fault_checker")
                        fault_checker(response)
                    return response
                # Catch the VimFaultException that is raised by the fault
                # check of the SOAP response
                except error_util.VimFaultException, excep:
                    raise
                except suds.WebFault, excep:
                    doc = excep.document
                    detail = doc.childAtPath("/Envelope/Body/Fault/detail")
                    fault_list = []
                    for child in detail.getChildren():
                        fault_list.append(child.get("type"))
                    raise error_util.VimFaultException(fault_list, excep)
                except AttributeError, excep:
                    raise error_util.VimAttributeError(_("No such SOAP method "
                         "'%s' provided by VI SDK") % (attr_name), excep)
                except (httplib.CannotSendRequest,
                        httplib.ResponseNotReady,
                        httplib.CannotSendHeader), excep:
                    raise error_util.SessionOverLoadException(_("httplib "
                                    "error in %s: ") % (attr_name), excep)
                except Exception, excep:
                    # Socket errors which need special handling for they
                    # might be caused by ESX API call overload
                    if (str(excep).find(ADDRESS_IN_USE_ERROR) != -1 or
                        str(excep).find(CONN_ABORT_ERROR)) != -1:
                        raise error_util.SessionOverLoadException(_("Socket "
                                    "error in %s: ") % (attr_name), excep)
                    # Type error that needs special handling for it might be
                    # caused by ESX host API call overload
                    elif str(excep).find(RESP_NOT_XML_ERROR) != -1:
                        raise error_util.SessionOverLoadException(_("Type "
                                    "error in  %s: ") % (attr_name), excep)
                    else:
                        raise error_util.VimException(
                           _("Exception in %s ") % (attr_name), excep)
            return vim_request_handler

    def _request_managed_object_builder(self, managed_object):
        """Builds the request managed object."""
        # Request Managed Object Builder
        if isinstance(managed_object, str):
            mo = suds.sudsobject.Property(managed_object)
            mo._type = managed_object
        else:
            mo = managed_object
        return mo

    def __repr__(self):
        return "VIM Object"

    def __str__(self):
        return "VIM Object"