diff --git a/ceilometer/compute/virt/xenapi/inspector.py b/ceilometer/compute/virt/xenapi/inspector.py index 767662ed72..451dc0fd8a 100644 --- a/ceilometer/compute/virt/xenapi/inspector.py +++ b/ceilometer/compute/virt/xenapi/inspector.py @@ -13,23 +13,18 @@ # under the License. """Implementation of Inspector abstraction for XenAPI.""" +from os_xenapi.client import session as xenapi_session +from os_xenapi.client import XenAPI from oslo_config import cfg +from oslo_log import log as logging from oslo_utils import units -import six.moves.urllib.parse as urlparse -try: - import XenAPI as api -except ImportError: - api = None - -try: - import cPickle as pickle -except ImportError: - import pickle from ceilometer.compute.pollsters import util from ceilometer.compute.virt import inspector as virt_inspector from ceilometer.i18n import _ +LOG = logging.getLogger(__name__) + opt_group = cfg.OptGroup(name='xenapi', title='Options for XenAPI') @@ -50,22 +45,7 @@ class XenapiException(virt_inspector.InspectorException): pass -def swap_xapi_host(url, host_addr): - """Replace the XenServer address present in 'url' with 'host_addr'.""" - temp_url = urlparse.urlparse(url) - # The connection URL is served by XAPI and doesn't support having a - # path for the connection url after the port. And username/password - # will be pass separately. So the URL like "http://abc:abc@abc:433/abc" - # should not appear for XAPI case. - temp_netloc = temp_url.netloc.replace(temp_url.hostname, '%s' % host_addr) - replaced = temp_url._replace(netloc=temp_netloc) - return urlparse.urlunparse(replaced) - - def get_api_session(conf): - if not api: - raise ImportError(_('XenAPI not installed')) - url = conf.xenapi.connection_url username = conf.xenapi.connection_username password = conf.xenapi.connection_password @@ -74,22 +54,12 @@ def get_api_session(conf): 'connection_password to use')) try: - session = (api.xapi_local() if url == 'unix://local' - else api.Session(url)) - session.login_with_password(username, password) - except api.Failure as e: - if e.details[0] == 'HOST_IS_SLAVE': - master = e.details[1] - url = swap_xapi_host(url, master) - try: - session = api.Session(url) - session.login_with_password(username, password) - except api.Failure as es: - raise XenapiException(_('Could not connect slave host: %s') % - es.details[0]) - else: - msg = _("Could not connect to XenAPI: %s") % e.details[0] - raise XenapiException(msg) + session = xenapi_session.XenAPISession(url, username, password, + originator="ceilometer") + LOG.debug("XenAPI session is created successfully, %s", session) + except XenAPI.Failure as e: + msg = _("Could not connect to XenAPI: %s") % e.details[0] + raise XenapiException(msg) return session @@ -98,31 +68,9 @@ class XenapiInspector(virt_inspector.Inspector): def __init__(self, conf): super(XenapiInspector, self).__init__(conf) self.session = get_api_session(self.conf) - self.host_ref = self._get_host_ref() - self.host_uuid = self._get_host_uuid() - - def _get_host_ref(self): - """Return the xenapi host on which nova-compute runs on.""" - return self.session.xenapi.session.get_this_host(self.session.handle) - - def _get_host_uuid(self): - return self.session.xenapi.host.get_uuid(self.host_ref) - - def _call_xenapi(self, method, *args): - return self.session.xenapi_request(method, args) - - def _call_plugin(self, plugin, fn, args): - args['host_uuid'] = self.host_uuid - return self.session.xenapi.host.call_plugin( - self.host_ref, plugin, fn, args) - - def _call_plugin_serialized(self, plugin, fn, *args, **kwargs): - params = {'params': pickle.dumps(dict(args=args, kwargs=kwargs))} - rv = self._call_plugin(plugin, fn, params) - return pickle.loads(rv) def _lookup_by_name(self, instance_name): - vm_refs = self._call_xenapi("VM.get_by_name_label", instance_name) + vm_refs = self.session.VM.get_by_name_label(instance_name) n = len(vm_refs) if n == 0: raise virt_inspector.InstanceNotFoundException( @@ -138,30 +86,28 @@ class XenapiInspector(virt_inspector.Inspector): vm_ref = self._lookup_by_name(instance_name) cpu_util = self._get_cpu_usage(vm_ref, instance_name) memory_usage = self._get_memory_usage(vm_ref) + LOG.debug("inspect_instance, cpu_util: %(cpu)s, memory_usage: %(mem)s", + {'cpu': cpu_util, 'mem': memory_usage}, instance=instance) return virt_inspector.InstanceStats(cpu_util=cpu_util, memory_usage=memory_usage) def _get_cpu_usage(self, vm_ref, instance_name): - vcpus_number = int(self._call_xenapi("VM.get_VCPUs_max", vm_ref)) + vcpus_number = int(self.session.VM.get_VCPUs_max(vm_ref)) if vcpus_number <= 0: msg = _("Could not get VM %s CPU number") % instance_name raise XenapiException(msg) cpu_util = 0.0 for index in range(vcpus_number): - cpu_util += float(self._call_xenapi("VM.query_data_source", - vm_ref, - "cpu%d" % index)) + cpu_util += float(self.session.VM.query_data_source( + vm_ref, "cpu%d" % index)) return cpu_util / int(vcpus_number) * 100 def _get_memory_usage(self, vm_ref): - total_mem = float(self._call_xenapi("VM.query_data_source", - vm_ref, - "memory")) + total_mem = float(self.session.VM.query_data_source(vm_ref, "memory")) try: - free_mem = float(self._call_xenapi("VM.query_data_source", - vm_ref, - "memory_internal_free")) - except api.Failure: + free_mem = float(self.session.VM.query_data_source( + vm_ref, "memory_internal_free")) + except XenAPI.Failure: # If PV tools is not installed in the guest instance, it's # impossible to get free memory. So give it a default value # as 0. @@ -174,12 +120,15 @@ class XenapiInspector(virt_inspector.Inspector): def inspect_vnics(self, instance, duration): instance_name = util.instance_name(instance) vm_ref = self._lookup_by_name(instance_name) - dom_id = self._call_xenapi("VM.get_domid", vm_ref) - vif_refs = self._call_xenapi("VM.get_VIFs", vm_ref) - bw_all = self._call_plugin_serialized('bandwidth', - 'fetch_all_bandwidth') + dom_id = self.session.VM.get_domid(vm_ref) + vif_refs = self.session.VM.get_VIFs(vm_ref) + bw_all = self.session.call_plugin_serialized('bandwidth', + 'fetch_all_bandwidth') + LOG.debug("inspect_vnics, all bandwidth: %s", bw_all, + instance=instance) + for vif_ref in vif_refs or []: - vif_rec = self._call_xenapi("VIF.get_record", vif_ref) + vif_rec = self.session.VIF.get_record(vif_ref) bw_vif = bw_all[dom_id][vif_rec['device']] @@ -197,17 +146,15 @@ class XenapiInspector(virt_inspector.Inspector): def inspect_vnic_rates(self, instance, duration): instance_name = util.instance_name(instance) vm_ref = self._lookup_by_name(instance_name) - vif_refs = self._call_xenapi("VM.get_VIFs", vm_ref) + vif_refs = self.session.VM.get_VIFs(vm_ref) if vif_refs: for vif_ref in vif_refs: - vif_rec = self._call_xenapi("VIF.get_record", vif_ref) + vif_rec = self.session.VIF.get_record(vif_ref) - rx_rate = float(self._call_xenapi( - "VM.query_data_source", vm_ref, - "vif_%s_rx" % vif_rec['device'])) - tx_rate = float(self._call_xenapi( - "VM.query_data_source", vm_ref, - "vif_%s_tx" % vif_rec['device'])) + rx_rate = float(self.session.VM.query_data_source( + vm_ref, "vif_%s_rx" % vif_rec['device'])) + tx_rate = float(self.session.VM.query_data_source( + vm_ref, "vif_%s_tx" % vif_rec['device'])) yield virt_inspector.InterfaceRateStats( name=vif_rec['uuid'], @@ -220,17 +167,15 @@ class XenapiInspector(virt_inspector.Inspector): def inspect_disk_rates(self, instance, duration): instance_name = util.instance_name(instance) vm_ref = self._lookup_by_name(instance_name) - vbd_refs = self._call_xenapi("VM.get_VBDs", vm_ref) + vbd_refs = self.session.VM.get_VBDs(vm_ref) if vbd_refs: for vbd_ref in vbd_refs: - vbd_rec = self._call_xenapi("VBD.get_record", vbd_ref) + vbd_rec = self.session.VBD.get_record(vbd_ref) - read_rate = float(self._call_xenapi( - "VM.query_data_source", vm_ref, - "vbd_%s_read" % vbd_rec['device'])) - write_rate = float(self._call_xenapi( - "VM.query_data_source", vm_ref, - "vbd_%s_write" % vbd_rec['device'])) + read_rate = float(self.session.VM.query_data_source( + vm_ref, "vbd_%s_read" % vbd_rec['device'])) + write_rate = float(self.session.VM.query_data_source( + vm_ref, "vbd_%s_write" % vbd_rec['device'])) yield virt_inspector.DiskRateStats( device=vbd_rec['device'], read_bytes_rate=read_rate, diff --git a/ceilometer/tests/unit/compute/virt/xenapi/fake_XenAPI.py b/ceilometer/tests/unit/compute/virt/xenapi/fake_XenAPI.py deleted file mode 100644 index 1f33f78489..0000000000 --- a/ceilometer/tests/unit/compute/virt/xenapi/fake_XenAPI.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2016 Citrix -# -# 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. -"""fake XenAPI for testing in case XenAPI doesn't exist in the test env""" - - -class Failure(Exception): - def __init__(self, details): - self.details = details diff --git a/ceilometer/tests/unit/compute/virt/xenapi/test_inspector.py b/ceilometer/tests/unit/compute/virt/xenapi/test_inspector.py index f1910e4135..07d18a4897 100644 --- a/ceilometer/tests/unit/compute/virt/xenapi/test_inspector.py +++ b/ceilometer/tests/unit/compute/virt/xenapi/test_inspector.py @@ -19,34 +19,6 @@ from oslotest import base from ceilometer.compute.virt.xenapi import inspector as xenapi_inspector from ceilometer import service -from ceilometer.tests.unit.compute.virt.xenapi import fake_XenAPI - - -class TestSwapXapiHost(base.BaseTestCase): - - def test_swapping(self): - self.assertEqual( - "http://otherserver:8765/somepath", - xenapi_inspector.swap_xapi_host( - "http://someserver:8765/somepath", 'otherserver')) - - def test_no_port(self): - self.assertEqual( - "http://otherserver/somepath", - xenapi_inspector.swap_xapi_host( - "http://someserver/somepath", 'otherserver')) - - def test_no_path(self): - self.assertEqual( - "http://otherserver", - xenapi_inspector.swap_xapi_host( - "http://someserver", 'otherserver')) - - def test_same_hostname_path(self): - self.assertEqual( - "http://other:80/some", - xenapi_inspector.swap_xapi_host( - "http://some:80/some", 'other')) class TestXenapiInspection(base.BaseTestCase): @@ -64,26 +36,14 @@ class TestXenapiInspection(base.BaseTestCase): fake_total_mem = 134217728.0 fake_free_mem = 65536.0 - def fake_xenapi_request(method, args): - if method == 'VM.get_by_name_label': - return ['vm_ref'] - elif method == 'VM.get_VCPUs_max': - return '1' - elif method == 'VM.query_data_source': - if 'memory' in args: - return fake_total_mem - elif 'memory_internal_free' in args: - return fake_free_mem - elif 'cpu0' in args: - return 0.4 - else: - return None - else: - return None - session = self.inspector.session - with mock.patch.object(session, 'xenapi_request', - side_effect=fake_xenapi_request): + with mock.patch.object(session.VM, 'get_by_name_label') as mock_name, \ + mock.patch.object(session.VM, 'get_VCPUs_max') as mock_vcpu, \ + mock.patch.object(session.VM, 'query_data_source') \ + as mock_query: + mock_name.return_value = ['vm_ref'] + mock_vcpu.return_value = '1' + mock_query.side_effect = [0.4, fake_total_mem, fake_free_mem] stats = self.inspector.inspect_instance(fake_instance, None) self.assertEqual(40, stats.cpu_util) self.assertEqual(64, stats.memory_usage) @@ -91,35 +51,17 @@ class TestXenapiInspection(base.BaseTestCase): def test_inspect_memory_usage_without_freeMem(self): fake_instance = {'OS-EXT-SRV-ATTR:instance_name': 'fake_instance_name', 'id': 'fake_instance_id'} - - def _fake_xenapi_request(method, args): - if xenapi_inspector.api is None: - # the XenAPI may not exist in the test environment. - # In that case, we use the fake XenAPI for testing. - xenapi_inspector.api = fake_XenAPI - fake_total_mem = 134217728.0 - fake_details = ['INTERNAL_ERROR', - 'Rrd.Invalid_data_source("memory_internal_free")'] - - if method == 'VM.get_by_name_label': - return ['vm_ref'] - elif method == 'VM.get_VCPUs_max': - return '1' - elif method == 'VM.query_data_source': - if 'memory' in args: - return fake_total_mem - elif 'memory_internal_free' in args: - raise xenapi_inspector.api.Failure(fake_details) - elif 'cpu0' in args: - return 0.4 - else: - return None - else: - return None + fake_total_mem = 134217728.0 + fake_free_mem = 0 session = self.inspector.session - with mock.patch.object(session, 'xenapi_request', - side_effect=_fake_xenapi_request): + with mock.patch.object(session.VM, 'get_by_name_label') as mock_name, \ + mock.patch.object(session.VM, 'get_VCPUs_max') as mock_vcpu, \ + mock.patch.object(session.VM, 'query_data_source') \ + as mock_query: + mock_name.return_value = ['vm_ref'] + mock_vcpu.return_value = '1' + mock_query.side_effect = [0.4, fake_total_mem, fake_free_mem] stats = self.inspector.inspect_instance(fake_instance, None) self.assertEqual(128, stats.memory_usage) @@ -132,7 +74,6 @@ class TestXenapiInspection(base.BaseTestCase): 'MAC': 'vif_mac', 'device': '0', } - request_returns = [['vm_ref'], '10', ['vif_ref'], vif_rec] bandwidth_returns = [{ '10': { '0': { @@ -141,21 +82,26 @@ class TestXenapiInspection(base.BaseTestCase): } }] session = self.inspector.session - with mock.patch.object(session, 'xenapi_request', - side_effect=request_returns): - with mock.patch.object(self.inspector, - '_call_plugin_serialized', - side_effect=bandwidth_returns): + with mock.patch.object(session.VM, 'get_by_name_label') as mock_name, \ + mock.patch.object(session.VM, 'get_domid') as mock_domid, \ + mock.patch.object(session.VM, 'get_VIFs') as mock_vif, \ + mock.patch.object(session.VIF, 'get_record') as mock_record, \ + mock.patch.object(session, 'call_plugin_serialized') \ + as mock_plugin: + mock_name.return_value = ['vm_ref'] + mock_domid.return_value = '10' + mock_vif.return_value = ['vif_ref'] + mock_record.return_value = vif_rec + mock_plugin.side_effect = bandwidth_returns + interfaces = list(self.inspector.inspect_vnics( + fake_instance, None)) - interfaces = list( - self.inspector.inspect_vnics(fake_instance, None)) - - self.assertEqual(1, len(interfaces)) - vnic0 = interfaces[0] - self.assertEqual('vif_uuid', vnic0.name) - self.assertEqual('vif_mac', vnic0.mac) - self.assertEqual(1024, vnic0.rx_bytes) - self.assertEqual(2048, vnic0.tx_bytes) + self.assertEqual(1, len(interfaces)) + vnic0 = interfaces[0] + self.assertEqual('vif_uuid', vnic0.name) + self.assertEqual('vif_mac', vnic0.mac) + self.assertEqual(1024, vnic0.rx_bytes) + self.assertEqual(2048, vnic0.tx_bytes) def test_inspect_vnic_rates(self): fake_instance = {'OS-EXT-SRV-ATTR:instance_name': 'fake_instance_name', @@ -167,11 +113,17 @@ class TestXenapiInspection(base.BaseTestCase): 'MAC': 'vif_mac', 'device': '0', } - side_effects = [['vm_ref'], ['vif_ref'], vif_rec, 1024.0, 2048.0] session = self.inspector.session - with mock.patch.object(session, 'xenapi_request', - side_effect=side_effects): + with mock.patch.object(session.VM, 'get_by_name_label') as mock_name, \ + mock.patch.object(session.VM, 'get_VIFs') as mock_vif, \ + mock.patch.object(session.VIF, 'get_record') as mock_record, \ + mock.patch.object(session.VM, 'query_data_source') \ + as mock_query: + mock_name.return_value = ['vm_ref'] + mock_vif.return_value = ['vif_ref'] + mock_record.return_value = vif_rec + mock_query.side_effect = [1024.0, 2048.0] interfaces = list(self.inspector.inspect_vnic_rates( fake_instance, None)) @@ -189,11 +141,17 @@ class TestXenapiInspection(base.BaseTestCase): vbd_rec = { 'device': 'xvdd' } - side_effects = [['vm_ref'], ['vbd_ref'], vbd_rec, 1024.0, 2048.0] session = self.inspector.session - with mock.patch.object(session, 'xenapi_request', - side_effect=side_effects): + with mock.patch.object(session.VM, 'get_by_name_label') as mock_name, \ + mock.patch.object(session.VM, 'get_VBDs') as mock_vbds, \ + mock.patch.object(session.VBD, 'get_record') as mock_records, \ + mock.patch.object(session.VM, 'query_data_source') \ + as mock_query: + mock_name.return_value = ['vm_ref'] + mock_vbds.return_value = ['vbd_ref'] + mock_records.return_value = vbd_rec + mock_query.side_effect = [1024.0, 2048.0] disks = list(self.inspector.inspect_disk_rates( fake_instance, None)) diff --git a/requirements.txt b/requirements.txt index a91af1bcf7..51346d0489 100644 --- a/requirements.txt +++ b/requirements.txt @@ -49,3 +49,4 @@ WSME>=0.8 # MIT # NOTE(jd) We do not import it directly, but WSME datetime string parsing # behaviour changes when this library is installed python-dateutil>=2.4.2 # BSD +os-xenapi>=0.1.1 # Apache-2.0