From 62b475f3ee33d7fb9e03d5478d024c0cdb8502e0 Mon Sep 17 00:00:00 2001 From: Daryl Walleck Date: Sun, 6 Oct 2013 02:28:51 -0500 Subject: [PATCH] Adds basic XenServer client and models * Models for VM, VBD, and VDI * Basic client calls for VM, VBD, and VDI records Change-Id: I942f146e17fa82ee87dd00444d38d04fad095f2b --- cloudcafe/compute/hypervisors/__init__.py | 15 + .../compute/hypervisors/xenserver/__init__.py | 15 + .../compute/hypervisors/xenserver/client.py | 115 ++++++++ .../hypervisors/xenserver/models/__init__.py | 15 + .../xenserver/models/virtual_machine.py | 256 ++++++++++++++++++ pip-requires | 3 +- 6 files changed, 418 insertions(+), 1 deletion(-) create mode 100644 cloudcafe/compute/hypervisors/__init__.py create mode 100644 cloudcafe/compute/hypervisors/xenserver/__init__.py create mode 100644 cloudcafe/compute/hypervisors/xenserver/client.py create mode 100644 cloudcafe/compute/hypervisors/xenserver/models/__init__.py create mode 100644 cloudcafe/compute/hypervisors/xenserver/models/virtual_machine.py diff --git a/cloudcafe/compute/hypervisors/__init__.py b/cloudcafe/compute/hypervisors/__init__.py new file mode 100644 index 00000000..59ab77fa --- /dev/null +++ b/cloudcafe/compute/hypervisors/__init__.py @@ -0,0 +1,15 @@ +""" +Copyright 2013 Rackspace + +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. +""" diff --git a/cloudcafe/compute/hypervisors/xenserver/__init__.py b/cloudcafe/compute/hypervisors/xenserver/__init__.py new file mode 100644 index 00000000..59ab77fa --- /dev/null +++ b/cloudcafe/compute/hypervisors/xenserver/__init__.py @@ -0,0 +1,15 @@ +""" +Copyright 2013 Rackspace + +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. +""" diff --git a/cloudcafe/compute/hypervisors/xenserver/client.py b/cloudcafe/compute/hypervisors/xenserver/client.py new file mode 100644 index 00000000..e0fe31bf --- /dev/null +++ b/cloudcafe/compute/hypervisors/xenserver/client.py @@ -0,0 +1,115 @@ +""" +Copyright 2013 Rackspace + +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. +""" + +import XenAPI + +from cafe.common.reporting import cclogging +from cafe.engine.clients.base import BaseClient +from cloudcafe.compute.hypervisors.xenserver.models.virtual_machine \ + import VirtualMachine, VirtualBlockDevice, VirtualDiskImage + + +def _log_failure(log): + def _decorator(func): + def _wrapper(*args, **kwargs): + try: + response = func(*args, **kwargs) + except XenAPI.Failure as exception: + log.error(exception) + raise exception + return response + return _wrapper + return _decorator + + +class XenAPIClient(BaseClient): + + _log = cclogging.getLogger(__name__) + + def __init__(self, url=None, username=None, password=None): + """ + Initialization + + @param url: URL for the target XenServer instance + @type url: string + @param username: Username used to connect to XenServer + @type username: string + @param password: Password for the provided username + @type password: string + """ + + self.session = XenAPI.Session(url) + self.session.xenapi.login_with_password(username, password) + + @_log_failure(log=_log) + def get_vm_record(self, server_id): + """ + Retrieves a VM Record for the given Compute uuid + + @param server_id: The uuid of the Compute instance + @type server_id: string + @rtype: VirtualMachine + """ + vm = self._get_vm_by_compute_id(server_id) + record = self.session.xenapi.VM.get_record(vm) + virtual_machine = VirtualMachine._dict_to_obj(**record) + return virtual_machine + + @_log_failure(log=_log) + def get_vbd_record(self, vbd): + """ + Retrieves a VBD Record + + @param vbd: The OpaqueRef of the VBD + @type vbd: string + @rtype: VirtualBlockDevice + """ + + record = self.session.xenapi.VBD.get_record(vbd) + block_device = VirtualBlockDevice._dict_to_obj(record) + return block_device + + @_log_failure(log=_log) + def get_vdi_record(self, vdi): + """ + Retrieves a VDI Record + + @param vdi: The OpaqueRef of the VDI + @type vdi: string + @rtype: VirtualDiskImage + """ + + record = self.session.xenapi.VDI.get_record(vdi) + vdi = VirtualDiskImage._dict_to_obj(record) + return vdi + + @_log_failure(log=_log) + def _get_vm_by_compute_id(self, server_id): + """ + Retrieves VM given a Compute uuid + + @param server_id: The Compute uuid of an instance + @type server_id: string + @rtype: VM + """ + + expected_vm_name = 'instance-{uuid}'.format(uuid=server_id) + virtual_machines = self.session.xenapi.VM.get_all() + for vm in virtual_machines: + vm_name = self.session.xenapi.VM.get_name_label(vm) + if expected_vm_name == vm_name: + return vm + return None diff --git a/cloudcafe/compute/hypervisors/xenserver/models/__init__.py b/cloudcafe/compute/hypervisors/xenserver/models/__init__.py new file mode 100644 index 00000000..59ab77fa --- /dev/null +++ b/cloudcafe/compute/hypervisors/xenserver/models/__init__.py @@ -0,0 +1,15 @@ +""" +Copyright 2013 Rackspace + +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. +""" diff --git a/cloudcafe/compute/hypervisors/xenserver/models/virtual_machine.py b/cloudcafe/compute/hypervisors/xenserver/models/virtual_machine.py new file mode 100644 index 00000000..1c3b3b35 --- /dev/null +++ b/cloudcafe/compute/hypervisors/xenserver/models/virtual_machine.py @@ -0,0 +1,256 @@ +""" +Copyright 2013 Rackspace + +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. +""" + +from cafe.engine.models.base import BaseModel + + +class VirtualMachine(BaseModel): + + def __init__(self, label=None, description=None, vcpus_at_startup=None, + power_state=None, vcpus_params=None, vcpus_max=None, + xenstore_data=None, memory_static_min=None, + memory_static_max=None, memory_dynamic_min=None, + memory_dynamic_max=None, allowed_operations=None, + blocked_operations=None, ha_restart_priority=None, + pv_bootloader=None, snapshots=None, shutdown_delay=None, + domid=None, pci_bus=None, children=None, + hvm_shadow_multiplier=None, start_delay=None, + actions_after_crash=None, memory_target=None, uuid=None, + pv_ramdisk=None, tags=None, recommendations=None, + is_control_domain=None, hvm_boot_params=None, + snapshot_time=None, actions_after_shutdown=None, + user_version=None, snapshot_info=None, + transportable_snapshot_id=None, is_a_template=None, + crash_dumps=None, is_snapshot_from_vmpp=None, + is_a_snapshot=None, blobs=None, version=None, + current_operations=None, domarch=None, + pv_bootloader_args=None, snapshot_metadata=None, + other_config=None, actions_after_reboot=None, + attached_pcis=None, pv_legacy_args=None, bios_strings=None, + last_boot_cpu_flags=None, order=None): + super(VirtualMachine, self).__init__() + self.label = label + self.description = description + self.vcpus_at_startup = vcpus_at_startup + self.power_state = power_state + self.vcpus_params = vcpus_params + self.vcpus_max = vcpus_max + self.xenstore_data = xenstore_data + self.memory_static_min = memory_static_min + self.memory_static_max = memory_static_max + self.memory_dynamic_max = memory_dynamic_min + self.memory_dynamic_min = memory_dynamic_max + self.allowed_operations = allowed_operations + self.blocked_operations = blocked_operations + self.ha_restart_priority = ha_restart_priority + self.pv_bootloader = pv_bootloader + self.snapshots = snapshots + self.shutdown_delay = shutdown_delay + self.domid = domid + self.pci_bus = pci_bus + self.children = children + self.hvm_shadow_multiplier = hvm_shadow_multiplier + self.start_delay = start_delay + self.actions_after_crash = actions_after_crash + self.memory_target = memory_target + self.uuid = uuid + self.pv_ramdisk = pv_ramdisk + self.tags = tags + self.recommendations = recommendations + self.is_control_domain = is_control_domain + self.hvm_boot_params = hvm_boot_params + self.snapshot_time = snapshot_time + self.actions_after_shutdown = actions_after_shutdown + self.user_version = user_version + self.snapshot_info = snapshot_info + self.transportable_snapshot_id = transportable_snapshot_id + self.is_a_template = is_a_template + self.crash_dumps = crash_dumps + self.is_snapshot_from_vmpp = is_snapshot_from_vmpp + self.is_a_snapshot = is_a_snapshot + self.blobs = blobs + self.version = version + self.current_operations = current_operations + self.domarch = domarch + self.pv_bootloader_args = pv_bootloader_args + self.snapshot_metadata = snapshot_metadata + self.other_config = other_config + self.actions_after_reboot = actions_after_reboot + self.attached_pcis = attached_pcis + self.pv_legacy_args = pv_legacy_args + self.bios_strings = bios_strings + self.last_boot_cpu_flags = last_boot_cpu_flags + self.order = order + + @classmethod + def _dict_to_obj(cls, **kwargs): + vm = VirtualMachine( + label=kwargs.get('name_label'), + description=kwargs.get('name_description'), + vcpus_at_startup=kwargs.get('VCPUs_at_startup'), + power_state=kwargs.get('power_state'), + vcpus_params=kwargs.get('vcpus_params'), + vcpus_max=kwargs.get('VCPUs_max'), version=kwargs.get('version'), + xenstore_data=kwargs.get('xenstore_data'), + memory_static_min=kwargs.get('memory_static_min'), + memory_static_max=kwargs.get('memory_static_max'), + memory_dynamic_min=kwargs.get('memory_dynamic_min'), + memory_dynamic_max=kwargs.get('memory_dynamic_max'), + allowed_operations=kwargs.get('allowed_operations'), + blocked_operations=kwargs.get('blocked_operations'), + ha_restart_priority=kwargs.get('ha_restart_priority'), + pv_bootloader=kwargs.get('PV_bootloader'), + snapshots=kwargs.get('snapshots'), domid=kwargs.get('domid'), + shutdown_delay=kwargs.get('shutdown_delay'), + pci_bus=kwargs.get('PCI_bus'), children=kwargs.get('children'), + hvm_shadow_multiplier=kwargs.get('HVM_shadow_multiplier'), + start_delay=kwargs.get('start_delay'), tags=kwargs.get('tags'), + actions_after_crash=kwargs.get('actions_after_crash'), + memory_target=kwargs.get('memory_target'), + uuid=kwargs.get('uuid'), pv_ramdisk=kwargs.get('pv_ramdisk'), + recommendations=kwargs.get('recommendations'), + is_control_domain=kwargs.get('is_control_domain'), + hvm_boot_params=kwargs.get('HVM_boot_params'), + snapshot_time=kwargs.get('snapshot_time'), + actions_after_shutdown=kwargs.get('actions_after_shutdown'), + user_version=kwargs.get('user_version'), + snapshot_info=kwargs.get('snapshot_info'), + transportable_snapshot_id=kwargs.get('transportable_snapshot_id'), + is_a_template=kwargs.get('is_a_template'), + crash_dumps=kwargs.get('crash_dumps'), blobs=kwargs.get('blobs'), + is_snapshot_from_vmpp=kwargs.get('is_snapshot_from_vmpp'), + is_a_snapshot=kwargs.get('is_a_snapshot'), + current_operations=kwargs.get('current_operations'), + domarch=kwargs.get('domarch'), order=kwargs.get('order'), + pv_bootloader_args=kwargs.get('PV_bootloader_args'), + snapshot_metadata=kwargs.get('snapshot_metadata'), + other_config=kwargs.get('other_config'), + actions_after_reboot=kwargs.get('actions_after_reboot'), + attached_pcis=kwargs.get('attached_PCIs'), + pv_legacy_args=kwargs.get('PV_legacy_args'), + bios_strings=kwargs.get('bios_strings'), + last_boot_cpu_flags=kwargs.get('last_boot_cpu_flags')) + return vm + + +class VirtualBlockDevice(BaseModel): + + def __init__( + self, userdevice=None, runtime_properties=None, + allowed_operations=None, uuid=None, storage_lock=None, + qos_supported_algorithms=None, status_code=None, + type=None, empty=None, status_detail=None, device=None, + qos_algorithm_type=None, unpluggable=None, + current_operations=None, bootable=None, other_config=None, + currently_attached=None, mode=None, qos_algorithm_params=None): + super(VirtualBlockDevice, self).__init__() + self.userdevice = userdevice + self.runtime_properties = runtime_properties + self.allowed_operations = allowed_operations + self.uuid = uuid + self.storage_lock = storage_lock + self.qos_supported_algorithms = qos_supported_algorithms + self.status_code = status_code + self.type = type + self.empty = empty + self.status_detail = status_detail + self.device = device + self.qos_algorithm_type = qos_algorithm_type + self.unpluggable = unpluggable + self.current_operations = current_operations + self.bootable = bootable + self.other_config = other_config + self.currently_attached = currently_attached + self.mode = mode + self.qos_algorithm_params = qos_algorithm_params + + @classmethod + def _dict_to_obj(cls, **kwargs): + vbd = VirtualBlockDevice( + userdevice=kwargs.get('userdevice'), + runtime_properties=kwargs.get('runtime_properties'), + allowed_operations=kwargs.get('allowed_operations'), + uuid=kwargs.get('uuid'), storage_lock=kwargs.get('storage_lock'), + qos_supported_algorithms=kwargs.get('qos_supported_algorithms'), + status_code=kwargs.get('status_code'), type=kwargs.get('type'), + empty=kwargs.get('empty'), device=kwargs.get('device'), + status_detail=kwargs.get('status_detail'), + qos_algorithm_type=kwargs.get('qos_algorithm_type'), + unpluggable=kwargs.get('unpluggable'), + current_operations=kwargs.get('current_operations'), + bootable=kwargs.get('bootable'), mode=kwargs.get('mode'), + other_config=kwargs.get('other_config'), + currently_attached=kwargs.get('currently_attached'), + qos_algorithm_params=kwargs.get('qos_algorithm_params')) + return vbd + + +class VirtualDiskImage(BaseModel): + + def __init__(self, managed=None, snapshots=None, allowed_operations=None, + on_boot=None, description=None, read_only=None, uuid=None, + storage_lock=None, label=None, tags=None, location=None, + type=None, shareable=None, snapshot_time=None, missing=None, + xenstore_data=None, crash_dumps=None, virtual_size=None, + is_a_snapshot=None, current_operations=None, + physical_utilisation=None, allow_caching=None, + metadata_latest=None): + super(VirtualDiskImage, self).__init__() + self.managed = managed + self.snapshots = snapshots + self.allowed_operations = allowed_operations + self.on_boot = on_boot + self.description = description + self.read_only = read_only + self.uuid = uuid + self.storage_lock = storage_lock + self.label = label + self.tags = tags + self.location = location + self.type = type + self.shareable = shareable + self.snapshot_time = snapshot_time + self.missing = missing + self.xenstore_data = xenstore_data + self.crash_dumps = crash_dumps + self.virtual_size = virtual_size + self.is_a_snapshot = is_a_snapshot + self.current_operations = current_operations + self.physical_utilisation = physical_utilisation + self.allow_caching = allow_caching + self.metadata_latest = metadata_latest + + @classmethod + def _dict_to_obj(cls, **kwargs): + vdi = VirtualDiskImage( + managed=kwargs.get('managed'), snapshots=kwargs.get('snapshots'), + allowed_operations=kwargs.get('allowed_operations'), + on_boot=kwargs.get('on_boot'), read_only=kwargs.get('read_only'), + description=kwargs.get('description'), uuid=kwargs.get('uuid'), + storage_lock=kwargs.get('storage_lock'), tags=kwargs.get('tags'), + label=kwargs.get('label'), location=kwargs.get('location'), + type=kwargs.get('type'), shareable=kwargs.get('shareable'), + snapshot_time=kwargs.get('snapshot_time'), + missing=kwargs.get('missing'), + virtual_size=kwargs.get('virtual_size'), + crash_dumps=kwargs.get('crash_dumps'), + xenstore_data=kwargs.get('xenstore_data'), + is_a_snapshot=kwargs.get('is_a_snapshot'), + current_operations=kwargs.get('current_operations'), + physical_utilisation=kwargs.get('physical_utilisation'), + allow_caching=kwargs.get('allow_caching'), + metadata_latest=kwargs.get('metadata_latest')) + return vdi diff --git a/pip-requires b/pip-requires index 90539acb..8dcdc9a9 100644 --- a/pip-requires +++ b/pip-requires @@ -1,4 +1,5 @@ httpretty mock unittest2 -IPy \ No newline at end of file +IPy +XenAPI \ No newline at end of file