* Updated document vmware_readme.rst to mention VLAN networking

* Corrected docstrings as per pep0257 recommentations.
* Stream-lined the comments.
* Updated code with locals() where ever applicable.
* VIM : It stands for VMware Virtual Infrastructure Methodology. We have used the terminology from VMware.  we have added a question in FAQ inside vmware_readme.rst in doc/source
* New fake db: vmwareapi fake module uses a different set of fields and hence the structures required are different. Ex: bridge : 'xenbr0' does not hold good for VMware environment and bridge : 'vmnic0' is used instead. Also return values varies, hence went for implementing separate fake db.
* Now using eventlet library instead and removed io_utils.py from branch.
* Now using glance.client.Client instead of homegrown code to talk to Glance server to handle images.
* Corrected all mis-spelled function names and corresponding calls. Yeah, an auto-complete side-effect!
This commit is contained in:
sateesh 2011-03-16 21:54:02 +05:30
parent cd381ae3e1
commit 45ca7b71a8
20 changed files with 500 additions and 545 deletions

View File

@ -26,7 +26,7 @@ The basic requirement is to support VMware vSphere 4.1 as a compute provider wit
The 'vmwareapi' module is integrated with Glance, so that VM images can be streamed from there for boot on ESXi using Glance server for image storage & retrieval.
Currently supports Nova's flat networking model (Flat Manager).
Currently supports Nova's flat networking model (Flat Manager) & VLAN networking model.
.. image:: images/vmwareapi_blockdiagram.jpg
@ -213,3 +213,7 @@ FAQ
* VMware VMRC based consoles are supported. There are 2 options for credentials one is OTP (Secure but creates multiple session entries in DB for each OpenStack console create request.) & other is host based credentials (It may not be secure as ESX credentials are transmitted as clear text).
5. What does 'Vim' refer to as far as vmwareapi module is concerned?
* Vim refers to VMware Virtual Infrastructure Methodology. This is not to be confused with "VIM" editor.

View File

