From 50f3198477253d730d11d42215efe7e527a230d0 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Wed, 25 Jan 2012 11:57:25 +0000 Subject: Convert vmwareapi code to UNIX style line endings It's the only code in the codebase using evil CRLF line endings. Change-Id: I8b1a2b12a5707fbd4d32588c081599beaa34aca5 --- nova/tests/vmwareapi/__init__.py | 42 +- nova/tests/vmwareapi/db_fakes.py | 218 ++--- nova/tests/vmwareapi/stubs.py | 102 +- nova/virt/vmwareapi/error_util.py | 192 ++-- nova/virt/vmwareapi/fake.py | 1438 ++++++++++++++-------------- nova/virt/vmwareapi/io_util.py | 338 +++---- nova/virt/vmwareapi/network_utils.py | 340 +++---- nova/virt/vmwareapi/read_write_util.py | 358 +++---- nova/virt/vmwareapi/vim.py | 362 +++---- nova/virt/vmwareapi/vim_util.py | 446 ++++----- nova/virt/vmwareapi/vm_util.py | 650 ++++++------- nova/virt/vmwareapi/vmops.py | 1648 ++++++++++++++++---------------- nova/virt/vmwareapi/vmware_images.py | 290 +++--- 13 files changed, 3212 insertions(+), 3212 deletions(-) (limited to 'nova') diff --git a/nova/tests/vmwareapi/__init__.py b/nova/tests/vmwareapi/__init__.py index 478ee742b..228186621 100644 --- a/nova/tests/vmwareapi/__init__.py +++ b/nova/tests/vmwareapi/__init__.py @@ -1,21 +1,21 @@ -# 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. - -""" -:mod:`vmwareapi` -- Stubs for VMware API -======================================= -""" +# 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. + +""" +:mod:`vmwareapi` -- Stubs for VMware API +======================================= +""" diff --git a/nova/tests/vmwareapi/db_fakes.py b/nova/tests/vmwareapi/db_fakes.py index 1425a8751..ab2c01580 100644 --- a/nova/tests/vmwareapi/db_fakes.py +++ b/nova/tests/vmwareapi/db_fakes.py @@ -1,109 +1,109 @@ -# 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. - -""" -Stubouts, mocks and fixtures for the test suite -""" - -import time - -from nova import db -from nova import utils -from nova.compute import task_states -from nova.compute import vm_states - - -def stub_out_db_instance_api(stubs): - """Stubs out the db API for creating Instances.""" - - INSTANCE_TYPES = { - 'm1.tiny': dict(memory_mb=512, vcpus=1, root_gb=0, flavorid=1), - 'm1.small': dict(memory_mb=2048, vcpus=1, root_gb=20, flavorid=2), - 'm1.medium': - dict(memory_mb=4096, vcpus=2, root_gb=40, flavorid=3), - 'm1.large': dict(memory_mb=8192, vcpus=4, root_gb=80, flavorid=4), - 'm1.xlarge': - dict(memory_mb=16384, vcpus=8, root_gb=160, flavorid=5)} - - class FakeModel(object): - """Stubs out for model.""" - - def __init__(self, values): - self.values = values - - def __getattr__(self, name): - return self.values[name] - - def __getitem__(self, key): - if key in self.values: - return self.values[key] - else: - raise NotImplementedError() - - def fake_instance_create(context, values): - """Stubs out the db.instance_create method.""" - - type_data = INSTANCE_TYPES[values['instance_type']] - - base_options = { - 'name': values['name'], - 'id': values['id'], - 'uuid': utils.gen_uuid(), - 'reservation_id': utils.generate_uid('r'), - 'image_ref': values['image_ref'], - 'kernel_id': values['kernel_id'], - 'ramdisk_id': values['ramdisk_id'], - 'vm_state': vm_states.BUILDING, - 'task_state': task_states.SCHEDULING, - 'user_id': values['user_id'], - 'project_id': values['project_id'], - 'launch_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()), - 'instance_type': values['instance_type'], - 'memory_mb': type_data['memory_mb'], - 'vcpus': type_data['vcpus'], - 'mac_addresses': [{'address': values['mac_address']}], - 'root_gb': type_data['root_gb'], - } - return FakeModel(base_options) - - def fake_network_get_by_instance(context, instance_id): - """Stubs out the db.network_get_by_instance method.""" - - fields = { - 'bridge': 'vmnet0', - 'netmask': '255.255.255.0', - 'gateway': '10.10.10.1', - 'broadcast': '10.10.10.255', - 'dns1': 'fake', - 'vlan': 100} - return FakeModel(fields) - - def fake_instance_action_create(context, action): - """Stubs out the db.instance_action_create method.""" - pass - - def fake_instance_type_get_all(context, inactive=0, filters=None): - return INSTANCE_TYPES.values() - - def fake_instance_type_get_by_name(context, name): - return INSTANCE_TYPES[name] - - stubs.Set(db, 'instance_create', fake_instance_create) - stubs.Set(db, 'network_get_by_instance', fake_network_get_by_instance) - stubs.Set(db, 'instance_action_create', fake_instance_action_create) - stubs.Set(db, 'instance_type_get_all', fake_instance_type_get_all) - stubs.Set(db, 'instance_type_get_by_name', fake_instance_type_get_by_name) +# 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. + +""" +Stubouts, mocks and fixtures for the test suite +""" + +import time + +from nova import db +from nova import utils +from nova.compute import task_states +from nova.compute import vm_states + + +def stub_out_db_instance_api(stubs): + """Stubs out the db API for creating Instances.""" + + INSTANCE_TYPES = { + 'm1.tiny': dict(memory_mb=512, vcpus=1, root_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, root_gb=20, flavorid=2), + 'm1.medium': + dict(memory_mb=4096, vcpus=2, root_gb=40, flavorid=3), + 'm1.large': dict(memory_mb=8192, vcpus=4, root_gb=80, flavorid=4), + 'm1.xlarge': + dict(memory_mb=16384, vcpus=8, root_gb=160, flavorid=5)} + + class FakeModel(object): + """Stubs out for model.""" + + def __init__(self, values): + self.values = values + + def __getattr__(self, name): + return self.values[name] + + def __getitem__(self, key): + if key in self.values: + return self.values[key] + else: + raise NotImplementedError() + + def fake_instance_create(context, values): + """Stubs out the db.instance_create method.""" + + type_data = INSTANCE_TYPES[values['instance_type']] + + base_options = { + 'name': values['name'], + 'id': values['id'], + 'uuid': utils.gen_uuid(), + 'reservation_id': utils.generate_uid('r'), + 'image_ref': values['image_ref'], + 'kernel_id': values['kernel_id'], + 'ramdisk_id': values['ramdisk_id'], + 'vm_state': vm_states.BUILDING, + 'task_state': task_states.SCHEDULING, + 'user_id': values['user_id'], + 'project_id': values['project_id'], + 'launch_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()), + 'instance_type': values['instance_type'], + 'memory_mb': type_data['memory_mb'], + 'vcpus': type_data['vcpus'], + 'mac_addresses': [{'address': values['mac_address']}], + 'root_gb': type_data['root_gb'], + } + return FakeModel(base_options) + + def fake_network_get_by_instance(context, instance_id): + """Stubs out the db.network_get_by_instance method.""" + + fields = { + 'bridge': 'vmnet0', + 'netmask': '255.255.255.0', + 'gateway': '10.10.10.1', + 'broadcast': '10.10.10.255', + 'dns1': 'fake', + 'vlan': 100} + return FakeModel(fields) + + def fake_instance_action_create(context, action): + """Stubs out the db.instance_action_create method.""" + pass + + def fake_instance_type_get_all(context, inactive=0, filters=None): + return INSTANCE_TYPES.values() + + def fake_instance_type_get_by_name(context, name): + return INSTANCE_TYPES[name] + + stubs.Set(db, 'instance_create', fake_instance_create) + stubs.Set(db, 'network_get_by_instance', fake_network_get_by_instance) + stubs.Set(db, 'instance_action_create', fake_instance_action_create) + stubs.Set(db, 'instance_type_get_all', fake_instance_type_get_all) + stubs.Set(db, 'instance_type_get_by_name', fake_instance_type_get_by_name) diff --git a/nova/tests/vmwareapi/stubs.py b/nova/tests/vmwareapi/stubs.py index 7de10e612..cbb0da0fd 100644 --- a/nova/tests/vmwareapi/stubs.py +++ b/nova/tests/vmwareapi/stubs.py @@ -1,51 +1,51 @@ -# 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. - -""" -Stubouts for the test suite -""" - -from nova.virt import vmwareapi_conn -from nova.virt.vmwareapi import fake -from nova.virt.vmwareapi import vmware_images -from nova.virt.vmwareapi import vmops -from nova.virt.vmwareapi import network_utils - - -def fake_get_vim_object(arg): - """Stubs out the VMWareAPISession's get_vim_object method.""" - return fake.FakeVim() - - -def fake_is_vim_object(arg, module): - """Stubs out the VMWareAPISession's is_vim_object method.""" - return isinstance(module, fake.FakeVim) - - -def set_stubs(stubs): - """Set the stubs.""" - stubs.Set(vmops.VMWareVMOps, 'plug_vifs', fake.fake_plug_vifs) - stubs.Set(network_utils, 'get_network_with_the_name', - fake.fake_get_network) - stubs.Set(vmware_images, 'fetch_image', fake.fake_fetch_image) - stubs.Set(vmware_images, 'get_vmdk_size_and_properties', - fake.fake_get_vmdk_size_and_properties) - stubs.Set(vmware_images, 'upload_image', fake.fake_upload_image) - stubs.Set(vmwareapi_conn.VMWareAPISession, "_get_vim_object", - fake_get_vim_object) - stubs.Set(vmwareapi_conn.VMWareAPISession, "_is_vim_object", - fake_is_vim_object) +# 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. + +""" +Stubouts for the test suite +""" + +from nova.virt import vmwareapi_conn +from nova.virt.vmwareapi import fake +from nova.virt.vmwareapi import vmware_images +from nova.virt.vmwareapi import vmops +from nova.virt.vmwareapi import network_utils + + +def fake_get_vim_object(arg): + """Stubs out the VMWareAPISession's get_vim_object method.""" + return fake.FakeVim() + + +def fake_is_vim_object(arg, module): + """Stubs out the VMWareAPISession's is_vim_object method.""" + return isinstance(module, fake.FakeVim) + + +def set_stubs(stubs): + """Set the stubs.""" + stubs.Set(vmops.VMWareVMOps, 'plug_vifs', fake.fake_plug_vifs) + stubs.Set(network_utils, 'get_network_with_the_name', + fake.fake_get_network) + stubs.Set(vmware_images, 'fetch_image', fake.fake_fetch_image) + stubs.Set(vmware_images, 'get_vmdk_size_and_properties', + fake.fake_get_vmdk_size_and_properties) + stubs.Set(vmware_images, 'upload_image', fake.fake_upload_image) + stubs.Set(vmwareapi_conn.VMWareAPISession, "_get_vim_object", + fake_get_vim_object) + stubs.Set(vmwareapi_conn.VMWareAPISession, "_is_vim_object", + fake_is_vim_object) diff --git a/nova/virt/vmwareapi/error_util.py b/nova/virt/vmwareapi/error_util.py index 53fa8f24d..be82f4646 100644 --- a/nova/virt/vmwareapi/error_util.py +++ b/nova/virt/vmwareapi/error_util.py @@ -1,96 +1,96 @@ -# 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. - -""" -Exception classes and SOAP response error checking module. -""" - -FAULT_NOT_AUTHENTICATED = "NotAuthenticated" -FAULT_ALREADY_EXISTS = "AlreadyExists" - - -class VimException(Exception): - """The VIM Exception class.""" - - def __init__(self, exception_summary, excep): - Exception.__init__(self) - self.exception_summary = exception_summary - self.exception_obj = excep - - def __str__(self): - return self.exception_summary + str(self.exception_obj) - - -class SessionOverLoadException(VimException): - """Session Overload Exception.""" - pass - - -class VimAttributeError(VimException): - """VI Attribute Error.""" - pass - - -class VimFaultException(Exception): - """The VIM Fault exception class.""" - - def __init__(self, fault_list, excep): - Exception.__init__(self) - self.fault_list = fault_list - self.exception_obj = excep - - def __str__(self): - return str(self.exception_obj) - - -class FaultCheckers(object): - """ - Methods for fault checking of SOAP response. Per Method error handlers - for which we desire error checking are defined. SOAP faults are - embedded in the SOAP messages as properties and not as SOAP faults. - """ - - @staticmethod - def retrieveproperties_fault_checker(resp_obj): - """ - Checks the RetrieveProperties response for errors. Certain faults - are sent as part of the SOAP body as property of missingSet. - For example NotAuthenticated fault. - """ - fault_list = [] - if not resp_obj: - # This is the case when the session has timed out. ESX SOAP server - # sends an empty RetrievePropertiesResponse. Normally missingSet in - # the returnval field has the specifics about the error, but that's - # not the case with a timed out idle session. It is as bad as a - # terminated session for we cannot use the session. So setting - # fault to NotAuthenticated fault. - fault_list = ["NotAuthenticated"] - else: - for obj_cont in resp_obj: - if hasattr(obj_cont, "missingSet"): - for missing_elem in obj_cont.missingSet: - fault_type = \ - missing_elem.fault.fault.__class__.__name__ - # Fault needs to be added to the type of fault for - # uniformity in error checking as SOAP faults define - fault_list.append(fault_type) - if fault_list: - exc_msg_list = ', '.join(fault_list) - raise VimFaultException(fault_list, Exception(_("Error(s) %s " - "occurred in the call to RetrieveProperties") % - exc_msg_list)) +# 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. + +""" +Exception classes and SOAP response error checking module. +""" + +FAULT_NOT_AUTHENTICATED = "NotAuthenticated" +FAULT_ALREADY_EXISTS = "AlreadyExists" + + +class VimException(Exception): + """The VIM Exception class.""" + + def __init__(self, exception_summary, excep): + Exception.__init__(self) + self.exception_summary = exception_summary + self.exception_obj = excep + + def __str__(self): + return self.exception_summary + str(self.exception_obj) + + +class SessionOverLoadException(VimException): + """Session Overload Exception.""" + pass + + +class VimAttributeError(VimException): + """VI Attribute Error.""" + pass + + +class VimFaultException(Exception): + """The VIM Fault exception class.""" + + def __init__(self, fault_list, excep): + Exception.__init__(self) + self.fault_list = fault_list + self.exception_obj = excep + + def __str__(self): + return str(self.exception_obj) + + +class FaultCheckers(object): + """ + Methods for fault checking of SOAP response. Per Method error handlers + for which we desire error checking are defined. SOAP faults are + embedded in the SOAP messages as properties and not as SOAP faults. + """ + + @staticmethod + def retrieveproperties_fault_checker(resp_obj): + """ + Checks the RetrieveProperties response for errors. Certain faults + are sent as part of the SOAP body as property of missingSet. + For example NotAuthenticated fault. + """ + fault_list = [] + if not resp_obj: + # This is the case when the session has timed out. ESX SOAP server + # sends an empty RetrievePropertiesResponse. Normally missingSet in + # the returnval field has the specifics about the error, but that's + # not the case with a timed out idle session. It is as bad as a + # terminated session for we cannot use the session. So setting + # fault to NotAuthenticated fault. + fault_list = ["NotAuthenticated"] + else: + for obj_cont in resp_obj: + if hasattr(obj_cont, "missingSet"): + for missing_elem in obj_cont.missingSet: + fault_type = \ + missing_elem.fault.fault.__class__.__name__ + # Fault needs to be added to the type of fault for + # uniformity in error checking as SOAP faults define + fault_list.append(fault_type) + if fault_list: + exc_msg_list = ', '.join(fault_list) + raise VimFaultException(fault_list, Exception(_("Error(s) %s " + "occurred in the call to RetrieveProperties") % + exc_msg_list)) diff --git a/nova/virt/vmwareapi/fake.py b/nova/virt/vmwareapi/fake.py index fec644770..72f6cdc61 100644 --- a/nova/virt/vmwareapi/fake.py +++ b/nova/virt/vmwareapi/fake.py @@ -1,719 +1,719 @@ -# 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. - -""" -A fake VMWare VI API implementation. -""" - -from pprint import pformat -import uuid - -from nova import exception -from nova import log as logging -from nova.virt.vmwareapi import error_util - -_CLASSES = ['Datacenter', 'Datastore', 'ResourcePool', 'VirtualMachine', - 'Network', 'HostSystem', 'HostNetworkSystem', 'Task', 'session', - 'files'] - -_FAKE_FILE_SIZE = 1024 - -_db_content = {} - -LOG = logging.getLogger("nova.virt.vmwareapi.fake") - - -def log_db_contents(msg=None): - """Log DB Contents.""" - text = msg or "" - content = pformat(_db_content) - LOG.debug(_("%(text)s: _db_content => %(content)s") % locals()) - - -def reset(): - """Resets the db contents.""" - for c in _CLASSES: - # We fake the datastore by keeping the file references as a list of - # names in the db - if c == 'files': - _db_content[c] = [] - else: - _db_content[c] = {} - create_network() - create_host_network_system() - create_host() - create_datacenter() - create_datastore() - create_res_pool() - - -def cleanup(): - """Clear the db contents.""" - for c in _CLASSES: - _db_content[c] = {} - - -def _create_object(table, table_obj): - """Create an object in the db.""" - _db_content[table][table_obj.obj] = table_obj - - -def _get_objects(obj_type): - """Get objects of the type.""" - lst_objs = [] - for key in _db_content[obj_type]: - lst_objs.append(_db_content[obj_type][key]) - return lst_objs - - -class Prop(object): - """Property Object base class.""" - - def __init__(self): - self.name = None - self.val = None - - -class ManagedObject(object): - """Managed Data Object base class.""" - - def __init__(self, name="ManagedObject", obj_ref=None): - """Sets the obj property which acts as a reference to the object.""" - super(ManagedObject, self).__setattr__('objName', name) - if obj_ref is None: - obj_ref = str(uuid.uuid4()) - object.__setattr__(self, 'obj', obj_ref) - object.__setattr__(self, 'propSet', []) - - def set(self, attr, val): - """ - Sets an attribute value. Not using the __setattr__ directly for we - want to set attributes of the type 'a.b.c' and using this function - class we set the same. - """ - self.__setattr__(attr, val) - - def get(self, attr): - """ - Gets an attribute. Used as an intermediary to get nested - property like 'a.b.c' value. - """ - return self.__getattr__(attr) - - def __setattr__(self, attr, val): - for prop in self.propSet: - if prop.name == attr: - prop.val = val - return - elem = Prop() - elem.name = attr - elem.val = val - self.propSet.append(elem) - - def __getattr__(self, attr): - for elem in self.propSet: - if elem.name == attr: - return elem.val - raise exception.Error(_("Property %(attr)s not set for the managed " - "object %(objName)s") % - {'attr': attr, - 'objName': self.objName}) - - -class DataObject(object): - """Data object base class.""" - pass - - -class VirtualDisk(DataObject): - """ - Virtual Disk class. Does nothing special except setting - __class__.__name__ to 'VirtualDisk'. Refer place where __class__.__name__ - is used in the code. - """ - pass - - -class VirtualDiskFlatVer2BackingInfo(DataObject): - """VirtualDiskFlatVer2BackingInfo class.""" - pass - - -class VirtualLsiLogicController(DataObject): - """VirtualLsiLogicController class.""" - pass - - -class VirtualMachine(ManagedObject): - """Virtual Machine class.""" - - def __init__(self, **kwargs): - super(VirtualMachine, self).__init__("VirtualMachine") - self.set("name", kwargs.get("name")) - self.set("runtime.connectionState", - kwargs.get("conn_state", "connected")) - self.set("summary.config.guestId", kwargs.get("guest", "otherGuest")) - ds_do = DataObject() - ds_do.ManagedObjectReference = [kwargs.get("ds").obj] - self.set("datastore", ds_do) - self.set("summary.guest.toolsStatus", kwargs.get("toolsstatus", - "toolsOk")) - self.set("summary.guest.toolsRunningStatus", kwargs.get( - "toolsrunningstate", "guestToolsRunning")) - self.set("runtime.powerState", kwargs.get("powerstate", "poweredOn")) - self.set("config.files.vmPathName", kwargs.get("vmPathName")) - self.set("summary.config.numCpu", kwargs.get("numCpu", 1)) - self.set("summary.config.memorySizeMB", kwargs.get("mem", 1)) - self.set("config.hardware.device", kwargs.get("virtual_disk", None)) - self.set("config.extraConfig", kwargs.get("extra_config", None)) - - def reconfig(self, factory, val): - """ - Called to reconfigure the VM. Actually customizes the property - setting of the Virtual Machine object. - """ - try: - # Case of Reconfig of VM to attach disk - controller_key = val.deviceChange[1].device.controllerKey - filename = val.deviceChange[1].device.backing.fileName - - disk = VirtualDisk() - disk.controllerKey = controller_key - - disk_backing = VirtualDiskFlatVer2BackingInfo() - disk_backing.fileName = filename - disk_backing.key = -101 - disk.backing = disk_backing - - controller = VirtualLsiLogicController() - controller.key = controller_key - - self.set("config.hardware.device", [disk, controller]) - except AttributeError: - # Case of Reconfig of VM to set extra params - self.set("config.extraConfig", val.extraConfig) - - -class Network(ManagedObject): - """Network class.""" - - def __init__(self): - super(Network, self).__init__("Network") - self.set("summary.name", "vmnet0") - - -class ResourcePool(ManagedObject): - """Resource Pool class.""" - - def __init__(self): - super(ResourcePool, self).__init__("ResourcePool") - self.set("name", "ResPool") - - -class Datastore(ManagedObject): - """Datastore class.""" - - def __init__(self): - super(Datastore, self).__init__("Datastore") - self.set("summary.type", "VMFS") - self.set("summary.name", "fake-ds") - - -class HostNetworkSystem(ManagedObject): - """HostNetworkSystem class.""" - - def __init__(self): - super(HostNetworkSystem, self).__init__("HostNetworkSystem") - self.set("name", "networkSystem") - - pnic_do = DataObject() - pnic_do.device = "vmnic0" - - net_info_pnic = DataObject() - net_info_pnic.PhysicalNic = [pnic_do] - - self.set("networkInfo.pnic", net_info_pnic) - - -class HostSystem(ManagedObject): - """Host System class.""" - - def __init__(self): - super(HostSystem, self).__init__("HostSystem") - self.set("name", "ha-host") - if _db_content.get("HostNetworkSystem", None) is None: - create_host_network_system() - host_net_key = _db_content["HostNetworkSystem"].keys()[0] - host_net_sys = _db_content["HostNetworkSystem"][host_net_key].obj - self.set("configManager.networkSystem", host_net_sys) - - if _db_content.get("Network", None) is None: - create_network() - net_ref = _db_content["Network"][_db_content["Network"].keys()[0]].obj - network_do = DataObject() - network_do.ManagedObjectReference = [net_ref] - self.set("network", network_do) - - vswitch_do = DataObject() - vswitch_do.pnic = ["vmnic0"] - vswitch_do.name = "vSwitch0" - vswitch_do.portgroup = ["PortGroup-vmnet0"] - - net_swicth = DataObject() - net_swicth.HostVirtualSwitch = [vswitch_do] - self.set("config.network.vswitch", net_swicth) - - host_pg_do = DataObject() - host_pg_do.key = "PortGroup-vmnet0" - - pg_spec = DataObject() - pg_spec.vlanId = 0 - pg_spec.name = "vmnet0" - - host_pg_do.spec = pg_spec - - host_pg = DataObject() - host_pg.HostPortGroup = [host_pg_do] - self.set("config.network.portgroup", host_pg) - - def _add_port_group(self, spec): - """Adds a port group to the host system object in the db.""" - pg_name = spec.name - vswitch_name = spec.vswitchName - vlanid = spec.vlanId - - vswitch_do = DataObject() - vswitch_do.pnic = ["vmnic0"] - vswitch_do.name = vswitch_name - vswitch_do.portgroup = ["PortGroup-%s" % pg_name] - - vswitches = self.get("config.network.vswitch").HostVirtualSwitch - vswitches.append(vswitch_do) - - host_pg_do = DataObject() - host_pg_do.key = "PortGroup-%s" % pg_name - - pg_spec = DataObject() - pg_spec.vlanId = vlanid - pg_spec.name = pg_name - - host_pg_do.spec = pg_spec - host_pgrps = self.get("config.network.portgroup").HostPortGroup - host_pgrps.append(host_pg_do) - - -class Datacenter(ManagedObject): - """Datacenter class.""" - - def __init__(self): - super(Datacenter, self).__init__("Datacenter") - self.set("name", "ha-datacenter") - self.set("vmFolder", "vm_folder_ref") - if _db_content.get("Network", None) is None: - create_network() - net_ref = _db_content["Network"][_db_content["Network"].keys()[0]].obj - network_do = DataObject() - network_do.ManagedObjectReference = [net_ref] - self.set("network", network_do) - - -class Task(ManagedObject): - """Task class.""" - - def __init__(self, task_name, state="running"): - super(Task, self).__init__("Task") - info = DataObject - info.name = task_name - info.state = state - self.set("info", info) - - -def create_host_network_system(): - host_net_system = HostNetworkSystem() - _create_object("HostNetworkSystem", host_net_system) - - -def create_host(): - host_system = HostSystem() - _create_object('HostSystem', host_system) - - -def create_datacenter(): - data_center = Datacenter() - _create_object('Datacenter', data_center) - - -def create_datastore(): - data_store = Datastore() - _create_object('Datastore', data_store) - - -def create_res_pool(): - res_pool = ResourcePool() - _create_object('ResourcePool', res_pool) - - -def create_network(): - network = Network() - _create_object('Network', network) - - -def create_task(task_name, state="running"): - task = Task(task_name, state) - _create_object("Task", task) - return task - - -def _add_file(file_path): - """Adds a file reference to the db.""" - _db_content["files"].append(file_path) - - -def _remove_file(file_path): - """Removes a file reference from the db.""" - if _db_content.get("files") is None: - raise exception.NoFilesFound() - # Check if the remove is for a single file object or for a folder - if file_path.find(".vmdk") != -1: - if file_path not in _db_content.get("files"): - raise exception.FileNotFound(file_path=file_path) - _db_content.get("files").remove(file_path) - else: - # Removes the files in the folder and the folder too from the db - for file in _db_content.get("files"): - if file.find(file_path) != -1: - lst_files = _db_content.get("files") - if lst_files and lst_files.count(file): - lst_files.remove(file) - - -def fake_plug_vifs(*args, **kwargs): - """Fakes plugging vifs.""" - pass - - -def fake_get_network(*args, **kwargs): - """Fake get network.""" - return {'type': 'fake'} - - -def fake_fetch_image(context, image, instance, **kwargs): - """Fakes fetch image call. Just adds a reference to the db for the file.""" - ds_name = kwargs.get("datastore_name") - file_path = kwargs.get("file_path") - ds_file_path = "[" + ds_name + "] " + file_path - _add_file(ds_file_path) - - -def fake_upload_image(context, image, instance, **kwargs): - """Fakes the upload of an image.""" - pass - - -def fake_get_vmdk_size_and_properties(context, image_id, instance): - """Fakes the file size and properties fetch for the image file.""" - props = {"vmware_ostype": "otherGuest", - "vmware_adaptertype": "lsiLogic"} - return _FAKE_FILE_SIZE, props - - -def _get_vm_mdo(vm_ref): - """Gets the Virtual Machine with the ref from the db.""" - if _db_content.get("VirtualMachine", None) is None: - raise exception.NotFound(_("There is no VM registered")) - if vm_ref not in _db_content.get("VirtualMachine"): - raise exception.NotFound(_("Virtual Machine with ref %s is not " - "there") % vm_ref) - return _db_content.get("VirtualMachine")[vm_ref] - - -class FakeFactory(object): - """Fake factory class for the suds client.""" - - def create(self, obj_name): - """Creates a namespace object.""" - return DataObject() - - -class FakeVim(object): - """Fake VIM Class.""" - - def __init__(self, protocol="https", host="localhost", trace=None): - """ - Initializes the suds client object, sets the service content - contents and the cookies for the session. - """ - self._session = None - self.client = DataObject() - self.client.factory = FakeFactory() - - transport = DataObject() - transport.cookiejar = "Fake-CookieJar" - options = DataObject() - options.transport = transport - - self.client.options = options - - service_content = self.client.factory.create('ns0:ServiceContent') - service_content.propertyCollector = "PropCollector" - service_content.virtualDiskManager = "VirtualDiskManager" - service_content.fileManager = "FileManager" - service_content.rootFolder = "RootFolder" - service_content.sessionManager = "SessionManager" - self._service_content = service_content - - def get_service_content(self): - return self._service_content - - def __repr__(self): - return "Fake VIM Object" - - def __str__(self): - return "Fake VIM Object" - - def _login(self): - """Logs in and sets the session object in the db.""" - self._session = str(uuid.uuid4()) - session = DataObject() - session.key = self._session - _db_content['session'][self._session] = session - return session - - def _logout(self): - """Logs out and remove the session object ref from the db.""" - s = self._session - self._session = None - if s not in _db_content['session']: - raise exception.Error( - _("Logging out a session that is invalid or already logged " - "out: %s") % s) - del _db_content['session'][s] - - def _terminate_session(self, *args, **kwargs): - """Terminates a session.""" - s = kwargs.get("sessionId")[0] - if s not in _db_content['session']: - return - del _db_content['session'][s] - - def _check_session(self): - """Checks if the session is active.""" - if (self._session is None or self._session not in - _db_content['session']): - LOG.debug(_("Session is faulty")) - raise error_util.VimFaultException( - [error_util.FAULT_NOT_AUTHENTICATED], - _("Session Invalid")) - - def _create_vm(self, method, *args, **kwargs): - """Creates and registers a VM object with the Host System.""" - config_spec = kwargs.get("config") - ds = _db_content["Datastore"][_db_content["Datastore"].keys()[0]] - vm_dict = {"name": config_spec.name, - "ds": ds, - "powerstate": "poweredOff", - "vmPathName": config_spec.files.vmPathName, - "numCpu": config_spec.numCPUs, - "mem": config_spec.memoryMB} - virtual_machine = VirtualMachine(**vm_dict) - _create_object("VirtualMachine", virtual_machine) - task_mdo = create_task(method, "success") - return task_mdo.obj - - def _reconfig_vm(self, method, *args, **kwargs): - """Reconfigures a VM and sets the properties supplied.""" - vm_ref = args[0] - vm_mdo = _get_vm_mdo(vm_ref) - vm_mdo.reconfig(self.client.factory, kwargs.get("spec")) - task_mdo = create_task(method, "success") - return task_mdo.obj - - def _create_copy_disk(self, method, vmdk_file_path): - """Creates/copies a vmdk file object in the datastore.""" - # We need to add/create both .vmdk and .-flat.vmdk files - flat_vmdk_file_path = \ - vmdk_file_path.replace(".vmdk", "-flat.vmdk") - _add_file(vmdk_file_path) - _add_file(flat_vmdk_file_path) - task_mdo = create_task(method, "success") - return task_mdo.obj - - def _snapshot_vm(self, method): - """Snapshots a VM. Here we do nothing for faking sake.""" - task_mdo = create_task(method, "success") - return task_mdo.obj - - def _delete_disk(self, method, *args, **kwargs): - """Deletes .vmdk and -flat.vmdk files corresponding to the VM.""" - vmdk_file_path = kwargs.get("name") - flat_vmdk_file_path = \ - vmdk_file_path.replace(".vmdk", "-flat.vmdk") - _remove_file(vmdk_file_path) - _remove_file(flat_vmdk_file_path) - task_mdo = create_task(method, "success") - return task_mdo.obj - - def _delete_file(self, method, *args, **kwargs): - """Deletes a file from the datastore.""" - _remove_file(kwargs.get("name")) - task_mdo = create_task(method, "success") - return task_mdo.obj - - def _just_return(self): - """Fakes a return.""" - return - - def _unregister_vm(self, method, *args, **kwargs): - """Unregisters a VM from the Host System.""" - vm_ref = args[0] - _get_vm_mdo(vm_ref) - del _db_content["VirtualMachine"][vm_ref] - - def _search_ds(self, method, *args, **kwargs): - """Searches the datastore for a file.""" - ds_path = kwargs.get("datastorePath") - if _db_content.get("files", None) is None: - raise exception.NoFilesFound() - for file in _db_content.get("files"): - if file.find(ds_path) != -1: - task_mdo = create_task(method, "success") - return task_mdo.obj - task_mdo = create_task(method, "error") - return task_mdo.obj - - def _make_dir(self, method, *args, **kwargs): - """Creates a directory in the datastore.""" - ds_path = kwargs.get("name") - if _db_content.get("files", None) is None: - raise exception.NoFilesFound() - _db_content["files"].append(ds_path) - - def _set_power_state(self, method, vm_ref, pwr_state="poweredOn"): - """Sets power state for the VM.""" - if _db_content.get("VirtualMachine", None) is None: - raise exception.NotFound(_(" No Virtual Machine has been " - "registered yet")) - if vm_ref not in _db_content.get("VirtualMachine"): - raise exception.NotFound(_("Virtual Machine with ref %s is not " - "there") % vm_ref) - vm_mdo = _db_content.get("VirtualMachine").get(vm_ref) - vm_mdo.set("runtime.powerState", pwr_state) - task_mdo = create_task(method, "success") - return task_mdo.obj - - def _retrieve_properties(self, method, *args, **kwargs): - """Retrieves properties based on the type.""" - spec_set = kwargs.get("specSet")[0] - type = spec_set.propSet[0].type - properties = spec_set.propSet[0].pathSet - objs = spec_set.objectSet - lst_ret_objs = [] - for obj in objs: - try: - obj_ref = obj.obj - # This means that we are doing a search for the managed - # dataobjects of the type in the inventory - if obj_ref == "RootFolder": - for mdo_ref in _db_content[type]: - mdo = _db_content[type][mdo_ref] - # Create a temp Managed object which has the same ref - # as the parent object and copies just the properties - # asked for. We need .obj along with the propSet of - # just the properties asked for - temp_mdo = ManagedObject(mdo.objName, mdo.obj) - for prop in properties: - temp_mdo.set(prop, mdo.get(prop)) - lst_ret_objs.append(temp_mdo) - else: - if obj_ref in _db_content[type]: - mdo = _db_content[type][obj_ref] - temp_mdo = ManagedObject(mdo.objName, obj_ref) - for prop in properties: - temp_mdo.set(prop, mdo.get(prop)) - lst_ret_objs.append(temp_mdo) - except Exception, exc: - LOG.exception(exc) - continue - return lst_ret_objs - - def _add_port_group(self, method, *args, **kwargs): - """Adds a port group to the host system.""" - host_mdo = \ - _db_content["HostSystem"][_db_content["HostSystem"].keys()[0]] - host_mdo._add_port_group(kwargs.get("portgrp")) - - def __getattr__(self, attr_name): - if attr_name != "Login": - self._check_session() - if attr_name == "Login": - return lambda *args, **kwargs: self._login() - elif attr_name == "Logout": - self._logout() - elif attr_name == "TerminateSession": - return lambda *args, **kwargs: self._terminate_session( - *args, **kwargs) - elif attr_name == "CreateVM_Task": - return lambda *args, **kwargs: self._create_vm(attr_name, - *args, **kwargs) - elif attr_name == "ReconfigVM_Task": - return lambda *args, **kwargs: self._reconfig_vm(attr_name, - *args, **kwargs) - elif attr_name == "CreateVirtualDisk_Task": - return lambda *args, **kwargs: self._create_copy_disk(attr_name, - kwargs.get("name")) - elif attr_name == "DeleteDatastoreFile_Task": - return lambda *args, **kwargs: self._delete_file(attr_name, - *args, **kwargs) - elif attr_name == "PowerOnVM_Task": - return lambda *args, **kwargs: self._set_power_state(attr_name, - args[0], "poweredOn") - elif attr_name == "PowerOffVM_Task": - return lambda *args, **kwargs: self._set_power_state(attr_name, - args[0], "poweredOff") - elif attr_name == "RebootGuest": - return lambda *args, **kwargs: self._just_return() - elif attr_name == "ResetVM_Task": - return lambda *args, **kwargs: self._set_power_state(attr_name, - args[0], "poweredOn") - elif attr_name == "SuspendVM_Task": - return lambda *args, **kwargs: self._set_power_state(attr_name, - args[0], "suspended") - elif attr_name == "CreateSnapshot_Task": - return lambda *args, **kwargs: self._snapshot_vm(attr_name) - elif attr_name == "CopyVirtualDisk_Task": - return lambda *args, **kwargs: self._create_copy_disk(attr_name, - kwargs.get("destName")) - elif attr_name == "DeleteVirtualDisk_Task": - return lambda *args, **kwargs: self._delete_disk(attr_name, - *args, **kwargs) - elif attr_name == "UnregisterVM": - return lambda *args, **kwargs: self._unregister_vm(attr_name, - *args, **kwargs) - elif attr_name == "SearchDatastore_Task": - return lambda *args, **kwargs: self._search_ds(attr_name, - *args, **kwargs) - elif attr_name == "MakeDirectory": - return lambda *args, **kwargs: self._make_dir(attr_name, - *args, **kwargs) - elif attr_name == "RetrieveProperties": - return lambda *args, **kwargs: self._retrieve_properties( - attr_name, *args, **kwargs) - elif attr_name == "AcquireCloneTicket": - return lambda *args, **kwargs: self._just_return() - elif attr_name == "AddPortGroup": - return lambda *args, **kwargs: self._add_port_group(attr_name, - *args, **kwargs) +# 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. + +""" +A fake VMWare VI API implementation. +""" + +from pprint import pformat +import uuid + +from nova import exception +from nova import log as logging +from nova.virt.vmwareapi import error_util + +_CLASSES = ['Datacenter', 'Datastore', 'ResourcePool', 'VirtualMachine', + 'Network', 'HostSystem', 'HostNetworkSystem', 'Task', 'session', + 'files'] + +_FAKE_FILE_SIZE = 1024 + +_db_content = {} + +LOG = logging.getLogger("nova.virt.vmwareapi.fake") + + +def log_db_contents(msg=None): + """Log DB Contents.""" + text = msg or "" + content = pformat(_db_content) + LOG.debug(_("%(text)s: _db_content => %(content)s") % locals()) + + +def reset(): + """Resets the db contents.""" + for c in _CLASSES: + # We fake the datastore by keeping the file references as a list of + # names in the db + if c == 'files': + _db_content[c] = [] + else: + _db_content[c] = {} + create_network() + create_host_network_system() + create_host() + create_datacenter() + create_datastore() + create_res_pool() + + +def cleanup(): + """Clear the db contents.""" + for c in _CLASSES: + _db_content[c] = {} + + +def _create_object(table, table_obj): + """Create an object in the db.""" + _db_content[table][table_obj.obj] = table_obj + + +def _get_objects(obj_type): + """Get objects of the type.""" + lst_objs = [] + for key in _db_content[obj_type]: + lst_objs.append(_db_content[obj_type][key]) + return lst_objs + + +class Prop(object): + """Property Object base class.""" + + def __init__(self): + self.name = None + self.val = None + + +class ManagedObject(object): + """Managed Data Object base class.""" + + def __init__(self, name="ManagedObject", obj_ref=None): + """Sets the obj property which acts as a reference to the object.""" + super(ManagedObject, self).__setattr__('objName', name) + if obj_ref is None: + obj_ref = str(uuid.uuid4()) + object.__setattr__(self, 'obj', obj_ref) + object.__setattr__(self, 'propSet', []) + + def set(self, attr, val): + """ + Sets an attribute value. Not using the __setattr__ directly for we + want to set attributes of the type 'a.b.c' and using this function + class we set the same. + """ + self.__setattr__(attr, val) + + def get(self, attr): + """ + Gets an attribute. Used as an intermediary to get nested + property like 'a.b.c' value. + """ + return self.__getattr__(attr) + + def __setattr__(self, attr, val): + for prop in self.propSet: + if prop.name == attr: + prop.val = val + return + elem = Prop() + elem.name = attr + elem.val = val + self.propSet.append(elem) + + def __getattr__(self, attr): + for elem in self.propSet: + if elem.name == attr: + return elem.val + raise exception.Error(_("Property %(attr)s not set for the managed " + "object %(objName)s") % + {'attr': attr, + 'objName': self.objName}) + + +class DataObject(object): + """Data object base class.""" + pass + + +class VirtualDisk(DataObject): + """ + Virtual Disk class. Does nothing special except setting + __class__.__name__ to 'VirtualDisk'. Refer place where __class__.__name__ + is used in the code. + """ + pass + + +class VirtualDiskFlatVer2BackingInfo(DataObject): + """VirtualDiskFlatVer2BackingInfo class.""" + pass + + +class VirtualLsiLogicController(DataObject): + """VirtualLsiLogicController class.""" + pass + + +class VirtualMachine(ManagedObject): + """Virtual Machine class.""" + + def __init__(self, **kwargs): + super(VirtualMachine, self).__init__("VirtualMachine") + self.set("name", kwargs.get("name")) + self.set("runtime.connectionState", + kwargs.get("conn_state", "connected")) + self.set("summary.config.guestId", kwargs.get("guest", "otherGuest")) + ds_do = DataObject() + ds_do.ManagedObjectReference = [kwargs.get("ds").obj] + self.set("datastore", ds_do) + self.set("summary.guest.toolsStatus", kwargs.get("toolsstatus", + "toolsOk")) + self.set("summary.guest.toolsRunningStatus", kwargs.get( + "toolsrunningstate", "guestToolsRunning")) + self.set("runtime.powerState", kwargs.get("powerstate", "poweredOn")) + self.set("config.files.vmPathName", kwargs.get("vmPathName")) + self.set("summary.config.numCpu", kwargs.get("numCpu", 1)) + self.set("summary.config.memorySizeMB", kwargs.get("mem", 1)) + self.set("config.hardware.device", kwargs.get("virtual_disk", None)) + self.set("config.extraConfig", kwargs.get("extra_config", None)) + + def reconfig(self, factory, val): + """ + Called to reconfigure the VM. Actually customizes the property + setting of the Virtual Machine object. + """ + try: + # Case of Reconfig of VM to attach disk + controller_key = val.deviceChange[1].device.controllerKey + filename = val.deviceChange[1].device.backing.fileName + + disk = VirtualDisk() + disk.controllerKey = controller_key + + disk_backing = VirtualDiskFlatVer2BackingInfo() + disk_backing.fileName = filename + disk_backing.key = -101 + disk.backing = disk_backing + + controller = VirtualLsiLogicController() + controller.key = controller_key + + self.set("config.hardware.device", [disk, controller]) + except AttributeError: + # Case of Reconfig of VM to set extra params + self.set("config.extraConfig", val.extraConfig) + + +class Network(ManagedObject): + """Network class.""" + + def __init__(self): + super(Network, self).__init__("Network") + self.set("summary.name", "vmnet0") + + +class ResourcePool(ManagedObject): + """Resource Pool class.""" + + def __init__(self): + super(ResourcePool, self).__init__("ResourcePool") + self.set("name", "ResPool") + + +class Datastore(ManagedObject): + """Datastore class.""" + + def __init__(self): + super(Datastore, self).__init__("Datastore") + self.set("summary.type", "VMFS") + self.set("summary.name", "fake-ds") + + +class HostNetworkSystem(ManagedObject): + """HostNetworkSystem class.""" + + def __init__(self): + super(HostNetworkSystem, self).__init__("HostNetworkSystem") + self.set("name", "networkSystem") + + pnic_do = DataObject() + pnic_do.device = "vmnic0" + + net_info_pnic = DataObject() + net_info_pnic.PhysicalNic = [pnic_do] + + self.set("networkInfo.pnic", net_info_pnic) + + +class HostSystem(ManagedObject): + """Host System class.""" + + def __init__(self): + super(HostSystem, self).__init__("HostSystem") + self.set("name", "ha-host") + if _db_content.get("HostNetworkSystem", None) is None: + create_host_network_system() + host_net_key = _db_content["HostNetworkSystem"].keys()[0] + host_net_sys = _db_content["HostNetworkSystem"][host_net_key].obj + self.set("configManager.networkSystem", host_net_sys) + + if _db_content.get("Network", None) is None: + create_network() + net_ref = _db_content["Network"][_db_content["Network"].keys()[0]].obj + network_do = DataObject() + network_do.ManagedObjectReference = [net_ref] + self.set("network", network_do) + + vswitch_do = DataObject() + vswitch_do.pnic = ["vmnic0"] + vswitch_do.name = "vSwitch0" + vswitch_do.portgroup = ["PortGroup-vmnet0"] + + net_swicth = DataObject() + net_swicth.HostVirtualSwitch = [vswitch_do] + self.set("config.network.vswitch", net_swicth) + + host_pg_do = DataObject() + host_pg_do.key = "PortGroup-vmnet0" + + pg_spec = DataObject() + pg_spec.vlanId = 0 + pg_spec.name = "vmnet0" + + host_pg_do.spec = pg_spec + + host_pg = DataObject() + host_pg.HostPortGroup = [host_pg_do] + self.set("config.network.portgroup", host_pg) + + def _add_port_group(self, spec): + """Adds a port group to the host system object in the db.""" + pg_name = spec.name + vswitch_name = spec.vswitchName + vlanid = spec.vlanId + + vswitch_do = DataObject() + vswitch_do.pnic = ["vmnic0"] + vswitch_do.name = vswitch_name + vswitch_do.portgroup = ["PortGroup-%s" % pg_name] + + vswitches = self.get("config.network.vswitch").HostVirtualSwitch + vswitches.append(vswitch_do) + + host_pg_do = DataObject() + host_pg_do.key = "PortGroup-%s" % pg_name + + pg_spec = DataObject() + pg_spec.vlanId = vlanid + pg_spec.name = pg_name + + host_pg_do.spec = pg_spec + host_pgrps = self.get("config.network.portgroup").HostPortGroup + host_pgrps.append(host_pg_do) + + +class Datacenter(ManagedObject): + """Datacenter class.""" + + def __init__(self): + super(Datacenter, self).__init__("Datacenter") + self.set("name", "ha-datacenter") + self.set("vmFolder", "vm_folder_ref") + if _db_content.get("Network", None) is None: + create_network() + net_ref = _db_content["Network"][_db_content["Network"].keys()[0]].obj + network_do = DataObject() + network_do.ManagedObjectReference = [net_ref] + self.set("network", network_do) + + +class Task(ManagedObject): + """Task class.""" + + def __init__(self, task_name, state="running"): + super(Task, self).__init__("Task") + info = DataObject + info.name = task_name + info.state = state + self.set("info", info) + + +def create_host_network_system(): + host_net_system = HostNetworkSystem() + _create_object("HostNetworkSystem", host_net_system) + + +def create_host(): + host_system = HostSystem() + _create_object('HostSystem', host_system) + + +def create_datacenter(): + data_center = Datacenter() + _create_object('Datacenter', data_center) + + +def create_datastore(): + data_store = Datastore() + _create_object('Datastore', data_store) + + +def create_res_pool(): + res_pool = ResourcePool() + _create_object('ResourcePool', res_pool) + + +def create_network(): + network = Network() + _create_object('Network', network) + + +def create_task(task_name, state="running"): + task = Task(task_name, state) + _create_object("Task", task) + return task + + +def _add_file(file_path): + """Adds a file reference to the db.""" + _db_content["files"].append(file_path) + + +def _remove_file(file_path): + """Removes a file reference from the db.""" + if _db_content.get("files") is None: + raise exception.NoFilesFound() + # Check if the remove is for a single file object or for a folder + if file_path.find(".vmdk") != -1: + if file_path not in _db_content.get("files"): + raise exception.FileNotFound(file_path=file_path) + _db_content.get("files").remove(file_path) + else: + # Removes the files in the folder and the folder too from the db + for file in _db_content.get("files"): + if file.find(file_path) != -1: + lst_files = _db_content.get("files") + if lst_files and lst_files.count(file): + lst_files.remove(file) + + +def fake_plug_vifs(*args, **kwargs): + """Fakes plugging vifs.""" + pass + + +def fake_get_network(*args, **kwargs): + """Fake get network.""" + return {'type': 'fake'} + + +def fake_fetch_image(context, image, instance, **kwargs): + """Fakes fetch image call. Just adds a reference to the db for the file.""" + ds_name = kwargs.get("datastore_name") + file_path = kwargs.get("file_path") + ds_file_path = "[" + ds_name + "] " + file_path + _add_file(ds_file_path) + + +def fake_upload_image(context, image, instance, **kwargs): + """Fakes the upload of an image.""" + pass + + +def fake_get_vmdk_size_and_properties(context, image_id, instance): + """Fakes the file size and properties fetch for the image file.""" + props = {"vmware_ostype": "otherGuest", + "vmware_adaptertype": "lsiLogic"} + return _FAKE_FILE_SIZE, props + + +def _get_vm_mdo(vm_ref): + """Gets the Virtual Machine with the ref from the db.""" + if _db_content.get("VirtualMachine", None) is None: + raise exception.NotFound(_("There is no VM registered")) + if vm_ref not in _db_content.get("VirtualMachine"): + raise exception.NotFound(_("Virtual Machine with ref %s is not " + "there") % vm_ref) + return _db_content.get("VirtualMachine")[vm_ref] + + +class FakeFactory(object): + """Fake factory class for the suds client.""" + + def create(self, obj_name): + """Creates a namespace object.""" + return DataObject() + + +class FakeVim(object): + """Fake VIM Class.""" + + def __init__(self, protocol="https", host="localhost", trace=None): + """ + Initializes the suds client object, sets the service content + contents and the cookies for the session. + """ + self._session = None + self.client = DataObject() + self.client.factory = FakeFactory() + + transport = DataObject() + transport.cookiejar = "Fake-CookieJar" + options = DataObject() + options.transport = transport + + self.client.options = options + + service_content = self.client.factory.create('ns0:ServiceContent') + service_content.propertyCollector = "PropCollector" + service_content.virtualDiskManager = "VirtualDiskManager" + service_content.fileManager = "FileManager" + service_content.rootFolder = "RootFolder" + service_content.sessionManager = "SessionManager" + self._service_content = service_content + + def get_service_content(self): + return self._service_content + + def __repr__(self): + return "Fake VIM Object" + + def __str__(self): + return "Fake VIM Object" + + def _login(self): + """Logs in and sets the session object in the db.""" + self._session = str(uuid.uuid4()) + session = DataObject() + session.key = self._session + _db_content['session'][self._session] = session + return session + + def _logout(self): + """Logs out and remove the session object ref from the db.""" + s = self._session + self._session = None + if s not in _db_content['session']: + raise exception.Error( + _("Logging out a session that is invalid or already logged " + "out: %s") % s) + del _db_content['session'][s] + + def _terminate_session(self, *args, **kwargs): + """Terminates a session.""" + s = kwargs.get("sessionId")[0] + if s not in _db_content['session']: + return + del _db_content['session'][s] + + def _check_session(self): + """Checks if the session is active.""" + if (self._session is None or self._session not in + _db_content['session']): + LOG.debug(_("Session is faulty")) + raise error_util.VimFaultException( + [error_util.FAULT_NOT_AUTHENTICATED], + _("Session Invalid")) + + def _create_vm(self, method, *args, **kwargs): + """Creates and registers a VM object with the Host System.""" + config_spec = kwargs.get("config") + ds = _db_content["Datastore"][_db_content["Datastore"].keys()[0]] + vm_dict = {"name": config_spec.name, + "ds": ds, + "powerstate": "poweredOff", + "vmPathName": config_spec.files.vmPathName, + "numCpu": config_spec.numCPUs, + "mem": config_spec.memoryMB} + virtual_machine = VirtualMachine(**vm_dict) + _create_object("VirtualMachine", virtual_machine) + task_mdo = create_task(method, "success") + return task_mdo.obj + + def _reconfig_vm(self, method, *args, **kwargs): + """Reconfigures a VM and sets the properties supplied.""" + vm_ref = args[0] + vm_mdo = _get_vm_mdo(vm_ref) + vm_mdo.reconfig(self.client.factory, kwargs.get("spec")) + task_mdo = create_task(method, "success") + return task_mdo.obj + + def _create_copy_disk(self, method, vmdk_file_path): + """Creates/copies a vmdk file object in the datastore.""" + # We need to add/create both .vmdk and .-flat.vmdk files + flat_vmdk_file_path = \ + vmdk_file_path.replace(".vmdk", "-flat.vmdk") + _add_file(vmdk_file_path) + _add_file(flat_vmdk_file_path) + task_mdo = create_task(method, "success") + return task_mdo.obj + + def _snapshot_vm(self, method): + """Snapshots a VM. Here we do nothing for faking sake.""" + task_mdo = create_task(method, "success") + return task_mdo.obj + + def _delete_disk(self, method, *args, **kwargs): + """Deletes .vmdk and -flat.vmdk files corresponding to the VM.""" + vmdk_file_path = kwargs.get("name") + flat_vmdk_file_path = \ + vmdk_file_path.replace(".vmdk", "-flat.vmdk") + _remove_file(vmdk_file_path) + _remove_file(flat_vmdk_file_path) + task_mdo = create_task(method, "success") + return task_mdo.obj + + def _delete_file(self, method, *args, **kwargs): + """Deletes a file from the datastore.""" + _remove_file(kwargs.get("name")) + task_mdo = create_task(method, "success") + return task_mdo.obj + + def _just_return(self): + """Fakes a return.""" + return + + def _unregister_vm(self, method, *args, **kwargs): + """Unregisters a VM from the Host System.""" + vm_ref = args[0] + _get_vm_mdo(vm_ref) + del _db_content["VirtualMachine"][vm_ref] + + def _search_ds(self, method, *args, **kwargs): + """Searches the datastore for a file.""" + ds_path = kwargs.get("datastorePath") + if _db_content.get("files", None) is None: + raise exception.NoFilesFound() + for file in _db_content.get("files"): + if file.find(ds_path) != -1: + task_mdo = create_task(method, "success") + return task_mdo.obj + task_mdo = create_task(method, "error") + return task_mdo.obj + + def _make_dir(self, method, *args, **kwargs): + """Creates a directory in the datastore.""" + ds_path = kwargs.get("name") + if _db_content.get("files", None) is None: + raise exception.NoFilesFound() + _db_content["files"].append(ds_path) + + def _set_power_state(self, method, vm_ref, pwr_state="poweredOn"): + """Sets power state for the VM.""" + if _db_content.get("VirtualMachine", None) is None: + raise exception.NotFound(_(" No Virtual Machine has been " + "registered yet")) + if vm_ref not in _db_content.get("VirtualMachine"): + raise exception.NotFound(_("Virtual Machine with ref %s is not " + "there") % vm_ref) + vm_mdo = _db_content.get("VirtualMachine").get(vm_ref) + vm_mdo.set("runtime.powerState", pwr_state) + task_mdo = create_task(method, "success") + return task_mdo.obj + + def _retrieve_properties(self, method, *args, **kwargs): + """Retrieves properties based on the type.""" + spec_set = kwargs.get("specSet")[0] + type = spec_set.propSet[0].type + properties = spec_set.propSet[0].pathSet + objs = spec_set.objectSet + lst_ret_objs = [] + for obj in objs: + try: + obj_ref = obj.obj + # This means that we are doing a search for the managed + # dataobjects of the type in the inventory + if obj_ref == "RootFolder": + for mdo_ref in _db_content[type]: + mdo = _db_content[type][mdo_ref] + # Create a temp Managed object which has the same ref + # as the parent object and copies just the properties + # asked for. We need .obj along with the propSet of + # just the properties asked for + temp_mdo = ManagedObject(mdo.objName, mdo.obj) + for prop in properties: + temp_mdo.set(prop, mdo.get(prop)) + lst_ret_objs.append(temp_mdo) + else: + if obj_ref in _db_content[type]: + mdo = _db_content[type][obj_ref] + temp_mdo = ManagedObject(mdo.objName, obj_ref) + for prop in properties: + temp_mdo.set(prop, mdo.get(prop)) + lst_ret_objs.append(temp_mdo) + except Exception, exc: + LOG.exception(exc) + continue + return lst_ret_objs + + def _add_port_group(self, method, *args, **kwargs): + """Adds a port group to the host system.""" + host_mdo = \ + _db_content["HostSystem"][_db_content["HostSystem"].keys()[0]] + host_mdo._add_port_group(kwargs.get("portgrp")) + + def __getattr__(self, attr_name): + if attr_name != "Login": + self._check_session() + if attr_name == "Login": + return lambda *args, **kwargs: self._login() + elif attr_name == "Logout": + self._logout() + elif attr_name == "TerminateSession": + return lambda *args, **kwargs: self._terminate_session( + *args, **kwargs) + elif attr_name == "CreateVM_Task": + return lambda *args, **kwargs: self._create_vm(attr_name, + *args, **kwargs) + elif attr_name == "ReconfigVM_Task": + return lambda *args, **kwargs: self._reconfig_vm(attr_name, + *args, **kwargs) + elif attr_name == "CreateVirtualDisk_Task": + return lambda *args, **kwargs: self._create_copy_disk(attr_name, + kwargs.get("name")) + elif attr_name == "DeleteDatastoreFile_Task": + return lambda *args, **kwargs: self._delete_file(attr_name, + *args, **kwargs) + elif attr_name == "PowerOnVM_Task": + return lambda *args, **kwargs: self._set_power_state(attr_name, + args[0], "poweredOn") + elif attr_name == "PowerOffVM_Task": + return lambda *args, **kwargs: self._set_power_state(attr_name, + args[0], "poweredOff") + elif attr_name == "RebootGuest": + return lambda *args, **kwargs: self._just_return() + elif attr_name == "ResetVM_Task": + return lambda *args, **kwargs: self._set_power_state(attr_name, + args[0], "poweredOn") + elif attr_name == "SuspendVM_Task": + return lambda *args, **kwargs: self._set_power_state(attr_name, + args[0], "suspended") + elif attr_name == "CreateSnapshot_Task": + return lambda *args, **kwargs: self._snapshot_vm(attr_name) + elif attr_name == "CopyVirtualDisk_Task": + return lambda *args, **kwargs: self._create_copy_disk(attr_name, + kwargs.get("destName")) + elif attr_name == "DeleteVirtualDisk_Task": + return lambda *args, **kwargs: self._delete_disk(attr_name, + *args, **kwargs) + elif attr_name == "UnregisterVM": + return lambda *args, **kwargs: self._unregister_vm(attr_name, + *args, **kwargs) + elif attr_name == "SearchDatastore_Task": + return lambda *args, **kwargs: self._search_ds(attr_name, + *args, **kwargs) + elif attr_name == "MakeDirectory": + return lambda *args, **kwargs: self._make_dir(attr_name, + *args, **kwargs) + elif attr_name == "RetrieveProperties": + return lambda *args, **kwargs: self._retrieve_properties( + attr_name, *args, **kwargs) + elif attr_name == "AcquireCloneTicket": + return lambda *args, **kwargs: self._just_return() + elif attr_name == "AddPortGroup": + return lambda *args, **kwargs: self._add_port_group(attr_name, + *args, **kwargs) diff --git a/nova/virt/vmwareapi/io_util.py b/nova/virt/vmwareapi/io_util.py index cb9587afc..0e0159c97 100644 --- a/nova/virt/vmwareapi/io_util.py +++ b/nova/virt/vmwareapi/io_util.py @@ -1,169 +1,169 @@ -# 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. - -""" -Utility classes for defining the time saving transfer of data from the reader -to the write using a LightQueue as a Pipe between the reader and the writer. -""" - -from eventlet import event -from eventlet import greenthread -from eventlet.queue import LightQueue - -from nova import exception -from nova import log as logging - -LOG = logging.getLogger("nova.virt.vmwareapi.io_util") - -IO_THREAD_SLEEP_TIME = .01 -GLANCE_POLL_INTERVAL = 5 - - -class ThreadSafePipe(LightQueue): - """The pipe to hold the data which the reader writes to and the writer - reads from.""" - - def __init__(self, maxsize, transfer_size): - LightQueue.__init__(self, maxsize) - self.transfer_size = transfer_size - self.transferred = 0 - - def read(self, chunk_size): - """Read data from the pipe. Chunksize if ignored for we have ensured - that the data chunks written to the pipe by readers is the same as the - chunks asked for by the Writer.""" - if self.transferred < self.transfer_size: - data_item = self.get() - self.transferred += len(data_item) - return data_item - else: - return "" - - def write(self, data): - """Put a data item in the pipe.""" - self.put(data) - - def close(self): - """A place-holder to maintain consistency.""" - pass - - -class GlanceWriteThread(object): - """Ensures that image data is written to in the glance client and that - it is in correct ('active')state.""" - - def __init__(self, input, glance_client, image_id, image_meta=None): - if not image_meta: - image_meta = {} - - self.input = input - self.glance_client = glance_client - self.image_id = image_id - self.image_meta = image_meta - self._running = False - - def start(self): - self.done = event.Event() - - def _inner(): - """Function to do the image data transfer through an update - and thereon checks if the state is 'active'.""" - self.glance_client.update_image(self.image_id, - image_meta=self.image_meta, - image_data=self.input) - self._running = True - while self._running: - try: - image_status = \ - self.glance_client.get_image_meta(self.image_id).get( - "status") - if image_status == "active": - self.stop() - self.done.send(True) - # If the state is killed, then raise an exception. - elif image_status == "killed": - self.stop() - exc_msg = _("Glance image %s is in killed state") %\ - self.image_id - LOG.exception(exc_msg) - self.done.send_exception(exception.Error(exc_msg)) - elif image_status in ["saving", "queued"]: - greenthread.sleep(GLANCE_POLL_INTERVAL) - else: - self.stop() - exc_msg = _("Glance image " - "%(image_id)s is in unknown state " - "- %(state)s") % { - "image_id": self.image_id, - "state": image_status} - LOG.exception(exc_msg) - self.done.send_exception(exception.Error(exc_msg)) - except Exception, exc: - self.stop() - self.done.send_exception(exc) - - greenthread.spawn(_inner) - return self.done - - def stop(self): - self._running = False - - def wait(self): - return self.done.wait() - - def close(self): - pass - - -class IOThread(object): - """Class that reads chunks from the input file and writes them to the - output file till the transfer is completely done.""" - - def __init__(self, input, output): - self.input = input - self.output = output - self._running = False - self.got_exception = False - - def start(self): - self.done = event.Event() - - def _inner(): - """Read data from the input and write the same to the output - until the transfer completes.""" - self._running = True - while self._running: - try: - data = self.input.read(None) - if not data: - self.stop() - self.done.send(True) - self.output.write(data) - greenthread.sleep(IO_THREAD_SLEEP_TIME) - except Exception, exc: - self.stop() - LOG.exception(exc) - self.done.send_exception(exc) - - greenthread.spawn(_inner) - return self.done - - def stop(self): - self._running = False - - def wait(self): - return self.done.wait() +# 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. + +""" +Utility classes for defining the time saving transfer of data from the reader +to the write using a LightQueue as a Pipe between the reader and the writer. +""" + +from eventlet import event +from eventlet import greenthread +from eventlet.queue import LightQueue + +from nova import exception +from nova import log as logging + +LOG = logging.getLogger("nova.virt.vmwareapi.io_util") + +IO_THREAD_SLEEP_TIME = .01 +GLANCE_POLL_INTERVAL = 5 + + +class ThreadSafePipe(LightQueue): + """The pipe to hold the data which the reader writes to and the writer + reads from.""" + + def __init__(self, maxsize, transfer_size): + LightQueue.__init__(self, maxsize) + self.transfer_size = transfer_size + self.transferred = 0 + + def read(self, chunk_size): + """Read data from the pipe. Chunksize if ignored for we have ensured + that the data chunks written to the pipe by readers is the same as the + chunks asked for by the Writer.""" + if self.transferred < self.transfer_size: + data_item = self.get() + self.transferred += len(data_item) + return data_item + else: + return "" + + def write(self, data): + """Put a data item in the pipe.""" + self.put(data) + + def close(self): + """A place-holder to maintain consistency.""" + pass + + +class GlanceWriteThread(object): + """Ensures that image data is written to in the glance client and that + it is in correct ('active')state.""" + + def __init__(self, input, glance_client, image_id, image_meta=None): + if not image_meta: + image_meta = {} + + self.input = input + self.glance_client = glance_client + self.image_id = image_id + self.image_meta = image_meta + self._running = False + + def start(self): + self.done = event.Event() + + def _inner(): + """Function to do the image data transfer through an update + and thereon checks if the state is 'active'.""" + self.glance_client.update_image(self.image_id, + image_meta=self.image_meta, + image_data=self.input) + self._running = True + while self._running: + try: + image_status = \ + self.glance_client.get_image_meta(self.image_id).get( + "status") + if image_status == "active": + self.stop() + self.done.send(True) + # If the state is killed, then raise an exception. + elif image_status == "killed": + self.stop() + exc_msg = _("Glance image %s is in killed state") %\ + self.image_id + LOG.exception(exc_msg) + self.done.send_exception(exception.Error(exc_msg)) + elif image_status in ["saving", "queued"]: + greenthread.sleep(GLANCE_POLL_INTERVAL) + else: + self.stop() + exc_msg = _("Glance image " + "%(image_id)s is in unknown state " + "- %(state)s") % { + "image_id": self.image_id, + "state": image_status} + LOG.exception(exc_msg) + self.done.send_exception(exception.Error(exc_msg)) + except Exception, exc: + self.stop() + self.done.send_exception(exc) + + greenthread.spawn(_inner) + return self.done + + def stop(self): + self._running = False + + def wait(self): + return self.done.wait() + + def close(self): + pass + + +class IOThread(object): + """Class that reads chunks from the input file and writes them to the + output file till the transfer is completely done.""" + + def __init__(self, input, output): + self.input = input + self.output = output + self._running = False + self.got_exception = False + + def start(self): + self.done = event.Event() + + def _inner(): + """Read data from the input and write the same to the output + until the transfer completes.""" + self._running = True + while self._running: + try: + data = self.input.read(None) + if not data: + self.stop() + self.done.send(True) + self.output.write(data) + greenthread.sleep(IO_THREAD_SLEEP_TIME) + except Exception, exc: + self.stop() + LOG.exception(exc) + self.done.send_exception(exc) + + greenthread.spawn(_inner) + return self.done + + def stop(self): + self._running = False + + def wait(self): + return self.done.wait() diff --git a/nova/virt/vmwareapi/network_utils.py b/nova/virt/vmwareapi/network_utils.py index ec3b93fe7..9d1918bd3 100644 --- a/nova/virt/vmwareapi/network_utils.py +++ b/nova/virt/vmwareapi/network_utils.py @@ -1,170 +1,170 @@ -# 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. - -""" -Utility functions for ESX Networking. -""" - -from nova import exception -from nova import log as logging -from nova.virt.vmwareapi import error_util -from nova.virt.vmwareapi import vim_util -from nova.virt.vmwareapi import vm_util - -LOG = logging.getLogger("nova.virt.vmwareapi.network_utils") - - -def get_network_with_the_name(session, network_name="vmnet0"): - """ - Gets reference to the network whose name is passed as the - argument. - """ - hostsystems = session._call_method(vim_util, "get_objects", - "HostSystem", ["network"]) - vm_networks_ret = hostsystems[0].propSet[0].val - # Meaning there are no networks on the host. suds responds with a "" - # in the parent property field rather than a [] in the - # ManagedObjectRefernce property field of the parent - if not vm_networks_ret: - return None - vm_networks = vm_networks_ret.ManagedObjectReference - networks = session._call_method(vim_util, - "get_properties_for_a_collection_of_objects", - "Network", vm_networks, ["summary.name"]) - network_obj = {} - LOG.warn(vm_networks) - for network in vm_networks: - # Get network properties - if network._type == 'DistributedVirtualPortgroup': - props = session._call_method(vim_util, - "get_dynamic_property", network, - "DistributedVirtualPortgroup", "config") - # NOTE(asomya): This only works on ESXi if the port binding is - # set to ephemeral - if props.name == network_name: - network_obj['type'] = 'DistributedVirtualPortgroup' - network_obj['dvpg'] = props.key - network_obj['dvsw'] = props.distributedVirtualSwitch.value - else: - props = session._call_method(vim_util, - "get_dynamic_property", network, - "Network", "summary.name") - if props == network_name: - network_obj['type'] = 'Network' - network_obj['name'] = network_name - if (len(network_obj) > 0): - return network_obj - else: - return None - - -def get_vswitch_for_vlan_interface(session, vlan_interface): - """ - Gets the vswitch associated with the physical network adapter - with the name supplied. - """ - # Get the list of vSwicthes on the Host System - host_mor = session._call_method(vim_util, "get_objects", - "HostSystem")[0].obj - vswitches_ret = session._call_method(vim_util, - "get_dynamic_property", host_mor, - "HostSystem", "config.network.vswitch") - # Meaning there are no vSwitches on the host. Shouldn't be the case, - # but just doing code check - if not vswitches_ret: - return - vswitches = vswitches_ret.HostVirtualSwitch - # Get the vSwitch associated with the network adapter - for elem in vswitches: - try: - for nic_elem in elem.pnic: - if str(nic_elem).split('-')[-1].find(vlan_interface) != -1: - return elem.name - # Catching Attribute error as a vSwitch may not be associated with a - # physical NIC. - except AttributeError: - pass - - -def check_if_vlan_interface_exists(session, vlan_interface): - """Checks if the vlan_inteface exists on the esx host.""" - host_net_system_mor = session._call_method(vim_util, "get_objects", - "HostSystem", ["configManager.networkSystem"])[0].propSet[0].val - physical_nics_ret = session._call_method(vim_util, - "get_dynamic_property", host_net_system_mor, - "HostNetworkSystem", "networkInfo.pnic") - # Meaning there are no physical nics on the host - if not physical_nics_ret: - return False - physical_nics = physical_nics_ret.PhysicalNic - for pnic in physical_nics: - if vlan_interface == pnic.device: - return True - return False - - -def get_vlanid_and_vswitch_for_portgroup(session, pg_name): - """Get the vlan id and vswicth associated with the port group.""" - host_mor = session._call_method(vim_util, "get_objects", - "HostSystem")[0].obj - port_grps_on_host_ret = session._call_method(vim_util, - "get_dynamic_property", host_mor, - "HostSystem", "config.network.portgroup") - if not port_grps_on_host_ret: - excep = ("ESX SOAP server returned an empty port group " - "for the host system in its response") - LOG.exception(excep) - raise exception.Error(_(excep)) - port_grps_on_host = port_grps_on_host_ret.HostPortGroup - for p_gp in port_grps_on_host: - if p_gp.spec.name == pg_name: - p_grp_vswitch_name = p_gp.vswitch.split("-")[-1] - return p_gp.spec.vlanId, p_grp_vswitch_name - - -def create_port_group(session, pg_name, vswitch_name, vlan_id=0): - """ - Creates a port group on the host system with the vlan tags - supplied. VLAN id 0 means no vlan id association. - """ - client_factory = session._get_vim().client.factory - add_prt_grp_spec = vm_util.get_add_vswitch_port_group_spec( - client_factory, - vswitch_name, - pg_name, - vlan_id) - host_mor = session._call_method(vim_util, "get_objects", - "HostSystem")[0].obj - network_system_mor = session._call_method(vim_util, - "get_dynamic_property", host_mor, - "HostSystem", "configManager.networkSystem") - LOG.debug(_("Creating Port Group with name %s on " - "the ESX host") % pg_name) - try: - session._call_method(session._get_vim(), - "AddPortGroup", network_system_mor, - portgrp=add_prt_grp_spec) - except error_util.VimFaultException, exc: - # There can be a race condition when two instances try - # adding port groups at the same time. One succeeds, then - # the other one will get an exception. Since we are - # concerned with the port group being created, which is done - # by the other call, we can ignore the exception. - if error_util.FAULT_ALREADY_EXISTS not in exc.fault_list: - raise exception.Error(exc) - LOG.debug(_("Created Port Group with name %s on " - "the ESX host") % pg_name) +# 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. + +""" +Utility functions for ESX Networking. +""" + +from nova import exception +from nova import log as logging +from nova.virt.vmwareapi import error_util +from nova.virt.vmwareapi import vim_util +from nova.virt.vmwareapi import vm_util + +LOG = logging.getLogger("nova.virt.vmwareapi.network_utils") + + +def get_network_with_the_name(session, network_name="vmnet0"): + """ + Gets reference to the network whose name is passed as the + argument. + """ + hostsystems = session._call_method(vim_util, "get_objects", + "HostSystem", ["network"]) + vm_networks_ret = hostsystems[0].propSet[0].val + # Meaning there are no networks on the host. suds responds with a "" + # in the parent property field rather than a [] in the + # ManagedObjectRefernce property field of the parent + if not vm_networks_ret: + return None + vm_networks = vm_networks_ret.ManagedObjectReference + networks = session._call_method(vim_util, + "get_properties_for_a_collection_of_objects", + "Network", vm_networks, ["summary.name"]) + network_obj = {} + LOG.warn(vm_networks) + for network in vm_networks: + # Get network properties + if network._type == 'DistributedVirtualPortgroup': + props = session._call_method(vim_util, + "get_dynamic_property", network, + "DistributedVirtualPortgroup", "config") + # NOTE(asomya): This only works on ESXi if the port binding is + # set to ephemeral + if props.name == network_name: + network_obj['type'] = 'DistributedVirtualPortgroup' + network_obj['dvpg'] = props.key + network_obj['dvsw'] = props.distributedVirtualSwitch.value + else: + props = session._call_method(vim_util, + "get_dynamic_property", network, + "Network", "summary.name") + if props == network_name: + network_obj['type'] = 'Network' + network_obj['name'] = network_name + if (len(network_obj) > 0): + return network_obj + else: + return None + + +def get_vswitch_for_vlan_interface(session, vlan_interface): + """ + Gets the vswitch associated with the physical network adapter + with the name supplied. + """ + # Get the list of vSwicthes on the Host System + host_mor = session._call_method(vim_util, "get_objects", + "HostSystem")[0].obj + vswitches_ret = session._call_method(vim_util, + "get_dynamic_property", host_mor, + "HostSystem", "config.network.vswitch") + # Meaning there are no vSwitches on the host. Shouldn't be the case, + # but just doing code check + if not vswitches_ret: + return + vswitches = vswitches_ret.HostVirtualSwitch + # Get the vSwitch associated with the network adapter + for elem in vswitches: + try: + for nic_elem in elem.pnic: + if str(nic_elem).split('-')[-1].find(vlan_interface) != -1: + return elem.name + # Catching Attribute error as a vSwitch may not be associated with a + # physical NIC. + except AttributeError: + pass + + +def check_if_vlan_interface_exists(session, vlan_interface): + """Checks if the vlan_inteface exists on the esx host.""" + host_net_system_mor = session._call_method(vim_util, "get_objects", + "HostSystem", ["configManager.networkSystem"])[0].propSet[0].val + physical_nics_ret = session._call_method(vim_util, + "get_dynamic_property", host_net_system_mor, + "HostNetworkSystem", "networkInfo.pnic") + # Meaning there are no physical nics on the host + if not physical_nics_ret: + return False + physical_nics = physical_nics_ret.PhysicalNic + for pnic in physical_nics: + if vlan_interface == pnic.device: + return True + return False + + +def get_vlanid_and_vswitch_for_portgroup(session, pg_name): + """Get the vlan id and vswicth associated with the port group.""" + host_mor = session._call_method(vim_util, "get_objects", + "HostSystem")[0].obj + port_grps_on_host_ret = session._call_method(vim_util, + "get_dynamic_property", host_mor, + "HostSystem", "config.network.portgroup") + if not port_grps_on_host_ret: + excep = ("ESX SOAP server returned an empty port group " + "for the host system in its response") + LOG.exception(excep) + raise exception.Error(_(excep)) + port_grps_on_host = port_grps_on_host_ret.HostPortGroup + for p_gp in port_grps_on_host: + if p_gp.spec.name == pg_name: + p_grp_vswitch_name = p_gp.vswitch.split("-")[-1] + return p_gp.spec.vlanId, p_grp_vswitch_name + + +def create_port_group(session, pg_name, vswitch_name, vlan_id=0): + """ + Creates a port group on the host system with the vlan tags + supplied. VLAN id 0 means no vlan id association. + """ + client_factory = session._get_vim().client.factory + add_prt_grp_spec = vm_util.get_add_vswitch_port_group_spec( + client_factory, + vswitch_name, + pg_name, + vlan_id) + host_mor = session._call_method(vim_util, "get_objects", + "HostSystem")[0].obj + network_system_mor = session._call_method(vim_util, + "get_dynamic_property", host_mor, + "HostSystem", "configManager.networkSystem") + LOG.debug(_("Creating Port Group with name %s on " + "the ESX host") % pg_name) + try: + session._call_method(session._get_vim(), + "AddPortGroup", network_system_mor, + portgrp=add_prt_grp_spec) + except error_util.VimFaultException, exc: + # There can be a race condition when two instances try + # adding port groups at the same time. One succeeds, then + # the other one will get an exception. Since we are + # concerned with the port group being created, which is done + # by the other call, we can ignore the exception. + if error_util.FAULT_ALREADY_EXISTS not in exc.fault_list: + raise exception.Error(exc) + LOG.debug(_("Created Port Group with name %s on " + "the ESX host") % pg_name) diff --git a/nova/virt/vmwareapi/read_write_util.py b/nova/virt/vmwareapi/read_write_util.py index 33bef6d18..9663b1ca6 100644 --- a/nova/virt/vmwareapi/read_write_util.py +++ b/nova/virt/vmwareapi/read_write_util.py @@ -1,179 +1,179 @@ -# 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 to handle image files - -Collection of classes to handle image upload/download to/from Image service -(like Glance image storage and retrieval service) from/to ESX/ESXi server. - -""" - -import httplib -import urllib -import urllib2 -import urlparse - -from glance import client - -from nova import flags -from nova import log as logging - -LOG = logging.getLogger("nova.virt.vmwareapi.read_write_util") - -FLAGS = flags.FLAGS - -USER_AGENT = "OpenStack-ESX-Adapter" - -try: - READ_CHUNKSIZE = client.BaseClient.CHUNKSIZE -except AttributeError: - READ_CHUNKSIZE = 65536 - - -class GlanceFileRead(object): - """Glance file read handler class.""" - - def __init__(self, glance_read_iter): - self.glance_read_iter = glance_read_iter - self.iter = self.get_next() - - def read(self, chunk_size): - """Read an item from the queue. The chunk size is ignored for the - Client ImageBodyIterator uses its own CHUNKSIZE.""" - try: - return self.iter.next() - except StopIteration: - return "" - - def get_next(self): - """Get the next item from the image iterator.""" - for data in self.glance_read_iter: - yield data - - def close(self): - """A dummy close just to maintain consistency.""" - pass - - -class VMwareHTTPFile(object): - """Base class for HTTP file.""" - - def __init__(self, file_handle): - self.eof = False - self.file_handle = file_handle - - def set_eof(self, eof): - """Set the end of file marker.""" - self.eof = eof - - def get_eof(self): - """Check if the end of file has been reached.""" - return self.eof - - def close(self): - """Close the file handle.""" - try: - self.file_handle.close() - except Exception, exc: - LOG.exception(exc) - - def __del__(self): - """Close the file handle on garbage collection.""" - self.close() - - def _build_vim_cookie_headers(self, vim_cookies): - """Build ESX host session cookie headers.""" - cookie_header = "" - for vim_cookie in vim_cookies: - cookie_header = vim_cookie.name + "=" + vim_cookie.value - break - return cookie_header - - def write(self, data): - """Write data to the file.""" - raise NotImplementedError - - def read(self, chunk_size): - """Read a chunk of data.""" - raise NotImplementedError - - def get_size(self): - """Get size of the file to be read.""" - raise NotImplementedError - - -class VMWareHTTPWriteFile(VMwareHTTPFile): - """VMWare file write handler class.""" - - def __init__(self, host, data_center_name, datastore_name, cookies, - file_path, file_size, scheme="https"): - base_url = "%s://%s/folder/%s" % (scheme, host, file_path) - param_list = {"dcPath": data_center_name, "dsName": datastore_name} - base_url = base_url + "?" + urllib.urlencode(param_list) - (scheme, netloc, path, params, query, fragment) = \ - urlparse.urlparse(base_url) - if scheme == "http": - conn = httplib.HTTPConnection(netloc) - elif scheme == "https": - conn = httplib.HTTPSConnection(netloc) - conn.putrequest("PUT", path + "?" + query) - conn.putheader("User-Agent", USER_AGENT) - conn.putheader("Content-Length", file_size) - conn.putheader("Cookie", self._build_vim_cookie_headers(cookies)) - conn.endheaders() - self.conn = conn - VMwareHTTPFile.__init__(self, conn) - - def write(self, data): - """Write to the file.""" - self.file_handle.send(data) - - def close(self): - """Get the response and close the connection.""" - try: - self.conn.getresponse() - except Exception, excep: - LOG.debug(_("Exception during HTTP connection close in " - "VMWareHTTpWrite. Exception is %s") % excep) - super(VMWareHTTPWriteFile, self).close() - - -class VmWareHTTPReadFile(VMwareHTTPFile): - """VMWare file read handler class.""" - - def __init__(self, host, data_center_name, datastore_name, cookies, - file_path, scheme="https"): - base_url = "%s://%s/folder/%s" % (scheme, host, - urllib.pathname2url(file_path)) - param_list = {"dcPath": data_center_name, "dsName": datastore_name} - base_url = base_url + "?" + urllib.urlencode(param_list) - headers = {'User-Agent': USER_AGENT, - 'Cookie': self._build_vim_cookie_headers(cookies)} - request = urllib2.Request(base_url, None, headers) - conn = urllib2.urlopen(request) - VMwareHTTPFile.__init__(self, conn) - - def read(self, chunk_size): - """Read a chunk of data.""" - # We are ignoring the chunk size passed for we want the pipe to hold - # data items of the chunk-size that Glance Client uses for read - # while writing. - return self.file_handle.read(READ_CHUNKSIZE) - - def get_size(self): - """Get size of the file to be read.""" - return self.file_handle.headers.get("Content-Length", -1) +# 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 to handle image files + +Collection of classes to handle image upload/download to/from Image service +(like Glance image storage and retrieval service) from/to ESX/ESXi server. + +""" + +import httplib +import urllib +import urllib2 +import urlparse + +from glance import client + +from nova import flags +from nova import log as logging + +LOG = logging.getLogger("nova.virt.vmwareapi.read_write_util") + +FLAGS = flags.FLAGS + +USER_AGENT = "OpenStack-ESX-Adapter" + +try: + READ_CHUNKSIZE = client.BaseClient.CHUNKSIZE +except AttributeError: + READ_CHUNKSIZE = 65536 + + +class GlanceFileRead(object): + """Glance file read handler class.""" + + def __init__(self, glance_read_iter): + self.glance_read_iter = glance_read_iter + self.iter = self.get_next() + + def read(self, chunk_size): + """Read an item from the queue. The chunk size is ignored for the + Client ImageBodyIterator uses its own CHUNKSIZE.""" + try: + return self.iter.next() + except StopIteration: + return "" + + def get_next(self): + """Get the next item from the image iterator.""" + for data in self.glance_read_iter: + yield data + + def close(self): + """A dummy close just to maintain consistency.""" + pass + + +class VMwareHTTPFile(object): + """Base class for HTTP file.""" + + def __init__(self, file_handle): + self.eof = False + self.file_handle = file_handle + + def set_eof(self, eof): + """Set the end of file marker.""" + self.eof = eof + + def get_eof(self): + """Check if the end of file has been reached.""" + return self.eof + + def close(self): + """Close the file handle.""" + try: + self.file_handle.close() + except Exception, exc: + LOG.exception(exc) + + def __del__(self): + """Close the file handle on garbage collection.""" + self.close() + + def _build_vim_cookie_headers(self, vim_cookies): + """Build ESX host session cookie headers.""" + cookie_header = "" + for vim_cookie in vim_cookies: + cookie_header = vim_cookie.name + "=" + vim_cookie.value + break + return cookie_header + + def write(self, data): + """Write data to the file.""" + raise NotImplementedError + + def read(self, chunk_size): + """Read a chunk of data.""" + raise NotImplementedError + + def get_size(self): + """Get size of the file to be read.""" + raise NotImplementedError + + +class VMWareHTTPWriteFile(VMwareHTTPFile): + """VMWare file write handler class.""" + + def __init__(self, host, data_center_name, datastore_name, cookies, + file_path, file_size, scheme="https"): + base_url = "%s://%s/folder/%s" % (scheme, host, file_path) + param_list = {"dcPath": data_center_name, "dsName": datastore_name} + base_url = base_url + "?" + urllib.urlencode(param_list) + (scheme, netloc, path, params, query, fragment) = \ + urlparse.urlparse(base_url) + if scheme == "http": + conn = httplib.HTTPConnection(netloc) + elif scheme == "https": + conn = httplib.HTTPSConnection(netloc) + conn.putrequest("PUT", path + "?" + query) + conn.putheader("User-Agent", USER_AGENT) + conn.putheader("Content-Length", file_size) + conn.putheader("Cookie", self._build_vim_cookie_headers(cookies)) + conn.endheaders() + self.conn = conn + VMwareHTTPFile.__init__(self, conn) + + def write(self, data): + """Write to the file.""" + self.file_handle.send(data) + + def close(self): + """Get the response and close the connection.""" + try: + self.conn.getresponse() + except Exception, excep: + LOG.debug(_("Exception during HTTP connection close in " + "VMWareHTTpWrite. Exception is %s") % excep) + super(VMWareHTTPWriteFile, self).close() + + +class VmWareHTTPReadFile(VMwareHTTPFile): + """VMWare file read handler class.""" + + def __init__(self, host, data_center_name, datastore_name, cookies, + file_path, scheme="https"): + base_url = "%s://%s/folder/%s" % (scheme, host, + urllib.pathname2url(file_path)) + param_list = {"dcPath": data_center_name, "dsName": datastore_name} + base_url = base_url + "?" + urllib.urlencode(param_list) + headers = {'User-Agent': USER_AGENT, + 'Cookie': self._build_vim_cookie_headers(cookies)} + request = urllib2.Request(base_url, None, headers) + conn = urllib2.urlopen(request) + VMwareHTTPFile.__init__(self, conn) + + def read(self, chunk_size): + """Read a chunk of data.""" + # We are ignoring the chunk size passed for we want the pipe to hold + # data items of the chunk-size that Glance Client uses for read + # while writing. + return self.file_handle.read(READ_CHUNKSIZE) + + def get_size(self): + """Get size of the file to be read.""" + return self.file_handle.headers.get("Content-Length", -1) diff --git a/nova/virt/vmwareapi/vim.py b/nova/virt/vmwareapi/vim.py index e657824da..9a0647b28 100644 --- a/nova/virt/vmwareapi/vim.py +++ b/nova/virt/vmwareapi/vim.py @@ -1,181 +1,181 @@ -# 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 import flags -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' - -FLAGS = flags.FLAGS -flags.DEFINE_string('vmwareapi_wsdl_loc', - None, - 'VIM Service WSDL Location' - 'e.g http:///vimService.wsdl' - 'Due to a bug in vSphere ESX 4.1 default wsdl' - 'Refer readme-vmware to setup') - - -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. as opposed to 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 = FLAGS.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 object.__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" +# 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 import flags +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' + +FLAGS = flags.FLAGS +flags.DEFINE_string('vmwareapi_wsdl_loc', + None, + 'VIM Service WSDL Location' + 'e.g http:///vimService.wsdl' + 'Due to a bug in vSphere ESX 4.1 default wsdl' + 'Refer readme-vmware to setup') + + +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. as opposed to 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 = FLAGS.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 object.__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" diff --git a/nova/virt/vmwareapi/vim_util.py b/nova/virt/vmwareapi/vim_util.py index e03daddac..08c85d82e 100644 --- a/nova/virt/vmwareapi/vim_util.py +++ b/nova/virt/vmwareapi/vim_util.py @@ -1,223 +1,223 @@ -# 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. - -""" -The VMware API utility module. -""" - - -def build_selection_spec(client_factory, name): - """Builds the selection spec.""" - sel_spec = client_factory.create('ns0:SelectionSpec') - sel_spec.name = name - return sel_spec - - -def build_traversal_spec(client_factory, name, spec_type, path, skip, - select_set): - """Builds the traversal spec object.""" - traversal_spec = client_factory.create('ns0:TraversalSpec') - traversal_spec.name = name - traversal_spec.type = spec_type - traversal_spec.path = path - traversal_spec.skip = skip - traversal_spec.selectSet = select_set - return traversal_spec - - -def build_recursive_traversal_spec(client_factory): - """ - Builds the Recursive Traversal Spec to traverse the object managed - object hierarchy. - """ - visit_folders_select_spec = build_selection_spec(client_factory, - "visitFolders") - # For getting to hostFolder from datacenter - dc_to_hf = build_traversal_spec(client_factory, "dc_to_hf", "Datacenter", - "hostFolder", False, - [visit_folders_select_spec]) - # For getting to vmFolder from datacenter - dc_to_vmf = build_traversal_spec(client_factory, "dc_to_vmf", "Datacenter", - "vmFolder", False, - [visit_folders_select_spec]) - # For getting Host System to virtual machine - h_to_vm = build_traversal_spec(client_factory, "h_to_vm", "HostSystem", - "vm", False, - [visit_folders_select_spec]) - - # For getting to Host System from Compute Resource - cr_to_h = build_traversal_spec(client_factory, "cr_to_h", - "ComputeResource", "host", False, []) - - # For getting to datastore from Compute Resource - cr_to_ds = build_traversal_spec(client_factory, "cr_to_ds", - "ComputeResource", "datastore", False, []) - - rp_to_rp_select_spec = build_selection_spec(client_factory, "rp_to_rp") - rp_to_vm_select_spec = build_selection_spec(client_factory, "rp_to_vm") - # For getting to resource pool from Compute Resource - cr_to_rp = build_traversal_spec(client_factory, "cr_to_rp", - "ComputeResource", "resourcePool", False, - [rp_to_rp_select_spec, rp_to_vm_select_spec]) - - # For getting to child res pool from the parent res pool - rp_to_rp = build_traversal_spec(client_factory, "rp_to_rp", "ResourcePool", - "resourcePool", False, - [rp_to_rp_select_spec, rp_to_vm_select_spec]) - - # For getting to Virtual Machine from the Resource Pool - rp_to_vm = build_traversal_spec(client_factory, "rp_to_vm", "ResourcePool", - "vm", False, - [rp_to_rp_select_spec, rp_to_vm_select_spec]) - - # Get the assorted traversal spec which takes care of the objects to - # be searched for from the root folder - traversal_spec = build_traversal_spec(client_factory, "visitFolders", - "Folder", "childEntity", False, - [visit_folders_select_spec, dc_to_hf, - dc_to_vmf, cr_to_ds, cr_to_h, cr_to_rp, - rp_to_rp, h_to_vm, rp_to_vm]) - return traversal_spec - - -def build_property_spec(client_factory, type="VirtualMachine", - properties_to_collect=None, - all_properties=False): - """Builds the Property Spec.""" - if not properties_to_collect: - properties_to_collect = ["name"] - - property_spec = client_factory.create('ns0:PropertySpec') - property_spec.all = all_properties - property_spec.pathSet = properties_to_collect - property_spec.type = type - return property_spec - - -def build_object_spec(client_factory, root_folder, traversal_specs): - """Builds the object Spec.""" - object_spec = client_factory.create('ns0:ObjectSpec') - object_spec.obj = root_folder - object_spec.skip = False - object_spec.selectSet = traversal_specs - return object_spec - - -def build_property_filter_spec(client_factory, property_specs, object_specs): - """Builds the Property Filter Spec.""" - property_filter_spec = client_factory.create('ns0:PropertyFilterSpec') - property_filter_spec.propSet = property_specs - property_filter_spec.objectSet = object_specs - return property_filter_spec - - -def get_object_properties(vim, collector, mobj, type, properties): - """Gets the properties of the Managed object specified.""" - client_factory = vim.client.factory - if mobj is None: - return None - usecoll = collector - if usecoll is None: - usecoll = vim.get_service_content().propertyCollector - property_filter_spec = client_factory.create('ns0:PropertyFilterSpec') - property_spec = client_factory.create('ns0:PropertySpec') - property_spec.all = (properties is None or len(properties) == 0) - property_spec.pathSet = properties - property_spec.type = type - object_spec = client_factory.create('ns0:ObjectSpec') - object_spec.obj = mobj - object_spec.skip = False - property_filter_spec.propSet = [property_spec] - property_filter_spec.objectSet = [object_spec] - return vim.RetrieveProperties(usecoll, specSet=[property_filter_spec]) - - -def get_dynamic_property(vim, mobj, type, property_name): - """Gets a particular property of the Managed Object.""" - obj_content = \ - get_object_properties(vim, None, mobj, type, [property_name]) - property_value = None - if obj_content: - dynamic_property = obj_content[0].propSet - if dynamic_property: - property_value = dynamic_property[0].val - return property_value - - -def get_objects(vim, type, properties_to_collect=None, all=False): - """Gets the list of objects of the type specified.""" - if not properties_to_collect: - properties_to_collect = ["name"] - - client_factory = vim.client.factory - object_spec = build_object_spec(client_factory, - vim.get_service_content().rootFolder, - [build_recursive_traversal_spec(client_factory)]) - property_spec = build_property_spec(client_factory, type=type, - properties_to_collect=properties_to_collect, - all_properties=all) - property_filter_spec = build_property_filter_spec(client_factory, - [property_spec], - [object_spec]) - return vim.RetrieveProperties(vim.get_service_content().propertyCollector, - specSet=[property_filter_spec]) - - -def get_prop_spec(client_factory, spec_type, properties): - """Builds the Property Spec Object.""" - prop_spec = client_factory.create('ns0:PropertySpec') - prop_spec.type = spec_type - prop_spec.pathSet = properties - return prop_spec - - -def get_obj_spec(client_factory, obj, select_set=None): - """Builds the Object Spec object.""" - obj_spec = client_factory.create('ns0:ObjectSpec') - obj_spec.obj = obj - obj_spec.skip = False - if select_set is not None: - obj_spec.selectSet = select_set - return obj_spec - - -def get_prop_filter_spec(client_factory, obj_spec, prop_spec): - """Builds the Property Filter Spec Object.""" - prop_filter_spec = \ - client_factory.create('ns0:PropertyFilterSpec') - prop_filter_spec.propSet = prop_spec - prop_filter_spec.objectSet = obj_spec - return prop_filter_spec - - -def get_properties_for_a_collection_of_objects(vim, type, - obj_list, properties): - """ - Gets the list of properties for the collection of - objects of the type specified. - """ - client_factory = vim.client.factory - if len(obj_list) == 0: - return [] - prop_spec = get_prop_spec(client_factory, type, properties) - lst_obj_specs = [] - for obj in obj_list: - lst_obj_specs.append(get_obj_spec(client_factory, obj)) - prop_filter_spec = get_prop_filter_spec(client_factory, - lst_obj_specs, [prop_spec]) - return vim.RetrieveProperties(vim.get_service_content().propertyCollector, - specSet=[prop_filter_spec]) +# 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. + +""" +The VMware API utility module. +""" + + +def build_selection_spec(client_factory, name): + """Builds the selection spec.""" + sel_spec = client_factory.create('ns0:SelectionSpec') + sel_spec.name = name + return sel_spec + + +def build_traversal_spec(client_factory, name, spec_type, path, skip, + select_set): + """Builds the traversal spec object.""" + traversal_spec = client_factory.create('ns0:TraversalSpec') + traversal_spec.name = name + traversal_spec.type = spec_type + traversal_spec.path = path + traversal_spec.skip = skip + traversal_spec.selectSet = select_set + return traversal_spec + + +def build_recursive_traversal_spec(client_factory): + """ + Builds the Recursive Traversal Spec to traverse the object managed + object hierarchy. + """ + visit_folders_select_spec = build_selection_spec(client_factory, + "visitFolders") + # For getting to hostFolder from datacenter + dc_to_hf = build_traversal_spec(client_factory, "dc_to_hf", "Datacenter", + "hostFolder", False, + [visit_folders_select_spec]) + # For getting to vmFolder from datacenter + dc_to_vmf = build_traversal_spec(client_factory, "dc_to_vmf", "Datacenter", + "vmFolder", False, + [visit_folders_select_spec]) + # For getting Host System to virtual machine + h_to_vm = build_traversal_spec(client_factory, "h_to_vm", "HostSystem", + "vm", False, + [visit_folders_select_spec]) + + # For getting to Host System from Compute Resource + cr_to_h = build_traversal_spec(client_factory, "cr_to_h", + "ComputeResource", "host", False, []) + + # For getting to datastore from Compute Resource + cr_to_ds = build_traversal_spec(client_factory, "cr_to_ds", + "ComputeResource", "datastore", False, []) + + rp_to_rp_select_spec = build_selection_spec(client_factory, "rp_to_rp") + rp_to_vm_select_spec = build_selection_spec(client_factory, "rp_to_vm") + # For getting to resource pool from Compute Resource + cr_to_rp = build_traversal_spec(client_factory, "cr_to_rp", + "ComputeResource", "resourcePool", False, + [rp_to_rp_select_spec, rp_to_vm_select_spec]) + + # For getting to child res pool from the parent res pool + rp_to_rp = build_traversal_spec(client_factory, "rp_to_rp", "ResourcePool", + "resourcePool", False, + [rp_to_rp_select_spec, rp_to_vm_select_spec]) + + # For getting to Virtual Machine from the Resource Pool + rp_to_vm = build_traversal_spec(client_factory, "rp_to_vm", "ResourcePool", + "vm", False, + [rp_to_rp_select_spec, rp_to_vm_select_spec]) + + # Get the assorted traversal spec which takes care of the objects to + # be searched for from the root folder + traversal_spec = build_traversal_spec(client_factory, "visitFolders", + "Folder", "childEntity", False, + [visit_folders_select_spec, dc_to_hf, + dc_to_vmf, cr_to_ds, cr_to_h, cr_to_rp, + rp_to_rp, h_to_vm, rp_to_vm]) + return traversal_spec + + +def build_property_spec(client_factory, type="VirtualMachine", + properties_to_collect=None, + all_properties=False): + """Builds the Property Spec.""" + if not properties_to_collect: + properties_to_collect = ["name"] + + property_spec = client_factory.create('ns0:PropertySpec') + property_spec.all = all_properties + property_spec.pathSet = properties_to_collect + property_spec.type = type + return property_spec + + +def build_object_spec(client_factory, root_folder, traversal_specs): + """Builds the object Spec.""" + object_spec = client_factory.create('ns0:ObjectSpec') + object_spec.obj = root_folder + object_spec.skip = False + object_spec.selectSet = traversal_specs + return object_spec + + +def build_property_filter_spec(client_factory, property_specs, object_specs): + """Builds the Property Filter Spec.""" + property_filter_spec = client_factory.create('ns0:PropertyFilterSpec') + property_filter_spec.propSet = property_specs + property_filter_spec.objectSet = object_specs + return property_filter_spec + + +def get_object_properties(vim, collector, mobj, type, properties): + """Gets the properties of the Managed object specified.""" + client_factory = vim.client.factory + if mobj is None: + return None + usecoll = collector + if usecoll is None: + usecoll = vim.get_service_content().propertyCollector + property_filter_spec = client_factory.create('ns0:PropertyFilterSpec') + property_spec = client_factory.create('ns0:PropertySpec') + property_spec.all = (properties is None or len(properties) == 0) + property_spec.pathSet = properties + property_spec.type = type + object_spec = client_factory.create('ns0:ObjectSpec') + object_spec.obj = mobj + object_spec.skip = False + property_filter_spec.propSet = [property_spec] + property_filter_spec.objectSet = [object_spec] + return vim.RetrieveProperties(usecoll, specSet=[property_filter_spec]) + + +def get_dynamic_property(vim, mobj, type, property_name): + """Gets a particular property of the Managed Object.""" + obj_content = \ + get_object_properties(vim, None, mobj, type, [property_name]) + property_value = None + if obj_content: + dynamic_property = obj_content[0].propSet + if dynamic_property: + property_value = dynamic_property[0].val + return property_value + + +def get_objects(vim, type, properties_to_collect=None, all=False): + """Gets the list of objects of the type specified.""" + if not properties_to_collect: + properties_to_collect = ["name"] + + client_factory = vim.client.factory + object_spec = build_object_spec(client_factory, + vim.get_service_content().rootFolder, + [build_recursive_traversal_spec(client_factory)]) + property_spec = build_property_spec(client_factory, type=type, + properties_to_collect=properties_to_collect, + all_properties=all) + property_filter_spec = build_property_filter_spec(client_factory, + [property_spec], + [object_spec]) + return vim.RetrieveProperties(vim.get_service_content().propertyCollector, + specSet=[property_filter_spec]) + + +def get_prop_spec(client_factory, spec_type, properties): + """Builds the Property Spec Object.""" + prop_spec = client_factory.create('ns0:PropertySpec') + prop_spec.type = spec_type + prop_spec.pathSet = properties + return prop_spec + + +def get_obj_spec(client_factory, obj, select_set=None): + """Builds the Object Spec object.""" + obj_spec = client_factory.create('ns0:ObjectSpec') + obj_spec.obj = obj + obj_spec.skip = False + if select_set is not None: + obj_spec.selectSet = select_set + return obj_spec + + +def get_prop_filter_spec(client_factory, obj_spec, prop_spec): + """Builds the Property Filter Spec Object.""" + prop_filter_spec = \ + client_factory.create('ns0:PropertyFilterSpec') + prop_filter_spec.propSet = prop_spec + prop_filter_spec.objectSet = obj_spec + return prop_filter_spec + + +def get_properties_for_a_collection_of_objects(vim, type, + obj_list, properties): + """ + Gets the list of properties for the collection of + objects of the type specified. + """ + client_factory = vim.client.factory + if len(obj_list) == 0: + return [] + prop_spec = get_prop_spec(client_factory, type, properties) + lst_obj_specs = [] + for obj in obj_list: + lst_obj_specs.append(get_obj_spec(client_factory, obj)) + prop_filter_spec = get_prop_filter_spec(client_factory, + lst_obj_specs, [prop_spec]) + return vim.RetrieveProperties(vim.get_service_content().propertyCollector, + specSet=[prop_filter_spec]) diff --git a/nova/virt/vmwareapi/vm_util.py b/nova/virt/vmwareapi/vm_util.py index dd1c81196..a23b1575c 100644 --- a/nova/virt/vmwareapi/vm_util.py +++ b/nova/virt/vmwareapi/vm_util.py @@ -1,325 +1,325 @@ -# 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. -""" -The VMware API VM utility module to build SOAP object specs. -""" - - -def build_datastore_path(datastore_name, path): - """Build the datastore compliant path.""" - return "[%s] %s" % (datastore_name, path) - - -def split_datastore_path(datastore_path): - """ - Split the VMWare style datastore path to get the Datastore - name and the entity path. - """ - spl = datastore_path.split('[', 1)[1].split(']', 1) - path = "" - if len(spl) == 1: - datastore_url = spl[0] - else: - datastore_url, path = spl - return datastore_url, path.strip() - - -def get_vm_create_spec(client_factory, instance, data_store_name, - vif_infos, os_type="otherGuest"): - """Builds the VM Create spec.""" - config_spec = client_factory.create('ns0:VirtualMachineConfigSpec') - config_spec.name = instance.name - config_spec.guestId = os_type - - vm_file_info = client_factory.create('ns0:VirtualMachineFileInfo') - vm_file_info.vmPathName = "[" + data_store_name + "]" - config_spec.files = vm_file_info - - tools_info = client_factory.create('ns0:ToolsConfigInfo') - tools_info.afterPowerOn = True - tools_info.afterResume = True - tools_info.beforeGuestStandby = True - tools_info.beforeGuestShutdown = True - tools_info.beforeGuestReboot = True - - config_spec.tools = tools_info - config_spec.numCPUs = int(instance.vcpus) - config_spec.memoryMB = int(instance.memory_mb) - - vif_spec_list = [] - for vif_info in vif_infos: - vif_spec = create_network_spec(client_factory, vif_info) - vif_spec_list.append(vif_spec) - - device_config_spec = vif_spec_list - - config_spec.deviceChange = device_config_spec - return config_spec - - -def create_controller_spec(client_factory, key): - """ - Builds a Config Spec for the LSI Logic Controller's addition - which acts as the controller for the virtual hard disk to be attached - to the VM. - """ - # Create a controller for the Virtual Hard Disk - virtual_device_config = \ - client_factory.create('ns0:VirtualDeviceConfigSpec') - virtual_device_config.operation = "add" - virtual_lsi = \ - client_factory.create('ns0:VirtualLsiLogicController') - virtual_lsi.key = key - virtual_lsi.busNumber = 0 - virtual_lsi.sharedBus = "noSharing" - virtual_device_config.device = virtual_lsi - return virtual_device_config - - -def create_network_spec(client_factory, vif_info): - """ - Builds a config spec for the addition of a new network - adapter to the VM. - """ - network_spec = \ - client_factory.create('ns0:VirtualDeviceConfigSpec') - network_spec.operation = "add" - - # Get the recommended card type for the VM based on the guest OS of the VM - net_device = client_factory.create('ns0:VirtualPCNet32') - - # NOTE(asomya): Only works on ESXi if the portgroup binding is set to - # ephemeral. Invalid configuration if set to static and the NIC does - # not come up on boot if set to dynamic. - network_ref = vif_info['network_ref'] - network_name = vif_info['network_name'] - mac_address = vif_info['mac_address'] - backing = None - if (network_ref and - network_ref['type'] == "DistributedVirtualPortgroup"): - backing_name = \ - 'ns0:VirtualEthernetCardDistributedVirtualPortBackingInfo' - backing = \ - client_factory.create(backing_name) - portgroup = \ - client_factory.create('ns0:DistributedVirtualSwitchPortConnection') - portgroup.switchUuid = network_ref['dvsw'] - portgroup.portgroupKey = network_ref['dvpg'] - backing.port = portgroup - else: - backing = \ - client_factory.create('ns0:VirtualEthernetCardNetworkBackingInfo') - backing.deviceName = network_name - - connectable_spec = \ - client_factory.create('ns0:VirtualDeviceConnectInfo') - connectable_spec.startConnected = True - connectable_spec.allowGuestControl = True - connectable_spec.connected = True - - net_device.connectable = connectable_spec - net_device.backing = backing - - # The Server assigns a Key to the device. Here we pass a -ve temporary key. - # -ve because actual keys are +ve numbers and we don't - # want a clash with the key that server might associate with the device - net_device.key = -47 - net_device.addressType = "manual" - net_device.macAddress = mac_address - net_device.wakeOnLanEnabled = True - - network_spec.device = net_device - return network_spec - - -def get_vmdk_attach_config_spec(client_factory, disksize, file_path, - adapter_type="lsiLogic"): - """Builds the vmdk attach config spec.""" - config_spec = client_factory.create('ns0:VirtualMachineConfigSpec') - - # The controller Key pertains to the Key of the LSI Logic Controller, which - # controls this Hard Disk - device_config_spec = [] - # For IDE devices, there are these two default controllers created in the - # VM having keys 200 and 201 - if adapter_type == "ide": - controller_key = 200 - else: - controller_key = -101 - controller_spec = create_controller_spec(client_factory, - controller_key) - device_config_spec.append(controller_spec) - virtual_device_config_spec = create_virtual_disk_spec(client_factory, - disksize, controller_key, file_path) - - device_config_spec.append(virtual_device_config_spec) - - config_spec.deviceChange = device_config_spec - return config_spec - - -def get_vmdk_file_path_and_adapter_type(client_factory, hardware_devices): - """Gets the vmdk file path and the storage adapter type.""" - if hardware_devices.__class__.__name__ == "ArrayOfVirtualDevice": - hardware_devices = hardware_devices.VirtualDevice - vmdk_file_path = None - vmdk_controler_key = None - - adapter_type_dict = {} - for device in hardware_devices: - if device.__class__.__name__ == "VirtualDisk" and \ - device.backing.__class__.__name__ \ - == "VirtualDiskFlatVer2BackingInfo": - vmdk_file_path = device.backing.fileName - vmdk_controler_key = device.controllerKey - elif device.__class__.__name__ == "VirtualLsiLogicController": - adapter_type_dict[device.key] = "lsiLogic" - elif device.__class__.__name__ == "VirtualBusLogicController": - adapter_type_dict[device.key] = "busLogic" - elif device.__class__.__name__ == "VirtualIDEController": - adapter_type_dict[device.key] = "ide" - elif device.__class__.__name__ == "VirtualLsiLogicSASController": - adapter_type_dict[device.key] = "lsiLogic" - - adapter_type = adapter_type_dict.get(vmdk_controler_key, "") - - return vmdk_file_path, adapter_type - - -def get_copy_virtual_disk_spec(client_factory, adapter_type="lsilogic"): - """Builds the Virtual Disk copy spec.""" - dest_spec = client_factory.create('ns0:VirtualDiskSpec') - dest_spec.adapterType = adapter_type - dest_spec.diskType = "thick" - return dest_spec - - -def get_vmdk_create_spec(client_factory, size_in_kb, adapter_type="lsiLogic"): - """Builds the virtual disk create spec.""" - create_vmdk_spec = \ - client_factory.create('ns0:FileBackedVirtualDiskSpec') - create_vmdk_spec.adapterType = adapter_type - create_vmdk_spec.diskType = "thick" - create_vmdk_spec.capacityKb = size_in_kb - return create_vmdk_spec - - -def create_virtual_disk_spec(client_factory, disksize, controller_key, - file_path=None): - """ - Builds spec for the creation of a new/ attaching of an already existing - Virtual Disk to the VM. - """ - virtual_device_config = \ - client_factory.create('ns0:VirtualDeviceConfigSpec') - virtual_device_config.operation = "add" - if file_path is None: - virtual_device_config.fileOperation = "create" - - virtual_disk = client_factory.create('ns0:VirtualDisk') - - disk_file_backing = \ - client_factory.create('ns0:VirtualDiskFlatVer2BackingInfo') - disk_file_backing.diskMode = "persistent" - disk_file_backing.thinProvisioned = False - if file_path is not None: - disk_file_backing.fileName = file_path - else: - disk_file_backing.fileName = "" - - connectable_spec = client_factory.create('ns0:VirtualDeviceConnectInfo') - connectable_spec.startConnected = True - connectable_spec.allowGuestControl = False - connectable_spec.connected = True - - virtual_disk.backing = disk_file_backing - virtual_disk.connectable = connectable_spec - - # The Server assigns a Key to the device. Here we pass a -ve random key. - # -ve because actual keys are +ve numbers and we don't - # want a clash with the key that server might associate with the device - virtual_disk.key = -100 - virtual_disk.controllerKey = controller_key - virtual_disk.unitNumber = 0 - virtual_disk.capacityInKB = disksize - - virtual_device_config.device = virtual_disk - - return virtual_device_config - - -def get_dummy_vm_create_spec(client_factory, name, data_store_name): - """Builds the dummy VM create spec.""" - config_spec = client_factory.create('ns0:VirtualMachineConfigSpec') - - config_spec.name = name - config_spec.guestId = "otherGuest" - - vm_file_info = client_factory.create('ns0:VirtualMachineFileInfo') - vm_file_info.vmPathName = "[" + data_store_name + "]" - config_spec.files = vm_file_info - - tools_info = client_factory.create('ns0:ToolsConfigInfo') - tools_info.afterPowerOn = True - tools_info.afterResume = True - tools_info.beforeGuestStandby = True - tools_info.beforeGuestShutdown = True - tools_info.beforeGuestReboot = True - - config_spec.tools = tools_info - config_spec.numCPUs = 1 - config_spec.memoryMB = 4 - - controller_key = -101 - controller_spec = create_controller_spec(client_factory, controller_key) - disk_spec = create_virtual_disk_spec(client_factory, 1024, controller_key) - - device_config_spec = [controller_spec, disk_spec] - - config_spec.deviceChange = device_config_spec - return config_spec - - -def get_machine_id_change_spec(client_factory, machine_id_str): - """Builds the machine id change config spec.""" - virtual_machine_config_spec = \ - client_factory.create('ns0:VirtualMachineConfigSpec') - - opt = client_factory.create('ns0:OptionValue') - opt.key = "machine.id" - opt.value = machine_id_str - virtual_machine_config_spec.extraConfig = [opt] - return virtual_machine_config_spec - - -def get_add_vswitch_port_group_spec(client_factory, vswitch_name, - port_group_name, vlan_id): - """Builds the virtual switch port group add spec.""" - vswitch_port_group_spec = client_factory.create('ns0:HostPortGroupSpec') - vswitch_port_group_spec.name = port_group_name - vswitch_port_group_spec.vswitchName = vswitch_name - - # VLAN ID of 0 means that VLAN tagging is not to be done for the network. - vswitch_port_group_spec.vlanId = int(vlan_id) - - policy = client_factory.create('ns0:HostNetworkPolicy') - nicteaming = client_factory.create('ns0:HostNicTeamingPolicy') - nicteaming.notifySwitches = True - policy.nicTeaming = nicteaming - - vswitch_port_group_spec.policy = policy - return vswitch_port_group_spec +# 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. +""" +The VMware API VM utility module to build SOAP object specs. +""" + + +def build_datastore_path(datastore_name, path): + """Build the datastore compliant path.""" + return "[%s] %s" % (datastore_name, path) + + +def split_datastore_path(datastore_path): + """ + Split the VMWare style datastore path to get the Datastore + name and the entity path. + """ + spl = datastore_path.split('[', 1)[1].split(']', 1) + path = "" + if len(spl) == 1: + datastore_url = spl[0] + else: + datastore_url, path = spl + return datastore_url, path.strip() + + +def get_vm_create_spec(client_factory, instance, data_store_name, + vif_infos, os_type="otherGuest"): + """Builds the VM Create spec.""" + config_spec = client_factory.create('ns0:VirtualMachineConfigSpec') + config_spec.name = instance.name + config_spec.guestId = os_type + + vm_file_info = client_factory.create('ns0:VirtualMachineFileInfo') + vm_file_info.vmPathName = "[" + data_store_name + "]" + config_spec.files = vm_file_info + + tools_info = client_factory.create('ns0:ToolsConfigInfo') + tools_info.afterPowerOn = True + tools_info.afterResume = True + tools_info.beforeGuestStandby = True + tools_info.beforeGuestShutdown = True + tools_info.beforeGuestReboot = True + + config_spec.tools = tools_info + config_spec.numCPUs = int(instance.vcpus) + config_spec.memoryMB = int(instance.memory_mb) + + vif_spec_list = [] + for vif_info in vif_infos: + vif_spec = create_network_spec(client_factory, vif_info) + vif_spec_list.append(vif_spec) + + device_config_spec = vif_spec_list + + config_spec.deviceChange = device_config_spec + return config_spec + + +def create_controller_spec(client_factory, key): + """ + Builds a Config Spec for the LSI Logic Controller's addition + which acts as the controller for the virtual hard disk to be attached + to the VM. + """ + # Create a controller for the Virtual Hard Disk + virtual_device_config = \ + client_factory.create('ns0:VirtualDeviceConfigSpec') + virtual_device_config.operation = "add" + virtual_lsi = \ + client_factory.create('ns0:VirtualLsiLogicController') + virtual_lsi.key = key + virtual_lsi.busNumber = 0 + virtual_lsi.sharedBus = "noSharing" + virtual_device_config.device = virtual_lsi + return virtual_device_config + + +def create_network_spec(client_factory, vif_info): + """ + Builds a config spec for the addition of a new network + adapter to the VM. + """ + network_spec = \ + client_factory.create('ns0:VirtualDeviceConfigSpec') + network_spec.operation = "add" + + # Get the recommended card type for the VM based on the guest OS of the VM + net_device = client_factory.create('ns0:VirtualPCNet32') + + # NOTE(asomya): Only works on ESXi if the portgroup binding is set to + # ephemeral. Invalid configuration if set to static and the NIC does + # not come up on boot if set to dynamic. + network_ref = vif_info['network_ref'] + network_name = vif_info['network_name'] + mac_address = vif_info['mac_address'] + backing = None + if (network_ref and + network_ref['type'] == "DistributedVirtualPortgroup"): + backing_name = \ + 'ns0:VirtualEthernetCardDistributedVirtualPortBackingInfo' + backing = \ + client_factory.create(backing_name) + portgroup = \ + client_factory.create('ns0:DistributedVirtualSwitchPortConnection') + portgroup.switchUuid = network_ref['dvsw'] + portgroup.portgroupKey = network_ref['dvpg'] + backing.port = portgroup + else: + backing = \ + client_factory.create('ns0:VirtualEthernetCardNetworkBackingInfo') + backing.deviceName = network_name + + connectable_spec = \ + client_factory.create('ns0:VirtualDeviceConnectInfo') + connectable_spec.startConnected = True + connectable_spec.allowGuestControl = True + connectable_spec.connected = True + + net_device.connectable = connectable_spec + net_device.backing = backing + + # The Server assigns a Key to the device. Here we pass a -ve temporary key. + # -ve because actual keys are +ve numbers and we don't + # want a clash with the key that server might associate with the device + net_device.key = -47 + net_device.addressType = "manual" + net_device.macAddress = mac_address + net_device.wakeOnLanEnabled = True + + network_spec.device = net_device + return network_spec + + +def get_vmdk_attach_config_spec(client_factory, disksize, file_path, + adapter_type="lsiLogic"): + """Builds the vmdk attach config spec.""" + config_spec = client_factory.create('ns0:VirtualMachineConfigSpec') + + # The controller Key pertains to the Key of the LSI Logic Controller, which + # controls this Hard Disk + device_config_spec = [] + # For IDE devices, there are these two default controllers created in the + # VM having keys 200 and 201 + if adapter_type == "ide": + controller_key = 200 + else: + controller_key = -101 + controller_spec = create_controller_spec(client_factory, + controller_key) + device_config_spec.append(controller_spec) + virtual_device_config_spec = create_virtual_disk_spec(client_factory, + disksize, controller_key, file_path) + + device_config_spec.append(virtual_device_config_spec) + + config_spec.deviceChange = device_config_spec + return config_spec + + +def get_vmdk_file_path_and_adapter_type(client_factory, hardware_devices): + """Gets the vmdk file path and the storage adapter type.""" + if hardware_devices.__class__.__name__ == "ArrayOfVirtualDevice": + hardware_devices = hardware_devices.VirtualDevice + vmdk_file_path = None + vmdk_controler_key = None + + adapter_type_dict = {} + for device in hardware_devices: + if device.__class__.__name__ == "VirtualDisk" and \ + device.backing.__class__.__name__ \ + == "VirtualDiskFlatVer2BackingInfo": + vmdk_file_path = device.backing.fileName + vmdk_controler_key = device.controllerKey + elif device.__class__.__name__ == "VirtualLsiLogicController": + adapter_type_dict[device.key] = "lsiLogic" + elif device.__class__.__name__ == "VirtualBusLogicController": + adapter_type_dict[device.key] = "busLogic" + elif device.__class__.__name__ == "VirtualIDEController": + adapter_type_dict[device.key] = "ide" + elif device.__class__.__name__ == "VirtualLsiLogicSASController": + adapter_type_dict[device.key] = "lsiLogic" + + adapter_type = adapter_type_dict.get(vmdk_controler_key, "") + + return vmdk_file_path, adapter_type + + +def get_copy_virtual_disk_spec(client_factory, adapter_type="lsilogic"): + """Builds the Virtual Disk copy spec.""" + dest_spec = client_factory.create('ns0:VirtualDiskSpec') + dest_spec.adapterType = adapter_type + dest_spec.diskType = "thick" + return dest_spec + + +def get_vmdk_create_spec(client_factory, size_in_kb, adapter_type="lsiLogic"): + """Builds the virtual disk create spec.""" + create_vmdk_spec = \ + client_factory.create('ns0:FileBackedVirtualDiskSpec') + create_vmdk_spec.adapterType = adapter_type + create_vmdk_spec.diskType = "thick" + create_vmdk_spec.capacityKb = size_in_kb + return create_vmdk_spec + + +def create_virtual_disk_spec(client_factory, disksize, controller_key, + file_path=None): + """ + Builds spec for the creation of a new/ attaching of an already existing + Virtual Disk to the VM. + """ + virtual_device_config = \ + client_factory.create('ns0:VirtualDeviceConfigSpec') + virtual_device_config.operation = "add" + if file_path is None: + virtual_device_config.fileOperation = "create" + + virtual_disk = client_factory.create('ns0:VirtualDisk') + + disk_file_backing = \ + client_factory.create('ns0:VirtualDiskFlatVer2BackingInfo') + disk_file_backing.diskMode = "persistent" + disk_file_backing.thinProvisioned = False + if file_path is not None: + disk_file_backing.fileName = file_path + else: + disk_file_backing.fileName = "" + + connectable_spec = client_factory.create('ns0:VirtualDeviceConnectInfo') + connectable_spec.startConnected = True + connectable_spec.allowGuestControl = False + connectable_spec.connected = True + + virtual_disk.backing = disk_file_backing + virtual_disk.connectable = connectable_spec + + # The Server assigns a Key to the device. Here we pass a -ve random key. + # -ve because actual keys are +ve numbers and we don't + # want a clash with the key that server might associate with the device + virtual_disk.key = -100 + virtual_disk.controllerKey = controller_key + virtual_disk.unitNumber = 0 + virtual_disk.capacityInKB = disksize + + virtual_device_config.device = virtual_disk + + return virtual_device_config + + +def get_dummy_vm_create_spec(client_factory, name, data_store_name): + """Builds the dummy VM create spec.""" + config_spec = client_factory.create('ns0:VirtualMachineConfigSpec') + + config_spec.name = name + config_spec.guestId = "otherGuest" + + vm_file_info = client_factory.create('ns0:VirtualMachineFileInfo') + vm_file_info.vmPathName = "[" + data_store_name + "]" + config_spec.files = vm_file_info + + tools_info = client_factory.create('ns0:ToolsConfigInfo') + tools_info.afterPowerOn = True + tools_info.afterResume = True + tools_info.beforeGuestStandby = True + tools_info.beforeGuestShutdown = True + tools_info.beforeGuestReboot = True + + config_spec.tools = tools_info + config_spec.numCPUs = 1 + config_spec.memoryMB = 4 + + controller_key = -101 + controller_spec = create_controller_spec(client_factory, controller_key) + disk_spec = create_virtual_disk_spec(client_factory, 1024, controller_key) + + device_config_spec = [controller_spec, disk_spec] + + config_spec.deviceChange = device_config_spec + return config_spec + + +def get_machine_id_change_spec(client_factory, machine_id_str): + """Builds the machine id change config spec.""" + virtual_machine_config_spec = \ + client_factory.create('ns0:VirtualMachineConfigSpec') + + opt = client_factory.create('ns0:OptionValue') + opt.key = "machine.id" + opt.value = machine_id_str + virtual_machine_config_spec.extraConfig = [opt] + return virtual_machine_config_spec + + +def get_add_vswitch_port_group_spec(client_factory, vswitch_name, + port_group_name, vlan_id): + """Builds the virtual switch port group add spec.""" + vswitch_port_group_spec = client_factory.create('ns0:HostPortGroupSpec') + vswitch_port_group_spec.name = port_group_name + vswitch_port_group_spec.vswitchName = vswitch_name + + # VLAN ID of 0 means that VLAN tagging is not to be done for the network. + vswitch_port_group_spec.vlanId = int(vlan_id) + + policy = client_factory.create('ns0:HostNetworkPolicy') + nicteaming = client_factory.create('ns0:HostNicTeamingPolicy') + nicteaming.notifySwitches = True + policy.nicTeaming = nicteaming + + vswitch_port_group_spec.policy = policy + return vswitch_port_group_spec diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index 1a2d1dc19..780caa65b 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -1,824 +1,824 @@ -# 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. - -""" -Class for VM tasks like spawn, snapshot, suspend, resume etc. -""" - -import base64 -import os -import time -import urllib -import urllib2 -import uuid - -from nova.compute import power_state -from nova import exception -from nova import flags -from nova import log as logging -from nova import utils -from nova.virt.vmwareapi import vim_util -from nova.virt.vmwareapi import vm_util -from nova.virt.vmwareapi import vmware_images -from nova.virt.vmwareapi import network_utils - -FLAGS = flags.FLAGS -flags.DEFINE_string('vmware_vif_driver', - 'nova.virt.vmwareapi.vif.VMWareVlanBridgeDriver', - 'The VMWare VIF driver to configure the VIFs.') - -LOG = logging.getLogger("nova.virt.vmwareapi.vmops") - -VMWARE_POWER_STATES = { - 'poweredOff': power_state.SHUTDOWN, - 'poweredOn': power_state.RUNNING, - 'suspended': power_state.PAUSED} - - -class VMWareVMOps(object): - """Management class for VM-related tasks.""" - - def __init__(self, session): - """Initializer.""" - self._session = session - self._vif_driver = utils.import_object(FLAGS.vmware_vif_driver) - - def list_instances(self): - """Lists the VM instances that are registered with the ESX host.""" - LOG.debug(_("Getting list of instances")) - vms = self._session._call_method(vim_util, "get_objects", - "VirtualMachine", - ["name", "runtime.connectionState"]) - lst_vm_names = [] - for vm in vms: - vm_name = None - conn_state = None - for prop in vm.propSet: - if prop.name == "name": - vm_name = prop.val - elif prop.name == "runtime.connectionState": - conn_state = prop.val - # Ignoring the oprhaned or inaccessible VMs - if conn_state not in ["orphaned", "inaccessible"]: - lst_vm_names.append(vm_name) - LOG.debug(_("Got total of %s instances") % str(len(lst_vm_names))) - return lst_vm_names - - def spawn(self, context, instance, image_meta, network_info): - """ - Creates a VM instance. - - Steps followed are: - 1. Create a VM with no disk and the specifics in the instance object - like RAM size. - 2. Create a dummy vmdk of the size of the disk file that is to be - uploaded. This is required just to create the metadata file. - 3. Delete the -flat.vmdk file created in the above step and retain - the metadata .vmdk file. - 4. Upload the disk file. - 5. Attach the disk to the VM by reconfiguring the same. - 6. Power on the VM. - """ - vm_ref = self._get_vm_ref_from_the_name(instance.name) - if vm_ref: - raise exception.InstanceExists(name=instance.name) - - client_factory = self._session._get_vim().client.factory - service_content = self._session._get_vim().get_service_content() - - def _get_datastore_ref(): - """Get the datastore list and choose the first local storage.""" - data_stores = self._session._call_method(vim_util, "get_objects", - "Datastore", ["summary.type", "summary.name"]) - for elem in data_stores: - ds_name = None - ds_type = None - for prop in elem.propSet: - if prop.name == "summary.type": - ds_type = prop.val - elif prop.name == "summary.name": - ds_name = prop.val - # Local storage identifier - if ds_type == "VMFS": - data_store_name = ds_name - return data_store_name - - if data_store_name is None: - msg = _("Couldn't get a local Datastore reference") - LOG.exception(msg) - raise exception.Error(msg) - - data_store_name = _get_datastore_ref() - - def _get_image_properties(): - """ - Get the Size of the flat vmdk file that is there on the storage - repository. - """ - image_size, image_properties = \ - vmware_images.get_vmdk_size_and_properties(context, - instance.image_ref, instance) - vmdk_file_size_in_kb = int(image_size) / 1024 - os_type = image_properties.get("vmware_ostype", "otherGuest") - adapter_type = image_properties.get("vmware_adaptertype", - "lsiLogic") - return vmdk_file_size_in_kb, os_type, adapter_type - - vmdk_file_size_in_kb, os_type, adapter_type = _get_image_properties() - - def _get_vmfolder_and_res_pool_mors(): - """Get the Vm folder ref from the datacenter.""" - dc_objs = self._session._call_method(vim_util, "get_objects", - "Datacenter", ["vmFolder"]) - # There is only one default datacenter in a standalone ESX host - vm_folder_mor = dc_objs[0].propSet[0].val - - # Get the resource pool. Taking the first resource pool coming our - # way. Assuming that is the default resource pool. - res_pool_mor = self._session._call_method(vim_util, "get_objects", - "ResourcePool")[0].obj - return vm_folder_mor, res_pool_mor - - vm_folder_mor, res_pool_mor = _get_vmfolder_and_res_pool_mors() - - def _check_if_network_bridge_exists(network_name): - network_ref = \ - network_utils.get_network_with_the_name(self._session, - network_name) - if network_ref is None: - raise exception.NetworkNotFoundForBridge(bridge=network_name) - return network_ref - - def _get_vif_infos(): - vif_infos = [] - for (network, mapping) in network_info: - mac_address = mapping['mac'] - network_name = network['bridge'] - if mapping.get('should_create_vlan'): - network_ref = self._vif_driver.ensure_vlan_bridge( - self._session, network) - else: - network_ref = _check_if_network_bridge_exists(network_name) - vif_infos.append({'network_name': network_name, - 'mac_address': mac_address, - 'network_ref': network_ref, - }) - return vif_infos - - vif_infos = _get_vif_infos() - - # Get the create vm config spec - config_spec = vm_util.get_vm_create_spec( - client_factory, instance, - data_store_name, vif_infos, os_type) - - def _execute_create_vm(): - """Create VM on ESX host.""" - LOG.debug(_("Creating VM with the name %s on the ESX host") % - instance.name) - # Create the VM on the ESX host - vm_create_task = self._session._call_method( - self._session._get_vim(), - "CreateVM_Task", vm_folder_mor, - config=config_spec, pool=res_pool_mor) - self._session._wait_for_task(instance['uuid'], vm_create_task) - - LOG.debug(_("Created VM with the name %s on the ESX host") % - instance.name) - - _execute_create_vm() - - # Set the machine.id parameter of the instance to inject - # the NIC configuration inside the VM - if FLAGS.flat_injected: - self._set_machine_id(client_factory, instance, network_info) - - # Naming the VM files in correspondence with the VM instance name - # The flat vmdk file name - flat_uploaded_vmdk_name = "%s/%s-flat.vmdk" % (instance.name, - instance.name) - # The vmdk meta-data file - uploaded_vmdk_name = "%s/%s.vmdk" % (instance.name, instance.name) - flat_uploaded_vmdk_path = vm_util.build_datastore_path(data_store_name, - flat_uploaded_vmdk_name) - uploaded_vmdk_path = vm_util.build_datastore_path(data_store_name, - uploaded_vmdk_name) - - def _create_virtual_disk(): - """Create a virtual disk of the size of flat vmdk file.""" - # Create a Virtual Disk of the size of the flat vmdk file. This is - # done just to generate the meta-data file whose specifics - # depend on the size of the disk, thin/thick provisioning and the - # storage adapter type. - # Here we assume thick provisioning and lsiLogic for the adapter - # type - LOG.debug(_("Creating Virtual Disk of size " - "%(vmdk_file_size_in_kb)s KB and adapter type " - "%(adapter_type)s on the ESX host local store" - " %(data_store_name)s") % - {"vmdk_file_size_in_kb": vmdk_file_size_in_kb, - "adapter_type": adapter_type, - "data_store_name": data_store_name}) - vmdk_create_spec = vm_util.get_vmdk_create_spec(client_factory, - vmdk_file_size_in_kb, adapter_type) - vmdk_create_task = self._session._call_method( - self._session._get_vim(), - "CreateVirtualDisk_Task", - service_content.virtualDiskManager, - name=uploaded_vmdk_path, - datacenter=self._get_datacenter_name_and_ref()[0], - spec=vmdk_create_spec) - self._session._wait_for_task(instance['uuid'], vmdk_create_task) - LOG.debug(_("Created Virtual Disk of size %(vmdk_file_size_in_kb)s" - " KB on the ESX host local store " - "%(data_store_name)s") % - {"vmdk_file_size_in_kb": vmdk_file_size_in_kb, - "data_store_name": data_store_name}) - - _create_virtual_disk() - - def _delete_disk_file(): - LOG.debug(_("Deleting the file %(flat_uploaded_vmdk_path)s " - "on the ESX host local" - "store %(data_store_name)s") % - {"flat_uploaded_vmdk_path": flat_uploaded_vmdk_path, - "data_store_name": data_store_name}) - # Delete the -flat.vmdk file created. .vmdk file is retained. - vmdk_delete_task = self._session._call_method( - self._session._get_vim(), - "DeleteDatastoreFile_Task", - service_content.fileManager, - name=flat_uploaded_vmdk_path) - self._session._wait_for_task(instance['uuid'], vmdk_delete_task) - LOG.debug(_("Deleted the file %(flat_uploaded_vmdk_path)s on the " - "ESX host local store %(data_store_name)s") % - {"flat_uploaded_vmdk_path": flat_uploaded_vmdk_path, - "data_store_name": data_store_name}) - - _delete_disk_file() - - cookies = self._session._get_vim().client.options.transport.cookiejar - - def _fetch_image_on_esx_datastore(): - """Fetch image from Glance to ESX datastore.""" - LOG.debug(_("Downloading image file data %(image_ref)s to the ESX " - "data store %(data_store_name)s") % - ({'image_ref': instance.image_ref, - 'data_store_name': data_store_name})) - # Upload the -flat.vmdk file whose meta-data file we just created - # above - vmware_images.fetch_image( - context, - instance.image_ref, - instance, - host=self._session._host_ip, - data_center_name=self._get_datacenter_name_and_ref()[1], - datastore_name=data_store_name, - cookies=cookies, - file_path=flat_uploaded_vmdk_name) - LOG.debug(_("Downloaded image file data %(image_ref)s to the ESX " - "data store %(data_store_name)s") % - ({'image_ref': instance.image_ref, - 'data_store_name': data_store_name})) - _fetch_image_on_esx_datastore() - - vm_ref = self._get_vm_ref_from_the_name(instance.name) - - def _attach_vmdk_to_the_vm(): - """ - Attach the vmdk uploaded to the VM. VM reconfigure is done - to do so. - """ - vmdk_attach_config_spec = vm_util.get_vmdk_attach_config_spec( - client_factory, - vmdk_file_size_in_kb, uploaded_vmdk_path, - adapter_type) - LOG.debug(_("Reconfiguring VM instance %s to attach the image " - "disk") % instance.name) - reconfig_task = self._session._call_method( - self._session._get_vim(), - "ReconfigVM_Task", vm_ref, - spec=vmdk_attach_config_spec) - self._session._wait_for_task(instance['uuid'], reconfig_task) - LOG.debug(_("Reconfigured VM instance %s to attach the image " - "disk") % instance.name) - - _attach_vmdk_to_the_vm() - - def _power_on_vm(): - """Power on the VM.""" - LOG.debug(_("Powering on the VM instance %s") % instance.name) - # Power On the VM - power_on_task = self._session._call_method( - self._session._get_vim(), - "PowerOnVM_Task", vm_ref) - self._session._wait_for_task(instance['uuid'], power_on_task) - LOG.debug(_("Powered on the VM instance %s") % instance.name) - _power_on_vm() - - def snapshot(self, context, instance, snapshot_name): - """ - Create snapshot from a running VM instance. - Steps followed are: - 1. Get the name of the vmdk file which the VM points to right now. - Can be a chain of snapshots, so we need to know the last in the - chain. - 2. Create the snapshot. A new vmdk is created which the VM points to - now. The earlier vmdk becomes read-only. - 3. Call CopyVirtualDisk which coalesces the disk chain to form a single - vmdk, rather a .vmdk metadata file and a -flat.vmdk disk data file. - 4. Now upload the -flat.vmdk file to the image store. - 5. Delete the coalesced .vmdk and -flat.vmdk created. - """ - vm_ref = self._get_vm_ref_from_the_name(instance.name) - if vm_ref is None: - raise exception.InstanceNotFound(instance_id=instance.id) - - client_factory = self._session._get_vim().client.factory - service_content = self._session._get_vim().get_service_content() - - def _get_vm_and_vmdk_attribs(): - # Get the vmdk file name that the VM is pointing to - hardware_devices = self._session._call_method(vim_util, - "get_dynamic_property", vm_ref, - "VirtualMachine", "config.hardware.device") - vmdk_file_path_before_snapshot, adapter_type = \ - vm_util.get_vmdk_file_path_and_adapter_type(client_factory, - hardware_devices) - datastore_name = vm_util.split_datastore_path( - vmdk_file_path_before_snapshot)[0] - os_type = self._session._call_method(vim_util, - "get_dynamic_property", vm_ref, - "VirtualMachine", "summary.config.guestId") - return (vmdk_file_path_before_snapshot, adapter_type, - datastore_name, os_type) - - vmdk_file_path_before_snapshot, adapter_type, datastore_name,\ - os_type = _get_vm_and_vmdk_attribs() - - def _create_vm_snapshot(): - # Create a snapshot of the VM - LOG.debug(_("Creating Snapshot of the VM instance %s ") % - instance.name) - snapshot_task = self._session._call_method( - self._session._get_vim(), - "CreateSnapshot_Task", vm_ref, - name="%s-snapshot" % instance.name, - description="Taking Snapshot of the VM", - memory=True, - quiesce=True) - self._session._wait_for_task(instance['uuid'], snapshot_task) - LOG.debug(_("Created Snapshot of the VM instance %s ") % - instance.name) - - _create_vm_snapshot() - - def _check_if_tmp_folder_exists(): - # Copy the contents of the VM that were there just before the - # snapshot was taken - ds_ref_ret = vim_util.get_dynamic_property( - self._session._get_vim(), - vm_ref, - "VirtualMachine", - "datastore") - if not ds_ref_ret: - raise exception.DatastoreNotFound() - ds_ref = ds_ref_ret.ManagedObjectReference[0] - ds_browser = vim_util.get_dynamic_property( - self._session._get_vim(), - ds_ref, - "Datastore", - "browser") - # Check if the vmware-tmp folder exists or not. If not, create one - tmp_folder_path = vm_util.build_datastore_path(datastore_name, - "vmware-tmp") - if not self._path_exists(ds_browser, tmp_folder_path): - self._mkdir(vm_util.build_datastore_path(datastore_name, - "vmware-tmp")) - - _check_if_tmp_folder_exists() - - # Generate a random vmdk file name to which the coalesced vmdk content - # will be copied to. A random name is chosen so that we don't have - # name clashes. - random_name = str(uuid.uuid4()) - dest_vmdk_file_location = vm_util.build_datastore_path(datastore_name, - "vmware-tmp/%s.vmdk" % random_name) - dc_ref = self._get_datacenter_name_and_ref()[0] - - def _copy_vmdk_content(): - # Copy the contents of the disk ( or disks, if there were snapshots - # done earlier) to a temporary vmdk file. - copy_spec = vm_util.get_copy_virtual_disk_spec(client_factory, - adapter_type) - LOG.debug(_("Copying disk data before snapshot of the VM " - " instance %s") % instance.name) - copy_disk_task = self._session._call_method( - self._session._get_vim(), - "CopyVirtualDisk_Task", - service_content.virtualDiskManager, - sourceName=vmdk_file_path_before_snapshot, - sourceDatacenter=dc_ref, - destName=dest_vmdk_file_location, - destDatacenter=dc_ref, - destSpec=copy_spec, - force=False) - self._session._wait_for_task(instance['uuid'], copy_disk_task) - LOG.debug(_("Copied disk data before snapshot of the VM " - "instance %s") % instance.name) - - _copy_vmdk_content() - - cookies = self._session._get_vim().client.options.transport.cookiejar - - def _upload_vmdk_to_image_repository(): - # Upload the contents of -flat.vmdk file which has the disk data. - LOG.debug(_("Uploading image %s") % snapshot_name) - vmware_images.upload_image( - context, - snapshot_name, - instance, - os_type=os_type, - adapter_type=adapter_type, - image_version=1, - host=self._session._host_ip, - data_center_name=self._get_datacenter_name_and_ref()[1], - datastore_name=datastore_name, - cookies=cookies, - file_path="vmware-tmp/%s-flat.vmdk" % random_name) - LOG.debug(_("Uploaded image %s") % snapshot_name) - - _upload_vmdk_to_image_repository() - - def _clean_temp_data(): - """ - Delete temporary vmdk files generated in image handling - operations. - """ - # Delete the temporary vmdk created above. - LOG.debug(_("Deleting temporary vmdk file %s") - % dest_vmdk_file_location) - remove_disk_task = self._session._call_method( - self._session._get_vim(), - "DeleteVirtualDisk_Task", - service_content.virtualDiskManager, - name=dest_vmdk_file_location, - datacenter=dc_ref) - self._session._wait_for_task(instance['uuid'], remove_disk_task) - LOG.debug(_("Deleted temporary vmdk file %s") - % dest_vmdk_file_location) - - _clean_temp_data() - - def reboot(self, instance, network_info): - """Reboot a VM instance.""" - vm_ref = self._get_vm_ref_from_the_name(instance.name) - if vm_ref is None: - raise exception.InstanceNotFound(instance_id=instance.id) - - self.plug_vifs(instance, network_info) - - lst_properties = ["summary.guest.toolsStatus", "runtime.powerState", - "summary.guest.toolsRunningStatus"] - props = self._session._call_method(vim_util, "get_object_properties", - None, vm_ref, "VirtualMachine", - lst_properties) - pwr_state = None - tools_status = None - tools_running_status = False - for elem in props: - for prop in elem.propSet: - if prop.name == "runtime.powerState": - pwr_state = prop.val - elif prop.name == "summary.guest.toolsStatus": - tools_status = prop.val - elif prop.name == "summary.guest.toolsRunningStatus": - tools_running_status = prop.val - - # Raise an exception if the VM is not powered On. - if pwr_state not in ["poweredOn"]: - reason = _("instance is not powered on") - raise exception.InstanceRebootFailure(reason=reason) - - # If latest vmware tools are installed in the VM, and that the tools - # are running, then only do a guest reboot. Otherwise do a hard reset. - if (tools_status == "toolsOk" and - tools_running_status == "guestToolsRunning"): - LOG.debug(_("Rebooting guest OS of VM %s") % instance.name) - self._session._call_method(self._session._get_vim(), "RebootGuest", - vm_ref) - LOG.debug(_("Rebooted guest OS of VM %s") % instance.name) - else: - LOG.debug(_("Doing hard reboot of VM %s") % instance.name) - reset_task = self._session._call_method(self._session._get_vim(), - "ResetVM_Task", vm_ref) - self._session._wait_for_task(instance['uuid'], reset_task) - LOG.debug(_("Did hard reboot of VM %s") % instance.name) - - def destroy(self, instance, network_info): - """ - Destroy a VM instance. Steps followed are: - 1. Power off the VM, if it is in poweredOn state. - 2. Un-register a VM. - 3. Delete the contents of the folder holding the VM related data. - """ - try: - vm_ref = self._get_vm_ref_from_the_name(instance.name) - if vm_ref is None: - LOG.debug(_("instance - %s not present") % instance.name) - return - lst_properties = ["config.files.vmPathName", "runtime.powerState"] - props = self._session._call_method(vim_util, - "get_object_properties", - None, vm_ref, "VirtualMachine", lst_properties) - pwr_state = None - for elem in props: - vm_config_pathname = None - for prop in elem.propSet: - if prop.name == "runtime.powerState": - pwr_state = prop.val - elif prop.name == "config.files.vmPathName": - vm_config_pathname = prop.val - if vm_config_pathname: - datastore_name, vmx_file_path = \ - vm_util.split_datastore_path(vm_config_pathname) - # Power off the VM if it is in PoweredOn state. - if pwr_state == "poweredOn": - LOG.debug(_("Powering off the VM %s") % instance.name) - poweroff_task = self._session._call_method( - self._session._get_vim(), - "PowerOffVM_Task", vm_ref) - self._session._wait_for_task(instance['uuid'], poweroff_task) - LOG.debug(_("Powered off the VM %s") % instance.name) - - # Un-register the VM - try: - LOG.debug(_("Unregistering the VM %s") % instance.name) - self._session._call_method(self._session._get_vim(), - "UnregisterVM", vm_ref) - LOG.debug(_("Unregistered the VM %s") % instance.name) - except Exception, excep: - LOG.warn(_("In vmwareapi:vmops:destroy, got this exception" - " while un-registering the VM: %s") % str(excep)) - - self._unplug_vifs(instance, network_info) - - # Delete the folder holding the VM related content on - # the datastore. - try: - dir_ds_compliant_path = vm_util.build_datastore_path( - datastore_name, - os.path.dirname(vmx_file_path)) - LOG.debug(_("Deleting contents of the VM %(name)s from " - "datastore %(datastore_name)s") % - ({'name': instance.name, - 'datastore_name': datastore_name})) - delete_task = self._session._call_method( - self._session._get_vim(), - "DeleteDatastoreFile_Task", - self._session._get_vim().get_service_content().fileManager, - name=dir_ds_compliant_path) - self._session._wait_for_task(instance['uuid'], delete_task) - LOG.debug(_("Deleted contents of the VM %(name)s from " - "datastore %(datastore_name)s") % - ({'name': instance.name, - 'datastore_name': datastore_name})) - except Exception, excep: - LOG.warn(_("In vmwareapi:vmops:destroy, " - "got this exception while deleting" - " the VM contents from the disk: %s") - % str(excep)) - except Exception, exc: - LOG.exception(exc) - - def pause(self, instance): - """Pause a VM instance.""" - raise exception.ApiError("pause not supported for vmwareapi") - - def unpause(self, instance): - """Un-Pause a VM instance.""" - raise exception.ApiError("unpause not supported for vmwareapi") - - def suspend(self, instance): - """Suspend the specified instance.""" - vm_ref = self._get_vm_ref_from_the_name(instance.name) - if vm_ref is None: - raise exception.InstanceNotFound(instance_id=instance.id) - - pwr_state = self._session._call_method(vim_util, - "get_dynamic_property", vm_ref, - "VirtualMachine", "runtime.powerState") - # Only PoweredOn VMs can be suspended. - if pwr_state == "poweredOn": - LOG.debug(_("Suspending the VM %s ") % instance.name) - suspend_task = self._session._call_method(self._session._get_vim(), - "SuspendVM_Task", vm_ref) - self._session._wait_for_task(instance['uuid'], suspend_task) - LOG.debug(_("Suspended the VM %s ") % instance.name) - # Raise Exception if VM is poweredOff - elif pwr_state == "poweredOff": - reason = _("instance is powered off and can not be suspended.") - raise exception.InstanceSuspendFailure(reason=reason) - - LOG.debug(_("VM %s was already in suspended state. So returning " - "without doing anything") % instance.name) - - def resume(self, instance): - """Resume the specified instance.""" - vm_ref = self._get_vm_ref_from_the_name(instance.name) - if vm_ref is None: - raise exception.InstanceNotFound(instance_id=instance.id) - - pwr_state = self._session._call_method(vim_util, - "get_dynamic_property", vm_ref, - "VirtualMachine", "runtime.powerState") - if pwr_state.lower() == "suspended": - LOG.debug(_("Resuming the VM %s") % instance.name) - suspend_task = self._session._call_method( - self._session._get_vim(), - "PowerOnVM_Task", vm_ref) - self._session._wait_for_task(instance['uuid'], suspend_task) - LOG.debug(_("Resumed the VM %s ") % instance.name) - else: - reason = _("instance is not in a suspended state") - raise exception.InstanceResumeFailure(reason=reason) - - def get_info(self, instance_name): - """Return data about the VM instance.""" - vm_ref = self._get_vm_ref_from_the_name(instance_name) - if vm_ref is None: - raise exception.InstanceNotFound(instance_id=instance_name) - - lst_properties = ["summary.config.numCpu", - "summary.config.memorySizeMB", - "runtime.powerState"] - vm_props = self._session._call_method(vim_util, - "get_object_properties", None, vm_ref, "VirtualMachine", - lst_properties) - max_mem = None - pwr_state = None - num_cpu = None - for elem in vm_props: - for prop in elem.propSet: - if prop.name == "summary.config.numCpu": - num_cpu = int(prop.val) - elif prop.name == "summary.config.memorySizeMB": - # In MB, but we want in KB - max_mem = int(prop.val) * 1024 - elif prop.name == "runtime.powerState": - pwr_state = VMWARE_POWER_STATES[prop.val] - - return {'state': pwr_state, - 'max_mem': max_mem, - 'mem': max_mem, - 'num_cpu': num_cpu, - 'cpu_time': 0} - - def get_diagnostics(self, instance): - """Return data about VM diagnostics.""" - raise exception.ApiError("get_diagnostics not implemented for " - "vmwareapi") - - def get_console_output(self, instance): - """Return snapshot of console.""" - vm_ref = self._get_vm_ref_from_the_name(instance.name) - if vm_ref is None: - raise exception.InstanceNotFound(instance_id=instance.id) - param_list = {"id": str(vm_ref)} - base_url = "%s://%s/screen?%s" % (self._session._scheme, - self._session._host_ip, - urllib.urlencode(param_list)) - request = urllib2.Request(base_url) - base64string = base64.encodestring( - '%s:%s' % ( - self._session._host_username, - self._session._host_password)).replace('\n', '') - request.add_header("Authorization", "Basic %s" % base64string) - result = urllib2.urlopen(request) - if result.code == 200: - return result.read() - else: - return "" - - def get_ajax_console(self, instance): - """Return link to instance's ajax console.""" - return 'http://fakeajaxconsole/fake_url' - - def _set_machine_id(self, client_factory, instance, network_info): - """ - Set the machine id of the VM for guest tools to pick up and reconfigure - the network interfaces. - """ - vm_ref = self._get_vm_ref_from_the_name(instance.name) - if vm_ref is None: - raise exception.InstanceNotFound(instance_id=instance.id) - - machine_id_str = '' - for (network, info) in network_info: - # TODO(vish): add support for dns2 - # TODO(sateesh): add support for injection of ipv6 configuration - ip_v4 = ip_v6 = None - if 'ips' in info and len(info['ips']) > 0: - ip_v4 = info['ips'][0] - if 'ip6s' in info and len(info['ip6s']) > 0: - ip_v6 = info['ip6s'][0] - if len(info['dns']) > 0: - dns = info['dns'][0] - else: - dns = '' - - interface_str = "%s;%s;%s;%s;%s;%s" % \ - (info['mac'], - ip_v4 and ip_v4['ip'] or '', - ip_v4 and ip_v4['netmask'] or '', - info['gateway'], - info['broadcast'], - dns) - machine_id_str = machine_id_str + interface_str + '#' - - machine_id_change_spec = \ - vm_util.get_machine_id_change_spec(client_factory, machine_id_str) - - LOG.debug(_("Reconfiguring VM instance %(name)s to set the machine id " - "with ip - %(ip_addr)s") % - ({'name': instance.name, - 'ip_addr': ip_v4['ip']})) - reconfig_task = self._session._call_method(self._session._get_vim(), - "ReconfigVM_Task", vm_ref, - spec=machine_id_change_spec) - self._session._wait_for_task(instance['uuid'], reconfig_task) - LOG.debug(_("Reconfigured VM instance %(name)s to set the machine id " - "with ip - %(ip_addr)s") % - ({'name': instance.name, - 'ip_addr': ip_v4['ip']})) - - def _get_datacenter_name_and_ref(self): - """Get the datacenter name and the reference.""" - dc_obj = self._session._call_method(vim_util, "get_objects", - "Datacenter", ["name"]) - return dc_obj[0].obj, dc_obj[0].propSet[0].val - - def _path_exists(self, ds_browser, ds_path): - """Check if the path exists on the datastore.""" - search_task = self._session._call_method(self._session._get_vim(), - "SearchDatastore_Task", - ds_browser, - datastorePath=ds_path) - # Wait till the state changes from queued or running. - # If an error state is returned, it means that the path doesn't exist. - while True: - task_info = self._session._call_method(vim_util, - "get_dynamic_property", - search_task, "Task", "info") - if task_info.state in ['queued', 'running']: - time.sleep(2) - continue - break - if task_info.state == "error": - return False - return True - - def _mkdir(self, ds_path): - """ - Creates a directory at the path specified. If it is just "NAME", - then a directory with this name is created at the topmost level of the - DataStore. - """ - LOG.debug(_("Creating directory with path %s") % ds_path) - self._session._call_method(self._session._get_vim(), "MakeDirectory", - self._session._get_vim().get_service_content().fileManager, - name=ds_path, createParentDirectories=False) - LOG.debug(_("Created directory with path %s") % ds_path) - - def _get_vm_ref_from_the_name(self, vm_name): - """Get reference to the VM with the name specified.""" - vms = self._session._call_method(vim_util, "get_objects", - "VirtualMachine", ["name"]) - for vm in vms: - if vm.propSet[0].val == vm_name: - return vm.obj - return None - - def plug_vifs(self, instance, network_info): - """Plug VIFs into networks.""" - for (network, mapping) in network_info: - self._vif_driver.plug(instance, network, mapping) - - def _unplug_vifs(self, instance, network_info): - """Unplug VIFs from networks.""" - for (network, mapping) in network_info: - self._vif_driver.unplug(instance, network, mapping) +# 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. + +""" +Class for VM tasks like spawn, snapshot, suspend, resume etc. +""" + +import base64 +import os +import time +import urllib +import urllib2 +import uuid + +from nova.compute import power_state +from nova import exception +from nova import flags +from nova import log as logging +from nova import utils +from nova.virt.vmwareapi import vim_util +from nova.virt.vmwareapi import vm_util +from nova.virt.vmwareapi import vmware_images +from nova.virt.vmwareapi import network_utils + +FLAGS = flags.FLAGS +flags.DEFINE_string('vmware_vif_driver', + 'nova.virt.vmwareapi.vif.VMWareVlanBridgeDriver', + 'The VMWare VIF driver to configure the VIFs.') + +LOG = logging.getLogger("nova.virt.vmwareapi.vmops") + +VMWARE_POWER_STATES = { + 'poweredOff': power_state.SHUTDOWN, + 'poweredOn': power_state.RUNNING, + 'suspended': power_state.PAUSED} + + +class VMWareVMOps(object): + """Management class for VM-related tasks.""" + + def __init__(self, session): + """Initializer.""" + self._session = session + self._vif_driver = utils.import_object(FLAGS.vmware_vif_driver) + + def list_instances(self): + """Lists the VM instances that are registered with the ESX host.""" + LOG.debug(_("Getting list of instances")) + vms = self._session._call_method(vim_util, "get_objects", + "VirtualMachine", + ["name", "runtime.connectionState"]) + lst_vm_names = [] + for vm in vms: + vm_name = None + conn_state = None + for prop in vm.propSet: + if prop.name == "name": + vm_name = prop.val + elif prop.name == "runtime.connectionState": + conn_state = prop.val + # Ignoring the oprhaned or inaccessible VMs + if conn_state not in ["orphaned", "inaccessible"]: + lst_vm_names.append(vm_name) + LOG.debug(_("Got total of %s instances") % str(len(lst_vm_names))) + return lst_vm_names + + def spawn(self, context, instance, image_meta, network_info): + """ + Creates a VM instance. + + Steps followed are: + 1. Create a VM with no disk and the specifics in the instance object + like RAM size. + 2. Create a dummy vmdk of the size of the disk file that is to be + uploaded. This is required just to create the metadata file. + 3. Delete the -flat.vmdk file created in the above step and retain + the metadata .vmdk file. + 4. Upload the disk file. + 5. Attach the disk to the VM by reconfiguring the same. + 6. Power on the VM. + """ + vm_ref = self._get_vm_ref_from_the_name(instance.name) + if vm_ref: + raise exception.InstanceExists(name=instance.name) + + client_factory = self._session._get_vim().client.factory + service_content = self._session._get_vim().get_service_content() + + def _get_datastore_ref(): + """Get the datastore list and choose the first local storage.""" + data_stores = self._session._call_method(vim_util, "get_objects", + "Datastore", ["summary.type", "summary.name"]) + for elem in data_stores: + ds_name = None + ds_type = None + for prop in elem.propSet: + if prop.name == "summary.type": + ds_type = prop.val + elif prop.name == "summary.name": + ds_name = prop.val + # Local storage identifier + if ds_type == "VMFS": + data_store_name = ds_name + return data_store_name + + if data_store_name is None: + msg = _("Couldn't get a local Datastore reference") + LOG.exception(msg) + raise exception.Error(msg) + + data_store_name = _get_datastore_ref() + + def _get_image_properties(): + """ + Get the Size of the flat vmdk file that is there on the storage + repository. + """ + image_size, image_properties = \ + vmware_images.get_vmdk_size_and_properties(context, + instance.image_ref, instance) + vmdk_file_size_in_kb = int(image_size) / 1024 + os_type = image_properties.get("vmware_ostype", "otherGuest") + adapter_type = image_properties.get("vmware_adaptertype", + "lsiLogic") + return vmdk_file_size_in_kb, os_type, adapter_type + + vmdk_file_size_in_kb, os_type, adapter_type = _get_image_properties() + + def _get_vmfolder_and_res_pool_mors(): + """Get the Vm folder ref from the datacenter.""" + dc_objs = self._session._call_method(vim_util, "get_objects", + "Datacenter", ["vmFolder"]) + # There is only one default datacenter in a standalone ESX host + vm_folder_mor = dc_objs[0].propSet[0].val + + # Get the resource pool. Taking the first resource pool coming our + # way. Assuming that is the default resource pool. + res_pool_mor = self._session._call_method(vim_util, "get_objects", + "ResourcePool")[0].obj + return vm_folder_mor, res_pool_mor + + vm_folder_mor, res_pool_mor = _get_vmfolder_and_res_pool_mors() + + def _check_if_network_bridge_exists(network_name): + network_ref = \ + network_utils.get_network_with_the_name(self._session, + network_name) + if network_ref is None: + raise exception.NetworkNotFoundForBridge(bridge=network_name) + return network_ref + + def _get_vif_infos(): + vif_infos = [] + for (network, mapping) in network_info: + mac_address = mapping['mac'] + network_name = network['bridge'] + if mapping.get('should_create_vlan'): + network_ref = self._vif_driver.ensure_vlan_bridge( + self._session, network) + else: + network_ref = _check_if_network_bridge_exists(network_name) + vif_infos.append({'network_name': network_name, + 'mac_address': mac_address, + 'network_ref': network_ref, + }) + return vif_infos + + vif_infos = _get_vif_infos() + + # Get the create vm config spec + config_spec = vm_util.get_vm_create_spec( + client_factory, instance, + data_store_name, vif_infos, os_type) + + def _execute_create_vm(): + """Create VM on ESX host.""" + LOG.debug(_("Creating VM with the name %s on the ESX host") % + instance.name) + # Create the VM on the ESX host + vm_create_task = self._session._call_method( + self._session._get_vim(), + "CreateVM_Task", vm_folder_mor, + config=config_spec, pool=res_pool_mor) + self._session._wait_for_task(instance['uuid'], vm_create_task) + + LOG.debug(_("Created VM with the name %s on the ESX host") % + instance.name) + + _execute_create_vm() + + # Set the machine.id parameter of the instance to inject + # the NIC configuration inside the VM + if FLAGS.flat_injected: + self._set_machine_id(client_factory, instance, network_info) + + # Naming the VM files in correspondence with the VM instance name + # The flat vmdk file name + flat_uploaded_vmdk_name = "%s/%s-flat.vmdk" % (instance.name, + instance.name) + # The vmdk meta-data file + uploaded_vmdk_name = "%s/%s.vmdk" % (instance.name, instance.name) + flat_uploaded_vmdk_path = vm_util.build_datastore_path(data_store_name, + flat_uploaded_vmdk_name) + uploaded_vmdk_path = vm_util.build_datastore_path(data_store_name, + uploaded_vmdk_name) + + def _create_virtual_disk(): + """Create a virtual disk of the size of flat vmdk file.""" + # Create a Virtual Disk of the size of the flat vmdk file. This is + # done just to generate the meta-data file whose specifics + # depend on the size of the disk, thin/thick provisioning and the + # storage adapter type. + # Here we assume thick provisioning and lsiLogic for the adapter + # type + LOG.debug(_("Creating Virtual Disk of size " + "%(vmdk_file_size_in_kb)s KB and adapter type " + "%(adapter_type)s on the ESX host local store" + " %(data_store_name)s") % + {"vmdk_file_size_in_kb": vmdk_file_size_in_kb, + "adapter_type": adapter_type, + "data_store_name": data_store_name}) + vmdk_create_spec = vm_util.get_vmdk_create_spec(client_factory, + vmdk_file_size_in_kb, adapter_type) + vmdk_create_task = self._session._call_method( + self._session._get_vim(), + "CreateVirtualDisk_Task", + service_content.virtualDiskManager, + name=uploaded_vmdk_path, + datacenter=self._get_datacenter_name_and_ref()[0], + spec=vmdk_create_spec) + self._session._wait_for_task(instance['uuid'], vmdk_create_task) + LOG.debug(_("Created Virtual Disk of size %(vmdk_file_size_in_kb)s" + " KB on the ESX host local store " + "%(data_store_name)s") % + {"vmdk_file_size_in_kb": vmdk_file_size_in_kb, + "data_store_name": data_store_name}) + + _create_virtual_disk() + + def _delete_disk_file(): + LOG.debug(_("Deleting the file %(flat_uploaded_vmdk_path)s " + "on the ESX host local" + "store %(data_store_name)s") % + {"flat_uploaded_vmdk_path": flat_uploaded_vmdk_path, + "data_store_name": data_store_name}) + # Delete the -flat.vmdk file created. .vmdk file is retained. + vmdk_delete_task = self._session._call_method( + self._session._get_vim(), + "DeleteDatastoreFile_Task", + service_content.fileManager, + name=flat_uploaded_vmdk_path) + self._session._wait_for_task(instance['uuid'], vmdk_delete_task) + LOG.debug(_("Deleted the file %(flat_uploaded_vmdk_path)s on the " + "ESX host local store %(data_store_name)s") % + {"flat_uploaded_vmdk_path": flat_uploaded_vmdk_path, + "data_store_name": data_store_name}) + + _delete_disk_file() + + cookies = self._session._get_vim().client.options.transport.cookiejar + + def _fetch_image_on_esx_datastore(): + """Fetch image from Glance to ESX datastore.""" + LOG.debug(_("Downloading image file data %(image_ref)s to the ESX " + "data store %(data_store_name)s") % + ({'image_ref': instance.image_ref, + 'data_store_name': data_store_name})) + # Upload the -flat.vmdk file whose meta-data file we just created + # above + vmware_images.fetch_image( + context, + instance.image_ref, + instance, + host=self._session._host_ip, + data_center_name=self._get_datacenter_name_and_ref()[1], + datastore_name=data_store_name, + cookies=cookies, + file_path=flat_uploaded_vmdk_name) + LOG.debug(_("Downloaded image file data %(image_ref)s to the ESX " + "data store %(data_store_name)s") % + ({'image_ref': instance.image_ref, + 'data_store_name': data_store_name})) + _fetch_image_on_esx_datastore() + + vm_ref = self._get_vm_ref_from_the_name(instance.name) + + def _attach_vmdk_to_the_vm(): + """ + Attach the vmdk uploaded to the VM. VM reconfigure is done + to do so. + """ + vmdk_attach_config_spec = vm_util.get_vmdk_attach_config_spec( + client_factory, + vmdk_file_size_in_kb, uploaded_vmdk_path, + adapter_type) + LOG.debug(_("Reconfiguring VM instance %s to attach the image " + "disk") % instance.name) + reconfig_task = self._session._call_method( + self._session._get_vim(), + "ReconfigVM_Task", vm_ref, + spec=vmdk_attach_config_spec) + self._session._wait_for_task(instance['uuid'], reconfig_task) + LOG.debug(_("Reconfigured VM instance %s to attach the image " + "disk") % instance.name) + + _attach_vmdk_to_the_vm() + + def _power_on_vm(): + """Power on the VM.""" + LOG.debug(_("Powering on the VM instance %s") % instance.name) + # Power On the VM + power_on_task = self._session._call_method( + self._session._get_vim(), + "PowerOnVM_Task", vm_ref) + self._session._wait_for_task(instance['uuid'], power_on_task) + LOG.debug(_("Powered on the VM instance %s") % instance.name) + _power_on_vm() + + def snapshot(self, context, instance, snapshot_name): + """ + Create snapshot from a running VM instance. + Steps followed are: + 1. Get the name of the vmdk file which the VM points to right now. + Can be a chain of snapshots, so we need to know the last in the + chain. + 2. Create the snapshot. A new vmdk is created which the VM points to + now. The earlier vmdk becomes read-only. + 3. Call CopyVirtualDisk which coalesces the disk chain to form a single + vmdk, rather a .vmdk metadata file and a -flat.vmdk disk data file. + 4. Now upload the -flat.vmdk file to the image store. + 5. Delete the coalesced .vmdk and -flat.vmdk created. + """ + vm_ref = self._get_vm_ref_from_the_name(instance.name) + if vm_ref is None: + raise exception.InstanceNotFound(instance_id=instance.id) + + client_factory = self._session._get_vim().client.factory + service_content = self._session._get_vim().get_service_content() + + def _get_vm_and_vmdk_attribs(): + # Get the vmdk file name that the VM is pointing to + hardware_devices = self._session._call_method(vim_util, + "get_dynamic_property", vm_ref, + "VirtualMachine", "config.hardware.device") + vmdk_file_path_before_snapshot, adapter_type = \ + vm_util.get_vmdk_file_path_and_adapter_type(client_factory, + hardware_devices) + datastore_name = vm_util.split_datastore_path( + vmdk_file_path_before_snapshot)[0] + os_type = self._session._call_method(vim_util, + "get_dynamic_property", vm_ref, + "VirtualMachine", "summary.config.guestId") + return (vmdk_file_path_before_snapshot, adapter_type, + datastore_name, os_type) + + vmdk_file_path_before_snapshot, adapter_type, datastore_name,\ + os_type = _get_vm_and_vmdk_attribs() + + def _create_vm_snapshot(): + # Create a snapshot of the VM + LOG.debug(_("Creating Snapshot of the VM instance %s ") % + instance.name) + snapshot_task = self._session._call_method( + self._session._get_vim(), + "CreateSnapshot_Task", vm_ref, + name="%s-snapshot" % instance.name, + description="Taking Snapshot of the VM", + memory=True, + quiesce=True) + self._session._wait_for_task(instance['uuid'], snapshot_task) + LOG.debug(_("Created Snapshot of the VM instance %s ") % + instance.name) + + _create_vm_snapshot() + + def _check_if_tmp_folder_exists(): + # Copy the contents of the VM that were there just before the + # snapshot was taken + ds_ref_ret = vim_util.get_dynamic_property( + self._session._get_vim(), + vm_ref, + "VirtualMachine", + "datastore") + if not ds_ref_ret: + raise exception.DatastoreNotFound() + ds_ref = ds_ref_ret.ManagedObjectReference[0] + ds_browser = vim_util.get_dynamic_property( + self._session._get_vim(), + ds_ref, + "Datastore", + "browser") + # Check if the vmware-tmp folder exists or not. If not, create one + tmp_folder_path = vm_util.build_datastore_path(datastore_name, + "vmware-tmp") + if not self._path_exists(ds_browser, tmp_folder_path): + self._mkdir(vm_util.build_datastore_path(datastore_name, + "vmware-tmp")) + + _check_if_tmp_folder_exists() + + # Generate a random vmdk file name to which the coalesced vmdk content + # will be copied to. A random name is chosen so that we don't have + # name clashes. + random_name = str(uuid.uuid4()) + dest_vmdk_file_location = vm_util.build_datastore_path(datastore_name, + "vmware-tmp/%s.vmdk" % random_name) + dc_ref = self._get_datacenter_name_and_ref()[0] + + def _copy_vmdk_content(): + # Copy the contents of the disk ( or disks, if there were snapshots + # done earlier) to a temporary vmdk file. + copy_spec = vm_util.get_copy_virtual_disk_spec(client_factory, + adapter_type) + LOG.debug(_("Copying disk data before snapshot of the VM " + " instance %s") % instance.name) + copy_disk_task = self._session._call_method( + self._session._get_vim(), + "CopyVirtualDisk_Task", + service_content.virtualDiskManager, + sourceName=vmdk_file_path_before_snapshot, + sourceDatacenter=dc_ref, + destName=dest_vmdk_file_location, + destDatacenter=dc_ref, + destSpec=copy_spec, + force=False) + self._session._wait_for_task(instance['uuid'], copy_disk_task) + LOG.debug(_("Copied disk data before snapshot of the VM " + "instance %s") % instance.name) + + _copy_vmdk_content() + + cookies = self._session._get_vim().client.options.transport.cookiejar + + def _upload_vmdk_to_image_repository(): + # Upload the contents of -flat.vmdk file which has the disk data. + LOG.debug(_("Uploading image %s") % snapshot_name) + vmware_images.upload_image( + context, + snapshot_name, + instance, + os_type=os_type, + adapter_type=adapter_type, + image_version=1, + host=self._session._host_ip, + data_center_name=self._get_datacenter_name_and_ref()[1], + datastore_name=datastore_name, + cookies=cookies, + file_path="vmware-tmp/%s-flat.vmdk" % random_name) + LOG.debug(_("Uploaded image %s") % snapshot_name) + + _upload_vmdk_to_image_repository() + + def _clean_temp_data(): + """ + Delete temporary vmdk files generated in image handling + operations. + """ + # Delete the temporary vmdk created above. + LOG.debug(_("Deleting temporary vmdk file %s") + % dest_vmdk_file_location) + remove_disk_task = self._session._call_method( + self._session._get_vim(), + "DeleteVirtualDisk_Task", + service_content.virtualDiskManager, + name=dest_vmdk_file_location, + datacenter=dc_ref) + self._session._wait_for_task(instance['uuid'], remove_disk_task) + LOG.debug(_("Deleted temporary vmdk file %s") + % dest_vmdk_file_location) + + _clean_temp_data() + + def reboot(self, instance, network_info): + """Reboot a VM instance.""" + vm_ref = self._get_vm_ref_from_the_name(instance.name) + if vm_ref is None: + raise exception.InstanceNotFound(instance_id=instance.id) + + self.plug_vifs(instance, network_info) + + lst_properties = ["summary.guest.toolsStatus", "runtime.powerState", + "summary.guest.toolsRunningStatus"] + props = self._session._call_method(vim_util, "get_object_properties", + None, vm_ref, "VirtualMachine", + lst_properties) + pwr_state = None + tools_status = None + tools_running_status = False + for elem in props: + for prop in elem.propSet: + if prop.name == "runtime.powerState": + pwr_state = prop.val + elif prop.name == "summary.guest.toolsStatus": + tools_status = prop.val + elif prop.name == "summary.guest.toolsRunningStatus": + tools_running_status = prop.val + + # Raise an exception if the VM is not powered On. + if pwr_state not in ["poweredOn"]: + reason = _("instance is not powered on") + raise exception.InstanceRebootFailure(reason=reason) + + # If latest vmware tools are installed in the VM, and that the tools + # are running, then only do a guest reboot. Otherwise do a hard reset. + if (tools_status == "toolsOk" and + tools_running_status == "guestToolsRunning"): + LOG.debug(_("Rebooting guest OS of VM %s") % instance.name) + self._session._call_method(self._session._get_vim(), "RebootGuest", + vm_ref) + LOG.debug(_("Rebooted guest OS of VM %s") % instance.name) + else: + LOG.debug(_("Doing hard reboot of VM %s") % instance.name) + reset_task = self._session._call_method(self._session._get_vim(), + "ResetVM_Task", vm_ref) + self._session._wait_for_task(instance['uuid'], reset_task) + LOG.debug(_("Did hard reboot of VM %s") % instance.name) + + def destroy(self, instance, network_info): + """ + Destroy a VM instance. Steps followed are: + 1. Power off the VM, if it is in poweredOn state. + 2. Un-register a VM. + 3. Delete the contents of the folder holding the VM related data. + """ + try: + vm_ref = self._get_vm_ref_from_the_name(instance.name) + if vm_ref is None: + LOG.debug(_("instance - %s not present") % instance.name) + return + lst_properties = ["config.files.vmPathName", "runtime.powerState"] + props = self._session._call_method(vim_util, + "get_object_properties", + None, vm_ref, "VirtualMachine", lst_properties) + pwr_state = None + for elem in props: + vm_config_pathname = None + for prop in elem.propSet: + if prop.name == "runtime.powerState": + pwr_state = prop.val + elif prop.name == "config.files.vmPathName": + vm_config_pathname = prop.val + if vm_config_pathname: + datastore_name, vmx_file_path = \ + vm_util.split_datastore_path(vm_config_pathname) + # Power off the VM if it is in PoweredOn state. + if pwr_state == "poweredOn": + LOG.debug(_("Powering off the VM %s") % instance.name) + poweroff_task = self._session._call_method( + self._session._get_vim(), + "PowerOffVM_Task", vm_ref) + self._session._wait_for_task(instance['uuid'], poweroff_task) + LOG.debug(_("Powered off the VM %s") % instance.name) + + # Un-register the VM + try: + LOG.debug(_("Unregistering the VM %s") % instance.name) + self._session._call_method(self._session._get_vim(), + "UnregisterVM", vm_ref) + LOG.debug(_("Unregistered the VM %s") % instance.name) + except Exception, excep: + LOG.warn(_("In vmwareapi:vmops:destroy, got this exception" + " while un-registering the VM: %s") % str(excep)) + + self._unplug_vifs(instance, network_info) + + # Delete the folder holding the VM related content on + # the datastore. + try: + dir_ds_compliant_path = vm_util.build_datastore_path( + datastore_name, + os.path.dirname(vmx_file_path)) + LOG.debug(_("Deleting contents of the VM %(name)s from " + "datastore %(datastore_name)s") % + ({'name': instance.name, + 'datastore_name': datastore_name})) + delete_task = self._session._call_method( + self._session._get_vim(), + "DeleteDatastoreFile_Task", + self._session._get_vim().get_service_content().fileManager, + name=dir_ds_compliant_path) + self._session._wait_for_task(instance['uuid'], delete_task) + LOG.debug(_("Deleted contents of the VM %(name)s from " + "datastore %(datastore_name)s") % + ({'name': instance.name, + 'datastore_name': datastore_name})) + except Exception, excep: + LOG.warn(_("In vmwareapi:vmops:destroy, " + "got this exception while deleting" + " the VM contents from the disk: %s") + % str(excep)) + except Exception, exc: + LOG.exception(exc) + + def pause(self, instance): + """Pause a VM instance.""" + raise exception.ApiError("pause not supported for vmwareapi") + + def unpause(self, instance): + """Un-Pause a VM instance.""" + raise exception.ApiError("unpause not supported for vmwareapi") + + def suspend(self, instance): + """Suspend the specified instance.""" + vm_ref = self._get_vm_ref_from_the_name(instance.name) + if vm_ref is None: + raise exception.InstanceNotFound(instance_id=instance.id) + + pwr_state = self._session._call_method(vim_util, + "get_dynamic_property", vm_ref, + "VirtualMachine", "runtime.powerState") + # Only PoweredOn VMs can be suspended. + if pwr_state == "poweredOn": + LOG.debug(_("Suspending the VM %s ") % instance.name) + suspend_task = self._session._call_method(self._session._get_vim(), + "SuspendVM_Task", vm_ref) + self._session._wait_for_task(instance['uuid'], suspend_task) + LOG.debug(_("Suspended the VM %s ") % instance.name) + # Raise Exception if VM is poweredOff + elif pwr_state == "poweredOff": + reason = _("instance is powered off and can not be suspended.") + raise exception.InstanceSuspendFailure(reason=reason) + + LOG.debug(_("VM %s was already in suspended state. So returning " + "without doing anything") % instance.name) + + def resume(self, instance): + """Resume the specified instance.""" + vm_ref = self._get_vm_ref_from_the_name(instance.name) + if vm_ref is None: + raise exception.InstanceNotFound(instance_id=instance.id) + + pwr_state = self._session._call_method(vim_util, + "get_dynamic_property", vm_ref, + "VirtualMachine", "runtime.powerState") + if pwr_state.lower() == "suspended": + LOG.debug(_("Resuming the VM %s") % instance.name) + suspend_task = self._session._call_method( + self._session._get_vim(), + "PowerOnVM_Task", vm_ref) + self._session._wait_for_task(instance['uuid'], suspend_task) + LOG.debug(_("Resumed the VM %s ") % instance.name) + else: + reason = _("instance is not in a suspended state") + raise exception.InstanceResumeFailure(reason=reason) + + def get_info(self, instance_name): + """Return data about the VM instance.""" + vm_ref = self._get_vm_ref_from_the_name(instance_name) + if vm_ref is None: + raise exception.InstanceNotFound(instance_id=instance_name) + + lst_properties = ["summary.config.numCpu", + "summary.config.memorySizeMB", + "runtime.powerState"] + vm_props = self._session._call_method(vim_util, + "get_object_properties", None, vm_ref, "VirtualMachine", + lst_properties) + max_mem = None + pwr_state = None + num_cpu = None + for elem in vm_props: + for prop in elem.propSet: + if prop.name == "summary.config.numCpu": + num_cpu = int(prop.val) + elif prop.name == "summary.config.memorySizeMB": + # In MB, but we want in KB + max_mem = int(prop.val) * 1024 + elif prop.name == "runtime.powerState": + pwr_state = VMWARE_POWER_STATES[prop.val] + + return {'state': pwr_state, + 'max_mem': max_mem, + 'mem': max_mem, + 'num_cpu': num_cpu, + 'cpu_time': 0} + + def get_diagnostics(self, instance): + """Return data about VM diagnostics.""" + raise exception.ApiError("get_diagnostics not implemented for " + "vmwareapi") + + def get_console_output(self, instance): + """Return snapshot of console.""" + vm_ref = self._get_vm_ref_from_the_name(instance.name) + if vm_ref is None: + raise exception.InstanceNotFound(instance_id=instance.id) + param_list = {"id": str(vm_ref)} + base_url = "%s://%s/screen?%s" % (self._session._scheme, + self._session._host_ip, + urllib.urlencode(param_list)) + request = urllib2.Request(base_url) + base64string = base64.encodestring( + '%s:%s' % ( + self._session._host_username, + self._session._host_password)).replace('\n', '') + request.add_header("Authorization", "Basic %s" % base64string) + result = urllib2.urlopen(request) + if result.code == 200: + return result.read() + else: + return "" + + def get_ajax_console(self, instance): + """Return link to instance's ajax console.""" + return 'http://fakeajaxconsole/fake_url' + + def _set_machine_id(self, client_factory, instance, network_info): + """ + Set the machine id of the VM for guest tools to pick up and reconfigure + the network interfaces. + """ + vm_ref = self._get_vm_ref_from_the_name(instance.name) + if vm_ref is None: + raise exception.InstanceNotFound(instance_id=instance.id) + + machine_id_str = '' + for (network, info) in network_info: + # TODO(vish): add support for dns2 + # TODO(sateesh): add support for injection of ipv6 configuration + ip_v4 = ip_v6 = None + if 'ips' in info and len(info['ips']) > 0: + ip_v4 = info['ips'][0] + if 'ip6s' in info and len(info['ip6s']) > 0: + ip_v6 = info['ip6s'][0] + if len(info['dns']) > 0: + dns = info['dns'][0] + else: + dns = '' + + interface_str = "%s;%s;%s;%s;%s;%s" % \ + (info['mac'], + ip_v4 and ip_v4['ip'] or '', + ip_v4 and ip_v4['netmask'] or '', + info['gateway'], + info['broadcast'], + dns) + machine_id_str = machine_id_str + interface_str + '#' + + machine_id_change_spec = \ + vm_util.get_machine_id_change_spec(client_factory, machine_id_str) + + LOG.debug(_("Reconfiguring VM instance %(name)s to set the machine id " + "with ip - %(ip_addr)s") % + ({'name': instance.name, + 'ip_addr': ip_v4['ip']})) + reconfig_task = self._session._call_method(self._session._get_vim(), + "ReconfigVM_Task", vm_ref, + spec=machine_id_change_spec) + self._session._wait_for_task(instance['uuid'], reconfig_task) + LOG.debug(_("Reconfigured VM instance %(name)s to set the machine id " + "with ip - %(ip_addr)s") % + ({'name': instance.name, + 'ip_addr': ip_v4['ip']})) + + def _get_datacenter_name_and_ref(self): + """Get the datacenter name and the reference.""" + dc_obj = self._session._call_method(vim_util, "get_objects", + "Datacenter", ["name"]) + return dc_obj[0].obj, dc_obj[0].propSet[0].val + + def _path_exists(self, ds_browser, ds_path): + """Check if the path exists on the datastore.""" + search_task = self._session._call_method(self._session._get_vim(), + "SearchDatastore_Task", + ds_browser, + datastorePath=ds_path) + # Wait till the state changes from queued or running. + # If an error state is returned, it means that the path doesn't exist. + while True: + task_info = self._session._call_method(vim_util, + "get_dynamic_property", + search_task, "Task", "info") + if task_info.state in ['queued', 'running']: + time.sleep(2) + continue + break + if task_info.state == "error": + return False + return True + + def _mkdir(self, ds_path): + """ + Creates a directory at the path specified. If it is just "NAME", + then a directory with this name is created at the topmost level of the + DataStore. + """ + LOG.debug(_("Creating directory with path %s") % ds_path) + self._session._call_method(self._session._get_vim(), "MakeDirectory", + self._session._get_vim().get_service_content().fileManager, + name=ds_path, createParentDirectories=False) + LOG.debug(_("Created directory with path %s") % ds_path) + + def _get_vm_ref_from_the_name(self, vm_name): + """Get reference to the VM with the name specified.""" + vms = self._session._call_method(vim_util, "get_objects", + "VirtualMachine", ["name"]) + for vm in vms: + if vm.propSet[0].val == vm_name: + return vm.obj + return None + + def plug_vifs(self, instance, network_info): + """Plug VIFs into networks.""" + for (network, mapping) in network_info: + self._vif_driver.plug(instance, network, mapping) + + def _unplug_vifs(self, instance, network_info): + """Unplug VIFs from networks.""" + for (network, mapping) in network_info: + self._vif_driver.unplug(instance, network, mapping) diff --git a/nova/virt/vmwareapi/vmware_images.py b/nova/virt/vmwareapi/vmware_images.py index b03ccae25..89f84d0f6 100644 --- a/nova/virt/vmwareapi/vmware_images.py +++ b/nova/virt/vmwareapi/vmware_images.py @@ -1,145 +1,145 @@ -# 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. -""" -Utility functions for Image transfer. -""" - -from nova import exception -from nova.image import glance -from nova import log as logging -from nova.virt.vmwareapi import io_util -from nova.virt.vmwareapi import read_write_util - -LOG = logging.getLogger("nova.virt.vmwareapi.vmware_images") - -QUEUE_BUFFER_SIZE = 10 - - -def start_transfer(read_file_handle, data_size, write_file_handle=None, - glance_client=None, image_id=None, image_meta=None): - """Start the data transfer from the reader to the writer. - Reader writes to the pipe and the writer reads from the pipe. This means - that the total transfer time boils down to the slower of the read/write - and not the addition of the two times.""" - - if not image_meta: - image_meta = {} - - # The pipe that acts as an intermediate store of data for reader to write - # to and writer to grab from. - thread_safe_pipe = io_util.ThreadSafePipe(QUEUE_BUFFER_SIZE, data_size) - # The read thread. In case of glance it is the instance of the - # GlanceFileRead class. The glance client read returns an iterator - # and this class wraps that iterator to provide datachunks in calls - # to read. - read_thread = io_util.IOThread(read_file_handle, thread_safe_pipe) - - # In case of Glance - VMWare transfer, we just need a handle to the - # HTTP Connection that is to send transfer data to the VMWare datastore. - if write_file_handle: - write_thread = io_util.IOThread(thread_safe_pipe, write_file_handle) - # In case of VMWare - Glance transfer, we relinquish VMWare HTTP file read - # handle to Glance Client instance, but to be sure of the transfer we need - # to be sure of the status of the image on glnace changing to active. - # The GlanceWriteThread handles the same for us. - elif glance_client and image_id: - write_thread = io_util.GlanceWriteThread(thread_safe_pipe, - glance_client, image_id, image_meta) - # Start the read and write threads. - read_event = read_thread.start() - write_event = write_thread.start() - try: - # Wait on the read and write events to signal their end - read_event.wait() - write_event.wait() - except Exception, exc: - # In case of any of the reads or writes raising an exception, - # stop the threads so that we un-necessarily don't keep the other one - # waiting. - read_thread.stop() - write_thread.stop() - - # Log and raise the exception. - LOG.exception(exc) - raise exception.Error(exc) - finally: - # No matter what, try closing the read and write handles, if it so - # applies. - read_file_handle.close() - if write_file_handle: - write_file_handle.close() - - -def fetch_image(context, image, instance, **kwargs): - """Download image from the glance image server.""" - LOG.debug(_("Downloading image %s from glance image server") % image) - (glance_client, image_id) = glance.get_glance_client(context, image) - metadata, read_iter = glance_client.get_image(image_id) - read_file_handle = read_write_util.GlanceFileRead(read_iter) - file_size = int(metadata['size']) - write_file_handle = read_write_util.VMWareHTTPWriteFile( - kwargs.get("host"), - kwargs.get("data_center_name"), - kwargs.get("datastore_name"), - kwargs.get("cookies"), - kwargs.get("file_path"), - file_size) - start_transfer(read_file_handle, file_size, - write_file_handle=write_file_handle) - LOG.debug(_("Downloaded image %s from glance image server") % image) - - -def upload_image(context, image, instance, **kwargs): - """Upload the snapshotted vm disk file to Glance image server.""" - LOG.debug(_("Uploading image %s to the Glance image server") % image) - read_file_handle = read_write_util.VmWareHTTPReadFile( - kwargs.get("host"), - kwargs.get("data_center_name"), - kwargs.get("datastore_name"), - kwargs.get("cookies"), - kwargs.get("file_path")) - file_size = read_file_handle.get_size() - (glance_client, image_id) = glance.get_glance_client(context, image) - # The properties and other fields that we need to set for the image. - image_metadata = {"is_public": True, - "disk_format": "vmdk", - "container_format": "bare", - "type": "vmdk", - "properties": {"vmware_adaptertype": - kwargs.get("adapter_type"), - "vmware_ostype": kwargs.get("os_type"), - "vmware_image_version": - kwargs.get("image_version")}} - start_transfer(read_file_handle, file_size, glance_client=glance_client, - image_id=image_id, image_meta=image_metadata) - LOG.debug(_("Uploaded image %s to the Glance image server") % image) - - -def get_vmdk_size_and_properties(context, image, instance): - """ - Get size of the vmdk file that is to be downloaded for attach in spawn. - Need this to create the dummy virtual disk for the meta-data file. The - geometry of the disk created depends on the size. - """ - - LOG.debug(_("Getting image size for the image %s") % image) - (glance_client, image_id) = glance.get_glance_client(context, image) - meta_data = glance_client.get_image_meta(image_id) - size, properties = meta_data["size"], meta_data["properties"] - LOG.debug(_("Got image size of %(size)s for the image %(image)s") % - locals()) - return size, properties +# 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. +""" +Utility functions for Image transfer. +""" + +from nova import exception +from nova.image import glance +from nova import log as logging +from nova.virt.vmwareapi import io_util +from nova.virt.vmwareapi import read_write_util + +LOG = logging.getLogger("nova.virt.vmwareapi.vmware_images") + +QUEUE_BUFFER_SIZE = 10 + + +def start_transfer(read_file_handle, data_size, write_file_handle=None, + glance_client=None, image_id=None, image_meta=None): + """Start the data transfer from the reader to the writer. + Reader writes to the pipe and the writer reads from the pipe. This means + that the total transfer time boils down to the slower of the read/write + and not the addition of the two times.""" + + if not image_meta: + image_meta = {} + + # The pipe that acts as an intermediate store of data for reader to write + # to and writer to grab from. + thread_safe_pipe = io_util.ThreadSafePipe(QUEUE_BUFFER_SIZE, data_size) + # The read thread. In case of glance it is the instance of the + # GlanceFileRead class. The glance client read returns an iterator + # and this class wraps that iterator to provide datachunks in calls + # to read. + read_thread = io_util.IOThread(read_file_handle, thread_safe_pipe) + + # In case of Glance - VMWare transfer, we just need a handle to the + # HTTP Connection that is to send transfer data to the VMWare datastore. + if write_file_handle: + write_thread = io_util.IOThread(thread_safe_pipe, write_file_handle) + # In case of VMWare - Glance transfer, we relinquish VMWare HTTP file read + # handle to Glance Client instance, but to be sure of the transfer we need + # to be sure of the status of the image on glnace changing to active. + # The GlanceWriteThread handles the same for us. + elif glance_client and image_id: + write_thread = io_util.GlanceWriteThread(thread_safe_pipe, + glance_client, image_id, image_meta) + # Start the read and write threads. + read_event = read_thread.start() + write_event = write_thread.start() + try: + # Wait on the read and write events to signal their end + read_event.wait() + write_event.wait() + except Exception, exc: + # In case of any of the reads or writes raising an exception, + # stop the threads so that we un-necessarily don't keep the other one + # waiting. + read_thread.stop() + write_thread.stop() + + # Log and raise the exception. + LOG.exception(exc) + raise exception.Error(exc) + finally: + # No matter what, try closing the read and write handles, if it so + # applies. + read_file_handle.close() + if write_file_handle: + write_file_handle.close() + + +def fetch_image(context, image, instance, **kwargs): + """Download image from the glance image server.""" + LOG.debug(_("Downloading image %s from glance image server") % image) + (glance_client, image_id) = glance.get_glance_client(context, image) + metadata, read_iter = glance_client.get_image(image_id) + read_file_handle = read_write_util.GlanceFileRead(read_iter) + file_size = int(metadata['size']) + write_file_handle = read_write_util.VMWareHTTPWriteFile( + kwargs.get("host"), + kwargs.get("data_center_name"), + kwargs.get("datastore_name"), + kwargs.get("cookies"), + kwargs.get("file_path"), + file_size) + start_transfer(read_file_handle, file_size, + write_file_handle=write_file_handle) + LOG.debug(_("Downloaded image %s from glance image server") % image) + + +def upload_image(context, image, instance, **kwargs): + """Upload the snapshotted vm disk file to Glance image server.""" + LOG.debug(_("Uploading image %s to the Glance image server") % image) + read_file_handle = read_write_util.VmWareHTTPReadFile( + kwargs.get("host"), + kwargs.get("data_center_name"), + kwargs.get("datastore_name"), + kwargs.get("cookies"), + kwargs.get("file_path")) + file_size = read_file_handle.get_size() + (glance_client, image_id) = glance.get_glance_client(context, image) + # The properties and other fields that we need to set for the image. + image_metadata = {"is_public": True, + "disk_format": "vmdk", + "container_format": "bare", + "type": "vmdk", + "properties": {"vmware_adaptertype": + kwargs.get("adapter_type"), + "vmware_ostype": kwargs.get("os_type"), + "vmware_image_version": + kwargs.get("image_version")}} + start_transfer(read_file_handle, file_size, glance_client=glance_client, + image_id=image_id, image_meta=image_metadata) + LOG.debug(_("Uploaded image %s to the Glance image server") % image) + + +def get_vmdk_size_and_properties(context, image, instance): + """ + Get size of the vmdk file that is to be downloaded for attach in spawn. + Need this to create the dummy virtual disk for the meta-data file. The + geometry of the disk created depends on the size. + """ + + LOG.debug(_("Getting image size for the image %s") % image) + (glance_client, image_id) = glance.get_glance_client(context, image) + meta_data = glance_client.get_image_meta(image_id) + size, properties = meta_data["size"], meta_data["properties"] + LOG.debug(_("Got image size of %(size)s for the image %(image)s") % + locals()) + return size, properties -- cgit