diff --git a/.gitignore b/.gitignore index c979982..5b5dc02 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ /virt-who-1.31.7.tar.gz /virt-who-1.31.8.tar.gz /virt-who-1.31.9.tar.gz +/virt-who-1.31.18.tar.gz diff --git a/build-rpm-no-ahv.patch b/build-rpm-no-ahv.patch deleted file mode 100644 index 833a2f2..0000000 --- a/build-rpm-no-ahv.patch +++ /dev/null @@ -1,1772 +0,0 @@ -From d782073fe7c7e06fba327ffad6f3902e03c99e6b Mon Sep 17 00:00:00 2001 -From: Kevin Howell -Date: Fri, 18 Oct 2019 09:54:54 -0400 -Subject: [PATCH] Remove AHV support - ---- - tests/test_ahv.py | 566 --------------------- - virtwho/config.py | 4 +- - virtwho/parser.py | 4 +- - virtwho/virt/ahv/__init__.py | 6 - - virtwho/virt/ahv/ahv.py | 258 ---------- - virtwho/virt/ahv/ahv_constants.py | 21 - - virtwho/virt/ahv/ahv_interface.py | 804 ------------------------------ - virtwho/virt/virt.py | 1 - - 8 files changed, 3 insertions(+), 1661 deletions(-) - delete mode 100644 tests/test_ahv.py - delete mode 100644 virtwho/virt/ahv/__init__.py - delete mode 100644 virtwho/virt/ahv/ahv.py - delete mode 100644 virtwho/virt/ahv/ahv_constants.py - delete mode 100644 virtwho/virt/ahv/ahv_interface.py - -diff --git a/tests/test_ahv.py b/tests/test_ahv.py -deleted file mode 100644 -index d8ccbc8..0000000 ---- a/tests/test_ahv.py -+++ /dev/null -@@ -1,566 +0,0 @@ --from __future__ import print_function -- --import six -- --from base import TestBase --from mock import patch, call, ANY, MagicMock --from requests import Session --from six.moves.queue import Queue --from threading import Event -- --from virtwho import DefaultInterval --from virtwho.datastore import Datastore --from virtwho.virt.ahv.ahv import AhvConfigSection --from virtwho.virt import Virt, VirtError, Guest, Hypervisor -- -- -- --MY_SECTION_NAME = 'test-ahv' --DefaultUpdateInterval = 1800 --# Values used for testing AhvConfigSection. --PE_SECTION_VALUES = { -- 'type': 'ahv', -- 'server': '10.10.10.10', -- 'username': 'root', -- 'password': 'root_password', -- 'owner': 'nutanix', -- 'hypervisor_id': 'uuid', -- 'is_hypervisor': True, -- 'internal_debug': False, -- 'update_interval': 60 --} -- --HOST_UVM_MAP = \ -- {u'08469de5-be42-43e6-8c32-20167d3b58f7': -- {u'oplog_disk_pct': 3.4, -- u'memory_capacity_in_bytes': 135009402880, -- u'has_csr': False, -- u'default_vm_storage_container_uuid': None, -- u'hypervisor_username': u'root', -- u'key_management_device_to_certificate_status': {}, -- u'service_vmnat_ip': None, -- u'hypervisor_key': u'10.53.97.188', -- u'acropolis_connection_state': u'kConnected', -- u'management_server_name': u'10.53.97.188', -- u'failover_cluster_fqdn': None, -- u'serial': u'OM155S016008', -- u'bmc_version': u'01.92', -- u'hba_firmwares_list': -- [{u'hba_model': u'LSI Logic SAS3008', -- u'hba_version': u'MPTFW-06.00.00.00-IT'}], -- u'hypervisor_state': u'kAcropolisNormal', -- u'num_cpu_threads': 32, -- u'monitored': True, -- u'uuid': u'08469de5-be42-43e6-8c32-20167d3b58f7', -- u'reboot_pending': False, -- u'cpu_capacity_in_hz': 38384000000, -- u'num_cpu_sockets': 2, -- u'host_maintenance_mode_reason': None, -- u'hypervisor_address': u'10.53.97.188', -- u'host_gpus': None, -- u'failover_cluster_node_state': None, -- u'state': u'NORMAL', -- u'num_cpu_cores': 16, -- 'guest_list': -- [{u'vm_features': -- {u'AGENT_VM': False, -- u'VGA_CONSOLE': True}, -- u'name': u'am2', u'num_cores_per_vcpu': 1, -- u'gpus_assigned': False, u'num_vcpus': 2, -- u'memory_mb': 4096, -- u'power_state': u'on', -- u'ha_priority': 0, -- u'allow_live_migrate': True, -- u'timezone': u'America/Los_Angeles', -- u'vm_logical_timestamp': 48, -- u'host_uuid': u'08469de5-be42-43e6-8c32-20167d3b58f7', -- u'uuid': u'01dcfc0b-3092-4f1b-94fb-81b44ed352be'}, -- {u'vm_features': -- {u'AGENT_VM': False, u'VGA_CONSOLE': True}, -- u'name': u'am3', -- u'num_cores_per_vcpu': 1, -- u'gpus_assigned': False, -- u'num_vcpus': 2, u'memory_mb': 4096, -- u'power_state': u'on', -- u'ha_priority': 0, -- u'allow_live_migrate': True, -- u'timezone': u'America/Los_Angeles', -- u'vm_logical_timestamp': 3, -- u'host_uuid': u'08469de5-be42-43e6-8c32-20167d3b58f7', -- u'uuid': u'422f9171-db1f-48b0-a3de-b0bb92a8f559'}, -- {u'vm_features': -- {u'AGENT_VM': False, -- u'VGA_CONSOLE': True}, -- u'name': u'win_vm', -- u'num_cores_per_vcpu': 1, -- u'gpus_assigned': False, -- u'num_vcpus': 2, -- u'memory_mb': 4096, -- u'power_state': u'on', -- u'ha_priority': 0, -- u'allow_live_migrate': True, -- u'timezone': u'America/Los_Angeles', -- u'vm_logical_timestamp': 3, -- u'host_uuid': u'08469de5-be42-43e6-8c32-20167d3b58f7', -- u'uuid': u'98839f35-bd62-4255-a7cd-7668bc143554'}], -- u'cpu_model': u'Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz', -- u'ipmi_username': u'ADMIN', -- u'service_vmid': u'0005809e-62e4-75c7-611b-0cc47ac3b354::7', -- u'bmc_model': u'X10_ATEN', -- u'host_nic_ids': [], -- u'cluster_uuid': u'0005809e-62e4-75c7-611b-0cc47ac3b354', -- u'ipmi_password': None, -- u'cpu_frequency_in_hz': 2399000000, -- u'stats': -- {u'num_read_io': u'8', -- u'controller_read_io_bandwidth_kBps': u'0', -- u'content_cache_hit_ppm': u'1000000', -- }, -- u'num_vms': 4, u'default_vm_storage_container_id': None, -- u'metadata_store_status': u'kNormalMode', -- u'name': u'foyt-4', u'hypervisor_password': None, -- u'service_vmnat_port': None, -- u'hypervisor_full_name': u'Nutanix 20180802.100874', -- u'is_degraded': False, u'host_type': u'HYPER_CONVERGED', -- u'default_vhd_storage_container_uuid': None, -- u'block_serial': u'15SM60250038', -- u'disk_hardware_configs': -- {u'1': -- { -- u'mount_path': u'/home/nutanix/data/stargate-storage/disks/BTHC506101XL480MGN', -- }, -- u'3': -- { -- u'mount_path': u'/home/nutanix/data/stargate-storage/disks/9XG8E6QE', -- }, -- u'2': -- { -- u'mount_path': u'/home/nutanix/data/stargate-storage/disks/BTHC50610246480MGN', -- }, -- u'5': -- { -- u'mount_path': u'/home/nutanix/data/stargate-storage/disks/9XG8E835', -- }, -- u'4': { -- u'mount_path': u'/home/nutanix/data/stargate-storage/disks/9XG8E8B1', -- }, -- u'6': { -- u'mount_path': u'/home/nutanix/data/stargate-storage/disks/9XG8E7B3', -- }}, -- u'ipmi_address': u'10.49.27.28', u'bios_model': u'0824', -- u'default_vm_location': None, u'hypervisor_type': u'kKvm', -- u'service_vmexternal_ip': u'10.53.97.192', -- u'controller_vm_backplane_ip': u'10.53.97.192'}, -- u'54830446-b55e-4f16-aa74-7b6a9ac9a7a4': -- {u'oplog_disk_pct': 3.4, -- u'memory_capacity_in_bytes': 135009402880, u'has_csr': False, -- u'default_vm_storage_container_uuid': None, -- u'hypervisor_username': u'root', -- u'service_vmnat_ip': None, u'hypervisor_key': u'10.53.97.187', -- u'acropolis_connection_state': u'kConnected', -- u'hypervisor_state': u'kAcropolisNormal', -- u'num_cpu_threads': 32, u'monitored': True, -- u'uuid': u'54830446-b55e-4f16-aa74-7b6a9ac9a7a4', -- u'reboot_pending': False, u'cpu_capacity_in_hz': 38384000000, -- u'num_cpu_sockets': 2, u'host_maintenance_mode_reason': None, -- u'hypervisor_address': u'10.53.97.187', u'host_gpus': None, -- u'failover_cluster_node_state': None, u'state': u'NORMAL', -- u'num_cpu_cores': 16, u'block_model': u'UseLayout', -- 'guest_list': -- [{u'vm_features': -- {u'AGENT_VM': False, u'VGA_CONSOLE': True}, -- u'name': u'PC', u'num_cores_per_vcpu': 1, -- u'gpus_assigned': False, u'num_vcpus': 4, -- u'memory_mb': 16384, u'power_state': u'on', -- u'ha_priority': 0, u'allow_live_migrate': True, -- u'timezone': u'UTC', u'vm_logical_timestamp': 10, -- u'host_uuid': u'54830446-b55e-4f16-aa74-7b6a9ac9a7a4', -- u'uuid': u'd90b5443-97f0-47eb-986d-f14e062448d4'}, -- {u'vm_features': -- {u'AGENT_VM': False, u'VGA_CONSOLE': True}, -- u'name': u'am1', u'num_cores_per_vcpu': 1, -- u'gpus_assigned': False, u'num_vcpus': 2, -- u'memory_mb': 4096, u'power_state': u'on', -- u'ha_priority': 0, u'allow_live_migrate': True, -- u'timezone': u'America/Los_Angeles', -- u'vm_logical_timestamp': 14, -- u'host_uuid': u'54830446-b55e-4f16-aa74-7b6a9ac9a7a4', -- u'uuid': u'0af0a010-0ad0-4fba-aa33-7cc3d0b6cb7e'}], -- u'cpu_model': u'Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz', -- u'ipmi_username': u'ADMIN', -- u'service_vmid': u'0005809e-62e4-75c7-611b-0cc47ac3b354::6', -- u'bmc_model': u'X10_ATEN', u'host_nic_ids': [], -- u'cluster_uuid': u'0005809e-62e4-75c7-611b-0cc47ac3b354', -- u'stats': -- {u'num_read_io': u'27', -- u'controller_read_io_bandwidth_kBps': u'0', -- u'content_cache_hit_ppm': u'1000000', -- }, u'backplane_ip': None, -- u'vzone_name': u'', u'default_vhd_location': None, -- u'metadata_store_status_message': u'Metadata store enabled on the node', -- u'num_vms': 3, u'default_vm_storage_container_id': None, -- u'metadata_store_status': u'kNormalMode', u'name': u'foyt-3', -- u'hypervisor_password': None, u'service_vmnat_port': None, -- u'hypervisor_full_name': u'Nutanix 20180802.100874', -- u'is_degraded': False, u'host_type': u'HYPER_CONVERGED', -- u'default_vhd_storage_container_uuid': None, -- u'block_serial': u'15SM60250038', -- u'disk_hardware_configs': -- {u'1': -- { -- u'mount_path': u'/home/nutanix/data/stargate-storage/disks/BTHC506101ST480MGN', -- }, -- u'3': -- { -- u'mount_path': u'/home/nutanix/data/stargate-storage/disks/9XG8DXYY', -- }, -- u'2': { -- u'mount_path': u'/home/nutanix/data/stargate-storage/disks/BTHC506101D4480MGN', -- }, -- u'5': { -- u'mount_path': u'/home/nutanix/data/stargate-storage/disks/9XG8DQRM', -- }, -- u'4': { -- u'mount_path': u'/home/nutanix/data/stargate-storage/disks/9XG8DJ7E', -- }, -- u'6': { -- u'mount_path': u'/home/nutanix/data/stargate-storage/disks/9XG8DVGG', -- }}, -- u'ipmi_address': u'10.49.27.27', u'bios_model': u'0824', -- u'hypervisor_type': u'kKvm', -- u'service_vmexternal_ip': u'10.53.97.191', -- u'controller_vm_backplane_ip': u'10.53.97.191' -- }, -- u'acc819fe-e0ff-4963-93a4-5a0e1d3c77d3': -- {u'oplog_disk_pct': 3.4, -- u'memory_capacity_in_bytes': 270302969856, u'has_csr': False, -- u'default_vm_storage_container_uuid': None, -- u'hypervisor_username': u'root', -- u'key_management_device_to_certificate_status': {}, -- u'service_vmnat_ip': None, u'hypervisor_key': u'10.53.96.75', -- u'acropolis_connection_state': u'kConnected', -- u'management_server_name': u'10.53.96.75', -- u'failover_cluster_fqdn': None, u'serial': u'ZM162S002621', -- u'bmc_version': u'01.97', -- u'hba_firmwares_list': -- [{u'hba_model': u'LSI Logic SAS3008', -- u'hba_version': u'MPTFW-10.00.03.00-IT'}], -- u'hypervisor_state': u'kAcropolisNormal', -- u'num_cpu_threads': 32, u'monitored': True, -- u'uuid': u'acc819fe-e0ff-4963-93a4-5a0e1d3c77d3', -- u'num_cpu_sockets': 2, u'host_maintenance_mode_reason': None, -- u'hypervisor_address': u'10.53.96.75', -- 'guest_list': -- [{u'vm_features': {u'AGENT_VM': False, u'VGA_CONSOLE': True}, -- u'name': u'am_RH_satellite', u'num_cores_per_vcpu': 2, -- u'gpus_assigned': False, u'num_vcpus': 4, u'memory_mb': 16384, -- u'power_state': u'on', u'ha_priority': 0, -- u'allow_live_migrate': True, u'timezone': u'America/Los_Angeles', -- u'vm_logical_timestamp': 5, -- u'host_uuid': u'acc819fe-e0ff-4963-93a4-5a0e1d3c77d3', -- u'uuid': u'e30f381d-d4bc-4958-a88c-79448efe5112'}, -- {u'vm_features': {u'AGENT_VM': False, u'VGA_CONSOLE': True}, -- u'name': u'am4', u'num_cores_per_vcpu': 1, -- u'gpus_assigned': False, u'num_vcpus': 2, u'memory_mb': 4096, -- u'power_state': u'on', u'ha_priority': 0, -- u'allow_live_migrate': True, u'timezone': u'America/Los_Angeles', -- u'vm_logical_timestamp': 2, -- u'host_uuid': u'acc819fe-e0ff-4963-93a4-5a0e1d3c77d3', -- u'uuid': u'f1e3362b-0377-4d70-bccd-63d2a1c09225'}], -- u'dynamic_ring_changing_node': None, -- u'cpu_model': u'Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz', -- u'ipmi_username': u'ADMIN', -- u'cluster_uuid': u'0005809e-62e4-75c7-611b-0cc47ac3b354', -- u'ipmi_password': None, u'cpu_frequency_in_hz': 2400000000, -- u'stats': {u'num_read_io': u'47', -- u'controller_read_io_bandwidth_kBps': u'0', -- u'content_cache_hit_ppm': u'1000000', -- }, u'backplane_ip': None, -- u'num_vms': 3, -- u'name': u'watermelon02-4', u'hypervisor_password': None, -- u'hypervisor_full_name': u'Nutanix 20180802.100874', -- u'is_degraded': False, u'host_type': u'HYPER_CONVERGED', -- u'default_vhd_storage_container_uuid': None, -- u'block_serial': u'16AP60170033', u'usage_stats': { -- }, -- u'disk_hardware_configs': { -- u'1': { -- u'mount_path': u'/home/nutanix/data/stargate-storage/disks/BTHC549209M3480MGN', -- }, -- u'3': { -- u'mount_path': u'/home/nutanix/data/stargate-storage/disks/9XG9TRZQ'}, -- u'2': { -- u'mount_path': u'/home/nutanix/data/stargate-storage/disks/BTHC550503XF480MGN', -- }, -- u'5': { -- u'mount_path': u'/home/nutanix/data/stargate-storage/disks/9XG9TS0N', -- }, -- u'4': { -- u'mount_path': u'/home/nutanix/data/stargate-storage/disks/9XG9TSF7', -- }, -- u'6': { -- u'mount_path': u'/home/nutanix/data/stargate-storage/disks/9XG9TREW', -- }}, u'ipmi_address': u'10.49.26.188', -- u'bios_model': u'0824', u'default_vm_location': None, -- u'hypervisor_type': u'kKvm', -- u'position': {u'ordinal': 4, u'physical_position': None, -- u'name': u''}, -- u'service_vmexternal_ip': u'10.53.96.79', -- u'controller_vm_backplane_ip': u'10.53.96.79'}} -- -- --class TestAhvConfigSection(TestBase): -- """ -- Test base for testing class AhvConfigSection. -- """ -- -- def __init__(self, *args, **kwargs): -- super(TestAhvConfigSection, self).__init__(*args, **kwargs) -- self.ahv_config = None -- -- def init_virt_config_section(self, is_pc=False): -- """ -- Method executed before each unit test. -- """ -- self.ahv_config = AhvConfigSection(MY_SECTION_NAME, None) -- if is_pc: -- self.ahv_config['prism_central'] = True -- # We need to set values using this way, because we need -- # to trigger __setitem__ of virt_config. -- for key, value in PE_SECTION_VALUES.items(): -- self.ahv_config[key] = value -- -- def test_validate_ahv_PE_config(self): -- """ -- Test validation of ahv section. -- """ -- # PE validation. -- self.init_virt_config_section() -- result = self.ahv_config.validate() -- self.assertEqual(len(result), 0) -- -- # PC validation. -- self.init_virt_config_section(is_pc=True) -- result = self.ahv_config.validate() -- self.assertEqual(len(result), 0) -- -- def test_validate_ahv_invalid_server_ip(self): -- """ -- Test validation of ahv config. Invalid server IP. -- """ -- self.init_virt_config_section() -- self.ahv_config['server'] = '10.0.0.' -- result = self.ahv_config.validate() -- expected_result = ['Invalid server IP address provided'] -- six.assertCountEqual(self, expected_result, result) -- -- def test_validate_ahv_config_missing_username_password(self): -- """ -- Test validation of ahv config. Username and password is required. -- """ -- self.init_virt_config_section() -- del self.ahv_config['username'] -- del self.ahv_config['password'] -- result = self.ahv_config.validate() -- expected_result = [ -- ('error', 'Required option: "username" not set.'), -- ('error', 'Required option: "password" not set.') -- ] -- six.assertCountEqual(self, expected_result, result) -- -- def test_validate_ahv_config_invalid_internal_debug_flag(self): -- """ -- Test validation of ahv config. If update_interval and internal debug -- are not set then we get a warning message for each flag. -- """ -- self.init_virt_config_section() -- self.ahv_config['update_interval'] = 40 -- result = self.ahv_config.validate() -- message = "Interval value can't be lower than {min} seconds. " \ -- "Default value of {min} " \ -- "seconds will be used.".format(min=DefaultUpdateInterval) -- expected_result = [("warning", message)] -- six.assertCountEqual(self, expected_result, result) -- -- --class TestAhv(TestBase): -- -- @staticmethod -- def create_config(name, wrapper, **kwargs): -- config = AhvConfigSection(name, wrapper) -- config.update(**kwargs) -- config.validate() -- return config -- -- def setUp(self, is_pc=False): -- config = self.create_config(name='test', wrapper=None, type='ahv', -- server='10.10.10.10', username='username', -- password='password', owner='owner', -- prism_central=is_pc) -- self.ahv = Virt.from_config(self.logger, config, Datastore(), -- interval=DefaultInterval) -- -- @patch('virtwho.virt.ahv.ahv_interface.AhvInterface._progressbar') -- def run_once(self, queue=None): -- """Run AHV in oneshot mode.""" -- self.ahv._oneshot = True -- self.ahv.dest = queue or Queue() -- self.ahv._terminate_event = Event() -- self.ahv._oneshot = True -- self.ahv._interval = 0 -- self.ahv._run() -- -- @patch.object(Session, 'get') -- def test_connect_PE(self, mock_get): -- mock_get.return_value.status_code = 200 -- self.run_once() -- -- self.assertEqual(mock_get.call_count, 3) -- call_list = [ -- call('https://10.10.10.10:9440/api/nutanix/v2.0/clusters', -- data=ANY, headers=ANY, timeout=ANY, verify=ANY), -- call('https://10.10.10.10:9440/api/nutanix/v2.0/vms', -- data=ANY, headers=ANY, timeout=ANY, verify=ANY), -- call('https://10.10.10.10:9440/api/nutanix/v2.0/hosts', -- data=ANY, headers=ANY, timeout=ANY, verify=ANY) -- ] -- mock_get.assert_has_calls(call_list, any_order=True) -- -- @patch.object(Session, 'post') -- def test_connect_PC(self, mock_post): -- self.setUp(is_pc=True) -- -- mock_post.return_value.status_code = 200 -- self.run_once() -- -- self.assertEqual(mock_post.call_count, 3) -- call_list = [ -- call('https://10.10.10.10:9440/api/nutanix/v3/clusters/list', -- data=ANY, headers=ANY, timeout=ANY, verify=ANY), -- call('https://10.10.10.10:9440/api/nutanix/v3/vms/list', -- data=ANY, headers=ANY, timeout=ANY, verify=ANY), -- call('https://10.10.10.10:9440/api/nutanix/v3/hosts/list', -- data=ANY, headers=ANY, timeout=ANY, verify=ANY) -- ] -- mock_post.assert_has_calls(call_list, any_order=True) -- -- @patch.object(Session, 'get') -- def test_invalid_login_PE(self, mock_get): -- mock_get.return_value.ok = False -- mock_get.return_value.status_code = 401 -- self.assertRaises(VirtError, self.run_once) -- -- mock_get.return_value.status_code = 403 -- self.assertRaises(VirtError, self.run_once) -- -- @patch.object(Session, 'post') -- def test_invalid_login_PC(self, mock_post): -- self.setUp(is_pc=True) -- mock_post.return_value.ok = False -- mock_post.return_value.status_code = 401 -- self.assertRaises(VirtError, self.run_once) -- -- mock_post.return_value.status_code = 403 -- self.assertRaises(VirtError, self.run_once) -- -- @patch.object(Session, 'get') -- def test_connection_conflict_PE(self, mock_get): -- mock_get.return_value.ok = False -- mock_get.return_value.status_code = 409 -- self.assertRaises(VirtError, self.run_once) -- -- @patch.object(Session, 'post') -- def test_connection_conflict_PC(self, mock_post): -- self.setUp(is_pc=True) -- mock_post.return_value.ok = False -- mock_post.return_value.status_code = 409 -- self.assertRaises(VirtError, self.run_once) -- -- @patch('virtwho.virt.ahv.ahv_interface.AhvInterface.get_vm', return_value=None) -- @patch.object(Session, 'get') -- def test_no_retry_http_erros_PE(self, mock_get, mock_get_vm): -- mock_get.return_value.ok = False -- mock_get.return_value.status_code = 400 -- mock_get.return_value.text = 'Bad Request' -- self.assertEqual(mock_get_vm.return_value, None) -- -- mock_get.return_value.status_code = 404 -- mock_get.return_value.text = 'Not Found Error' -- self.assertEqual(mock_get_vm.return_value, None) -- -- mock_get.return_value.status_code = 500 -- mock_get.return_value.text = 'Internal Server Error' -- self.assertEqual(mock_get_vm.return_value, None) -- -- mock_get.return_value.status_code = 502 -- mock_get.return_value.tex = 'Bad Gateway' -- self.assertEqual(mock_get_vm.return_value, None) -- -- mock_get.return_value.status_code = 503 -- mock_get.return_value.text = 'Service Unavailable ' -- self.assertEqual(mock_get_vm.return_value, None) -- -- @patch('virtwho.virt.ahv.ahv_interface.AhvInterface.get_vm', return_value=None) -- @patch.object(Session, 'post') -- def test_no_retry_http_erros_PC(self, mock_post, mock_get_vm): -- self.setUp(is_pc=True) -- mock_post.return_value.ok = False -- mock_post.return_value.status_code = 400 -- mock_post.return_value.text = 'Bad Request' -- self.assertEqual(mock_get_vm.return_value, None) -- -- mock_post.return_value.status_code = 404 -- mock_post.return_value.text = 'Not Found Error' -- self.assertEqual(mock_get_vm.return_value, None) -- -- mock_post.return_value.status_code = 500 -- mock_post.return_value.text = 'Internal Server Error' -- self.assertEqual(mock_get_vm.return_value, None) -- -- mock_post.return_value.status_code = 502 -- mock_post.return_value.tex = 'Bad Gateway' -- self.assertEqual(mock_get_vm.return_value, None) -- -- mock_post.return_value.status_code = 503 -- mock_post.return_value.text = 'Service Unavailable ' -- self.assertEqual(mock_get_vm.return_value, None) -- -- @patch('virtwho.virt.ahv.ahv_interface.AhvInterface.build_host_to_uvm_map') -- def test_getHostGuestMapping(self, host_to_uvm_map): -- host_to_uvm_map.return_value = HOST_UVM_MAP -- -- expected_result = [] -- -- for host_uuid in HOST_UVM_MAP: -- host = HOST_UVM_MAP[host_uuid] -- hypervisor_id = host_uuid -- host_name = host['name'] -- cluster_uuid = host['cluster_uuid'] -- guests = [] -- for guest_vm in host['guest_list']: -- state = guest_vm['power_state'] -- guests.append(Guest(guest_vm['uuid'], self.ahv.CONFIG_TYPE, -- state)) -- -- facts = { -- Hypervisor.CPU_SOCKET_FACT: '2', -- Hypervisor.HYPERVISOR_TYPE_FACT: u'kKvm', -- Hypervisor.HYPERVISOR_VERSION_FACT: 'Nutanix 20180802.100874', -- Hypervisor.HYPERVISOR_CLUSTER: str(cluster_uuid) -- } -- -- expected_result.append(Hypervisor( -- name=host_name, -- hypervisorId=hypervisor_id, -- guestIds=guests, -- facts=facts -- )) -- -- result = self.ahv.getHostGuestMapping()['hypervisors'] -- -- self.assertEqual(len(result), len(expected_result), 'lists length ' -- 'do not match') -- for index in range(0, len(result)): -- self.assertEqual(expected_result[index].toDict(), -- result[index].toDict()) -- -diff --git a/virtwho/config.py b/virtwho/config.py -index 3740e28..fdff5dd 100644 ---- a/virtwho/config.py -+++ b/virtwho/config.py -@@ -50,7 +50,7 @@ logger = log.getLogger(name='config', queue=False) - _effective_config = None - - VW_CONF_DIR = "/etc/virt-who.d/" --VW_TYPES = ("libvirt", "esx", "rhevm", "hyperv", "fake", "xen", "kubevirt", "ahv") -+VW_TYPES = ("libvirt", "esx", "rhevm", "hyperv", "fake", "xen", "kubevirt") - VW_GENERAL_CONF_PATH = "/etc/virt-who.conf" - VW_GLOBAL = "global" - VW_VIRT_DEFAULTS_SECTION_NAME = "defaults" -@@ -1151,7 +1151,7 @@ class VirtConfigSection(ConfigSection): - result = None - sm_type = self._values['sm_type'] - virt_type = self._values.get('type') -- if sm_type == 'sam' and virt_type in ('esx', 'rhevm', 'hyperv', 'xen', 'ahv'): -+ if sm_type == 'sam' and virt_type in ('esx', 'rhevm', 'hyperv', 'xen'): - if key not in self: - result = ( - 'warning', -diff --git a/virtwho/parser.py b/virtwho/parser.py -index 5b12d41..dd70af2 100644 ---- a/virtwho/parser.py -+++ b/virtwho/parser.py -@@ -47,7 +47,6 @@ SAT5_VM_DISPATCHER = { - 'rhevm': {'owner': False, 'server': True, 'username': True}, - 'hyperv': {'owner': False, 'server': True, 'username': True}, - 'kubevirt': {'owner': False, 'server': False, 'username': False, 'kubeconfig': True, 'kubeversion': False, 'insecure': False}, -- 'ahv' : {'owner': False, 'server': False, 'username': False}, - } - - SAT6_VM_DISPATCHER = { -@@ -57,7 +56,6 @@ SAT6_VM_DISPATCHER = { - 'rhevm': {'owner': True, 'server': True, 'username': True}, - 'hyperv': {'owner': True, 'server': True, 'username': True}, - 'kubevirt': {'owner': True, 'server': False, 'username': False, 'kubeconfig': True, 'kubeversion': False, 'insecure': False}, -- 'ahv' : {'owner': False, 'server': False, 'username': False}, - } - - class OptionError(Exception): -@@ -75,7 +73,7 @@ class StoreGroupArgument(Action): - def __call__(self, parser, namespace, values, option_string=None): - """ - When the argument from group is used, then this argument has to match -- virtualization backend [--libvirt|--esx|--rhevm|--hyperv|--xen|--kubevirt|--ahv] -+ virtualization backend [--libvirt|--esx|--rhevm|--hyperv|--xen|--kubevirt] - """ - options = vars(namespace) - virt_type = options['virt_type'] -diff --git a/virtwho/virt/ahv/__init__.py b/virtwho/virt/ahv/__init__.py -deleted file mode 100644 -index 01a19f8..0000000 ---- a/virtwho/virt/ahv/__init__.py -+++ /dev/null -@@ -1,6 +0,0 @@ --# -*- coding: utf-8 -*- --from __future__ import absolute_import, print_function -- --from .ahv import Ahv -- --__all__ = ['Ahv'] -diff --git a/virtwho/virt/ahv/ahv.py b/virtwho/virt/ahv/ahv.py -deleted file mode 100644 -index 47a786d..0000000 ---- a/virtwho/virt/ahv/ahv.py -+++ /dev/null -@@ -1,258 +0,0 @@ --import socket -- --from . import ahv_constants --from .ahv_interface import AhvInterface, Failure --from time import time --from virtwho import virt --from virtwho.config import VirtConfigSection --from virtwho.virt import Hypervisor, Guest -- --DefaultUpdateInterval = 1800 --MinimumUpdateInterval = 60 -- --class Ahv(virt.Virt): -- "AHV Rest client" -- CONFIG_TYPE = "ahv" -- def __init__(self, logger, config, dest, interval=None, -- terminate_event=None, oneshot=False): -- """ -- Args: -- logger (Logger): Framework logger. -- config (onfigSection): Virtwho configuration. -- dest (Datastore): Data store for destination. -- interval (Int): Wait interval for continuous run. -- terminate_event (Event): Event on termination. -- one_shot (bool): Flag to run virtwho as onetime or continuously. -- Returns: -- None. -- """ -- super(Ahv, self).__init__(logger, config, dest, -- terminate_event=terminate_event, -- interval=interval, -- oneshot=oneshot) -- self.config = config -- self.version = ahv_constants.VERSION_2 -- self.is_pc = False -- if 'prism_central' in self.config: -- if self.config['prism_central']: -- self.version = ahv_constants.VERSION_3 -- self.is_pc = True -- -- self.port = ahv_constants.DEFAULT_PORT -- self.url = ahv_constants.SERVER_BASE_URIL % (self.config['server'], -- self.port, self.version) -- self.port = ahv_constants.DEFAULT_PORT -- self.username = self.config['username'] -- self.password = self.config['password'] -- self.update_interval = self.config['update_interval'] -- self._interface = AhvInterface(logger, self.url, self.username, -- self.password, self.port, -- internal_debug=self.config['internal_debug']) -- -- def prepare(self): -- """ -- Prepare for obtaining information from AHV server. -- Args: -- None -- Returns: -- None -- """ -- self.logger.debug("Logging into Acropolis server %s" % self.url) -- self._interface.login(self.version) -- -- def _wait_for_update(self, timeout): -- """ -- Wait for an update from AHV. -- Args: -- timeout (int): timeout -- Returns: -- task list (list): List of vm or host related tasks. -- """ -- try: -- end_time = time() + timeout -- timestamp = int(time() * 1e6) -- while time() < end_time and not self.is_terminated(): -- try: -- response = self._interface.get_tasks(timestamp, self.version, -- self.is_pc) -- if len(response) == 0: -- # No events, continue to wait -- continue -- self.logger.debug('AHV event found: %s\n' % response) -- return response -- except Failure as e: -- if 'timeout' not in e.details: -- raise -- except Exception: -- self.logger.exception("Waiting on AHV events failed: ") -- -- return [] -- -- def getHostGuestMapping(self): -- """ -- Get a dict of host to uvm mapping. -- Args: -- None. -- Returns: -- None. -- """ -- mapping = {'hypervisors': []} -- -- host_uvm_map = self._interface.build_host_to_uvm_map(self.version) -- -- for host_uuid in host_uvm_map: -- host = host_uvm_map[host_uuid] -- -- try: -- if self.config['hypervisor_id'] == 'uuid': -- hypervisor_id = host_uuid -- elif self.config['hypervisor_id'] == 'hostname': -- hypervisor_id = host['name'] -- -- except KeyError: -- self.logger.debug("Host '%s' doesn't have hypervisor_id property", -- host_uuid) -- continue -- -- guests = [] -- if 'guest_list' in host and len(host['guest_list']) > 0: -- for guest_vm in host['guest_list']: -- try: -- state = guest_vm['power_state'] -- except KeyError: -- self.logger.warning("Guest %s is missing power state. Perhaps they" -- " are powered off", guest_vm['uuid']) -- continue -- guests.append(Guest(guest_vm['uuid'], self.CONFIG_TYPE, state)) -- else: -- self.logger.debug("Host '%s' doesn't have any vms", host_uuid) -- -- cluster_uuid = self._interface.get_host_cluster_uuid(host) -- host_version = self._interface.get_host_version(host) -- host_name = host['name'] -- -- facts = { -- Hypervisor.CPU_SOCKET_FACT: str(host['num_cpu_sockets']), -- Hypervisor.HYPERVISOR_TYPE_FACT: host.get('hypervisor_type', 'AHV'), -- Hypervisor.HYPERVISOR_VERSION_FACT: str(host_version), -- Hypervisor.HYPERVISOR_CLUSTER: str(cluster_uuid)} -- -- mapping['hypervisors'].append(virt.Hypervisor(hypervisorId=hypervisor_id, -- guestIds=guests, -- name=host_name, -- facts=facts)) -- return mapping -- -- def _run(self): -- """ -- Continuous run loop for virt-who on AHV. -- Args: -- None. -- Returns: -- None. -- """ -- self.prepare() -- next_update = time() -- initial = True -- wait_result = None -- while self._oneshot or not self.is_terminated(): -- -- delta = next_update - time() -- -- if initial: -- assoc = self.getHostGuestMapping() -- self._send_data(virt.HostGuestAssociationReport(self.config, assoc)) -- initial = False -- continue -- -- if delta > 0: -- # Wait for update. -- wait_result = self._wait_for_update(60 if initial else delta) -- if wait_result: -- events = wait_result -- else: -- events = [] -- else: -- events = [] -- -- if len(events) > 0 or delta > 0: -- assoc = self.getHostGuestMapping() -- self._send_data(virt.HostGuestAssociationReport(self.config, assoc)) -- -- if self._oneshot: -- break -- else: -- next_update = time() + self.update_interval -- --class AhvConfigSection(VirtConfigSection): -- """Class for intializing and processing AHV config""" -- VIRT_TYPE = 'ahv' -- HYPERVISOR_ID = ('uuid', 'hwuuid', 'hostname') -- -- def __init__(self, *args, **kwargs): -- """ -- Initialize AHV config and add config keys. -- Args: -- args: args -- kwargs : kwargs -- Returns: -- None. -- """ -- super(AhvConfigSection, self).__init__(*args, **kwargs) -- self.add_key('server', validation_method=self._validate_server, -- required=True) -- self.add_key('username', validation_method=self._validate_username, -- required=True) -- self.add_key('password', -- validation_method=self._validate_unencrypted_password, -- required=True) -- self.add_key('is_hypervisor', validation_method=self._validate_str_to_bool, -- default=True) -- self.add_key('prism_central', validation_method=self._validate_str_to_bool, -- default=None) -- self.add_key('internal_debug', validation_method=self._validate_str_to_bool, -- default=False) -- self.add_key('update_interval', -- validation_method=self._validate_update_interval, -- default=DefaultUpdateInterval) -- -- def _validate_server(self, key): -- """ -- Validate the server IP address. -- Args: -- key (Str): server Ip address. -- Returns: -- Socket error is returned in case of an invalid ip. -- """ -- error = super(AhvConfigSection, self)._validate_server(key) -- try: -- ip = self._values[key] -- socket.inet_aton(ip) -- except socket.error: -- error = 'Invalid server IP address provided' -- return error -- -- def _validate_update_interval(self, key): -- """ -- Validate the update internal flag. -- Args: -- key (Int): Update internal value. -- Returns: -- A warning is returned in case interval is not valid. -- """ -- result = None -- try: -- self._values[key] = int(self._values[key]) -- -- if self._values[key] < MinimumUpdateInterval: -- message = "Interval value can't be lower than {min} seconds. " \ -- "Default value of {min} " \ -- "seconds will be used.".format(min=DefaultUpdateInterval) -- result = ("warning", message) -- self._values['interval'] = DefaultUpdateInterval -- except KeyError: -- result = ('warning', '%s is missing' % key) -- except (TypeError, ValueError) as e: -- result = ( -- 'warning', '%s was not set to a valid integer: %s' % (key, str(e))) -- return result -diff --git a/virtwho/virt/ahv/ahv_constants.py b/virtwho/virt/ahv/ahv_constants.py -deleted file mode 100644 -index 95534a0..0000000 ---- a/virtwho/virt/ahv/ahv_constants.py -+++ /dev/null -@@ -1,21 +0,0 @@ --SERVER_BASE_URIL = 'https://%s:%d/api/nutanix/%s' --AHV_HYPERVIRSOR = ['kKvm', 'AHV', 'ahv', 'kvm'] --TASK_COMPLETE_MSG = ['SUCCEEDED', 'Succeeded'] --DEFAULT_PORT = 9440 --VERSION_2 = 'v2.0' --VERSION_3 = 'v3' -- --CMN_RST_CMD = {'get_vm': {'url': '/vms/%s', 'method': 'get'}, -- 'get_host': {'url': '/hosts/%s', 'method': 'get'}, -- 'get_tasks': {'url': '/tasks/list', 'method': 'post'}, -- 'get_task': {'url': '/tasks/%s', 'method': 'get'}} -- --REST_CMD = {VERSION_2: {'list_vms': {'url': '/vms', 'method': 'get'}, -- 'list_hosts': {'url': '/hosts', 'method': 'get'}, -- 'list_clusters' : {'url': '/clusters', -- 'method': 'get'}}, -- VERSION_3: {'list_vms': {'url': '/vms/list', 'method': 'post'}, -- 'list_hosts': {'url': '/hosts/list', 'method': 'post'}, -- 'list_clusters' : {'url': '/clusters/list', -- 'method': 'post'}}} -- -diff --git a/virtwho/virt/ahv/ahv_interface.py b/virtwho/virt/ahv/ahv_interface.py -deleted file mode 100644 -index 8b75cb6..0000000 ---- a/virtwho/virt/ahv/ahv_interface.py -+++ /dev/null -@@ -1,804 +0,0 @@ --import json --import math --import time --import sys --from . import ahv_constants --from requests import Session --from requests.exceptions import ConnectionError, ReadTimeout --from virtwho import virt -- --class AhvInterface(object): -- """ AHV REST Api interface class""" -- NO_RETRY_HTTP_CODES = [400, 404, 500, 502, 503] -- event_types = ['node', 'vm'] -- -- def __init__(self, logger, url, username, password, port, **kwargs): -- """ -- Args: -- logger (Log): Logger. -- url (str): Rest server url. -- username (str): Username. -- password (str): Password for rest client. -- port (int): Port number for ssp. -- kwargs(dict): Accepts following arguments: -- timeout(optional, int): Max seconds to wait before HTTP connection -- times-out. Default 30 seconds. -- retries (optional, int): Maximum number of retires. Default: 5. -- retry_interval (optional, int): Time to sleep between retry intervals. -- internal_debug (optional, bool): Detail log of the rest calls. -- Default: 5 seconds. -- """ -- self._session = Session() -- self._timeout = kwargs.get('timeout', 30) -- self._retries = kwargs.get('retries', 5) -- self._retry_interval = kwargs.get('retry_interval', 30) -- self._logger = logger -- self._url = url -- self._user = username -- self._password = password -- self._port = port -- self._internal_debug = kwargs.get('internal_debug', False) -- self._create_session(self._user, self._password) -- -- def _create_session(self, user=None, password=None): -- """ -- Creates rest session. -- Args: -- user (str): Username. -- password (str): Password for rest session. -- Returns: -- None. -- """ -- if user is None: -- user = self._user -- if password is None: -- password = self._password -- self._session.auth = (user, password) -- -- def _make_url(self, uri, *args): -- """ -- Creates base url. -- uri would always begin with a slash -- Args: -- uri (str): Uri. -- args (list): Args. -- Returns: -- url (str): Url with uri. -- """ -- if not uri.startswith("/"): -- uri = "/%s" % uri -- url = "%s%s" % (self._url, uri) -- for arg in args: -- url += "/%s" % str(arg) -- return url -- -- def _format_response(self, data): -- """ -- Format the data based on the response's version. -- Args: -- data (dict): Data dictionary. -- Returns: -- formatted_data (dict): Formatted dictionary. -- """ -- if 'entities' in data: -- return self._process_entities_list(data['entities']) -- else: -- return self._process_dict_response(data) -- -- def _process_dict_response(self, data): -- """ -- Format the data when we only have a dictionary. -- Args: -- data (dict): Data dictionary. -- Returns: -- formatted_data (dict): Formatted data. -- """ -- formatted_data = data -- if 'status' in data and 'metadata' in data: -- formatted_data = dict(data['status'], **data['metadata']) -- -- if 'resources' in formatted_data: -- if 'power_state' in formatted_data['resources']: -- formatted_data['power_state'] = \ -- formatted_data['resources']['power_state'] -- if 'num_cpu_sockets' in formatted_data['resources']: -- formatted_data['num_cpu_sockets'] = \ -- formatted_data['resources']['num_cpu_sockets'] -- -- return formatted_data -- -- def _process_entities_list(self, data): -- """ -- Format data for the list of entities. -- Args: -- data (list): List of entities dictionary. -- Returns: -- formatted_data (dict): Formatted data after processing list fo entities. -- """ -- formatted_data = data -- initial = True -- for entity in data: -- if 'status' in entity and 'metadata' in entity: -- if initial: -- formatted_data = [] -- initial = False -- formatted_data.append(dict(entity['status'], **entity['metadata'])) -- -- for ent_obj in formatted_data: -- if 'resources' in ent_obj: -- if 'nodes' in ent_obj['resources']: -- nodes = ent_obj['resources']['nodes'] -- if 'hypervisor_server_list' in nodes: -- ent_obj['hypervisor_types'] = [] -- for server in nodes['hypervisor_server_list']: -- ent_obj['hypervisor_types'].append(server['type']) -- -- if 'kind' in ent_obj: -- if ent_obj['kind'] == 'cluster': -- if 'uuid' in ent_obj: -- ent_obj['cluster_uuid'] = ent_obj['uuid'] -- -- return formatted_data -- -- def _progressbar(self, it, prefix="", size=60, file=sys.stdout, total=0, is_pc=False): -- count = total -- cursor = 0 -- def show(j): -- x = int(size*j/count) -- file.write("%s[%s%s] %i/%i\r" % (prefix, "#"*x, "."*(size-x), j, count)) -- file.flush() -- show(0) -- -- for i, item in enumerate(it): -- if is_pc: -- yield item -- for i in range(20): -- show(cursor+1) -- cursor += 1 -- if cursor == count: -- break -- time.sleep(0.1) -- else: -- show(i+1) -- -- yield item -- file.write("\n") -- file.flush() -- -- def login(self, version): -- """ -- Login to the rest server and ensure connection succeeds. -- Args: -- version (Str): Interface version. -- Returns: -- None. -- """ -- (url, cmd_method) = self.get_diff_ver_url_and_method( -- cmd_key='list_clusters', intf_version=version) -- self.make_rest_call(method=cmd_method, uri=url) -- self._logger.info("Successfully logged into the AHV REST server") -- -- def get_hypervisor_type(self, version, host_entity=None, vm_entity=None): -- """ -- Get the hypervisor type of the guest vm. -- Args: -- version (Str): API version. -- host_entity (Dict): Host info dict. -- vm_entity (Dict): Vm info dict. -- Returns: -- hypervisor_type (str): Vm hypervisor type. -- """ -- hypervisor_type = None -- if version == 'v2.0': -- if host_entity: -- hypervisor_type = host_entity['hypervisor_type'] -- else: -- self._logger.warning("Cannot retrieve the host type. Version:%s" -- % version) -- else: -- if vm_entity: -- if 'resources' in vm_entity: -- if 'hypervisor_type' in vm_entity['resources']: -- hypervisor_type = vm_entity['resources']['hypervisor_type'] -- else: -- self._logger.debug("Hypervisor type of the %s is not available" -- % vm_entity['uuid']) -- else: -- self._logger.warning("No vm entity is provided for version %s. " -- "Therefore it's unable to retrieve host type" -- % version) -- return hypervisor_type -- -- def get_common_ver_url_and_method(self, cmd_key): -- """ -- Gets the correct cmd name based on its corresponding version. -- Args: -- cmd_key (str): Key name to search for in the command dict. -- Returns: -- (str, str) : Tuple of (command, rest_type). -- """ -- return (ahv_constants.CMN_RST_CMD[cmd_key]['url'], -- ahv_constants.CMN_RST_CMD[cmd_key]['method']) -- -- def get_diff_ver_url_and_method(self, cmd_key, intf_version): -- """ -- Gets the correct cmd name based on its corresponding version -- Args: -- cmd_key (str): Key name to search for in the command dict. -- intf_version (str): Interface version. -- Returns: -- (str, str) : Tuple of (command, rest_type). -- """ -- return (ahv_constants.REST_CMD[intf_version][cmd_key]['url'], -- ahv_constants.REST_CMD[intf_version][cmd_key]['method']) -- -- def get(self, uri, *args, **kwargs): -- """ -- Args are appended to the url as components. -- /arg1/arg2/arg3 -- Send a get request with kwargs to the server. -- Args: -- uri (str): Uri. -- args (list): Args. -- kwargs (dict): Dictionary of params. -- Returns: -- Response (requests.Response): rsp. -- """ -- url = self._make_url(uri, *args) -- return self._send('get', url, **kwargs) -- -- def post(self, uri, **kwargs): -- """ -- Send a Post request to the server. -- Body can be either the dict or passed as kwargs -- headers is a dict. -- Args: -- uri (str): Uri. -- kwargs (dict): Dictionary of params. -- Returns: -- Response (requests.Response): rsp. -- """ -- url = self._make_url(uri) -- return self._send('post', url, **kwargs) -- -- def make_rest_call(self, method, uri, *args, **kwargs): -- """This method calls the appropriate rest method based on the arguments. -- -- Args: -- method (str): HTTP method. -- uri (str): Relative_uri. -- args(any): Arguments. -- kwargs(dict): Key value pair for the additional args. -- -- Returns: -- rsp (dict): The response content loaded as a JSON. -- """ -- func = getattr(self, method) -- return func(uri, *args, **kwargs) -- -- def _send(self, method, url, **kwargs): -- """This private method acting as proxy for all http methods. -- Args: -- method (str): The http method type. -- url (str): The URL to for the Request -- kwargs (dict): Keyword args to be passed to the requests call. -- retries (int): The retry count in case of HTTP errors. -- Except the codes in the list NO_RETRY_HTTP_CODES. -- -- Returns: -- Response (requests.Response): The response object. -- """ -- kwargs['verify'] = kwargs.get('verify', False) -- if 'timeout' not in kwargs: -- kwargs['timeout'] = self._timeout -- if 'data' not in kwargs: -- body = {} -- kwargs['data'] = json.dumps(body) -- content_dict = {'content-type': 'application/json'} -- kwargs.setdefault('headers', {}) -- kwargs['headers'].update(content_dict) -- -- func = getattr(self._session, method) -- response = None -- -- retries = kwargs.pop("retries", None) -- retry_interval = kwargs.pop("retry_interval", self._retry_interval) -- retry_count = retries if retries else self._retries -- for ii in range(retry_count): -- try: -- response = func(url, **kwargs) -- if self._internal_debug: -- self._logger.debug("%s method The request url sent: %s" % ( -- method.upper(), response.request.url)) -- self._logger.debug('Response status: %d' % response.status_code) -- self._logger.debug('Response: %s' % json.dumps(response.json(), -- indent=4)) -- -- except (ConnectionError, ReadTimeout) as e: -- self._logger.warning("Request failed with error: %s" % e) -- if ii != retry_count - 1: -- time.sleep(retry_interval) -- continue -- finally: -- self._session.close() -- if response.ok: -- return response -- if response.status_code in [401, 403]: -- raise virt.VirtError('HTTP Auth Failed %s %s. \n res: response: %s' % -- (method, url, response)) -- elif response.status_code == 409: -- raise virt.VirtError('HTTP conflict with the current state of the ' -- 'target resource %s %s. \n res: %s' % -- (method, url, response)) -- elif response.status_code in self.NO_RETRY_HTTP_CODES: -- break -- if ii != retry_count - 1: -- time.sleep(retry_interval) -- -- if response is not None: -- msg = 'HTTP %s %s failed: ' % (method, url) -- if hasattr(response, "text") and response.text: -- msg = "\n".join([msg, response.text]).encode('utf-8') -- self._logger.error(msg) -- else: -- self._logger.error("Failed to make the HTTP request (%s, %s)" % -- (method, url)) -- -- def get_tasks(self, timestamp, version, is_pc=False): -- """ -- Returns a list of AHV tasks which happened after timestamp. -- Args: -- timestamp (int): Current timestamp. -- version (str): Interface version. -- is_pc (bool): Flag to determine f we need to poll for PC tasks. -- Returns: -- Task list (list): list of tasks. -- """ -- ahv_clusters = self.get_ahv_cluster_uuids(version) -- (uri, cmd_method) = self.get_common_ver_url_and_method(cmd_key='get_tasks') -- # For task return. Use fv2.0 for now. update the url to use v2.0. -- url = self._url[:(self._url).rfind('v')] + 'v2.0' + uri -- -- res = self._send(method=cmd_method, url=url) -- data = res.json() -- -- if is_pc: -- return self.get_pc_tasks(data, timestamp, ahv_clusters) -- else: -- return self.get_pe_tasks(data, timestamp, ahv_clusters) -- -- def get_pc_tasks(self, data, timestamp, ahv_clusters): -- """ -- Returns a list of AHV tasks on PC which happened after timestamp. -- Args: -- data (json): Rest response in json format. -- timestamp (str): Current timestamp. -- ahv_clusters (list): List of ahv clusters uuid. -- Returns: -- task_list (list): list of tasks on PC. -- """ -- (uri, cmd_method) = self.get_common_ver_url_and_method(cmd_key='get_task') -- # For task return. Use fv2.0 for now. update the url to use v2.0. -- url = self._url[:(self._url).rfind('v')] + 'v2.0' + uri -- -- task_completed = False -- task_list = [] -- if 'entities' in data: -- for task in data['entities']: -- if 'start_time_usecs' in task: -- if task['start_time_usecs'] > timestamp: -- -- if 'progress_status' in task: -- if task['progress_status'] in ahv_constants.TASK_COMPLETE_MSG: -- task_completed = True -- elif 'status' in task: -- if task['status'] in ahv_constants.TASK_COMPLETE_MSG: -- task_completed = True -- -- if task_completed: -- task_completed=False -- if 'subtask_uuid_list' in task: -- for subtask in task['subtask_uuid_list']: -- url = url % subtask -- subtask_resp = self._send(cmd_method, url) -- subtask_data = subtask_resp.json() -- -- if 'progress_status' in subtask_data: -- if subtask_data['progress_status'] in \ -- ahv_constants.TASK_COMPLETE_MSG: -- -- if 'cluster_uuid' in subtask_data: -- cluster_uuid = subtask_data['cluster_uuid'] -- else: -- # Task does not have any cluster associated with it, -- # skip it. -- continue -- -- if cluster_uuid in ahv_clusters: -- if 'entity_list' in task: -- entity_type_list = task['entity_list'] -- else: -- # Task doesn't have any entity list, skip it. -- continue -- -- if entity_type_list: -- for ent_type in entity_type_list: -- if 'entity_type' in ent_type: -- if (str(ent_type['entity_type'])).lower() \ -- in self.event_types: -- task_list.append(task) -- task_list.append(subtask_data) -- -- else: -- # Task has not finished or it failed, skip it and continue -- # the loop -- continue -- -- return task_list -- -- -- -- def get_pe_tasks(self, data, timestamp, ahv_clusters): -- """ -- Returns a list of AHV tasks on PE which happened after timestamp. -- Args: -- data (json): rest response in json format. -- timestamp (str): Current timestamp. -- ahv_clusters (list): list of ahv clusters uuid. -- Returns: -- task_list (list): list of tasks on PE. -- """ -- task_completed = False -- task_list = [] -- -- if 'entities' in data: -- for task in data['entities']: -- if 'start_time_usecs' in task: -- if task['start_time_usecs'] > timestamp: -- -- if 'progress_status' in task: -- if task['progress_status'] in ahv_constants.TASK_COMPLETE_MSG: -- task_completed = True -- elif 'status' in task: -- if task['status'] in ahv_constants.TASK_COMPLETE_MSG: -- task_completed = True -- -- if task_completed: -- task_completed = False -- if 'cluster_reference' in task: -- if 'uuid' in task['cluster_reference']: -- cluster_uuid = task['cluster_reference']['uuid'] -- elif 'cluster_uuid' in task: -- cluster_uuid = task['cluster_uuid'] -- else: -- # Task does not have any cluster associated with it, skip it. -- continue -- -- if cluster_uuid in ahv_clusters: -- if 'entity_list' in task: -- entity_type_list = task['entity_list'] -- elif 'entity_reference_list' in task: -- entity_type_list = task['entity_reference_list'] -- else: -- # Task doesn't have any entity list, skip it. -- continue -- -- for ent_type in entity_type_list: -- if 'entity_type' in ent_type: -- if (str(ent_type['entity_type'])).lower() \ -- in self.event_types: -- task_list.append(task) -- elif 'kind' in ent_type: -- if (str(ent_type['kind'])).lower() in self.event_types: -- task_list.append(task) -- else: -- # Task doesn't have any event type associated to it. -- continue -- return task_list -- -- def get_vms_uuid(self, version): -- """ -- Returns the list of vms uuid. -- Args: -- version (str): Interface version. -- Returns: -- vm_uuid_list (list): list of vm's uuid. -- """ -- self._logger.info("Getting the list of available vms") -- is_pc=True if version == 'v3' else False -- vm_uuid_list = [] -- length = 0 -- offset = 0 -- total_matches = 0 -- count = 1 -- current = 0 -- (url, cmd_method) = self.get_diff_ver_url_and_method( -- cmd_key='list_vms', intf_version=version) -- res = self.make_rest_call(method=cmd_method, uri=url) -- data = res.json() -- if 'metadata' in data: -- if 'total_matches' in data['metadata'] and 'length' in data['metadata']: -- length = data['metadata']['length'] -- total_matches = data['metadata']['total_matches'] -- elif 'count' in data['metadata'] and \ -- 'grand_total_entities' in data['metadata'] and \ -- 'total_entities' in data['metadata']: -- -- total_matches = data['metadata']['grand_total_entities'] -- count = data['metadata']['count'] -- length = data['metadata']['total_entities'] -- -- if length < total_matches: -- self._logger.debug('Number of vms %s returned from REST is less than the total'\ -- 'numberr:%s. Adjusting the offset and iterating over all'\ -- 'vms until evry vm is returned from the server.' % (length, -- total_matches)) -- count = math.ceil(total_matches/float(length)) -- -- body = {'length': length, 'offset': offset} -- for i in self._progressbar(range(int(count)), "Finding vms uuid: ", total=int(total_matches), is_pc=is_pc): -- if 'entities' in data: -- for vm_entity in data['entities']: -- if 'metadata' in vm_entity: -- vm_uuid_list.append(vm_entity['metadata']['uuid']) -- elif 'uuid' in vm_entity: -- vm_uuid_list.append(vm_entity['uuid']) -- else: -- self._logger.warning("Cannot access the uuid for the vm %s. " -- "vm object: %s" % (vm_entity['name'], -- vm_entity)) -- -- body['offset'] = body['offset'] + length -- body_data = json.dumps(body, indent=4) -- self._logger.debug('next vm list call has this body: %s' % body) -- res = self.make_rest_call(method=cmd_method, uri=url, data=body_data) -- data = res.json() -- current += 1 -- -- self._logger.info("Total number of vms uuids found and saved for processing %s" % len(vm_uuid_list)) -- return vm_uuid_list -- -- def get_hosts_uuid(self, version): -- """ -- Returns the list of host uuid. -- Args: -- version (str): Interface version. -- Returns: -- host_uuid_list (list): list of host's uuid. -- """ -- host_uuid_list = [] -- (url, cmd_method) = self.get_diff_ver_url_and_method( -- cmd_key='list_hosts', intf_version=version) -- -- res = self.make_rest_call(method=cmd_method, uri=url) -- data = res.json() -- if 'entities' in data: -- for host_entity in data['entities']: -- if 'status' in host_entity and'metadata' in host_entity: -- # Check if a physical host, not a cluster. -- if 'cpu_model' in host_entity['status']: -- host_uuid_list.append(host_entity['metadata']['uuid']) -- elif 'uuid' in host_entity: -- host_uuid_list.append(host_uuid_list['uuid']) -- else: -- self._logger.warning("Cannot access the uuid for the. " -- "host object: %s" % (host_entity)) -- -- -- def get_host_cluster_uuid(self, host_info): -- """ -- Returns host's cluster UUID. -- Args: -- host_info (dict): Host info dict. -- Returns: -- host_cluster_uuid (uuid): host's cluster uuid. -- """ -- if 'cluster_uuid' in host_info: -- return host_info['cluster_uuid'] -- elif 'cluster_reference' in host_info: -- return host_info['cluster_reference']['uuid'] -- -- def get_ahv_cluster_uuids(self, version): -- """ -- Returns list of ahv cluster uuids. -- Args: -- version (str): Interface version. -- Returns: -- ahv_host_cluster_uuids (List): Returns list of ahv cluster uuids. -- """ -- ahv_host_cluster_uuids = [] -- seen = set(ahv_host_cluster_uuids) -- -- (url, cmd_method) = self.get_diff_ver_url_and_method( -- cmd_key='list_clusters', intf_version=version) -- res = self.make_rest_call(method=cmd_method, uri=url) -- data = res.json() -- -- formatted_data = self._format_response(data) -- -- for cluster in formatted_data: -- if 'hypervisor_types' in cluster and 'cluster_uuid' in cluster: -- for hypevirsor_type in cluster['hypervisor_types']: -- if hypevirsor_type in ahv_constants.AHV_HYPERVIRSOR: -- cluster_uuid = cluster['cluster_uuid'] -- if cluster_uuid not in seen: -- seen.add(cluster_uuid) -- ahv_host_cluster_uuids.append(cluster['cluster_uuid']) -- break -- -- return ahv_host_cluster_uuids -- -- def get_host_version(self, host_info): -- """ -- Returns host's version. -- Args: -- host_info (dict): Host info dict. -- Returns: -- host_version (Str): Host version if found, None otherwise. -- """ -- host_version = None -- if 'resources' in host_info: -- host_resources = host_info['resources'] -- if 'hypervisor' in host_resources: -- if 'hypervisor_full_name' in host_resources['hypervisor']: -- host_version = host_resources['hypervisor']['hypervisor_full_name'] -- elif 'hypervisor_full_name' in host_info: -- host_version = host_info['hypervisor_full_name'] -- else: -- self._logger.warning("Cannot get host version for %s" -- % host_info['uuid']) -- -- return host_version -- -- def get_vm(self, uuid): -- """ -- Returns vm information -- Args: -- uuid (str): Vm uuid. -- Return: -- data (dict): Vm information. -- """ -- (url, cmd_method) = self.get_common_ver_url_and_method(cmd_key='get_vm') -- url = url % uuid -- res = self.make_rest_call(method=cmd_method, uri=url) -- if res: -- data = res.json() -- return self._format_response(data) -- return None -- -- def get_host(self, uuid): -- """ -- Returns host information -- Args: -- uuid (str): Host uuid. -- Return: -- data (dict): Host information. -- """ -- (url, cmd_method) = self.get_common_ver_url_and_method(cmd_key='get_host') -- url = url % uuid -- res = self.make_rest_call(method=cmd_method, uri=url) -- if res: -- data = res.json() -- return self._format_response(data) -- else: -- return None -- -- def get_vm_host_uuid_from_vm(self, vm_entity): -- """ -- Get the host uuid from the vm_entity response -- Args: -- vm_entity (dict): Vm info. -- Returns: -- host uuid (str): Vm host uuid if found, none otherwise. -- """ -- if 'resources' in vm_entity: -- if 'host_reference' in vm_entity['resources']: -- return vm_entity['resources']['host_reference']['uuid'] -- else: -- self._logger.warning("Did not find any host information for vm:%s" -- % vm_entity['uuid']) -- elif 'host_uuid' in vm_entity: -- return vm_entity['host_uuid'] -- else: -- # Vm is off therefore no host is assigned to it. -- self._logger.debug('Cannot get the host uuid of the vm:%s. ' -- 'perhaps the vm is powered off' % vm_entity['uuid']) -- return None -- -- def is_ahv_host(self, version, host_uuid, vm_entity=None): -- """ -- Determine if a given host is a AHV host. -- host uuid should match the host uuid in vm_entity. -- Args: -- version (str): API version. -- host_uuid (str): uuid of a host. -- vm_entity (dict): For v3 -- Returns: -- bool : True if host is ahv; false otehrwise. -- """ -- if version == 'v2.0': -- host = self.get_host(host_uuid) -- if 'hypervisor_type' in host: -- return host['hypervisor_type'] in ahv_constants.AHV_HYPERVIRSOR -- else: -- if 'resources' in vm_entity: -- if 'hypervisor_type' in vm_entity['resources']: -- return vm_entity['resources']['hypervisor_type'] in \ -- ahv_constants.AHV_HYPERVIRSOR -- self._logger.debug('Hypervisor type not found. \nversion:%s, ' -- '\nhost_uuid:%s, \nvm_entity:%s' -- % (version, host_uuid, vm_entity)) -- return False -- -- def build_host_to_uvm_map(self, version): -- """ -- Builds a dictionary of every ahv host along with the vms they are hosting -- Args: -- version (Str): API version -- Returns: -- host_uvm_map (dict): Dict of ahv host with its uvms. -- """ -- host_uvm_map = {} -- vm_entity = None -- host_uuid = None -- vm_uuids = self.get_vms_uuid(version) -- -- self._logger.info("Processing hosts for each vm.") -- if len(vm_uuids) > 0: -- for vm_uuid in vm_uuids: -- vm_entity = self.get_vm(vm_uuid) -- if vm_entity: -- host_uuid = self.get_vm_host_uuid_from_vm(vm_entity) -- if host_uuid: -- if self.is_ahv_host(version, host_uuid, vm_entity): -- host = self.get_host(host_uuid) -- if host: -- if host_uuid not in host_uvm_map: -- host_uvm_map[host_uuid] = host -- if 'guest_list' in host_uvm_map[host_uuid]: -- host_uvm_map[host_uuid]['guest_list'].append(vm_entity) -- else: -- host_uvm_map[host_uuid]['guest_list'] = [] -- host_uvm_map[host_uuid]['guest_list'].append(vm_entity) -- else: -- self._logger.warning("unable to read information for host %s" % host_uuid) -- continue -- else: -- self._logger.debug("Host %s is not ahv, skipping it." % host_uuid) -- continue -- host_type = self.get_hypervisor_type(version, host, vm_entity) -- host_uvm_map[host_uuid]['hypervisor_type'] = host_type -- else: -- self._logger.warning("No available vms found") -- try: -- host_uuids = self.get_hosts_uuid(version) -- if len(host_uuids) > 0: -- for host_uuid in host_uuids: -- host = self.get_host(host_uuid) -- if host_uuid not in host_uvm_map: -- host_uvm_map[host_uuid] = host -- host_uvm_map[host_uuid]['guest_list'] = [] -- -- else: -- self._logger.warning("No Available AHV host found") -- except TypeError: -- # In case there is no cluster registered to the PC. -- self._logger.warning("Unable to find any AHV hosts.") -- -- return host_uvm_map -- --class Failure(Exception): -- -- def __init__(self, details): -- self.details = details -- -- def __str__(self): -- try: -- return str(self.details) -- except Exception as exn: -- import sys -- print(exn) -- return "AHV-API failure: %s" % str(self.details) -- -- def _details_map(self): -- return dict([(str(i), self.details[i]) for i in range(len(self.details))]) -diff --git a/virtwho/virt/virt.py b/virtwho/virt/virt.py -index 5495657..466dc90 100644 ---- a/virtwho/virt/virt.py -+++ b/virtwho/virt/virt.py -@@ -923,7 +923,6 @@ class Virt(IntervalThread): - import virtwho.virt.hyperv # flake8: noqa - import virtwho.virt.fakevirt # flake8: noqa - import virtwho.virt.kubevirt # flake8: noqa -- import virtwho.virt.ahv # flake8: noqa - - return [subcls for subcls in cls.__subclasses__()] - --- -2.26.2 - diff --git a/sources b/sources index 1f54840..fdc2a23 100644 --- a/sources +++ b/sources @@ -1,2 +1 @@ -SHA512 (virt-who-1.31.9.tar.gz) = 967c908c5f363ba45b68150f989cb9d09a919560859aea5ffe46a12aed667553aa18ddc017b13c35c3fd5f1fb6997b354699400e1013c773a8fc0e1fef70ce7d -SHA512 (build-rpm-no-ahv.patch) = f7bdfe5522619d1c1e299c842dd2a686d12ebe9783398e45c1b94d4040e004ddf549e65c8b91516a29d326490ec94bb9d3c83b61a71df1c0ec5eadd5877d1eca +SHA512 (virt-who-1.31.18.tar.gz) = 9dff7d4dc2f5b7512cc15db2e6e62bb8fb92b546c3abb4082e25764c08788b91d7937ff55cc03fdc45f5cc77f9d54981d483ca4cc46e64ac9ca01fcebccc078b diff --git a/virt-who.spec b/virt-who.spec index 4a025b5..ef7e694 100644 --- a/virt-who.spec +++ b/virt-who.spec @@ -20,8 +20,8 @@ Name: virt-who -Version: 1.31.9 -Release: %{release_number}%{?dist}.3 +Version: 1.31.18 +Release: %{release_number}%{?dist} Summary: Agent for reporting virtual guest IDs to subscription-manager @@ -29,7 +29,6 @@ Group: System Environment/Base License: GPLv2+ URL: https://github.com/candlepin/virt-who Source0: %{name}-%{version}.tar.gz -Patch1: build-rpm-no-ahv.patch BuildArch: noarch BuildRequires: %{python_ver}-devel @@ -50,8 +49,6 @@ Requires: python3-subscription-manager-rhsm > 1.25.6 %else Requires: subscription-manager-rhsm > 1.25.6 %endif -# python-suds is required for vSphere support -Requires: %{python_ver}-suds # m2crypto OR python3-cryptography is required for Hyper-V support %if %{use_python3} Requires: python3-cryptography @@ -88,9 +85,6 @@ report them to the subscription manager. %prep %setup -q -%if 0%{?rhel} -%patch1 -p1 -%endif %build %{python_exec} setup.py build --rpm-version=%{version}-%{release_number} @@ -169,15 +163,75 @@ fi %changelog -* Fri Jul 23 2021 Fedora Release Engineering - 1.31.9-1.3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild +* Thu Dec 16 2021 William Poteat 1.31.18-1 +- 2000019: Convert non-latin1 username/password to bytes (jhnidek@redhat.com) +- 2018052: Fix virt-who -s, when Hyper-V with new API is used + (jhnidek@redhat.com) +- ENT-4511: Remove six package from requirements (mhorky@redhat.com) +- ENT-4511: Drop Python 2 only tests (mhorky@redhat.com) +- ENT-4511: Drop six usage in suds tests (mhorky@redhat.com) +- ENT-4511: Drop six usage in tests (mhorky@redhat.com) +- ENT-4511: Drop six usage in virt-who code (mhorky@redhat.com) +- ENT-4511: Drop OrderedDict implementation (mhorky@redhat.com) +- Update for deprecation of MutableMapping (wpoteat@redhat.com) +- Only query the VM related taks in AHV hypervisors. (amir.eibagi@nutanix.com) +- Remove unused NTLM code as only basic auth is used (wpoteat@redhat.com) + +* Tue Sep 14 2021 William Poteat 1.31.17-1 +- Bypass stylish check on suds code (wpoteat@redhat.com) +- 2000922: Add the suds code to virt-who in place of python3-suds package + (wpoteat@redhat.com) +- ENT-4004: Fix flake8 issues for test files (mhorky@redhat.com) +- ENT-4004: Use four spaces for indentation (mhorky@redhat.com) +- ENT-4004: Fix flake8 issues (mhorky@redhat.com) +- ENT-4004: Add stylish tests (mhorky@redhat.com) +- Cannot use ESX where python3-suds is not available (wpoteat@redhat.com) +- 1981249: Stop using NTLM which requires MD4 (OpenSSL 3.0) + (csnyder@redhat.com) +- 1989877: Status command does not reach actual credentials checking + (wpoteat@redhat.com) +- Make changes to follow Conscious language initiative (mhorky@redhat.com) + +* Fri Aug 06 2021 William Poteat 1.31.16-1 +- 1990550: Add the description for nutanix mode in man virt-who and man virt- + who-config (wpoteat@redhat.com) +- 1990337: The guest state in mapping should be uniform with other hypervisors + 1990338: The guest shows wrong active value "0" in mapping when it's running + (wpoteat@redhat.com) +- 1989646: Get UnboundLocalError when configured hypervisor_id=hwuuid + (wpoteat@redhat.com) +- 1989645: Add dmi.system.uuid to ahv facts (wpoteat@redhat.com) +- 1974624: proxy error with https (wpoteat@redhat.com) + +* Tue Aug 03 2021 William Poteat 1.31.15-1 +- 1986973: Take out AHV removal patch mechanism (wpoteat@redhat.com) +- Update AHV patch (wpoteat@redhat.com) + +* Fri Jul 16 2021 William Poteat 1.31.14-1 +- Merge run data into report (wpoteat@redhat.com) +- Update the man page for the status mode (wpoteat@redhat.com) +- Status execution (wpoteat@redhat.com) +- Record last dates of succcess for sources and destinations + (wpoteat@redhat.com) +- Update certs for complex tests (wpoteat@redhat.com) + +* Thu Jun 03 2021 William Poteat 1.31.13-1 +- 1965320: Clear previous report hash when hypervisor count is zero + (wpoteat@redhat.com) +- Added support for Packit service (jhnidek@redhat.com) +- Convert CI from Travis to Jenkins (wpoteat@redhat.com) + +* Fri May 21 2021 William Poteat 1.31.12-1 +- 1951347: Update patch for xen removal (wpoteat@redhat.com) -* Fri Jun 04 2021 Python Maint - 1.31.9-1.2 -- Rebuilt for Python 3.10 +* Thu May 20 2021 William Poteat 1.31.11-1 +- 1951347: Remove Xen from hypervisor types (wpoteat@redhat.com) +- Releaser for Centos (wpoteat@redhat.com) -* Tue Mar 02 2021 Zbigniew Jędrzejewski-Szmek - 1.31.9-1.1 -- Rebuilt for updated systemd-rpm-macros - See https://pagure.io/fesco/issue/2583. +* Mon May 17 2021 William Poteat 1.31.10-1 +- 1920322: Uncomment section header on migrate (wpoteat@redhat.com) +- Update CI link to use branch name main (wpoteat@redhat.com) +- Fedora master branch name changed to main (wpoteat@redhat.com) * Thu Feb 18 2021 William Poteat 1.31.9-1 - Man page update to describe the migration script (wpoteat@redhat.com)