@ -65,11 +65,13 @@ class VMRCConsole(object):
def fix_pool_password(self, password):
"""Encode password."""
#TODO:Encrypt pool password
# TODO(sateesh): Encrypt pool password
return password
def generate_password(self, vim_session, pool, instance_name):
"""Returns a VMRC Connection credentials
"""
Returns VMRC Connection credentials.
Return string is of the form '<VM PATH>:<ESX Username>@<ESX Password>'.
"""
username, password = pool['username'], pool['password']
@ -98,12 +100,12 @@ class VMRCConsole(object):
return base64.b64encode(json_data)
def is_otp(self):
"""Is one time password."""
"""Is one time password or not."""
return False
class VMRCSessionConsole(VMRCConsole):
"""VMRC console driver with VMRC One Time Sessions"""
"""VMRC console driver with VMRC One Time Sessions."""
def __init__(self):
super(VMRCSessionConsole, self).__init__()
@ -113,7 +115,9 @@ class VMRCSessionConsole(VMRCConsole):
return 'vmrc+session'
def generate_password(self, vim_session, pool, instance_name):
"""Returns a VMRC Session
"""
Returns a VMRC Session.
Return string is of the form '<VM MOID>:<VMRC Ticket>'.
"""
vms = vim_session._call_method(vim_util, "get_objects",
@ -136,5 +140,5 @@ class VMRCSessionConsole(VMRCConsole):
return base64.b64encode(json_data)
def is_otp(self):
"""Is one time password."""
"""Is one time password or not."""
return True

View File

@ -16,7 +16,7 @@
# under the License.
"""
VMRC Console Manager
VMRC Console Manager.
"""
from nova import exception
@ -25,6 +25,7 @@ from nova import log as logging
from nova import manager
from nova import rpc
from nova import utils
from nova.virt.vmwareapi_conn import VMWareAPISession
LOG = logging.getLogger("nova.console.vmrc_manager")
@ -39,8 +40,8 @@ flags.DEFINE_string('console_driver',
class ConsoleVMRCManager(manager.Manager):
"""Manager to handle VMRC connections needed for accessing
instance consoles
"""
Manager to handle VMRC connections needed for accessing instance consoles.
"""
def __init__(self, console_driver=None, *args, **kwargs):
@ -48,15 +49,29 @@ class ConsoleVMRCManager(manager.Manager):
super(ConsoleVMRCManager, self).__init__(*args, **kwargs)
def init_host(self):
self.sessions = {}
self.driver.init_host()
def _get_vim_session(self, pool):
"""Get VIM session for the pool specified."""
vim_session = None
if pool['id'] not in self.sessions.keys():
vim_session = VMWareAPISession(pool['address'],
pool['username'],
pool['password'],
FLAGS.console_vmrc_error_retries)
self.sessions[pool['id']] = vim_session
return self.sessions[pool['id']]
def _generate_console(self, context, pool, name, instance_id, instance):
"""Sets up console for the instance."""
LOG.debug(_("Adding console"))
password = self.driver.generate_password(
pool['address'],
pool['username'],
pool['password'],
self._get_vim_session(pool),
pool,
instance.name)
console_data = {'instance_name': name,
'instance_id': instance_id,
'password': password,
@ -69,6 +84,10 @@ class ConsoleVMRCManager(manager.Manager):
@exception.wrap_exception
def add_console(self, context, instance_id, password=None,
port=None, **kwargs):
"""
Adds a console for the instance. If it is one time password, then we
generate new console credentials.
"""
instance = self.db.instance_get(context, instance_id)
host = instance['host']
name = instance['name']
@ -95,6 +114,7 @@ class ConsoleVMRCManager(manager.Manager):
@exception.wrap_exception
def remove_console(self, context, console_id, **_kwargs):
"""Removes a console entry."""
try:
console = self.db.console_get(context, console_id)
except exception.NotFound:
@ -109,6 +129,7 @@ class ConsoleVMRCManager(manager.Manager):
self.driver.teardown_console(context, console)
def get_pool_for_instance_host(self, context, instance_host):
"""Gets console pool info for the instance."""
context = context.elevated()
console_type = self.driver.console_type
try:
@ -126,7 +147,7 @@ class ConsoleVMRCManager(manager.Manager):
pool_info['password'] = self.driver.fix_pool_password(
pool_info['password'])
pool_info['host'] = self.host
#ESX Address or Proxy Address
# ESX Address or Proxy Address
public_host_name = pool_info['address']
if FLAGS.console_public_hostname:
public_host_name = FLAGS.console_public_hostname

View File

@ -16,7 +16,7 @@
# under the License.
"""
Implements vlans for vmwareapi
Implements vlans for vmwareapi.
"""
from nova import db
@ -36,8 +36,8 @@ flags.DEFINE_string('vlan_interface', 'vmnic0',
def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None):
"""Create a vlan and bridge unless they already exist"""
#open vmwareapi session
"""Create a vlan and bridge unless they already exist."""
# Open vmwareapi session
host_ip = FLAGS.vmwareapi_host_ip
host_username = FLAGS.vmwareapi_host_username
host_password = FLAGS.vmwareapi_host_password
@ -49,49 +49,43 @@ def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None):
session = VMWareAPISession(host_ip, host_username, host_password,
FLAGS.vmwareapi_api_retry_count)
vlan_interface = FLAGS.vlan_interface
#Check if the vlan_interface physical network adapter exists on the host
# Check if the vlan_interface physical network adapter exists on the host
if not NetworkHelper.check_if_vlan_interface_exists(session,
vlan_interface):
raise exception.NotFound(_("There is no physical network adapter with "
"the name %s on the ESX host") % vlan_interface)
#Get the vSwitch associated with the Physical Adapter
# Get the vSwitch associated with the Physical Adapter
vswitch_associated = NetworkHelper.get_vswitch_for_vlan_interface(
session, vlan_interface)
if vswitch_associated is None:
raise exception.NotFound(_("There is no virtual switch associated "
"with the physical network adapter with name %s") %
vlan_interface)
#check whether bridge already exists and retrieve the the ref of the
#network whose name_label is "bridge"
# Check whether bridge already exists and retrieve the the ref of the
# network whose name_label is "bridge"
network_ref = NetworkHelper.get_network_with_the_name(session, bridge)
if network_ref == None:
#Create a port group on the vSwitch associated with the vlan_interface
#corresponding physical network adapter on the ESX host
# Create a port group on the vSwitch associated with the vlan_interface
# corresponding physical network adapter on the ESX host
NetworkHelper.create_port_group(session, bridge, vswitch_associated,
vlan_num)
else:
#Get the vlan id and vswitch corresponding to the port group
# Get the vlan id and vswitch corresponding to the port group
pg_vlanid, pg_vswitch = \
NetworkHelper.get_vlanid_and_vswicth_for_portgroup(session, bridge)
NetworkHelper.get_vlanid_and_vswitch_for_portgroup(session, bridge)
#Check if the vsiwtch associated is proper
# Check if the vsiwtch associated is proper
if pg_vswitch != vswitch_associated:
raise exception.Invalid(_("vSwitch which contains the port group "
"%(bridge)s is not associated with the desired "
"physical adapter. Expected vSwitch is "
"%(vswitch_associated)s, but the one associated"
" is %(pg_vswitch)s") %\
{"bridge": bridge,
"vswitch_associated": vswitch_associated,
"pg_vswitch": pg_vswitch})
" is %(pg_vswitch)s") % locals())
#Check if the vlan id is proper for the port group
# Check if the vlan id is proper for the port group
if pg_vlanid != vlan_num:
raise exception.Invalid(_("VLAN tag is not appropriate for the "
"port group %(bridge)s. Expected VLAN tag is "
"%(vlan_num)s, but the one associated with the "
"port group is %(pg_vlanid)s") %\
{"bridge": bridge,
"vlan_num": vlan_num,
"pg_vlanid": pg_vlanid})
"port group is %(pg_vlanid)s") % locals())

View File

@ -16,8 +16,9 @@
# under the License.
"""
Test suite for VMWareAPI
Test suite for VMWareAPI.
"""
import stubout
from nova import context
@ -38,9 +39,7 @@ FLAGS = flags.FLAGS
class VMWareAPIVMTestCase(test.TestCase):
"""
Unit tests for Vmware API connection calls
"""
"""Unit tests for Vmware API connection calls."""
def setUp(self):
super(VMWareAPIVMTestCase, self).setUp()
@ -61,7 +60,7 @@ class VMWareAPIVMTestCase(test.TestCase):
self.conn = vmwareapi_conn.get_connection(False)
def _create_vm(self):
""" Create and spawn the VM """
"""Create and spawn the VM."""
values = {'name': 1,
'id': 1,
'project_id': self.project.id,
@ -78,15 +77,17 @@ class VMWareAPIVMTestCase(test.TestCase):
self._check_vm_record()
def _check_vm_record(self):
""" Check if the spawned VM's properties corresponds to the instance in
the db """
"""
Check if the spawned VM's properties correspond to the instance in
the db.
"""
instances = self.conn.list_instances()
self.assertEquals(len(instances), 1)
# Get Nova record for VM
vm_info = self.conn.get_info(1)
# Get record for VMs
# Get record for VM
vms = vmwareapi_fake._get_objects("VirtualMachine")
vm = vms[0]
@ -106,8 +107,10 @@ class VMWareAPIVMTestCase(test.TestCase):
self.assertEquals(vm.get("runtime.powerState"), 'poweredOn')
def _check_vm_info(self, info, pwr_state=power_state.RUNNING):
""" Check if the get_info returned values correspond to the instance
object in the db """
"""
Check if the get_info returned values correspond to the instance
object in the db.
"""
mem_kib = long(self.type_data['memory_mb']) << 10
self.assertEquals(info["state"], pwr_state)
self.assertEquals(info["max_mem"], mem_kib)
@ -194,8 +197,9 @@ class VMWareAPIVMTestCase(test.TestCase):
pass
def dummy_callback_handler(self, ret):
""" Dummy callback function to be passed to suspend, resume, etc.
calls """
"""
Dummy callback function to be passed to suspend, resume, etc., calls.
"""
pass
def tearDown(self):

View File

@ -14,3 +14,8 @@
# 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
=======================================
"""

View File

@ -26,7 +26,7 @@ from nova import utils
def stub_out_db_instance_api(stubs):
""" Stubs out the db API for creating Instances """
"""Stubs out the db API for creating Instances."""
INSTANCE_TYPES = {
'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1),
@ -38,7 +38,7 @@ def stub_out_db_instance_api(stubs):
dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)}
class FakeModel(object):
""" Stubs out for model """
"""Stubs out for model."""
def __init__(self, values):
self.values = values
@ -53,7 +53,7 @@ def stub_out_db_instance_api(stubs):
raise NotImplementedError()
def fake_instance_create(values):
""" Stubs out the db.instance_create method """
"""Stubs out the db.instance_create method."""
type_data = INSTANCE_TYPES[values['instance_type']]
@ -77,7 +77,7 @@ def stub_out_db_instance_api(stubs):
return FakeModel(base_options)
def fake_network_get_by_instance(context, instance_id):
""" Stubs out the db.network_get_by_instance method """
"""Stubs out the db.network_get_by_instance method."""
fields = {
'bridge': 'vmnet0',
@ -87,11 +87,11 @@ def stub_out_db_instance_api(stubs):
return FakeModel(fields)
def fake_instance_action_create(context, action):
""" Stubs out the db.instance_action_create method """
"""Stubs out the db.instance_action_create method."""
pass
def fake_instance_get_fixed_address(context, instance_id):
""" Stubs out the db.instance_get_fixed_address method """
"""Stubs out the db.instance_get_fixed_address method."""
return '10.10.10.10'
def fake_instance_type_get_all(context, inactive=0):

View File

@ -25,17 +25,17 @@ from nova.virt.vmwareapi import vmware_images
def fake_get_vim_object(arg):
""" Stubs out the VMWareAPISession's get_vim_object method """
"""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 """
"""Stubs out the VMWareAPISession's is_vim_object method."""
return isinstance(module, fake.FakeVim)
def set_stubs(stubs):
""" Set the stubs """
"""Set the stubs."""
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)

View File

@ -15,5 +15,5 @@
# License for the specific language governing permissions and limitations
# under the License.
"""
:mod:`vmwareapi` -- Nova support for VMware ESX/ESXi Server through vSphere API
:mod:`vmwareapi` -- Nova support for VMware ESX/ESXi Server through VMware API.
"""

View File

@ -16,7 +16,7 @@
# under the License.
"""
Exception classes and SOAP response error checking module
Exception classes and SOAP response error checking module.
"""
FAULT_NOT_AUTHENTICATED = "NotAuthenticated"
@ -24,7 +24,7 @@ FAULT_ALREADY_EXISTS = "AlreadyExists"
class VimException(Exception):
"""The VIM Exception class"""
"""The VIM Exception class."""
def __init__(self, exception_summary, excep):
Exception.__init__(self)
@ -36,17 +36,17 @@ class VimException(Exception):
class SessionOverLoadException(VimException):
"""Session Overload Exception"""
"""Session Overload Exception."""
pass
class VimAttributeError(VimException):
"""VI Attribute Error"""
"""VI Attribute Error."""
pass
class VimFaultException(Exception):
"""The VIM Fault exception class"""
"""The VIM Fault exception class."""
def __init__(self, fault_list, excep):
Exception.__init__(self)
@ -58,23 +58,37 @@ class VimFaultException(Exception):
class FaultCheckers:
"""Methods for fault checking of SOAP response. Per Method error handlers
"""
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 as a property and not as a SOAP fault."""
embedded in the SOAP messages as properties and not as SOAP faults.
"""
@classmethod
def retrieveproperties_fault_checker(self, resp_obj):
"""Checks the RetrieveProperties response for errors. Certain faults
are sent as a part of the SOAP body as property of missingSet.
For example NotAuthenticated fault"""
"""
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 = []
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 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 "

View File

@ -39,14 +39,14 @@ LOG = logging.getLogger("nova.virt.vmwareapi.fake")
def log_db_contents(msg=None):
""" Log DB Contents"""
"""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 """
"""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
@ -63,18 +63,18 @@ def reset():
def cleanup():
""" Clear the db contents """
"""Clear the db contents."""
for c in _CLASSES:
_db_content[c] = {}
def _create_object(table, obj):
""" Create an object in the db """
"""Create an object in the db."""
_db_content[table][obj.obj] = obj
def _get_objects(type):
""" Get objects of the type """
"""Get objects of the type."""
lst_objs = []
for key in _db_content[type]:
lst_objs.append(_db_content[type][key])
@ -82,7 +82,7 @@ def _get_objects(type):
class Prop(object):
""" Property Object base class """
"""Property Object base class."""
def __init__(self):
self.name = None
@ -96,10 +96,10 @@ class Prop(object):
class ManagedObject(object):
""" Managed Data Object base class """
"""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"""
"""Sets the obj property which acts as a reference to the object."""
object.__setattr__(self, 'objName', name)
if obj_ref is None:
obj_ref = str(uuid.uuid4())
@ -107,14 +107,18 @@ class ManagedObject(object):
object.__setattr__(self, 'propSet', [])
def set(self, attr, val):
""" Sets an attribute value. Not using the __setattr__ directly for we
"""
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 """
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 """
"""
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):
@ -138,7 +142,7 @@ class ManagedObject(object):
class DataObject(object):
""" Data object base class """
"""Data object base class."""
def __init__(self):
pass
@ -151,30 +155,32 @@ class DataObject(object):
class VirtualDisk(DataObject):
""" Virtual Disk class. Does nothing special except setting
"""
Virtual Disk class. Does nothing special except setting
__class__.__name__ to 'VirtualDisk'. Refer place where __class__.__name__
is used in the code """
is used in the code.
"""
def __init__(self):
DataObject.__init__(self)
class VirtualDiskFlatVer2BackingInfo(DataObject):
""" VirtualDiskFlatVer2BackingInfo class """
"""VirtualDiskFlatVer2BackingInfo class."""
def __init__(self):
DataObject.__init__(self)
class VirtualLsiLogicController(DataObject):
""" VirtualLsiLogicController class """
"""VirtualLsiLogicController class."""
def __init__(self):
DataObject.__init__(self)
class VirtualMachine(ManagedObject):
""" Virtual Machine class """
"""Virtual Machine class."""
def __init__(self, **kwargs):
ManagedObject.__init__(self, "VirtualMachine")
@ -195,8 +201,10 @@ class VirtualMachine(ManagedObject):
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 """
"""
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
@ -220,7 +228,7 @@ class VirtualMachine(ManagedObject):
class Network(ManagedObject):
""" Network class """
"""Network class."""
def __init__(self):
ManagedObject.__init__(self, "Network")
@ -228,7 +236,7 @@ class Network(ManagedObject):
class ResourcePool(ManagedObject):
""" Resource Pool class """
"""Resource Pool class."""
def __init__(self):
ManagedObject.__init__(self, "ResourcePool")
@ -236,7 +244,7 @@ class ResourcePool(ManagedObject):
class Datastore(ManagedObject):
""" Datastore class """
"""Datastore class."""
def __init__(self):
ManagedObject.__init__(self, "Datastore")
@ -245,7 +253,7 @@ class Datastore(ManagedObject):
class HostNetworkSystem(ManagedObject):
""" HostNetworkSystem class """
"""HostNetworkSystem class."""
def __init__(self):
ManagedObject.__init__(self, "HostNetworkSystem")
@ -261,7 +269,7 @@ class HostNetworkSystem(ManagedObject):
class HostSystem(ManagedObject):
""" Host System class """
"""Host System class."""
def __init__(self):
ManagedObject.__init__(self, "HostSystem")
@ -302,7 +310,7 @@ class HostSystem(ManagedObject):
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 """
"""Adds a port group to the host system object in the db."""
pg_name = spec.name
vswitch_name = spec.vswitchName
vlanid = spec.vlanId
@ -328,7 +336,7 @@ class HostSystem(ManagedObject):
class Datacenter(ManagedObject):
""" Datacenter class """
"""Datacenter class."""
def __init__(self):
ManagedObject.__init__(self, "Datacenter")
@ -343,7 +351,7 @@ class Datacenter(ManagedObject):
class Task(ManagedObject):
""" Task class """
"""Task class."""
def __init__(self, task_name, state="running"):
ManagedObject.__init__(self, "Task")
@ -390,12 +398,12 @@ def create_task(task_name, state="running"):
def _add_file(file_path):
""" Adds a file reference to the db """
"""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 """
"""Removes a file reference from the db."""
if _db_content.get("files", None) is None:
raise exception.NotFound(_("No files have been added yet"))
#Check if the remove is for a single file object or for a folder
@ -415,7 +423,7 @@ def _remove_file(file_path):
def fake_fetch_image(image, instance, **kwargs):
"""Fakes fetch image call. Just adds a reference to the db for the file """
"""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
@ -423,19 +431,19 @@ def fake_fetch_image(image, instance, **kwargs):
def fake_upload_image(image, instance, **kwargs):
"""Fakes the upload of an image """
"""Fakes the upload of an image."""
pass
def fake_get_vmdk_size_and_properties(image_id, instance):
""" Fakes the file size and properties fetch for the image file """
"""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 """
"""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"):
@ -445,22 +453,24 @@ def _get_vm_mdo(vm_ref):
class FakeFactory(object):
""" Fake factory class for the suds client """
"""Fake factory class for the suds client."""
def __init__(self):
pass
def create(self, obj_name):
""" Creates a namespace object """
"""Creates a namespace object."""
return DataObject()
class FakeVim(object):
"""Fake VIM Class"""
"""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 """
"""
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()
@ -490,7 +500,7 @@ class FakeVim(object):
return "Fake VIM Object"
def _login(self):
""" Logs in and sets the session object in the db """
"""Logs in and sets the session object in the db."""
self._session = str(uuid.uuid4())
session = DataObject()
session.key = self._session
@ -498,7 +508,7 @@ class FakeVim(object):
return session
def _logout(self):
""" Logs out and remove the session object ref from the db """
"""Logs out and remove the session object ref from the db."""
s = self._session
self._session = None
if s not in _db_content['session']:
@ -508,14 +518,14 @@ class FakeVim(object):
del _db_content['session'][s]
def _terminate_session(self, *args, **kwargs):
""" Terminates a session """
"""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 """
"""Checks if the session is active."""
if (self._session is None or self._session not in
_db_content['session']):
LOG.debug(_("Session is faulty"))
@ -524,7 +534,7 @@ class FakeVim(object):
_("Session Invalid"))
def _create_vm(self, method, *args, **kwargs):
""" Creates and registers a VM object with the Host System """
"""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,
@ -539,7 +549,7 @@ class FakeVim(object):
return task_mdo.obj
def _reconfig_vm(self, method, *args, **kwargs):
""" Reconfigures a VM and sets the properties supplied """
"""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"))
@ -547,7 +557,7 @@ class FakeVim(object):
return task_mdo.obj
def _create_copy_disk(self, method, vmdk_file_path):
""" Creates/copies a vmdk file object in the datastore """
"""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")
@ -557,12 +567,12 @@ class FakeVim(object):
return task_mdo.obj
def _snapshot_vm(self, method):
""" Snapshots a VM. Here we do nothing for faking sake """
"""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 """
"""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")
@ -572,23 +582,23 @@ class FakeVim(object):
return task_mdo.obj
def _delete_file(self, method, *args, **kwargs):
""" Deletes a file from the datastore """
"""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 """
"""Fakes a return."""
return
def _unregister_vm(self, method, *args, **kwargs):
""" Unregisters a VM from the Host System """
"""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 """
"""Searches the datastore for a file."""
ds_path = kwargs.get("datastorePath")
if _db_content.get("files", None) is None:
raise exception.NotFound(_("No files have been added yet"))
@ -600,14 +610,14 @@ class FakeVim(object):
return task_mdo.obj
def _make_dir(self, method, *args, **kwargs):
""" Creates a directory in the datastore """
"""Creates a directory in the datastore."""
ds_path = kwargs.get("name")
if _db_content.get("files", None) is None:
raise exception.NotFound(_("No files have been added yet"))
_db_content["files"].append(ds_path)
def _set_power_state(self, method, vm_ref, pwr_state="poweredOn"):
""" Sets power state for the VM """
"""Sets power state for the VM."""
if _db_content.get("VirtualMachine", None) is None:
raise exception.NotFound(_(" No Virtual Machine has been "
"registered yet"))
@ -620,7 +630,7 @@ class FakeVim(object):
return task_mdo.obj
def _retrieve_properties(self, method, *args, **kwargs):
""" Retrieves properties based on the type """
"""Retrieves properties based on the type."""
spec_set = kwargs.get("specSet")[0]
type = spec_set.propSet[0].type
properties = spec_set.propSet[0].pathSet
@ -654,7 +664,7 @@ class FakeVim(object):
return lst_ret_objs
def _add_port_group(self, method, *args, **kwargs):
""" Adds a port group to the host system """
"""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"))

View File

@ -16,7 +16,7 @@
# under the License.
"""
Utility functions for ESX Networking
Utility functions for ESX Networking.
"""
from nova import exception
@ -32,8 +32,10 @@ class NetworkHelper:
@classmethod
def get_network_with_the_name(cls, session, network_name="vmnet0"):
""" Gets reference to the network whose name is passed as the
argument. """
"""
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
@ -44,7 +46,7 @@ class NetworkHelper:
return None
vm_networks = vm_networks_ret.ManagedObjectReference
networks = session._call_method(vim_util,
"get_properites_for_a_collection_of_objects",
"get_properties_for_a_collection_of_objects",
"Network", vm_networks, ["summary.name"])
for network in networks:
if network.propSet[0].val == network_name:
@ -53,8 +55,10 @@ class NetworkHelper:
@classmethod
def get_vswitch_for_vlan_interface(cls, session, vlan_interface):
""" Gets the vswitch associated with the physical
network adapter with the name supplied"""
"""
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
@ -77,7 +81,7 @@ class NetworkHelper:
@classmethod
def check_if_vlan_interface_exists(cls, session, vlan_interface):
""" Checks if the vlan_inteface exists on the esx host """
"""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,
@ -93,8 +97,8 @@ class NetworkHelper:
return False
@classmethod
def get_vlanid_and_vswicth_for_portgroup(cls, session, pg_name):
""" Get the vlan id and vswicth associated with the port group """
def get_vlanid_and_vswitch_for_portgroup(cls, 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,
@ -113,8 +117,10 @@ class NetworkHelper:
@classmethod
def create_port_group(cls, 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 """
"""
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,

View File

@ -15,7 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
""" Classes to handle image files
"""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.
@ -29,8 +29,6 @@ import urlparse
from nova import flags
from nova import log as logging
from nova import utils
from nova.auth.manager import AuthManager
FLAGS = flags.FLAGS
@ -41,145 +39,34 @@ USER_AGENT = "OpenStack-ESX-Adapter"
LOG = logging.getLogger("nova.virt.vmwareapi.read_write_util")
class ImageServiceFile:
"""The base image service class"""
def __init__(self, file_handle):
self.eof = False
self.file_handle = file_handle
def write(self, data):
"""Write data to the file"""
raise NotImplementedError
def read(self, chunk_size=READ_CHUNKSIZE):
"""Read a chunk of data from the file"""
raise NotImplementedError
def get_size(self):
"""Get the size of the file whose data is to be read"""
raise NotImplementedError
def set_eof(self, eof):
"""Set the end of file marker"""
self.eof = eof
def get_eof(self):
"""Check if the file end has been reached or not"""
return self.eof
def close(self):
"""Close the file handle"""
try:
self.file_handle.close()
except Exception:
pass
def get_image_properties(self):
"""Get the image properties"""
raise NotImplementedError
def __del__(self):
"""Close the file handle on garbage collection"""
self.close()
class GlanceHTTPWriteFile(ImageServiceFile):
"""Glance file write handler class"""
def __init__(self, host, port, image_id, file_size, os_type, adapter_type,
version=1, scheme="http"):
base_url = "%s://%s:%s/images/%s" % (scheme, host, port, image_id)
(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)
conn.putheader("User-Agent", USER_AGENT)
conn.putheader("Content-Length", file_size)
conn.putheader("Content-Type", "application/octet-stream")
conn.putheader("x-image-meta-store", "file")
conn.putheader("x-image-meta-is_public", "True")
conn.putheader("x-image-meta-type", "raw")
conn.putheader("x-image-meta-size", file_size)
conn.putheader("x-image-meta-property-kernel_id", "")
conn.putheader("x-image-meta-property-ramdisk_id", "")
conn.putheader("x-image-meta-property-vmware_ostype", os_type)
conn.putheader("x-image-meta-property-vmware_adaptertype",
adapter_type)
conn.putheader("x-image-meta-property-vmware_image_version", version)
conn.endheaders()
ImageServiceFile.__init__(self, conn)
def write(self, data):
"""Write data to the file"""
self.file_handle.send(data)
class GlanceHTTPReadFile(ImageServiceFile):
"""Glance file read handler class"""
def __init__(self, host, port, image_id, scheme="http"):
base_url = "%s://%s:%s/images/%s" % (scheme, host, port,
urllib.pathname2url(image_id))
headers = {'User-Agent': USER_AGENT}
request = urllib2.Request(base_url, None, headers)
conn = urllib2.urlopen(request)
ImageServiceFile.__init__(self, conn)
def read(self, chunk_size=READ_CHUNKSIZE):
"""Read a chunk of data"""
return self.file_handle.read(chunk_size)
def get_size(self):
"""Get the size of the file to be read"""
return self.file_handle.headers.get("X-Image-Meta-Size", -1)
def get_image_properties(self):
"""Get the image properties like say OS Type and the
Adapter Type
"""
return {"vmware_ostype":
self.file_handle.headers.get(
"X-Image-Meta-Property-Vmware_ostype"),
"vmware_adaptertype":
self.file_handle.headers.get(
"X-Image-Meta-Property-Vmware_adaptertype"),
"vmware_image_version":
self.file_handle.headers.get(
"X-Image-Meta-Property-Vmware_image_version")}
class VMwareHTTPFile(object):
"""Base class for HTTP file"""
"""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"""
"""Set the end of file marker."""
self.eof = eof
def get_eof(self):
"""Check if the end of file has been reached"""
"""Check if the end of file has been reached."""
return self.eof
def close(self):
"""Close the file handle"""
"""Close the file handle."""
try:
self.file_handle.close()
except Exception:
pass
def __del__(self):
"""Close the file handle on garbage collection"""
"""Close the file handle on garbage collection."""
self.close()
def _build_vim_cookie_headers(self, vim_cookies):
"""Build ESX host session cookie headers"""
"""Build ESX host session cookie headers."""
cookie_header = ""
for vim_cookie in vim_cookies:
cookie_header = vim_cookie.name + "=" + vim_cookie.value
@ -187,20 +74,20 @@ class VMwareHTTPFile(object):
return cookie_header
def write(self, data):
"""Write data to the file"""
"""Write data to the file."""
raise NotImplementedError
def read(self, chunk_size=READ_CHUNKSIZE):
"""Read a chunk of data"""
"""Read a chunk of data."""
raise NotImplementedError
def get_size(self):
"""Get size of the file to be read"""
"""Get size of the file to be read."""
raise NotImplementedError
class VMWareHTTPWriteFile(VMwareHTTPFile):
"""VMWare file write handler class"""
"""VMWare file write handler class."""
def __init__(self, host, data_center_name, datastore_name, cookies,
file_path, file_size, scheme="https"):
@ -222,11 +109,11 @@ class VMWareHTTPWriteFile(VMwareHTTPFile):
VMwareHTTPFile.__init__(self, conn)
def write(self, data):
"""Write to the file"""
"""Write to the file."""
self.file_handle.send(data)
def close(self):
"""Get the response and close the connection"""
"""Get the response and close the connection."""
try:
self.conn.getresponse()
except Exception, excep:
@ -236,7 +123,7 @@ class VMWareHTTPWriteFile(VMwareHTTPFile):
class VmWareHTTPReadFile(VMwareHTTPFile):
"""VMWare file read handler class"""
"""VMWare file read handler class."""
def __init__(self, host, data_center_name, datastore_name, cookies,
file_path, scheme="https"):
@ -251,9 +138,9 @@ class VmWareHTTPReadFile(VMwareHTTPFile):
VMwareHTTPFile.__init__(self, conn)
def read(self, chunk_size=READ_CHUNKSIZE):
"""Read a chunk of data"""
"""Read a chunk of data."""
return self.file_handle.read(chunk_size)
def get_size(self):
"""Get size of the file to be read"""
"""Get size of the file to be read."""
return self.file_handle.headers.get("Content-Length", -1)

View File

@ -16,7 +16,7 @@
# under the License.
"""
Classes for making VMware VI SOAP calls
Classes for making VMware VI SOAP calls.
"""
import httplib
@ -37,49 +37,50 @@ FLAGS = flags.FLAGS
flags.DEFINE_string('vmwareapi_wsdl_loc',
None,
'VIM Service WSDL Location'
'E.g http://<server>/vimService.wsdl'
'e.g http://<server>/vimService.wsdl'
'Due to a bug in vSphere ESX 4.1 default wsdl'
'Read the readme for vmware to setup')
'Refer readme-vmware to setup')
class VIMMessagePlugin(MessagePlugin):
def addAttributeForValue(self, node):
#suds does not handle AnyType properly
#VI SDK requires type attribute to be set when AnyType is used
# 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.
"""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
nodes and fixup nodes before sending it to the server.
"""
#suds builds the entire request object based on the wsdl schema
#VI SDK throws server errors if optional SOAP nodes are sent without
#values. E.g <test/> as opposed to <test>test</test>
# suds builds the entire request object based on the wsdl schema.
# VI SDK throws server errors if optional SOAP nodes are sent without
# values, e.g. <test/> as opposed to <test>test</test>
context.envelope.prune()
context.envelope.walk(self.addAttributeForValue)
class Vim:
"""The VIM Object"""
"""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]
Creates the necessary Communication interfaces, Gets the
ServiceContent for initiating SOAP transactions
"""
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"))
#Use this when VMware fixes their faulty wsdl
# 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)
@ -89,37 +90,41 @@ class Vim:
self.RetrieveServiceContent("ServiceInstance")
def get_service_content(self):
"""Gets the service content object"""
"""Gets the service content object."""
return self._service_content
def __getattr__(self, attr_name):
"""Makes the API calls and gets the result"""
"""Makes the API calls and gets the result."""
try:
return object.__getattr__(self, attr_name)
except AttributeError:
def vim_request_handler(managed_object, **kwargs):
"""managed_object : Managed Object Reference or Managed
Object Name
**kw : Keyword arguments of the call
"""
#Dynamic handler for VI SDK Calls
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
# 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
# Catch the VimFaultException that is raised by the fault
# check of the SOAP response
except error_util.VimFaultException, excep:
raise
except WebFault, excep:
@ -155,8 +160,8 @@ class Vim:
return vim_request_handler
def _request_managed_object_builder(self, managed_object):
"""Builds the request managed object"""
#Request Managed Object Builder
"""Builds the request managed object."""
# Request Managed Object Builder
if type(managed_object) == type(""):
mo = Property(managed_object)
mo._type = managed_object

View File

@ -16,19 +16,19 @@
# under the License.
"""
The VMware API utility module
The VMware API utility module.
"""
def build_selcetion_spec(client_factory, name):
""" Builds the selection spec """
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, type, path, skip, select_set):
""" Builds the traversal spec object """
"""Builds the traversal spec object."""
traversal_spec = client_factory.create('ns0:TraversalSpec')
traversal_spec.name = name
traversal_spec.type = type
@ -39,9 +39,11 @@ def build_traversal_spec(client_factory, name, type, path, skip, select_set):
def build_recursive_traversal_spec(client_factory):
""" Builds the Recursive Traversal Spec to traverse the object managed
object hierarchy """
visit_folders_select_spec = build_selcetion_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 datacnetr
dc_to_hf = build_traversal_spec(client_factory, "dc_to_hf", "Datacenter",
@ -64,8 +66,8 @@ def build_recursive_traversal_spec(client_factory):
cr_to_ds = build_traversal_spec(client_factory, "cr_to_ds",
"ComputeResource", "datastore", False, [])
rp_to_rp_select_spec = build_selcetion_spec(client_factory, "rp_to_rp")
rp_to_vm_select_spec = build_selcetion_spec(client_factory, "rp_to_vm")
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,
@ -94,7 +96,7 @@ def build_recursive_traversal_spec(client_factory):
def build_property_spec(client_factory, type="VirtualMachine",
properties_to_collect=["name"],
all_properties=False):
"""Builds the Property Spec"""
"""Builds the Property Spec."""
property_spec = client_factory.create('ns0:PropertySpec')
property_spec.all = all_properties
property_spec.pathSet = properties_to_collect
@ -103,7 +105,7 @@ def build_property_spec(client_factory, type="VirtualMachine",
def build_object_spec(client_factory, root_folder, traversal_specs):
"""Builds the object Spec"""
"""Builds the object Spec."""
object_spec = client_factory.create('ns0:ObjectSpec')
object_spec.obj = root_folder
object_spec.skip = False
@ -112,7 +114,7 @@ def build_object_spec(client_factory, root_folder, traversal_specs):
def build_property_filter_spec(client_factory, property_specs, object_specs):
"""Builds the Property Filter Spec"""
"""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
@ -120,7 +122,7 @@ def build_property_filter_spec(client_factory, property_specs, object_specs):
def get_object_properties(vim, collector, mobj, type, properties):
"""Gets the properties of the Managed object specified"""
"""Gets the properties of the Managed object specified."""
client_factory = vim.client.factory
if mobj is None:
return None
@ -141,7 +143,7 @@ def get_object_properties(vim, collector, mobj, type, properties):
def get_dynamic_property(vim, mobj, type, property_name):
"""Gets a particular property of the Managed Object"""
"""Gets a particular property of the Managed Object."""
obj_content = \
get_object_properties(vim, None, mobj, type, [property_name])
property_value = None
@ -153,7 +155,7 @@ def get_dynamic_property(vim, mobj, type, property_name):
def get_objects(vim, type, properties_to_collect=["name"], all=False):
"""Gets the list of objects of the type specified"""
"""Gets the list of objects of the type specified."""
client_factory = vim.client.factory
object_spec = build_object_spec(client_factory,
vim.get_service_content().rootFolder,
@ -169,7 +171,7 @@ def get_objects(vim, type, properties_to_collect=["name"], all=False):
def get_prop_spec(client_factory, type, properties):
"""Builds the Property Spec Object"""
"""Builds the Property Spec Object."""
prop_spec = client_factory.create('ns0:PropertySpec')
prop_spec.type = type
prop_spec.pathSet = properties
@ -177,7 +179,7 @@ def get_prop_spec(client_factory, type, properties):
def get_obj_spec(client_factory, obj, select_set=None):
"""Builds the Object Spec object"""
"""Builds the Object Spec object."""
obj_spec = client_factory.create('ns0:ObjectSpec')
obj_spec.obj = obj
obj_spec.skip = False
@ -187,7 +189,7 @@ def get_obj_spec(client_factory, obj, select_set=None):
def get_prop_filter_spec(client_factory, obj_spec, prop_spec):
"""Builds the Property Filter Spec Object"""
"""Builds the Property Filter Spec Object."""
prop_filter_spec = \
client_factory.create('ns0:PropertyFilterSpec')
prop_filter_spec.propSet = prop_spec
@ -195,10 +197,11 @@ def get_prop_filter_spec(client_factory, obj_spec, prop_spec):
return prop_filter_spec
def get_properites_for_a_collection_of_objects(vim, type,
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
"""
Gets the list of properties for the collection of
objects of the type specified.
"""
client_factory = vim.client.factory
if len(obj_list) == 0:

View File

@ -15,18 +15,19 @@
# License for the specific language governing permissions and limitations
# under the License.
"""
The VMware API VM utility module to build SOAP object specs
The VMware API VM utility module to build SOAP object specs.
"""
def build_datastore_path(datastore_name, path):
"""Build the datastore compliant 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
"""
Split the VMWare style datastore path to get the Datastore
name and the entity path.
"""
spl = datastore_path.split('[', 1)[1].split(']', 1)
path = ""
@ -40,7 +41,7 @@ def split_datastore_path(datastore_path):
def get_vm_create_spec(client_factory, instance, data_store_name,
network_name="vmnet0",
os_type="otherGuest"):
"""Builds the VM Create spec"""
"""Builds the VM Create spec."""
config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
config_spec.name = instance.name
config_spec.guestId = os_type
@ -70,11 +71,12 @@ def get_vm_create_spec(client_factory, instance, data_store_name,
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
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"
@ -88,13 +90,15 @@ def create_controller_spec(client_factory, key):
def create_network_spec(client_factory, network_name, mac_address):
"""Builds a config spec for the addition of a new network
adapter to the VM"""
"""
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
# Get the recommended card type for the VM based on the guest OS of the VM
net_device = client_factory.create('ns0:VirtualPCNet32')
backing = \
@ -110,9 +114,9 @@ def create_network_spec(client_factory, network_name, mac_address):
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
# 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
@ -124,14 +128,14 @@ def create_network_spec(client_factory, network_name, mac_address):
def get_vmdk_attach_config_spec(client_factory,
disksize, file_path, adapter_type="lsiLogic"):
"""Builds the vmdk attach config spec"""
"""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
# 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
# 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:
@ -149,7 +153,7 @@ def get_vmdk_attach_config_spec(client_factory,
def get_vmdk_file_path_and_adapter_type(client_factory, hardware_devices):
"""Gets the vmdk file path and the storage adapter type"""
"""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
@ -177,7 +181,7 @@ def get_vmdk_file_path_and_adapter_type(client_factory, hardware_devices):
def get_copy_virtual_disk_spec(client_factory, adapter_type="lsilogic"):
"""Builds the Virtual Disk copy spec"""
"""Builds the Virtual Disk copy spec."""
dest_spec = client_factory.create('ns0:VirtualDiskSpec')
dest_spec.adapterType = adapter_type
dest_spec.diskType = "thick"
@ -185,7 +189,7 @@ def get_copy_virtual_disk_spec(client_factory, adapter_type="lsilogic"):
def get_vmdk_create_spec(client_factory, size_in_kb, adapter_type="lsiLogic"):
"""Builds the virtual disk create spec"""
"""Builds the virtual disk create spec."""
create_vmdk_spec = \
client_factory.create('ns0:FileBackedVirtualDiskSpec')
create_vmdk_spec.adapterType = adapter_type
@ -196,8 +200,10 @@ def get_vmdk_create_spec(client_factory, size_in_kb, adapter_type="lsiLogic"):
def create_virtual_disk_spec(client_factory,
disksize, controller_key, file_path=None):
"""Creates a Spec for the addition/attaching of a
Virtual Disk to the VM"""
"""
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"
@ -223,9 +229,9 @@ def create_virtual_disk_spec(client_factory,
virtual_disk.backing = disk_file_backing
virtual_disk.connectable = connectable_spec
#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
# 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
@ -237,7 +243,7 @@ def create_virtual_disk_spec(client_factory,
def get_dummy_vm_create_spec(client_factory, name, data_store_name):
"""Builds the dummy VM create spec"""
"""Builds the dummy VM create spec."""
config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
config_spec.name = name
@ -269,7 +275,7 @@ def get_dummy_vm_create_spec(client_factory, name, data_store_name):
def get_machine_id_change_spec(client_factory, mac, ip_addr, netmask, gateway):
"""Builds the machine id change config spec"""
"""Builds the machine id change config spec."""
machine_id_str = "%s;%s;%s;%s" % (mac, ip_addr, netmask, gateway)
virtual_machine_config_spec = \
client_factory.create('ns0:VirtualMachineConfigSpec')
@ -283,12 +289,12 @@ def get_machine_id_change_spec(client_factory, mac, ip_addr, netmask, gateway):
def get_add_vswitch_port_group_spec(client_factory, vswitch_name,
port_group_name, vlan_id):
"""Builds the virtual switch port group add spec"""
"""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.
# 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')

View File

@ -47,14 +47,14 @@ VMWARE_POWER_STATES = {
class VMWareVMOps(object):
""" Management class for VM-related tasks """
"""Management class for VM-related tasks."""
def __init__(self, session):
""" Initializer """
"""Initializer."""
self._session = session
def _wait_with_callback(self, instance_id, task, callback):
""" Waits for the task to finish and does a callback after """
"""Waits for the task to finish and does a callback after."""
ret = None
try:
ret = self._session._wait_for_task(instance_id, task)
@ -63,7 +63,7 @@ class VMWareVMOps(object):
callback(ret)
def list_instances(self):
""" Lists the VM instances that are registered with the ESX host """
"""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",
@ -96,7 +96,7 @@ class VMWareVMOps(object):
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
6. Power on the VM.
"""
vm_ref = self._get_vm_ref_from_the_name(instance.name)
if vm_ref:
@ -122,7 +122,7 @@ class VMWareVMOps(object):
_check_if_network_bridge_exists()
def _get_datastore_ref():
# Get the datastore list and choose the first local storage
"""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:
@ -133,7 +133,7 @@ class VMWareVMOps(object):
ds_type = prop.val
elif prop.name == "summary.name":
ds_name = prop.val
#Local storage identifier
# Local storage identifier
if ds_type == "VMFS":
data_store_name = ds_name
return data_store_name
@ -146,8 +146,10 @@ class VMWareVMOps(object):
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.
"""
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(
instance.image_id, instance)
@ -160,28 +162,29 @@ class VMWareVMOps(object):
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
"""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
# 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.
# 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()
#Get the create vm config spec
# Get the create vm config spec
config_spec = vm_util.get_vm_create_spec(client_factory, instance,
data_store_name, net_name, 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
# Create the VM on the ESX host
vm_create_task = self._session._call_method(
self._session._get_vim(),
"CreateVM_Task", vm_folder_mor,
@ -196,11 +199,11 @@ class VMWareVMOps(object):
# Set the machine id for the VM for setting the IP
self._set_machine_id(client_factory, instance)
#Naming the VM files in correspondence with the VM instance name
# 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
# 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)
@ -208,12 +211,13 @@ class VMWareVMOps(object):
uploaded_vmdk_name)
def _create_virtual_disk():
#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
"""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"
@ -245,7 +249,7 @@ class VMWareVMOps(object):
"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.
# Delete the -flat.vmdk file created. .vmdk file is retained.
vmdk_delete_task = self._session._call_method(
self._session._get_vim(),
"DeleteDatastoreFile_Task",
@ -262,12 +266,13 @@ class VMWareVMOps(object):
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_id)s to the ESX "
"data store %(data_store_name)s") %
({'image_id': instance.image_id,
'data_store_name': data_store_name}))
#Upload the -flat.vmdk file whose meta-data file we just created
#above
# Upload the -flat.vmdk file whose meta-data file we just created
# above
vmware_images.fetch_image(
instance.image_id,
instance,
@ -285,8 +290,10 @@ class VMWareVMOps(object):
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.
"""
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,
@ -304,8 +311,9 @@ class VMWareVMOps(object):
_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 the VM
power_on_task = self._session._call_method(
self._session._get_vim(),
"PowerOnVM_Task", vm_ref)
@ -325,7 +333,7 @@ class VMWareVMOps(object):
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
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:
@ -336,7 +344,7 @@ class VMWareVMOps(object):
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
# 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")
@ -355,7 +363,7 @@ class VMWareVMOps(object):
os_type = _get_vm_and_vmdk_attribs()
def _create_vm_snapshot():
#Create a snapshot of the VM
# Create a snapshot of the VM
LOG.debug(_("Creating Snapshot of the VM instance %s ") %
instance.name)
snapshot_task = self._session._call_method(
@ -372,8 +380,8 @@ class VMWareVMOps(object):
_create_vm_snapshot()
def _check_if_tmp_folder_exists():
#Copy the contents of the VM that were there just before the
#snapshot was taken
# 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,
@ -388,7 +396,7 @@ class VMWareVMOps(object):
ds_ref,
"Datastore",
"browser")
#Check if the vmware-tmp folder exists or not. If not, create one
# 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):
@ -397,17 +405,17 @@ class VMWareVMOps(object):
_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.
# 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 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 "
@ -431,7 +439,7 @@ class VMWareVMOps(object):
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.
# Upload the contents of -flat.vmdk file which has the disk data.
LOG.debug(_("Uploading image %s") % snapshot_name)
vmware_images.upload_image(
snapshot_name,
@ -449,7 +457,11 @@ class VMWareVMOps(object):
_upload_vmdk_to_image_repository()
def _clean_temp_data():
#Delete the temporary vmdk created above.
"""
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(
@ -465,7 +477,7 @@ class VMWareVMOps(object):
_clean_temp_data()
def reboot(self, instance):
""" Reboot a VM instance """
"""Reboot a VM instance."""
vm_ref = self._get_vm_ref_from_the_name(instance.name)
if vm_ref is None:
raise exception.NotFound(_("instance - %s not present") %
@ -483,13 +495,13 @@ class VMWareVMOps(object):
elif prop.name == "summary.guest.toolsStatus":
tools_status = prop.val
#Raise an exception if the VM is not powered On.
# Raise an exception if the VM is not powered On.
if pwr_state not in ["poweredOn"]:
raise exception.Invalid(_("instance - %s not poweredOn. So can't "
"be rebooted.") % instance.name)
#If vmware tools are installed in the VM, then do a guest reboot.
#Otherwise do a hard reset.
# If vmware tools are installed in the VM, then do a guest reboot.
# Otherwise do a hard reset.
if tools_status not in ['toolsNotInstalled', 'toolsNotRunning']:
LOG.debug(_("Rebooting guest OS of VM %s") % instance.name)
self._session._call_method(self._session._get_vim(), "RebootGuest",
@ -507,7 +519,7 @@ class VMWareVMOps(object):
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
3. Delete the contents of the folder holding the VM related data.
"""
try:
vm_ref = self._get_vm_ref_from_the_name(instance.name)
@ -529,7 +541,7 @@ class VMWareVMOps(object):
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.
# 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(
@ -538,7 +550,7 @@ class VMWareVMOps(object):
self._session._wait_for_task(instance.id, poweroff_task)
LOG.debug(_("Powered off the VM %s") % instance.name)
#Un-register the VM
# Un-register the VM
try:
LOG.debug(_("Unregistering the VM %s") % instance.name)
self._session._call_method(self._session._get_vim(),
@ -548,7 +560,8 @@ class VMWareVMOps(object):
LOG.warn(_("In vmwareapi:vmops:destroy, got this exception"
" while un-registering the VM: %s") % str(excep))
#Delete the folder holding the VM related content on the datastore.
# Delete the folder holding the VM related content on
# the datastore.
try:
dir_ds_compliant_path = vm_util.build_datastore_path(
datastore_name,
@ -564,7 +577,7 @@ class VMWareVMOps(object):
name=dir_ds_compliant_path)
self._session._wait_for_task(instance.id, delete_task)
LOG.debug(_("Deleted contents of the VM %(name)s from "
"datastore %(datastore_name)s")
"datastore %(datastore_name)s") %
({'name': instance.name,
'datastore_name': datastore_name}))
except Exception, excep:
@ -576,15 +589,15 @@ class VMWareVMOps(object):
LOG.exception(e)
def pause(self, instance, callback):
""" Pause a VM instance """
"""Pause a VM instance."""
raise exception.APIError("pause not supported for vmwareapi")
def unpause(self, instance, callback):
""" Un-Pause a VM instance """
"""Un-Pause a VM instance."""
raise exception.APIError("unpause not supported for vmwareapi")
def suspend(self, instance, callback):
""" Suspend the specified instance """
"""Suspend the specified instance."""
vm_ref = self._get_vm_ref_from_the_name(instance.name)
if vm_ref is None:
raise exception.NotFound(_("instance - %s not present") %
@ -593,14 +606,14 @@ class VMWareVMOps(object):
pwr_state = self._session._call_method(vim_util,
"get_dynamic_property", vm_ref,
"VirtualMachine", "runtime.powerState")
#Only PoweredOn VMs can be suspended.
# 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._wait_with_callback(instance.id, suspend_task, callback)
LOG.debug(_("Suspended the VM %s ") % instance.name)
#Raise Exception if VM is poweredOff
# Raise Exception if VM is poweredOff
elif pwr_state == "poweredOff":
raise exception.Invalid(_("instance - %s is poweredOff and hence "
" can't be suspended.") % instance.name)
@ -608,7 +621,7 @@ class VMWareVMOps(object):
"without doing anything") % instance.name)
def resume(self, instance, callback):
""" Resume the specified instance """
"""Resume the specified instance."""
vm_ref = self._get_vm_ref_from_the_name(instance.name)
if vm_ref is None:
raise exception.NotFound(_("instance - %s not present") %
@ -629,7 +642,7 @@ class VMWareVMOps(object):
"and hence can't be Resumed.") % instance.name)
def get_info(self, instance_name):
""" Return data about the VM instance """
"""Return data about the VM instance."""
vm_ref = self._get_vm_ref_from_the_name(instance_name)
if vm_ref is None:
raise exception.NotFound(_("instance - %s not present") %
@ -661,12 +674,12 @@ class VMWareVMOps(object):
'cpu_time': 0}
def get_diagnostics(self, instance):
""" Return data about VM diagnostics """
"""Return data about VM diagnostics."""
raise exception.APIError("get_diagnostics not implemented for "
"vmwareapi")
def get_console_output(self, instance):
""" Return snapshot of console """
"""Return snapshot of console."""
vm_ref = self._get_vm_ref_from_the_name(instance.name)
if vm_ref is None:
raise exception.NotFound(_("instance - %s not present") %
@ -688,12 +701,14 @@ class VMWareVMOps(object):
return ""
def get_ajax_console(self, instance):
""" Return link to instance's ajax console """
"""Return link to instance's ajax console."""
return 'http://fakeajaxconsole/fake_url'
def _set_machine_id(self, client_factory, instance):
""" Set the machine id of the VM for guest tools to pick up and change
the IP """
"""
Set the machine id of the VM for guest tools to pick up and change
the IP.
"""
vm_ref = self._get_vm_ref_from_the_name(instance.name)
if vm_ref is None:
raise exception.NotFound(_("instance - %s not present") %
@ -722,19 +737,19 @@ class VMWareVMOps(object):
'ip_addr': ip_addr}))
def _get_datacenter_name_and_ref(self):
""" Get the datacenter name and the reference. """
"""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"""
"""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.
# 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",
@ -748,9 +763,11 @@ class VMWareVMOps(object):
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 formed at the topmost level of the
DataStore. """
"""
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,
@ -758,7 +775,7 @@ class VMWareVMOps(object):
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. """
"""Get reference to the VM with the name specified."""
vms = self._session._call_method(vim_util, "get_objects",
"VirtualMachine", ["name"])
for vm in vms:

View File

@ -15,15 +15,14 @@
# License for the specific language governing permissions and limitations
# under the License.
"""
Utility functions for Image transfer
Utility functions for Image transfer.
"""
import time
import glance.client
from nova import exception
from nova import flags
from nova import log as logging
from nova.virt.vmwareapi import io_util
from nova.virt.vmwareapi import read_write_util
FLAGS = flags.FLAGS
@ -35,44 +34,9 @@ WRITE_CHUNKSIZE = 2 * 1024 * 1024
LOG = logging.getLogger("nova.virt.vmwareapi.vmware_images")
def start_transfer(read_file_handle, write_file_handle, data_size):
""" Start the data transfer from the read handle to the write handle. """
#The thread safe pipe
thread_safe_pipe = io_util.ThreadSafePipe(QUEUE_BUFFER_SIZE)
#The read thread
read_thread = io_util.IOThread(read_file_handle, thread_safe_pipe,
READ_CHUNKSIZE, long(data_size))
#The write thread
write_thread = io_util.IOThread(thread_safe_pipe, write_file_handle,
WRITE_CHUNKSIZE, long(data_size))
read_thread.start()
write_thread.start()
LOG.debug(_("Starting image file transfer"))
#Wait till both the read thread and the write thread are done
while not (read_thread.is_done() and write_thread.is_done()):
if read_thread.get_error() or write_thread.get_error():
read_thread.stop_io_transfer()
write_thread.stop_io_transfer()
# If there was an exception in reading or writing, raise the same.
read_excep = read_thread.get_exception()
write_excep = write_thread.get_exception()
if read_excep is not None:
LOG.exception(str(read_excep))
raise exception.Error(read_excep)
if write_excep is not None:
LOG.exception(str(write_excep))
raise exception.Error(write_excep)
time.sleep(2)
LOG.debug(_("Finished image file transfer and closing the file handles"))
#Close the file handles
read_file_handle.close()
write_file_handle.close()
def fetch_image(image, instance, **kwargs):
""" Fetch an image for attaching to the newly created VM """
#Depending upon the image service, make appropriate image service call
"""Fetch an image for attaching to the newly created VM."""
# Depending upon the image service, make appropriate image service call
if FLAGS.image_service == "nova.image.glance.GlanceImageService":
func = _get_glance_image
elif FLAGS.image_service == "nova.image.s3.S3ImageService":
@ -86,8 +50,8 @@ def fetch_image(image, instance, **kwargs):
def upload_image(image, instance, **kwargs):
""" Upload the newly snapshotted VM disk file. """
#Depending upon the image service, make appropriate image service call
"""Upload the newly snapshotted VM disk file."""
# Depending upon the image service, make appropriate image service call
if FLAGS.image_service == "nova.image.glance.GlanceImageService":
func = _put_glance_image
elif FLAGS.image_service == "nova.image.s3.S3ImageService":
@ -101,12 +65,11 @@ def upload_image(image, instance, **kwargs):
def _get_glance_image(image, instance, **kwargs):
""" Download image from the glance image server. """
"""Download image from the glance image server."""
LOG.debug(_("Downloading image %s from glance image server") % image)
read_file_handle = read_write_util.GlanceHTTPReadFile(FLAGS.glance_host,
FLAGS.glance_port,
image)
file_size = read_file_handle.get_size()
glance_client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port)
metadata, read_file_handle = glance_client.get_image(image)
file_size = int(metadata['size'])
write_file_handle = read_write_util.VMWareHTTPWriteFile(
kwargs.get("host"),
kwargs.get("data_center_name"),
@ -114,22 +77,23 @@ def _get_glance_image(image, instance, **kwargs):
kwargs.get("cookies"),
kwargs.get("file_path"),
file_size)
start_transfer(read_file_handle, write_file_handle, file_size)
for chunk in read_file_handle:
write_file_handle.write(chunk)
LOG.debug(_("Downloaded image %s from glance image server") % image)
def _get_s3_image(image, instance, **kwargs):
""" Download image from the S3 image server. """
"""Download image from the S3 image server."""
raise NotImplementedError
def _get_local_image(image, instance, **kwargs):
""" Download image from the local nova compute node. """
"""Download image from the local nova compute node."""
raise NotImplementedError
def _put_glance_image(image, instance, **kwargs):
""" Upload the snapshotted vm disk file to Glance image server """
"""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"),
@ -137,47 +101,48 @@ def _put_glance_image(image, instance, **kwargs):
kwargs.get("datastore_name"),
kwargs.get("cookies"),
kwargs.get("file_path"))
file_size = read_file_handle.get_size()
write_file_handle = read_write_util.GlanceHTTPWriteFile(
FLAGS.glance_host,
FLAGS.glance_port,
image,
file_size,
kwargs.get("os_type"),
kwargs.get("adapter_type"),
kwargs.get("image_version"))
start_transfer(read_file_handle, write_file_handle, file_size)
glance_client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port)
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")}}
glance_client.update_image(image, image_meta=image_metadata,
image_data=read_file_handle)
LOG.debug(_("Uploaded image %s to the Glance image server") % image)
def _put_local_image(image, instance, **kwargs):
""" Upload the snapshotted vm disk file to the local nova compute node. """
"""Upload the snapshotted vm disk file to the local nova compute node."""
raise NotImplementedError
def _put_s3_image(image, instance, **kwargs):
""" Upload the snapshotted vm disk file to S3 image server. """
"""Upload the snapshotted vm disk file to S3 image server."""
raise NotImplementedError
def get_vmdk_size_and_properties(image, instance):
""" Get size of the vmdk file that is to be downloaded for attach in spawn.
"""
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."""
geometry of the disk created depends on the size.
"""
LOG.debug(_("Getting image size for the image %s") % image)
if FLAGS.image_service == "nova.image.glance.GlanceImageService":
read_file_handle = read_write_util.GlanceHTTPReadFile(
FLAGS.glance_host,
FLAGS.glance_port,
image)
glance_client = glance.client.Client(FLAGS.glance_host,
FLAGS.glance_port)
meta_data = glance_client.get_image_meta(image)
size, properties = meta_data["size"], meta_data["properties"]
elif FLAGS.image_service == "nova.image.s3.S3ImageService":
raise NotImplementedError
elif FLAGS.image_service == "nova.image.local.LocalImageService":
raise NotImplementedError
size = read_file_handle.get_size()
properties = read_file_handle.get_image_properties()
read_file_handle.close()
LOG.debug(_("Got image size of %(size)s for the image %(image)s") %
locals())
return size, properties

View File

@ -29,6 +29,7 @@ A connection to the VMware ESX platform.
:vmwareapi_api_retry_count: The API retry count in case of failure such as
network failures (socket errors etc.)
(default: 10).
"""
import time
@ -78,7 +79,7 @@ TIME_BETWEEN_API_CALL_RETRIES = 2.0
class Failure(Exception):
"""Base Exception class for handling task failures"""
"""Base Exception class for handling task failures."""
def __init__(self, details):
self.details = details
@ -103,7 +104,7 @@ def get_connection(_):
class VMWareESXConnection(object):
"""The ESX host connection object"""
"""The ESX host connection object."""
def __init__(self, host_ip, host_username, host_password,
api_retry_count, scheme="https"):
@ -112,80 +113,85 @@ class VMWareESXConnection(object):
self._vmops = VMWareVMOps(session)
def init_host(self, host):
"""Do the initialization that needs to be done"""
#FIXME(sateesh): implement this
"""Do the initialization that needs to be done."""
# FIXME(sateesh): implement this
pass
def list_instances(self):
"""List VM instances"""
"""List VM instances."""
return self._vmops.list_instances()
def spawn(self, instance):
"""Create VM instance"""
"""Create VM instance."""
self._vmops.spawn(instance)
def snapshot(self, instance, name):
"""Create snapshot from a running VM instance"""
"""Create snapshot from a running VM instance."""
self._vmops.snapshot(instance, name)
def reboot(self, instance):
"""Reboot VM instance"""
"""Reboot VM instance."""
self._vmops.reboot(instance)
def destroy(self, instance):
"""Destroy VM instance"""
"""Destroy VM instance."""
self._vmops.destroy(instance)
def pause(self, instance, callback):
"""Pause VM instance"""
"""Pause VM instance."""
self._vmops.pause(instance, callback)
def unpause(self, instance, callback):
"""Unpause paused VM instance"""
"""Unpause paused VM instance."""
self._vmops.unpause(instance, callback)
def suspend(self, instance, callback):
"""Suspend the specified instance"""
"""Suspend the specified instance."""
self._vmops.suspend(instance, callback)
def resume(self, instance, callback):
"""Resume the suspended VM instance"""
"""Resume the suspended VM instance."""
self._vmops.resume(instance, callback)
def get_info(self, instance_id):
"""Return info about the VM instance"""
"""Return info about the VM instance."""
return self._vmops.get_info(instance_id)
def get_diagnostics(self, instance):
"""Return data about VM diagnostics"""
"""Return data about VM diagnostics."""
return self._vmops.get_info(instance)
def get_console_output(self, instance):
"""Return snapshot of console"""
"""Return snapshot of console."""
return self._vmops.get_console_output(instance)
def get_ajax_console(self, instance):
"""Return link to instance's ajax console"""
"""Return link to instance's ajax console."""
return self._vmops.get_ajax_console(instance)
def attach_volume(self, instance_name, device_path, mountpoint):
"""Attach volume storage to VM instance"""
"""Attach volume storage to VM instance."""
pass
def detach_volume(self, instance_name, mountpoint):
"""Detach volume storage to VM instance"""
"""Detach volume storage to VM instance."""
pass
def get_console_pool_info(self, console_type):
"""Get info about the host on which the VM resides"""
"""Get info about the host on which the VM resides."""
return {'address': FLAGS.vmwareapi_host_ip,
'username': FLAGS.vmwareapi_host_username,
'password': FLAGS.vmwareapi_host_password}
def update_available_resource(self, ctxt, host):
"""This method is supported only by libvirt."""
return
class VMWareAPISession(object):
"""Sets up a session with the ESX host and handles all
the calls made to the host
"""
Sets up a session with the ESX host and handles all
the calls made to the host.
"""
def __init__(self, host_ip, host_username, host_password,
@ -200,11 +206,11 @@ class VMWareAPISession(object):
self._create_session()
def _get_vim_object(self):
"""Create the VIM Object instance"""
"""Create the VIM Object instance."""
return vim.Vim(protocol=self._scheme, host=self._host_ip)
def _create_session(self):
"""Creates a session with the ESX host"""
"""Creates a session with the ESX host."""
while True:
try:
# Login and setup the session with the ESX host for making
@ -241,12 +247,13 @@ class VMWareAPISession(object):
pass
def _is_vim_object(self, module):
"""Check if the module is a VIM Object instance"""
"""Check if the module is a VIM Object instance."""
return isinstance(module, vim.Vim)
def _call_method(self, module, method, *args, **kwargs):
"""Calls a method within the module specified with
args provided
"""
Calls a method within the module specified with
args provided.
"""
args = list(args)
retry_count = 0
@ -254,7 +261,8 @@ class VMWareAPISession(object):
while True:
try:
if not self._is_vim_object(module):
#If it is not the first try, then get the latest vim object
# If it is not the first try, then get the latest
# vim object
if retry_count > 0:
args = args[1:]
args = [self.vim] + args
@ -264,8 +272,7 @@ class VMWareAPISession(object):
for method_elem in method.split("."):
temp_module = getattr(temp_module, method_elem)
ret_val = temp_module(*args, **kwargs)
return ret_val
return temp_module(*args, **kwargs)
except error_util.VimFaultException, excep:
# If it is a Session Fault Exception, it may point
# to a session gone bad. So we try re-creating a session
@ -274,9 +281,9 @@ class VMWareAPISession(object):
if error_util.FAULT_NOT_AUTHENTICATED in excep.fault_list:
self._create_session()
else:
#No re-trying for errors for API call has gone through
#and is the caller's fault. Caller should handle these
#errors. e.g, InvalidArgument fault.
# No re-trying for errors for API call has gone through
# and is the caller's fault. Caller should handle these
# errors. e.g, InvalidArgument fault.
break
except error_util.SessionOverLoadException, excep:
# For exceptions which may come because of session overload,
@ -299,13 +306,14 @@ class VMWareAPISession(object):
raise
def _get_vim(self):
"""Gets the VIM object reference"""
"""Gets the VIM object reference."""
if self.vim is None:
self._create_session()
return self.vim
def _wait_for_task(self, instance_id, task_ref):
"""Return a Deferred that will give the result of the given task.
"""
Return a Deferred that will give the result of the given task.
The task is polled until it completes.
"""
done = event.Event()
@ -317,7 +325,8 @@ class VMWareAPISession(object):
return ret_val
def _poll_task(self, instance_id, task_ref, done):
"""Poll the given task, and fires the given Deferred if we
"""
Poll the given task, and fires the given Deferred if we
get a result.
"""
try:
@ -331,7 +340,7 @@ class VMWareAPISession(object):
if task_info.state in ['queued', 'running']:
return
elif task_info.state == 'success':
LOG.info(_("Task [%(task_name)s] %(task_ref)s "
LOG.debug(_("Task [%(task_name)s] %(task_ref)s "
"status: success") % locals())
done.send("success")
else:

View File

@ -36,7 +36,7 @@ ARCH_32_BIT = '32bit'
ARCH_64_BIT = '64bit'
NO_MACHINE_ID = 'No machine id'
#Logging
# Logging
FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
if sys.platform == PLATFORM_WIN:
LOG_DIR = os.path.join(os.environ.get('ALLUSERSPROFILE'), 'openstack')
@ -56,7 +56,7 @@ else:
class ProcessExecutionError:
"""Process Execution Error Class"""
"""Process Execution Error Class."""
def __init__(self, exit_code, stdout, stderr, cmd):
self.exit_code = exit_code
@ -77,7 +77,8 @@ def _bytes2int(bytes):
def _parse_network_details(machine_id):
"""Parse the machine.id field to get MAC, IP, Netmask and Gateway fields
"""
Parse the machine.id field to get MAC, IP, Netmask and Gateway fields
machine.id is of the form MAC;IP;Netmask;Gateway;Broadcast;DNS1,DNS2
where ';' is the separator.
"""
@ -103,7 +104,7 @@ def _parse_network_details(machine_id):
def _get_windows_network_adapters():
"""Get the list of windows network adapters"""
"""Get the list of windows network adapters."""
import win32com.client
wbem_locator = win32com.client.Dispatch('WbemScripting.SWbemLocator')
wbem_service = wbem_locator.ConnectServer('.', 'root\cimv2')
@ -132,7 +133,7 @@ def _get_windows_network_adapters():
def _get_linux_network_adapters():
"""Get the list of Linux network adapters"""
"""Get the list of Linux network adapters."""
import fcntl
max_bytes = 8096
arch = platform.architecture()[0]
@ -177,7 +178,7 @@ def _get_linux_network_adapters():
def _get_adapter_name_and_ip_address(network_adapters, mac_address):
"""Get the adapter name based on the MAC address"""
"""Get the adapter name based on the MAC address."""
adapter_name = None
ip_address = None
for network_adapter in network_adapters:
@ -189,19 +190,19 @@ def _get_adapter_name_and_ip_address(network_adapters, mac_address):
def _get_win_adapter_name_and_ip_address(mac_address):
"""Get Windows network adapter name"""
"""Get Windows network adapter name."""
network_adapters = _get_windows_network_adapters()
return _get_adapter_name_and_ip_address(network_adapters, mac_address)
def _get_linux_adapter_name_and_ip_address(mac_address):
"""Get Linux network adapter name"""
"""Get Linux network adapter name."""
network_adapters = _get_linux_network_adapters()
return _get_adapter_name_and_ip_address(network_adapters, mac_address)
def _execute(cmd_list, process_input=None, check_exit_code=True):
"""Executes the command with the list of arguments specified"""
"""Executes the command with the list of arguments specified."""
cmd = ' '.join(cmd_list)
logging.debug(_("Executing command: '%s'") % cmd)
env = os.environ.copy()
@ -226,7 +227,7 @@ def _execute(cmd_list, process_input=None, check_exit_code=True):
def _windows_set_networking():
"""Set IP address for the windows VM"""
"""Set IP address for the windows VM."""
program_files = os.environ.get('PROGRAMFILES')
program_files_x86 = os.environ.get('PROGRAMFILES(X86)')
vmware_tools_bin = None
@ -256,7 +257,7 @@ def _windows_set_networking():
'name="%s"' % adapter_name, 'source=static', ip_address,
subnet_mask, gateway, '1']
_execute(cmd)
#Windows doesn't let you manually set the broadcast address
# Windows doesn't let you manually set the broadcast address
for dns_server in dns_servers:
if dns_server:
cmd = ['netsh', 'interface', 'ip', 'add', 'dns',
@ -285,9 +286,9 @@ def _set_rhel_networking(network_details=[]):
if adapter_name and not ip_address == current_ip_address:
interface_file_name = \
'/etc/sysconfig/network-scripts/ifcfg-%s' % adapter_name
#Remove file
# Remove file
os.remove(interface_file_name)
#Touch file
# Touch file
_execute(['touch', interface_file_name])
interface_file = open(interface_file_name, 'w')
interface_file.write('\nDEVICE=%s' % adapter_name)
@ -315,7 +316,7 @@ def _set_rhel_networking(network_details=[]):
def _linux_set_networking():
"""Set IP address for the Linux VM"""
"""Set IP address for the Linux VM."""
vmware_tools_bin = None
if os.path.exists('/usr/sbin/vmtoolsd'):
vmware_tools_bin = '/usr/sbin/vmtoolsd'
@ -329,7 +330,7 @@ def _linux_set_networking():
cmd = [vmware_tools_bin, '--cmd', 'machine.id.get']
network_details = _parse_network_details(_execute(cmd,
check_exit_code=False))
#TODO: For other distros like ubuntu, suse, debian, BSD, etc.
# TODO(sateesh): For other distros like ubuntu, suse, debian, BSD, etc.
_set_rhel_networking(network_details)
else:
logging.warn(_("VMware Tools is not installed"))