1178 lines
42 KiB
Python
1178 lines
42 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
|
# Copyright (c) 2012 VMware, Inc.
|
|
# Copyright (c) 2011 Citrix Systems, Inc.
|
|
# Copyright 2011 OpenStack Foundation
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
"""
|
|
A fake VMware VI API implementation.
|
|
"""
|
|
|
|
import collections
|
|
import pprint
|
|
|
|
from nova import exception
|
|
from nova.openstack.common.gettextutils import _
|
|
from nova.openstack.common import jsonutils
|
|
from nova.openstack.common import log as logging
|
|
from nova.openstack.common import uuidutils
|
|
from nova.virt.vmwareapi import error_util
|
|
|
|
_CLASSES = ['Datacenter', 'Datastore', 'ResourcePool', 'VirtualMachine',
|
|
'Network', 'HostSystem', 'HostNetworkSystem', 'Task', 'session',
|
|
'files', 'ClusterComputeResource', 'HostStorageSystem']
|
|
|
|
_FAKE_FILE_SIZE = 1024
|
|
|
|
_db_content = {}
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
def log_db_contents(msg=None):
|
|
"""Log DB Contents."""
|
|
LOG.debug(_("%(text)s: _db_content => %(content)s"),
|
|
{'text': msg or "", 'content': pprint.pformat(_db_content)})
|
|
|
|
|
|
def reset(vc=False):
|
|
"""Resets the db contents."""
|
|
cleanup()
|
|
create_network()
|
|
create_host_network_system()
|
|
create_host_storage_system()
|
|
create_host()
|
|
ds_ref1 = create_datastore('ds1', 1024, 500)
|
|
if vc:
|
|
create_host()
|
|
ds_ref2 = create_datastore('ds2', 1024, 500)
|
|
create_datacenter('dc1', ds_ref1)
|
|
if vc:
|
|
create_datacenter('dc2', ds_ref2)
|
|
create_res_pool()
|
|
if vc:
|
|
create_cluster('test_cluster', ds_ref1)
|
|
create_cluster('test_cluster2', ds_ref2)
|
|
|
|
|
|
def cleanup():
|
|
"""Clear the db contents."""
|
|
for c in _CLASSES:
|
|
# We fake the datastore by keeping the file references as a list of
|
|
# names in the db
|
|
if c == 'files':
|
|
_db_content[c] = []
|
|
else:
|
|
_db_content[c] = {}
|
|
|
|
|
|
def _create_object(table, table_obj):
|
|
"""Create an object in the db."""
|
|
_db_content[table][table_obj.obj] = table_obj
|
|
|
|
|
|
def _get_object(obj_ref):
|
|
"""Get object for the give reference."""
|
|
return _db_content[obj_ref.type][obj_ref]
|
|
|
|
|
|
def _get_objects(obj_type):
|
|
"""Get objects of the type."""
|
|
lst_objs = FakeRetrieveResult()
|
|
for key in _db_content[obj_type]:
|
|
lst_objs.add_object(_db_content[obj_type][key])
|
|
return lst_objs
|
|
|
|
|
|
def _convert_to_array_of_mor(mors):
|
|
"""Wraps the given array into a DataObject."""
|
|
array_of_mors = DataObject()
|
|
array_of_mors.ManagedObjectReference = mors
|
|
return array_of_mors
|
|
|
|
|
|
class FakeRetrieveResult(object):
|
|
"""Object to retrieve a ObjectContent list."""
|
|
|
|
def __init__(self):
|
|
self.objects = []
|
|
|
|
def add_object(self, object):
|
|
self.objects.append(object)
|
|
|
|
|
|
class MissingProperty(object):
|
|
"""Missing object in ObjectContent's missing set."""
|
|
def __init__(self, path='fake-path', message='fake_message',
|
|
method_fault=None):
|
|
self.path = path
|
|
self.fault = DataObject()
|
|
self.fault.localizedMessage = message
|
|
self.fault.fault = method_fault
|
|
|
|
|
|
def _get_object_refs(obj_type):
|
|
"""Get object References of the type."""
|
|
lst_objs = []
|
|
for key in _db_content[obj_type]:
|
|
lst_objs.append(key)
|
|
return lst_objs
|
|
|
|
|
|
def _update_object(table, table_obj):
|
|
"""Update objects of the type."""
|
|
_db_content[table][table_obj.obj] = table_obj
|
|
|
|
|
|
class Prop(object):
|
|
"""Property Object base class."""
|
|
|
|
def __init__(self, name=None, val=None):
|
|
self.name = name
|
|
self.val = val
|
|
|
|
|
|
class ManagedObjectReference(object):
|
|
"""A managed object reference is a remote identifier."""
|
|
|
|
def __init__(self, name="ManagedObject", value=None):
|
|
super(ManagedObjectReference, self)
|
|
# Managed Object Reference value attributes
|
|
# typically have values like vm-123 or
|
|
# host-232 and not UUID.
|
|
self.value = value
|
|
# Managed Object Reference type
|
|
# attributes hold the name of the type
|
|
# of the vCenter object the value
|
|
# attribute is the identifier for
|
|
self.type = name
|
|
self._type = name
|
|
|
|
|
|
class ObjectContent(object):
|
|
"""ObjectContent array holds dynamic properties."""
|
|
|
|
# This class is a *fake* of a class sent back to us by
|
|
# SOAP. It has its own names. These names are decided
|
|
# for us by the API we are *faking* here.
|
|
def __init__(self, obj_ref, prop_list=None, missing_list=None):
|
|
self.obj = obj_ref
|
|
|
|
if not isinstance(prop_list, collections.Iterable):
|
|
prop_list = []
|
|
|
|
if not isinstance(missing_list, collections.Iterable):
|
|
missing_list = []
|
|
|
|
# propSet is the name your Python code will need to
|
|
# use since this is the name that the API will use
|
|
self.propSet = prop_list
|
|
|
|
# missingSet is the name your python code will
|
|
# need to use since this is the name that the
|
|
# API we are talking to will use.
|
|
self.missingSet = missing_list
|
|
|
|
|
|
class ManagedObject(object):
|
|
"""Managed Object base class."""
|
|
_counter = 0
|
|
|
|
def __init__(self, mo_id_prefix="obj"):
|
|
"""Sets the obj property which acts as a reference to the object."""
|
|
object.__setattr__(self, 'mo_id', self._generate_moid(mo_id_prefix))
|
|
object.__setattr__(self, 'propSet', [])
|
|
object.__setattr__(self, 'obj',
|
|
ManagedObjectReference(self.__class__.__name__,
|
|
self.mo_id))
|
|
|
|
def set(self, attr, val):
|
|
"""
|
|
Sets an attribute value. Not using the __setattr__ directly for we
|
|
want to set attributes of the type 'a.b.c' and using this function
|
|
class we set the same.
|
|
"""
|
|
self.__setattr__(attr, val)
|
|
|
|
def get(self, attr):
|
|
"""
|
|
Gets an attribute. Used as an intermediary to get nested
|
|
property like 'a.b.c' value.
|
|
"""
|
|
return self.__getattr__(attr)
|
|
|
|
def __setattr__(self, attr, val):
|
|
# TODO(hartsocks): this is adds unnecessary complexity to the class
|
|
for prop in self.propSet:
|
|
if prop.name == attr:
|
|
prop.val = val
|
|
return
|
|
elem = Prop()
|
|
elem.name = attr
|
|
elem.val = val
|
|
self.propSet.append(elem)
|
|
|
|
def __getattr__(self, attr):
|
|
# TODO(hartsocks): remove this
|
|
# in a real ManagedObject you have to iterate the propSet
|
|
# in a real ManagedObject, the propSet is a *set* not a list
|
|
for elem in self.propSet:
|
|
if elem.name == attr:
|
|
return elem.val
|
|
msg = _("Property %(attr)s not set for the managed object %(name)s")
|
|
raise exception.NovaException(msg % {'attr': attr,
|
|
'name': self.__class__.__name__})
|
|
|
|
def _generate_moid(self, prefix):
|
|
"""Generates a new Managed Object ID."""
|
|
self.__class__._counter += 1
|
|
return prefix + "-" + str(self.__class__._counter)
|
|
|
|
def __repr__(self):
|
|
return jsonutils.dumps(dict([(elem.name, elem.val)
|
|
for elem in self.propSet]))
|
|
|
|
|
|
class DataObject(object):
|
|
"""Data object base class."""
|
|
|
|
def __init__(self, obj_name=None):
|
|
self.obj_name = obj_name
|
|
|
|
def __repr__(self):
|
|
return str(self.__dict__)
|
|
|
|
|
|
class HostInternetScsiHba(DataObject):
|
|
"""
|
|
iSCSI Host Bus Adapter
|
|
"""
|
|
|
|
def __init__(self):
|
|
super(HostInternetScsiHba, self).__init__()
|
|
self.device = 'vmhba33'
|
|
self.key = 'key-vmhba33'
|
|
|
|
|
|
class VirtualDisk(DataObject):
|
|
"""
|
|
Virtual Disk class.
|
|
"""
|
|
|
|
def __init__(self):
|
|
super(VirtualDisk, self).__init__()
|
|
self.key = 0
|
|
self.unitNumber = 0
|
|
|
|
|
|
class VirtualDiskFlatVer2BackingInfo(DataObject):
|
|
"""VirtualDiskFlatVer2BackingInfo class."""
|
|
|
|
def __init__(self):
|
|
super(VirtualDiskFlatVer2BackingInfo, self).__init__()
|
|
self.thinProvisioned = False
|
|
self.eagerlyScrub = False
|
|
|
|
|
|
class VirtualDiskRawDiskMappingVer1BackingInfo(DataObject):
|
|
"""VirtualDiskRawDiskMappingVer1BackingInfo class."""
|
|
|
|
def __init__(self):
|
|
super(VirtualDiskRawDiskMappingVer1BackingInfo, self).__init__()
|
|
self.lunUuid = ""
|
|
|
|
|
|
class VirtualLsiLogicController(DataObject):
|
|
"""VirtualLsiLogicController class."""
|
|
pass
|
|
|
|
|
|
class VirtualLsiLogicSASController(DataObject):
|
|
"""VirtualLsiLogicSASController class."""
|
|
pass
|
|
|
|
|
|
class VirtualPCNet32(DataObject):
|
|
"""VirtualPCNet32 class."""
|
|
|
|
def __init__(self):
|
|
super(VirtualPCNet32, self).__init__()
|
|
self.key = 4000
|
|
|
|
|
|
class VirtualMachine(ManagedObject):
|
|
"""Virtual Machine class."""
|
|
|
|
def __init__(self, **kwargs):
|
|
super(VirtualMachine, self).__init__("vm")
|
|
self.set("name", kwargs.get("name", 'test-vm'))
|
|
self.set("runtime.connectionState",
|
|
kwargs.get("conn_state", "connected"))
|
|
self.set("summary.config.guestId", kwargs.get("guest", "otherGuest"))
|
|
ds_do = kwargs.get("ds", None)
|
|
self.set("datastore", _convert_to_array_of_mor(ds_do))
|
|
self.set("summary.guest.toolsStatus", kwargs.get("toolsstatus",
|
|
"toolsOk"))
|
|
self.set("summary.guest.toolsRunningStatus", kwargs.get(
|
|
"toolsrunningstate", "guestToolsRunning"))
|
|
self.set("runtime.powerState", kwargs.get("powerstate", "poweredOn"))
|
|
self.set("config.files.vmPathName", kwargs.get("vmPathName"))
|
|
self.set("summary.config.numCpu", kwargs.get("numCpu", 1))
|
|
self.set("summary.config.memorySizeMB", kwargs.get("mem", 1))
|
|
self.set("config.hardware.device", kwargs.get("virtual_device", None))
|
|
self.set("config.extraConfig", kwargs.get("extra_config", None))
|
|
self.set('runtime.host', kwargs.get("runtime_host", None))
|
|
self.device = kwargs.get("virtual_device")
|
|
|
|
def reconfig(self, factory, val):
|
|
"""
|
|
Called to reconfigure the VM. Actually customizes the property
|
|
setting of the Virtual Machine object.
|
|
"""
|
|
try:
|
|
if not hasattr(val, 'deviceChange'):
|
|
return
|
|
|
|
if len(val.deviceChange) < 2:
|
|
return
|
|
|
|
# Case of Reconfig of VM to attach disk
|
|
controller_key = val.deviceChange[1].device.controllerKey
|
|
filename = val.deviceChange[1].device.backing.fileName
|
|
|
|
disk = VirtualDisk()
|
|
disk.controllerKey = controller_key
|
|
|
|
disk_backing = VirtualDiskFlatVer2BackingInfo()
|
|
disk_backing.fileName = filename
|
|
disk_backing.key = -101
|
|
disk.backing = disk_backing
|
|
|
|
controller = VirtualLsiLogicController()
|
|
controller.key = controller_key
|
|
|
|
self.set("config.hardware.device", [disk, controller,
|
|
self.device[0]])
|
|
except AttributeError:
|
|
# Case of Reconfig of VM to set extra params
|
|
self.set("config.extraConfig", val.extraConfig)
|
|
|
|
|
|
class Network(ManagedObject):
|
|
"""Network class."""
|
|
|
|
def __init__(self):
|
|
super(Network, self).__init__("network")
|
|
self.set("summary.name", "vmnet0")
|
|
|
|
|
|
class ResourcePool(ManagedObject):
|
|
"""Resource Pool class."""
|
|
|
|
def __init__(self, name="test_ResPool", value="resgroup-test"):
|
|
super(ResourcePool, self).__init__("rp")
|
|
self.set("name", name)
|
|
summary = DataObject()
|
|
runtime = DataObject()
|
|
config = DataObject()
|
|
memory = DataObject()
|
|
cpu = DataObject()
|
|
|
|
memoryAllocation = DataObject()
|
|
cpuAllocation = DataObject()
|
|
|
|
memory.maxUsage = 1000 * 1024 * 1024
|
|
memory.overallUsage = 500 * 1024 * 1024
|
|
cpu.maxUsage = 10000
|
|
cpu.overallUsage = 1000
|
|
runtime.cpu = cpu
|
|
runtime.memory = memory
|
|
summary.runtime = runtime
|
|
cpuAllocation.limit = 10000
|
|
memoryAllocation.limit = 1024
|
|
memoryAllocation.reservation = 1024
|
|
config.memoryAllocation = memoryAllocation
|
|
config.cpuAllocation = cpuAllocation
|
|
self.set("summary", summary)
|
|
self.set("summary.runtime.memory", memory)
|
|
self.set("config", config)
|
|
parent = ManagedObjectReference(value=value,
|
|
name=name)
|
|
owner = ManagedObjectReference(value=value,
|
|
name=name)
|
|
self.set("parent", parent)
|
|
self.set("owner", owner)
|
|
|
|
|
|
class DatastoreHostMount(DataObject):
|
|
def __init__(self, value='host-100'):
|
|
super(DatastoreHostMount, self).__init__()
|
|
host_ref = (_db_content["HostSystem"]
|
|
[_db_content["HostSystem"].keys()[0]].obj)
|
|
host_system = DataObject()
|
|
host_system.ManagedObjectReference = [host_ref]
|
|
host_system.value = value
|
|
self.key = host_system
|
|
|
|
|
|
class ClusterComputeResource(ManagedObject):
|
|
"""Cluster class."""
|
|
|
|
def __init__(self, name="test_cluster"):
|
|
super(ClusterComputeResource, self).__init__("domain")
|
|
self.set("name", name)
|
|
self.set("host", None)
|
|
self.set("datastore", None)
|
|
self.set("resourcePool", None)
|
|
|
|
summary = DataObject()
|
|
summary.numHosts = 0
|
|
summary.numCpuCores = 0
|
|
summary.numCpuThreads = 0
|
|
summary.numEffectiveHosts = 0
|
|
summary.totalMemory = 0
|
|
summary.effectiveMemory = 0
|
|
summary.effectiveCpu = 10000
|
|
self.set("summary", summary)
|
|
|
|
def _add_root_resource_pool(self, r_pool):
|
|
if r_pool:
|
|
self.set("resourcePool", r_pool)
|
|
|
|
def _add_host(self, host_sys):
|
|
if host_sys:
|
|
hosts = self.get("host")
|
|
if hosts is None:
|
|
hosts = DataObject()
|
|
hosts.ManagedObjectReference = []
|
|
self.set("host", hosts)
|
|
hosts.ManagedObjectReference.append(host_sys)
|
|
# Update summary every time a new host is added
|
|
self._update_summary()
|
|
|
|
def _add_datastore(self, datastore):
|
|
if datastore:
|
|
datastores = self.get("datastore")
|
|
if datastores is None:
|
|
datastores = DataObject()
|
|
datastores.ManagedObjectReference = []
|
|
self.set("datastore", datastores)
|
|
datastores.ManagedObjectReference.append(datastore)
|
|
|
|
# Method to update summary of a cluster upon host addition
|
|
def _update_summary(self):
|
|
summary = self.get("summary")
|
|
summary.numHosts = 0
|
|
summary.numCpuCores = 0
|
|
summary.numCpuThreads = 0
|
|
summary.numEffectiveHosts = 0
|
|
summary.totalMemory = 0
|
|
summary.effectiveMemory = 0
|
|
|
|
hosts = self.get("host")
|
|
# Compute the aggregate stats
|
|
summary.numHosts = len(hosts.ManagedObjectReference)
|
|
for host_ref in hosts.ManagedObjectReference:
|
|
host_sys = _get_object(host_ref)
|
|
connected = host_sys.get("connected")
|
|
host_summary = host_sys.get("summary")
|
|
summary.numCpuCores += host_summary.hardware.numCpuCores
|
|
summary.numCpuThreads += host_summary.hardware.numCpuThreads
|
|
summary.totalMemory += host_summary.hardware.memorySize
|
|
free_memory = (host_summary.hardware.memorySize / (1024 * 1024)
|
|
- host_summary.quickStats.overallMemoryUsage)
|
|
summary.effectiveMemory += free_memory if connected else 0
|
|
summary.numEffectiveHosts += 1 if connected else 0
|
|
self.set("summary", summary)
|
|
|
|
|
|
class Datastore(ManagedObject):
|
|
"""Datastore class."""
|
|
|
|
def __init__(self, name="fake-ds", capacity=1024, free=500):
|
|
super(Datastore, self).__init__("ds")
|
|
self.set("summary.type", "VMFS")
|
|
self.set("summary.name", name)
|
|
self.set("summary.capacity", 1024 * 1024 * 1024 * 1024)
|
|
self.set("summary.freeSpace", 500 * 1024 * 1024 * 1024)
|
|
self.set("summary.accessible", True)
|
|
|
|
|
|
class HostNetworkSystem(ManagedObject):
|
|
"""HostNetworkSystem class."""
|
|
|
|
def __init__(self, name="networkSystem"):
|
|
super(HostNetworkSystem, self).__init__("ns")
|
|
self.set("name", name)
|
|
|
|
pnic_do = DataObject()
|
|
pnic_do.device = "vmnic0"
|
|
|
|
net_info_pnic = DataObject()
|
|
net_info_pnic.PhysicalNic = [pnic_do]
|
|
|
|
self.set("networkInfo.pnic", net_info_pnic)
|
|
|
|
|
|
class HostStorageSystem(ManagedObject):
|
|
"""HostStorageSystem class."""
|
|
|
|
def __init__(self):
|
|
super(HostStorageSystem, self).__init__("storageSystem")
|
|
|
|
|
|
class HostSystem(ManagedObject):
|
|
"""Host System class."""
|
|
|
|
def __init__(self, name="ha-host", connected=True):
|
|
super(HostSystem, self).__init__("host")
|
|
self.set("name", name)
|
|
if _db_content.get("HostNetworkSystem", None) is None:
|
|
create_host_network_system()
|
|
if not _get_object_refs('HostStorageSystem'):
|
|
create_host_storage_system()
|
|
host_net_key = _db_content["HostNetworkSystem"].keys()[0]
|
|
host_net_sys = _db_content["HostNetworkSystem"][host_net_key].obj
|
|
self.set("configManager.networkSystem", host_net_sys)
|
|
host_storage_sys_key = _get_object_refs('HostStorageSystem')[0]
|
|
self.set("configManager.storageSystem", host_storage_sys_key)
|
|
|
|
summary = DataObject()
|
|
hardware = DataObject()
|
|
hardware.numCpuCores = 8
|
|
hardware.numCpuPkgs = 2
|
|
hardware.numCpuThreads = 16
|
|
hardware.vendor = "Intel"
|
|
hardware.cpuModel = "Intel(R) Xeon(R)"
|
|
hardware.uuid = "host-uuid"
|
|
hardware.memorySize = 1024 * 1024 * 1024
|
|
summary.hardware = hardware
|
|
|
|
quickstats = DataObject()
|
|
quickstats.overallMemoryUsage = 500
|
|
summary.quickStats = quickstats
|
|
|
|
product = DataObject()
|
|
product.name = "VMware ESXi"
|
|
product.version = "5.0.0"
|
|
config = DataObject()
|
|
config.product = product
|
|
summary.config = config
|
|
|
|
pnic_do = DataObject()
|
|
pnic_do.device = "vmnic0"
|
|
net_info_pnic = DataObject()
|
|
net_info_pnic.PhysicalNic = [pnic_do]
|
|
|
|
self.set("summary", summary)
|
|
self.set("capability.maxHostSupportedVcpus", 600)
|
|
self.set("summary.runtime.inMaintenanceMode", False)
|
|
self.set("runtime.connectionState", "connected")
|
|
self.set("summary.hardware", hardware)
|
|
self.set("config.network.pnic", net_info_pnic)
|
|
self.set("connected", connected)
|
|
|
|
if _db_content.get("Network", None) is None:
|
|
create_network()
|
|
net_ref = _db_content["Network"][_db_content["Network"].keys()[0]].obj
|
|
network_do = DataObject()
|
|
network_do.ManagedObjectReference = [net_ref]
|
|
self.set("network", network_do)
|
|
|
|
vswitch_do = DataObject()
|
|
vswitch_do.pnic = ["vmnic0"]
|
|
vswitch_do.name = "vSwitch0"
|
|
vswitch_do.portgroup = ["PortGroup-vmnet0"]
|
|
|
|
net_swicth = DataObject()
|
|
net_swicth.HostVirtualSwitch = [vswitch_do]
|
|
self.set("config.network.vswitch", net_swicth)
|
|
|
|
host_pg_do = DataObject()
|
|
host_pg_do.key = "PortGroup-vmnet0"
|
|
|
|
pg_spec = DataObject()
|
|
pg_spec.vlanId = 0
|
|
pg_spec.name = "vmnet0"
|
|
|
|
host_pg_do.spec = pg_spec
|
|
|
|
host_pg = DataObject()
|
|
host_pg.HostPortGroup = [host_pg_do]
|
|
self.set("config.network.portgroup", host_pg)
|
|
|
|
config = DataObject()
|
|
storageDevice = DataObject()
|
|
|
|
iscsi_hba = HostInternetScsiHba()
|
|
iscsi_hba.iScsiName = "iscsi-name"
|
|
host_bus_adapter_array = DataObject()
|
|
host_bus_adapter_array.HostHostBusAdapter = [iscsi_hba]
|
|
storageDevice.hostBusAdapter = host_bus_adapter_array
|
|
config.storageDevice = storageDevice
|
|
self.set("config.storageDevice.hostBusAdapter", host_bus_adapter_array)
|
|
|
|
# Set the same on the storage system managed object
|
|
host_storage_sys = _get_object(host_storage_sys_key)
|
|
host_storage_sys.set('storageDeviceInfo.hostBusAdapter',
|
|
host_bus_adapter_array)
|
|
|
|
def _add_iscsi_target(self, data):
|
|
default_lun = DataObject()
|
|
default_lun.scsiLun = 'key-vim.host.ScsiDisk-010'
|
|
default_lun.key = 'key-vim.host.ScsiDisk-010'
|
|
default_lun.deviceName = 'fake-device'
|
|
default_lun.uuid = 'fake-uuid'
|
|
scsi_lun_array = DataObject()
|
|
scsi_lun_array.ScsiLun = [default_lun]
|
|
self.set("config.storageDevice.scsiLun", scsi_lun_array)
|
|
|
|
transport = DataObject()
|
|
transport.address = [data['target_portal']]
|
|
transport.iScsiName = data['target_iqn']
|
|
default_target = DataObject()
|
|
default_target.lun = [default_lun]
|
|
default_target.transport = transport
|
|
|
|
iscsi_adapter = DataObject()
|
|
iscsi_adapter.adapter = 'key-vmhba33'
|
|
iscsi_adapter.transport = transport
|
|
iscsi_adapter.target = [default_target]
|
|
iscsi_topology = DataObject()
|
|
iscsi_topology.adapter = [iscsi_adapter]
|
|
self.set("config.storageDevice.scsiTopology", iscsi_topology)
|
|
|
|
def _add_port_group(self, spec):
|
|
"""Adds a port group to the host system object in the db."""
|
|
pg_name = spec.name
|
|
vswitch_name = spec.vswitchName
|
|
vlanid = spec.vlanId
|
|
|
|
vswitch_do = DataObject()
|
|
vswitch_do.pnic = ["vmnic0"]
|
|
vswitch_do.name = vswitch_name
|
|
vswitch_do.portgroup = ["PortGroup-%s" % pg_name]
|
|
|
|
vswitches = self.get("config.network.vswitch").HostVirtualSwitch
|
|
vswitches.append(vswitch_do)
|
|
|
|
host_pg_do = DataObject()
|
|
host_pg_do.key = "PortGroup-%s" % pg_name
|
|
|
|
pg_spec = DataObject()
|
|
pg_spec.vlanId = vlanid
|
|
pg_spec.name = pg_name
|
|
|
|
host_pg_do.spec = pg_spec
|
|
host_pgrps = self.get("config.network.portgroup").HostPortGroup
|
|
host_pgrps.append(host_pg_do)
|
|
|
|
|
|
class Datacenter(ManagedObject):
|
|
"""Datacenter class."""
|
|
|
|
def __init__(self, name="ha-datacenter", ds_ref=None):
|
|
super(Datacenter, self).__init__("dc")
|
|
self.set("name", name)
|
|
self.set("vmFolder", "vm_folder_ref")
|
|
if _db_content.get("Network", None) is None:
|
|
create_network()
|
|
net_ref = _db_content["Network"][_db_content["Network"].keys()[0]].obj
|
|
network_do = DataObject()
|
|
network_do.ManagedObjectReference = [net_ref]
|
|
self.set("network", network_do)
|
|
datastore = DataObject()
|
|
datastore.ManagedObjectReference = [ds_ref]
|
|
self.set("datastore", datastore)
|
|
|
|
|
|
class Task(ManagedObject):
|
|
"""Task class."""
|
|
|
|
def __init__(self, task_name, state="running", result=None):
|
|
super(Task, self).__init__("Task")
|
|
info = DataObject()
|
|
info.name = task_name
|
|
info.state = state
|
|
info.result = result
|
|
self.set("info", info)
|
|
|
|
|
|
def create_host_network_system():
|
|
host_net_system = HostNetworkSystem()
|
|
_create_object("HostNetworkSystem", host_net_system)
|
|
|
|
|
|
def create_host_storage_system():
|
|
host_storage_system = HostStorageSystem()
|
|
_create_object("HostStorageSystem", host_storage_system)
|
|
|
|
|
|
def create_host():
|
|
host_system = HostSystem()
|
|
_create_object('HostSystem', host_system)
|
|
|
|
|
|
def create_datacenter(name, ds_ref=None):
|
|
data_center = Datacenter(name, ds_ref)
|
|
_create_object('Datacenter', data_center)
|
|
|
|
|
|
def create_datastore(name, capacity, free):
|
|
data_store = Datastore(name, capacity, free)
|
|
_create_object('Datastore', data_store)
|
|
return data_store.obj
|
|
|
|
|
|
def create_res_pool():
|
|
res_pool = ResourcePool()
|
|
_create_object('ResourcePool', res_pool)
|
|
|
|
|
|
def create_network():
|
|
network = Network()
|
|
_create_object('Network', network)
|
|
|
|
|
|
def create_cluster(name, ds_ref):
|
|
cluster = ClusterComputeResource(name=name)
|
|
cluster._add_host(_get_object_refs("HostSystem")[0])
|
|
cluster._add_host(_get_object_refs("HostSystem")[1])
|
|
cluster._add_datastore(ds_ref)
|
|
cluster._add_root_resource_pool(_get_object_refs("ResourcePool")[0])
|
|
_create_object('ClusterComputeResource', cluster)
|
|
|
|
|
|
def create_task(task_name, state="running", result=None):
|
|
task = Task(task_name, state, result)
|
|
_create_object("Task", task)
|
|
return task
|
|
|
|
|
|
def _add_file(file_path):
|
|
"""Adds a file reference to the db."""
|
|
_db_content["files"].append(file_path)
|
|
|
|
|
|
def _remove_file(file_path):
|
|
"""Removes a file reference from the db."""
|
|
if _db_content.get("files") is None:
|
|
raise exception.NoFilesFound()
|
|
# Check if the remove is for a single file object or for a folder
|
|
if file_path.find(".vmdk") != -1:
|
|
if file_path not in _db_content.get("files"):
|
|
raise exception.FileNotFound(file_path=file_path)
|
|
_db_content.get("files").remove(file_path)
|
|
else:
|
|
# Removes the files in the folder and the folder too from the db
|
|
for file in _db_content.get("files"):
|
|
if file.find(file_path) != -1:
|
|
lst_files = _db_content.get("files")
|
|
if lst_files and lst_files.count(file):
|
|
lst_files.remove(file)
|
|
|
|
|
|
def fake_plug_vifs(*args, **kwargs):
|
|
"""Fakes plugging vifs."""
|
|
pass
|
|
|
|
|
|
def fake_get_network(*args, **kwargs):
|
|
"""Fake get network."""
|
|
return {'type': 'fake'}
|
|
|
|
|
|
def get_file(file_path):
|
|
"""Check if file exists in the db."""
|
|
if _db_content.get("files") is None:
|
|
raise exception.NoFilesFound()
|
|
return file_path in _db_content.get("files")
|
|
|
|
|
|
def fake_fetch_image(context, image, instance, **kwargs):
|
|
"""Fakes fetch image call. Just adds a reference to the db for the file."""
|
|
ds_name = kwargs.get("datastore_name")
|
|
file_path = kwargs.get("file_path")
|
|
ds_file_path = "[" + ds_name + "] " + file_path
|
|
_add_file(ds_file_path)
|
|
|
|
|
|
def fake_upload_image(context, image, instance, **kwargs):
|
|
"""Fakes the upload of an image."""
|
|
pass
|
|
|
|
|
|
def fake_get_vmdk_size_and_properties(context, image_id, instance):
|
|
"""Fakes the file size and properties fetch for the image file."""
|
|
props = {"vmware_ostype": "otherGuest",
|
|
"vmware_adaptertype": "lsiLogic"}
|
|
return _FAKE_FILE_SIZE, props
|
|
|
|
|
|
def _get_vm_mdo(vm_ref):
|
|
"""Gets the Virtual Machine with the ref from the db."""
|
|
if _db_content.get("VirtualMachine", None) is None:
|
|
raise exception.NotFound(_("There is no VM registered"))
|
|
if vm_ref not in _db_content.get("VirtualMachine"):
|
|
raise exception.NotFound(_("Virtual Machine with ref %s is not "
|
|
"there") % vm_ref)
|
|
return _db_content.get("VirtualMachine")[vm_ref]
|
|
|
|
|
|
class FakeFactory(object):
|
|
"""Fake factory class for the suds client."""
|
|
|
|
def create(self, obj_name):
|
|
"""Creates a namespace object."""
|
|
return DataObject(obj_name)
|
|
|
|
|
|
class FakeVim(object):
|
|
"""Fake VIM Class."""
|
|
|
|
def __init__(self, protocol="https", host="localhost", trace=None):
|
|
"""
|
|
Initializes the suds client object, sets the service content
|
|
contents and the cookies for the session.
|
|
"""
|
|
self._session = None
|
|
self.client = DataObject()
|
|
self.client.factory = FakeFactory()
|
|
|
|
transport = DataObject()
|
|
transport.cookiejar = "Fake-CookieJar"
|
|
options = DataObject()
|
|
options.transport = transport
|
|
|
|
self.client.options = options
|
|
|
|
service_content = self.client.factory.create('ns0:ServiceContent')
|
|
service_content.propertyCollector = "PropCollector"
|
|
service_content.virtualDiskManager = "VirtualDiskManager"
|
|
service_content.fileManager = "FileManager"
|
|
service_content.rootFolder = "RootFolder"
|
|
service_content.sessionManager = "SessionManager"
|
|
|
|
about_info = DataObject()
|
|
about_info.name = "VMware vCenter Server"
|
|
about_info.version = "5.1.0"
|
|
service_content.about = about_info
|
|
|
|
self._service_content = service_content
|
|
|
|
def get_service_content(self):
|
|
return self._service_content
|
|
|
|
def __repr__(self):
|
|
return "Fake VIM Object"
|
|
|
|
def __str__(self):
|
|
return "Fake VIM Object"
|
|
|
|
def _login(self):
|
|
"""Logs in and sets the session object in the db."""
|
|
self._session = uuidutils.generate_uuid()
|
|
session = DataObject()
|
|
session.key = self._session
|
|
session.userName = 'sessionUserName'
|
|
_db_content['session'][self._session] = session
|
|
return session
|
|
|
|
def _logout(self):
|
|
"""Logs out and remove the session object ref from the db."""
|
|
s = self._session
|
|
self._session = None
|
|
if s not in _db_content['session']:
|
|
raise exception.NovaException(
|
|
_("Logging out a session that is invalid or already logged "
|
|
"out: %s") % s)
|
|
del _db_content['session'][s]
|
|
|
|
def _terminate_session(self, *args, **kwargs):
|
|
"""Terminates a session."""
|
|
s = kwargs.get("sessionId")[0]
|
|
if s not in _db_content['session']:
|
|
return
|
|
del _db_content['session'][s]
|
|
|
|
def _check_session(self):
|
|
"""Checks if the session is active."""
|
|
if (self._session is None or self._session not in
|
|
_db_content['session']):
|
|
LOG.debug(_("Session is faulty"))
|
|
raise error_util.VimFaultException(
|
|
[error_util.FAULT_NOT_AUTHENTICATED],
|
|
_("Session Invalid"))
|
|
|
|
def _session_is_active(self, *args, **kwargs):
|
|
try:
|
|
self._check_session()
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
def _create_vm(self, method, *args, **kwargs):
|
|
"""Creates and registers a VM object with the Host System."""
|
|
config_spec = kwargs.get("config")
|
|
ds = _db_content["Datastore"].keys()[0]
|
|
host = _db_content["HostSystem"].keys()[0]
|
|
vm_dict = {"name": config_spec.name,
|
|
"ds": [ds],
|
|
"runtime_host": host,
|
|
"powerstate": "poweredOff",
|
|
"vmPathName": config_spec.files.vmPathName,
|
|
"numCpu": config_spec.numCPUs,
|
|
"mem": config_spec.memoryMB,
|
|
"extra_config": config_spec.extraConfig,
|
|
"virtual_device": config_spec.deviceChange}
|
|
virtual_machine = VirtualMachine(**vm_dict)
|
|
_create_object("VirtualMachine", virtual_machine)
|
|
task_mdo = create_task(method, "success")
|
|
return task_mdo.obj
|
|
|
|
def _reconfig_vm(self, method, *args, **kwargs):
|
|
"""Reconfigures a VM and sets the properties supplied."""
|
|
vm_ref = args[0]
|
|
vm_mdo = _get_vm_mdo(vm_ref)
|
|
vm_mdo.reconfig(self.client.factory, kwargs.get("spec"))
|
|
task_mdo = create_task(method, "success")
|
|
return task_mdo.obj
|
|
|
|
def _create_copy_disk(self, method, vmdk_file_path):
|
|
"""Creates/copies a vmdk file object in the datastore."""
|
|
# We need to add/create both .vmdk and .-flat.vmdk files
|
|
flat_vmdk_file_path = vmdk_file_path.replace(".vmdk", "-flat.vmdk")
|
|
_add_file(vmdk_file_path)
|
|
_add_file(flat_vmdk_file_path)
|
|
task_mdo = create_task(method, "success")
|
|
return task_mdo.obj
|
|
|
|
def _extend_disk(self, method, size):
|
|
"""Extend disk size when create a instance."""
|
|
task_mdo = create_task(method, "success")
|
|
return task_mdo.obj
|
|
|
|
def _snapshot_vm(self, method):
|
|
"""Snapshots a VM. Here we do nothing for faking sake."""
|
|
task_mdo = create_task(method, "success")
|
|
return task_mdo.obj
|
|
|
|
def _delete_disk(self, method, *args, **kwargs):
|
|
"""Deletes .vmdk and -flat.vmdk files corresponding to the VM."""
|
|
vmdk_file_path = kwargs.get("name")
|
|
flat_vmdk_file_path = vmdk_file_path.replace(".vmdk", "-flat.vmdk")
|
|
_remove_file(vmdk_file_path)
|
|
_remove_file(flat_vmdk_file_path)
|
|
task_mdo = create_task(method, "success")
|
|
return task_mdo.obj
|
|
|
|
def _delete_file(self, method, *args, **kwargs):
|
|
"""Deletes a file from the datastore."""
|
|
_remove_file(kwargs.get("name"))
|
|
task_mdo = create_task(method, "success")
|
|
return task_mdo.obj
|
|
|
|
def _just_return(self):
|
|
"""Fakes a return."""
|
|
return
|
|
|
|
def _just_return_task(self, method):
|
|
"""Fakes a task return."""
|
|
task_mdo = create_task(method, "success")
|
|
return task_mdo.obj
|
|
|
|
def _clone_vm(self, method, *args, **kwargs):
|
|
"""Fakes a VM clone."""
|
|
return self._just_return_task(method)
|
|
|
|
def _unregister_vm(self, method, *args, **kwargs):
|
|
"""Unregisters a VM from the Host System."""
|
|
vm_ref = args[0]
|
|
_get_vm_mdo(vm_ref)
|
|
del _db_content["VirtualMachine"][vm_ref]
|
|
|
|
def _search_ds(self, method, *args, **kwargs):
|
|
"""Searches the datastore for a file."""
|
|
ds_path = kwargs.get("datastorePath")
|
|
if _db_content.get("files", None) is None:
|
|
raise exception.NoFilesFound()
|
|
for file in _db_content.get("files"):
|
|
if file.find(ds_path) != -1:
|
|
result = DataObject()
|
|
result.path = ds_path
|
|
task_mdo = create_task(method, state="success",
|
|
result=result)
|
|
return task_mdo.obj
|
|
task_mdo = create_task(method, "error")
|
|
return task_mdo.obj
|
|
|
|
def _make_dir(self, method, *args, **kwargs):
|
|
"""Creates a directory in the datastore."""
|
|
ds_path = kwargs.get("name")
|
|
if _db_content.get("files", None) is None:
|
|
raise exception.NoFilesFound()
|
|
_db_content["files"].append(ds_path)
|
|
|
|
def _set_power_state(self, method, vm_ref, pwr_state="poweredOn"):
|
|
"""Sets power state for the VM."""
|
|
if _db_content.get("VirtualMachine", None) is None:
|
|
raise exception.NotFound(_("No Virtual Machine has been "
|
|
"registered yet"))
|
|
if vm_ref not in _db_content.get("VirtualMachine"):
|
|
raise exception.NotFound(_("Virtual Machine with ref %s is not "
|
|
"there") % vm_ref)
|
|
vm_mdo = _db_content.get("VirtualMachine").get(vm_ref)
|
|
vm_mdo.set("runtime.powerState", pwr_state)
|
|
task_mdo = create_task(method, "success")
|
|
return task_mdo.obj
|
|
|
|
def _retrieve_properties_continue(self, method, *args, **kwargs):
|
|
"""Continues the retrieve."""
|
|
return FakeRetrieveResult()
|
|
|
|
def _retrieve_properties_cancel(self, method, *args, **kwargs):
|
|
"""Cancels the retrieve."""
|
|
return None
|
|
|
|
def _retrieve_properties(self, method, *args, **kwargs):
|
|
"""Retrieves properties based on the type."""
|
|
spec_set = kwargs.get("specSet")[0]
|
|
type = spec_set.propSet[0].type
|
|
properties = spec_set.propSet[0].pathSet
|
|
if not isinstance(properties, list):
|
|
properties = properties.split()
|
|
objs = spec_set.objectSet
|
|
lst_ret_objs = FakeRetrieveResult()
|
|
for obj in objs:
|
|
try:
|
|
obj_ref = obj.obj
|
|
# This means that we are doing a search for the managed
|
|
# data objects of the type in the inventory
|
|
if obj_ref == "RootFolder":
|
|
mdo_refs = _db_content[type]
|
|
else:
|
|
mdo_refs = [obj_ref]
|
|
|
|
for mdo_ref in mdo_refs:
|
|
mdo = _db_content[type][mdo_ref]
|
|
prop_list = []
|
|
for prop_name in properties:
|
|
prop = Prop(prop_name, mdo.get(prop_name))
|
|
prop_list.append(prop)
|
|
obj_content = ObjectContent(mdo.obj, prop_list)
|
|
lst_ret_objs.add_object(obj_content)
|
|
except Exception as exc:
|
|
LOG.exception(exc)
|
|
continue
|
|
return lst_ret_objs
|
|
|
|
def _add_port_group(self, method, *args, **kwargs):
|
|
"""Adds a port group to the host system."""
|
|
_host_sk = _db_content["HostSystem"].keys()[0]
|
|
host_mdo = _db_content["HostSystem"][_host_sk]
|
|
host_mdo._add_port_group(kwargs.get("portgrp"))
|
|
|
|
def __getattr__(self, attr_name):
|
|
if attr_name != "Login":
|
|
self._check_session()
|
|
if attr_name == "Login":
|
|
return lambda *args, **kwargs: self._login()
|
|
elif attr_name == "Logout":
|
|
self._logout()
|
|
elif attr_name == "SessionIsActive":
|
|
return lambda *args, **kwargs: self._session_is_active(
|
|
*args, **kwargs)
|
|
elif attr_name == "TerminateSession":
|
|
return lambda *args, **kwargs: self._terminate_session(
|
|
*args, **kwargs)
|
|
elif attr_name == "CreateVM_Task":
|
|
return lambda *args, **kwargs: self._create_vm(attr_name,
|
|
*args, **kwargs)
|
|
elif attr_name == "ReconfigVM_Task":
|
|
return lambda *args, **kwargs: self._reconfig_vm(attr_name,
|
|
*args, **kwargs)
|
|
elif attr_name == "CreateVirtualDisk_Task":
|
|
return lambda *args, **kwargs: self._create_copy_disk(attr_name,
|
|
kwargs.get("name"))
|
|
elif attr_name == "DeleteDatastoreFile_Task":
|
|
return lambda *args, **kwargs: self._delete_file(attr_name,
|
|
*args, **kwargs)
|
|
elif attr_name == "PowerOnVM_Task":
|
|
return lambda *args, **kwargs: self._set_power_state(attr_name,
|
|
args[0], "poweredOn")
|
|
elif attr_name == "PowerOffVM_Task":
|
|
return lambda *args, **kwargs: self._set_power_state(attr_name,
|
|
args[0], "poweredOff")
|
|
elif attr_name == "RebootGuest":
|
|
return lambda *args, **kwargs: self._just_return()
|
|
elif attr_name == "ResetVM_Task":
|
|
return lambda *args, **kwargs: self._set_power_state(attr_name,
|
|
args[0], "poweredOn")
|
|
elif attr_name == "SuspendVM_Task":
|
|
return lambda *args, **kwargs: self._set_power_state(attr_name,
|
|
args[0], "suspended")
|
|
elif attr_name == "CreateSnapshot_Task":
|
|
return lambda *args, **kwargs: self._snapshot_vm(attr_name)
|
|
elif attr_name == "CopyVirtualDisk_Task":
|
|
return lambda *args, **kwargs: self._create_copy_disk(attr_name,
|
|
kwargs.get("destName"))
|
|
elif attr_name == "ExtendVirtualDisk_Task":
|
|
return lambda *args, **kwargs: self._extend_disk(attr_name,
|
|
kwargs.get("size"))
|
|
elif attr_name == "Destroy_Task":
|
|
return lambda *args, **kwargs: self._unregister_vm(attr_name,
|
|
*args, **kwargs)
|
|
elif attr_name == "UnregisterVM":
|
|
return lambda *args, **kwargs: self._unregister_vm(attr_name,
|
|
*args, **kwargs)
|
|
elif attr_name == "CloneVM_Task":
|
|
return lambda *args, **kwargs: self._clone_vm(attr_name,
|
|
*args, **kwargs)
|
|
elif attr_name == "Rename_Task":
|
|
return lambda *args, **kwargs: self._just_return_task(attr_name)
|
|
elif attr_name == "SearchDatastore_Task":
|
|
return lambda *args, **kwargs: self._search_ds(attr_name,
|
|
*args, **kwargs)
|
|
elif attr_name == "MakeDirectory":
|
|
return lambda *args, **kwargs: self._make_dir(attr_name,
|
|
*args, **kwargs)
|
|
elif attr_name == "RetrievePropertiesEx":
|
|
return lambda *args, **kwargs: self._retrieve_properties(
|
|
attr_name, *args, **kwargs)
|
|
elif attr_name == "ContinueRetrievePropertiesEx":
|
|
return lambda *args, **kwargs: self._retrieve_properties_continue(
|
|
attr_name, *args, **kwargs)
|
|
elif attr_name == "CancelRetrievePropertiesEx":
|
|
return lambda *args, **kwargs: self._retrieve_properties_cancel(
|
|
attr_name, *args, **kwargs)
|
|
elif attr_name == "AcquireCloneTicket":
|
|
return lambda *args, **kwargs: self._just_return()
|
|
elif attr_name == "AddPortGroup":
|
|
return lambda *args, **kwargs: self._add_port_group(attr_name,
|
|
*args, **kwargs)
|
|
elif attr_name == "RebootHost_Task":
|
|
return lambda *args, **kwargs: self._just_return_task(attr_name)
|
|
elif attr_name == "ShutdownHost_Task":
|
|
return lambda *args, **kwargs: self._just_return_task(attr_name)
|
|
elif attr_name == "PowerDownHostToStandBy_Task":
|
|
return lambda *args, **kwargs: self._just_return_task(attr_name)
|
|
elif attr_name == "PowerUpHostFromStandBy_Task":
|
|
return lambda *args, **kwargs: self._just_return_task(attr_name)
|
|
elif attr_name == "EnterMaintenanceMode_Task":
|
|
return lambda *args, **kwargs: self._just_return_task(attr_name)
|
|
elif attr_name == "ExitMaintenanceMode_Task":
|
|
return lambda *args, **kwargs: self._just_return_task(attr_name)
|