mirror of
https://gerrit.ovirt.org/vdsm
synced 2026-02-05 12:46:23 +01:00
Before VM CPU stats are available, Vdsm reports zero initial values for them. ovirt-hosted-engine-ha relies on those stats when handling the Engine VM. The initial fake VM stats may confuse Engine VM monitoring and induce undesirable actions such as restarting the VM on another host without a good reason. There is no good way to distinguish the initial fake CPU stats from real CPU stats on the Engine side. Let's add a new flag, cpuActual, distinguishing the two cases. It is set to true when all the CPU stats are based on actual measured values. It would be better to simply omit the initial fake CPU stats. But we must keep them for compatibility with Engine 4.2, which expects their presence. Change-Id: I5adb1b01653b0029a30949ecb89219fde794dfd8 Bug-Url: https://bugzilla.redhat.com/2026263 Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
1111 lines
46 KiB
Python
1111 lines
46 KiB
Python
#
|
|
# Copyright 2016-2021 Red Hat, Inc.
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
#
|
|
# Refer to the README and COPYING files for full details of the license
|
|
#
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
|
|
import json
|
|
import logging
|
|
import pickle
|
|
import yaml
|
|
|
|
from io import StringIO
|
|
from textwrap import dedent
|
|
from unittest import mock
|
|
|
|
from nose.plugins.attrib import attr
|
|
from vdsm.api import vdsmapi
|
|
from vdsm.api.schema_inconsistency_formatter \
|
|
import SchemaInconsistencyFormatter
|
|
from yajsonrpc.exception import JsonRpcErrorBase
|
|
|
|
from testlib import VdsmTestCase as TestCaseBase
|
|
from testValidation import xfail
|
|
|
|
try:
|
|
import vdsm.gluster.apiwrapper as gapi
|
|
_glusterEnabled = True
|
|
gapi
|
|
except ImportError:
|
|
_glusterEnabled = False
|
|
|
|
|
|
_events_schema = vdsmapi.Schema.vdsm_events(strict_mode=True)
|
|
_schema = vdsmapi.Schema.vdsm_api(strict_mode=True,
|
|
with_gluster=_glusterEnabled)
|
|
|
|
|
|
class FakeSchema(object):
|
|
|
|
METHOD_NAME = "Namespace.Method"
|
|
METHOD_REP = vdsmapi.MethodRep("Namespace", "Method")
|
|
|
|
@staticmethod
|
|
def with_types(types_yaml, arguments_yaml):
|
|
types_header = "types:\n"
|
|
types_yaml = FakeSchema._fix_yaml_indentation(types_yaml, 1)
|
|
method_header = "\n\nNamespace.Method:\n params:\n"
|
|
arguments_yaml = FakeSchema._fix_yaml_indentation(arguments_yaml, 2)
|
|
schema_yaml = (types_header + types_yaml + method_header +
|
|
arguments_yaml)
|
|
return FakeSchema._schema_from(schema_yaml)
|
|
|
|
@staticmethod
|
|
def with_dummy_types(arguments_yaml):
|
|
dummy_types = \
|
|
"""
|
|
DummyType: &DummyType
|
|
name: DummyType
|
|
sourcetype: string
|
|
type: alias
|
|
"""
|
|
return FakeSchema.with_types(dummy_types, arguments_yaml)
|
|
|
|
@staticmethod
|
|
def _fix_yaml_indentation(yaml_str, indentation_multiplier):
|
|
dedented = dedent(yaml_str)
|
|
split = dedented.split("\n")
|
|
indentation = " " * indentation_multiplier
|
|
return indentation + ("\n" + indentation).join(split[1:])
|
|
|
|
@staticmethod
|
|
def _schema_from(yaml_str):
|
|
pickled_yaml = pickle.dumps(yaml.safe_load(yaml_str))
|
|
mocked_open = mock.mock_open(read_data=pickled_yaml)
|
|
with mock.patch('{}.io.open'.format(vdsmapi.__name__),
|
|
mocked_open,
|
|
create=True):
|
|
return vdsmapi.Schema.vdsm_api(strict_mode=False)
|
|
|
|
|
|
class DataVerificationTests(TestCaseBase):
|
|
|
|
def test_optional_params(self):
|
|
params = {u"addr": u"rack05-pdu01-lab4.tlv.redhat.com", u"port": 54321,
|
|
u"agent": u"apc_snmp", u"username": u"emesika",
|
|
u"password": u"pass", u"action": u"off",
|
|
u"options": u"port=15"}
|
|
|
|
_schema.verify_args(vdsmapi.MethodRep('Host', 'fenceNode'), params)
|
|
|
|
def test_ok_response(self):
|
|
ret = {u'power': u'on'}
|
|
|
|
_schema.verify_retval(vdsmapi.MethodRep('Host', 'fenceNode'), ret)
|
|
|
|
def test_unknown_response_type(self):
|
|
with self.assertRaises(JsonRpcErrorBase) as e:
|
|
ret = {u'My caps': u'My capabilites'}
|
|
|
|
_schema.verify_retval(
|
|
vdsmapi.MethodRep('Host', 'getCapabilities'), ret)
|
|
|
|
self.assertIn('My caps', str(e.exception))
|
|
|
|
def test_unknown_param(self):
|
|
params = {u"storagepoolID": u"00000002-0002-0002-0002-0000000000f6",
|
|
u"onlyForce": True,
|
|
u"storagedomainID": u"773adfc7-10d4-4e60-b700-3272ee1871f9"}
|
|
|
|
with self.assertRaises(JsonRpcErrorBase) as e:
|
|
_schema.verify_args(
|
|
vdsmapi.MethodRep('StorageDomain', 'detach'), params)
|
|
|
|
self.assertIn('onlyForce', str(e.exception))
|
|
|
|
def test_wrong_param_type(self):
|
|
params = {u"storagepoolID": u"00000000-0000-0000-0000-000000000000",
|
|
u"domainType": u"1",
|
|
u"connectionParams": [{u"timeout": 0,
|
|
u"version": u"3",
|
|
u"export": u"1.1.1.1:/export/ovirt",
|
|
u"retrans": 1}]}
|
|
|
|
with self.assertRaises(JsonRpcErrorBase) as e:
|
|
_schema.verify_args(
|
|
vdsmapi.MethodRep('StoragePool', 'disconnectStorageServer'),
|
|
params)
|
|
|
|
self.assertIn('StorageDomainType', str(e.exception))
|
|
|
|
def test_list_ret(self):
|
|
ret = [{u"status": 0, u"id": u"f6de012c-be35-47cb-94fb-f01074a5f9ef"}]
|
|
|
|
_schema.verify_retval(
|
|
vdsmapi.MethodRep('StoragePool', 'disconnectStorageServer'), ret)
|
|
|
|
def test_complex_ret_type(self):
|
|
ret = {u"cpuStatistics": {u"1": {u"cpuUser": u"1.47",
|
|
u"nodeIndex": 0,
|
|
u"cpuSys": u"1.20",
|
|
u"cpuIdle": u"97.33"},
|
|
u"0": {u"cpuUser": u"0.33",
|
|
u"nodeIndex": 0,
|
|
u"cpuSys": u"0.33",
|
|
u"cpuIdle": u"99.34"},
|
|
u"3": {u"cpuUser": u"0.47",
|
|
u"nodeIndex": 0,
|
|
u"cpuSys": u"0.27",
|
|
u"cpuIdle": u"99.26"},
|
|
u"2": {u"cpuUser": u"0.33",
|
|
u"nodeIndex": 0,
|
|
u"cpuSys": u"0.27",
|
|
u"cpuIdle": u"99.40"},
|
|
u"5": {u"cpuUser": u"0.20",
|
|
u"nodeIndex": 0,
|
|
u"cpuSys": u"0.33",
|
|
u"cpuIdle": u"99.47"},
|
|
u"4": {u"cpuUser": u"0.47",
|
|
u"nodeIndex": 0,
|
|
u"cpuSys": u"0.27",
|
|
u"cpuIdle": u"99.26"},
|
|
u"7": {u"cpuUser": u"0.60",
|
|
u"nodeIndex": 0,
|
|
u"cpuSys": u"0.40",
|
|
u"cpuIdle": u"99.00"},
|
|
u"6": {u"cpuUser": u"0.47",
|
|
u"nodeIndex": 0,
|
|
u"cpuSys": u"0.40",
|
|
u"cpuIdle": u"99.13"}},
|
|
u"numaNodeMemFree": {u"0": {u"memPercent": 15,
|
|
u"memFree": u"13645",
|
|
u"hugepages": {
|
|
4: {
|
|
u"freePages": u"10"},
|
|
2048: {
|
|
u"freePages": u"20"}}}},
|
|
u"memShared": 0,
|
|
u"thpState": u"madvise",
|
|
u"vmCount": 0,
|
|
u"memUsed": u"3",
|
|
u"storageDomains": {},
|
|
u"incomingVmMigrations": 0,
|
|
u"network": {u"bond0": {u"rxErrors": u"0",
|
|
u"txErrors": u"0",
|
|
u"speed": u"1000",
|
|
u"rxDropped": u"0",
|
|
u"name": u"bond0",
|
|
u"tx": u"0",
|
|
u"txDropped": u"0",
|
|
u"sampleTime": 1456911173.218806,
|
|
u"rx": u"0",
|
|
u"state": u"down"},
|
|
u"ovirtmgmt": {u"rxErrors": u"0",
|
|
u"txErrors": u"0",
|
|
u"speed": u"1000",
|
|
u"rxDropped": u"0",
|
|
u"name": u"ovirtmgmt",
|
|
u"tx": u"560936",
|
|
u"txDropped": u"0",
|
|
u"sampleTime": 1456911173.21,
|
|
u"rx": u"2106573",
|
|
u"state": u"up"},
|
|
u"lo": {u"rxErrors": u"0",
|
|
u"txErrors": u"0",
|
|
u"speed": u"1000",
|
|
u"rxDropped": u"0",
|
|
u"name": u"lo",
|
|
u"tx": u"2308049",
|
|
u"txDropped": u"0",
|
|
u"sampleTime": 1456911173.218806,
|
|
u"rx": u"2308049",
|
|
u"state": u"up"},
|
|
u";vdsmdummy;": {u"rxErrors": u"0",
|
|
u"txErrors": u"0",
|
|
u"speed": u"1000",
|
|
u"rxDropped": u"0",
|
|
u"name": u";vdsmdummy;",
|
|
u"tx": u"0",
|
|
u"txDropped": u"0",
|
|
u"sampleTime": 145691117.2,
|
|
u"rx": u"0",
|
|
u"state": u"down"},
|
|
u"em1": {u"rxErrors": u"0",
|
|
u"txErrors": u"0",
|
|
u"speed": u"1000",
|
|
u"rxDropped": u"0",
|
|
u"name": u"em1",
|
|
u"tx": u"580586",
|
|
u"txDropped": u"0",
|
|
u"sampleTime": 1456911173.218806,
|
|
u"rx": u"2310757",
|
|
u"state": u"up"},
|
|
u"wlp1s2": {u"rxErrors": u"0",
|
|
u"txErrors": u"0",
|
|
u"speed": u"1000",
|
|
u"rxDropped": u"0",
|
|
u"name": u"wlp1s2",
|
|
u"tx": u"0",
|
|
u"txDropped": u"0",
|
|
u"sampleTime": 1456911173.21880,
|
|
u"rx": u"0",
|
|
u"state": u"down"}},
|
|
u"txDropped": u"0",
|
|
u"cpuUser": u"0.54",
|
|
u"ksmPages": 100,
|
|
u"elapsedTime": u"106",
|
|
u"cpuLoad": u"0.42",
|
|
u"cpuSys": u"0.43",
|
|
u"diskStats": {u"/var/log": {u"free": u"10810"},
|
|
u"/run/vdsm/": {u"free": u"7966"},
|
|
u"/tmp": {u"free": u"7967"}},
|
|
u"cpuUserVdsmd": u"1.07",
|
|
u"netConfigDirty": u"False",
|
|
u"ksmState": False,
|
|
u"vmMigrating": 0,
|
|
u"ksmMergeAcrossNodes": True,
|
|
u"ksmCpu": 0,
|
|
u"bootTime": u"1456910791",
|
|
u"haStats": {u"active": False,
|
|
u"configured": False,
|
|
u"score": 0,
|
|
u"localMaintenance": False,
|
|
u"globalMaintenance": False},
|
|
u"momStatus": u"active",
|
|
u"rxDropped": u"0",
|
|
u"outgoingVmMigrations": 0,
|
|
u"swapTotal": 8007,
|
|
u"swapFree": 8007,
|
|
u"dateTime": u"2016-03-02T09:32:54 GMT",
|
|
u"anonHugePages": u"0",
|
|
u"memFree": 15482,
|
|
u"cpuIdle": u"99.03",
|
|
u"vmActive": 0,
|
|
u"v2vJobs": {},
|
|
u"cpuSysVdsmd": u"0.53",
|
|
u"multipathHealth": {}}
|
|
|
|
_schema.verify_retval(vdsmapi.MethodRep('Host', 'getStats'), ret)
|
|
|
|
def test_allvmstats(self):
|
|
ret = [{'vcpuCount': '1',
|
|
'displayInfo': [{'tlsPort': u'5900',
|
|
'ipAddress': '0',
|
|
'type': u'spice',
|
|
'port': '-1'}],
|
|
'hash': '-3472228600028768455',
|
|
'acpiEnable': u'true',
|
|
'displayIp': '0',
|
|
'guestFQDN': '',
|
|
'vmId': u'f1eb5cc5-d793-46c6-b1e3-719345bfec0c',
|
|
'pid': '32632',
|
|
'cpuUsage': '2660000000',
|
|
'timeOffset': u'0',
|
|
'session': 'Unknown',
|
|
'displaySecurePort': u'5900',
|
|
'displayPort': '-1',
|
|
'memUsage': '0',
|
|
'guestIPs': '',
|
|
'pauseCode': 'NOERR',
|
|
'vcpuQuota': '-1',
|
|
'username': 'Unknown',
|
|
'kvmEnable': u'true',
|
|
'network': {u'vnet0': {'macAddr': u'00:1a:4a:16:01:51',
|
|
'rxDropped': '1572',
|
|
'tx': '0',
|
|
'rxErrors': '0',
|
|
'txDropped': '0',
|
|
'rx': '90',
|
|
'txErrors': '0',
|
|
'state': 'unknown',
|
|
'sampleTime': 4319358.22,
|
|
'speed': '1000',
|
|
'name': u'vnet0'}},
|
|
'displayType': 'qxl',
|
|
'cpuUser': '0.57',
|
|
'vmJobs': {},
|
|
'disks': {
|
|
u'vdq': {'readLatency': '0',
|
|
'writtenBytes': '0',
|
|
'writeOps': '0',
|
|
'apparentsize': '1073741824',
|
|
'readOps': '0',
|
|
'writeLatency': '0',
|
|
'imageID': u'95c06337-8c23-4dfb-b0bf-a5f30bc9d33',
|
|
'readBytes': '0',
|
|
'flushLatency': '0',
|
|
'readRate': '0.0',
|
|
'truesize': '0',
|
|
'writeRate': '0.0'},
|
|
u'vdp': {'readLatency': '0',
|
|
'writtenBytes': '0',
|
|
'writeOps': '0',
|
|
'apparentsize': '1073741824',
|
|
'readOps': '0',
|
|
'writeLatency': '0',
|
|
'imageID': u'702df0bd-fff6-41eb-817b-103b23e5bd9',
|
|
'readBytes': '0',
|
|
'flushLatency': '0',
|
|
'readRate': '0.0',
|
|
'truesize': '0',
|
|
'writeRate': '0.0'}},
|
|
'monitorResponse': '0',
|
|
'elapsedTime': '2560',
|
|
'vmType': u'kvm',
|
|
'cpuSys': '0.20',
|
|
'cpuActual': True,
|
|
'status': 'Up',
|
|
'guestCPUCount': -1,
|
|
'appsList': (),
|
|
'clientIp': '',
|
|
'statusTime': '4319358220',
|
|
'vmName': u'vm1',
|
|
'vcpuPeriod': 100000},
|
|
{'vcpuCount': '1',
|
|
'displayInfo': [{'tlsPort': u'5901',
|
|
'ipAddress': '0',
|
|
'type': u'spice',
|
|
'port': '-1'}],
|
|
'hash': '8478318448907411309',
|
|
'acpiEnable': u'true',
|
|
'displayIp': '0',
|
|
'guestFQDN': '',
|
|
'vmId': u'7d3efc8f-405e-40cc-b512-1f8de3d6d587',
|
|
'pid': '32734',
|
|
'cpuUsage': '1220000000',
|
|
'timeOffset': u'0',
|
|
'session': 'Unknown',
|
|
'displaySecurePort': u'5901',
|
|
'displayPort': '-1',
|
|
'memUsage': '0',
|
|
'guestIPs': '',
|
|
'pauseCode': 'NOERR',
|
|
'vcpuQuota': '-1',
|
|
'username': 'Unknown',
|
|
'kvmEnable': u'true',
|
|
'network': {u'vnet1': {'macAddr': u'00:1a:4a:16:01:52',
|
|
'rxDropped': '0',
|
|
'tx': '7478',
|
|
'rxErrors': '0',
|
|
'txDropped': '0',
|
|
'rx': '331023',
|
|
'txErrors': '0',
|
|
'state': 'unknown',
|
|
'sampleTime': 4319358.22,
|
|
'speed': '1000',
|
|
'name': u'vnet1'}},
|
|
'displayType': 'qxl',
|
|
'cpuUser': '0.34',
|
|
'vmJobs': {},
|
|
'disks': {
|
|
u'vda': {'readLatency': '0',
|
|
'writtenBytes': '219136',
|
|
'writeOps': '81',
|
|
'apparentsize': '2621440',
|
|
'readOps': '791',
|
|
'writeLatency': '0',
|
|
'imageID': u'e2461e60-ee91-4500-bebf-f50f2a2f644',
|
|
'readBytes': '15910400',
|
|
'flushLatency': '0',
|
|
'readRate': '0.0',
|
|
'truesize': '2564096',
|
|
'writeRate': '0.0'},
|
|
u'hdc': {'readLatency': '0',
|
|
'writtenBytes': '0',
|
|
'writeOps': '0',
|
|
'apparentsize': '0',
|
|
'readOps': '1',
|
|
'writeLatency': '0',
|
|
'readBytes': '30',
|
|
'flushLatency': '0',
|
|
'readRate': '0.0',
|
|
'truesize': '0',
|
|
'writeRate': '0.0'}},
|
|
'monitorResponse': '0',
|
|
'elapsedTime': '2541',
|
|
'vmType': u'kvm',
|
|
'cpuSys': '0.07',
|
|
'cpuActual': True,
|
|
'status': 'Up',
|
|
'guestCPUCount': -1,
|
|
'appsList': (),
|
|
'clientIp': '',
|
|
'statusTime': '4319358220',
|
|
'vmName': u'vm2',
|
|
'vcpuPeriod': 100000}]
|
|
|
|
_schema.verify_retval(vdsmapi.MethodRep('Host', 'getAllVmStats'), ret)
|
|
|
|
def test_missing_method(self):
|
|
with self.assertRaises(vdsmapi.MethodNotFound):
|
|
_schema.get_method(
|
|
vdsmapi.MethodRep('missing_class', 'missing_method'))
|
|
|
|
def test_missing_type(self):
|
|
with self.assertRaises(vdsmapi.TypeNotFound):
|
|
_schema.get_type('Missing_type')
|
|
|
|
def test_events_params(self):
|
|
params = {u"notify_time": 4303947020,
|
|
u"426aef82-ea1d-4442-91d3-fd876540e0f0":
|
|
{u"status": u"Up",
|
|
u"displayInfo": [{u"tlsPort": u"5901",
|
|
u"ipAddress": u"0",
|
|
u"type": u"spice",
|
|
u"port": u"5900"}],
|
|
u"hash": u"880508647164395013",
|
|
u"cpuUser": u"0.00",
|
|
u"displayIp": u"0",
|
|
u"monitorResponse": u"0",
|
|
u"elapsedTime": u"110",
|
|
u"displayType": u"qxl",
|
|
u"cpuSys": u"0.00",
|
|
u"pauseCode": u"NOERR",
|
|
u"displayPort": u"5900",
|
|
u"displaySecurePort": u"5901",
|
|
u"timeOffset": u"0",
|
|
u"clientIp": u"",
|
|
u"vcpuQuota": u"-1",
|
|
u"vcpuPeriod": 100000}}
|
|
sub_id = '|virt|VM_status|426aef82-ea1d-4442-91d3-fd876540e0f0'
|
|
|
|
_events_schema.verify_event_params(sub_id, params)
|
|
|
|
def test_get_caps(self):
|
|
ret = {'HBAInventory': {'iSCSI': [{'InitiatorName': 'iqn.1994-05.co'}],
|
|
'FC': []},
|
|
'packages2': {'kernel': {'release': '201.fc23.x86_64',
|
|
'version': '4.5.5'},
|
|
'glusterfs-rdma': {'release': '1.fc23',
|
|
'version': '3.7.11'},
|
|
'glusterfs-fuse': {'release': '1.fc23',
|
|
'version': '3.7.11'},
|
|
'spice-server': {'release': '1.fc23',
|
|
'version': '0.12.6'},
|
|
'librbd1': {'release': '2.fc23',
|
|
'version': '0.94.7'},
|
|
'vdsm': {'release': '73.git2105bb3.fc23',
|
|
'version': '4.18.999'},
|
|
'qemu-kvm': {'release': '10.fc23',
|
|
'version': '2.4.1'},
|
|
'glusterfs': {'release': '1.fc23',
|
|
'version': '3.7.11'},
|
|
'libvirt': {'release': '1.fc23',
|
|
'version': '1.2.18.3'},
|
|
'qemu-img': {'release': '10.fc23',
|
|
'version': '2.4.1'},
|
|
'mom': {'release': '1.fc23',
|
|
'version': '0.5.4'},
|
|
'glusterfs-geo-replication': {'release': '1.fc23',
|
|
'version': '3.7.1'},
|
|
'glusterfs-server': {'release': '1.fc23',
|
|
'version': '3.7.11'},
|
|
'glusterfs-cli': {'release': '1.fc23',
|
|
'version': '3.7.11'}},
|
|
'numaNodeDistance': {'0': [10]},
|
|
'cpuModel': 'Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz',
|
|
'liveMerge': 'true',
|
|
'hooks': {'before_nic_hotplug':
|
|
{'50_vmfex': {'md5': 'e05994261acaea7dcf4b88ea'}},
|
|
'before_device_migrate_destination':
|
|
{'50_vmfex': {'md5': 'e05994261acaea7dcf4b88ea'}},
|
|
'before_device_create':
|
|
{'50_vmfex': {'md5': 'e05994261acaea7dcf4b88ea'}},
|
|
'my_custom_hook':
|
|
{'my_name.py': {'md5': 'e05994261acaea7dcf4b88ea'}}},
|
|
'supportsIPv6': True,
|
|
'vmTypes': ['kvm'],
|
|
'selinux': {'mode': '1'},
|
|
'liveSnapshot': 'true',
|
|
'kdumpStatus': 0,
|
|
'networks': {'ovirtmgmt':
|
|
{'addr': '192.168.1.102',
|
|
'bridged': True,
|
|
'dhcpv4': True,
|
|
'dhcpv6': False,
|
|
'gateway': '192.168.1.1',
|
|
'iface': 'ovirtmgmt',
|
|
'ipv4addrs': ['192.168.1.102/24'],
|
|
'ipv4defaultroute': True,
|
|
'ipv6addrs': ['2a02:a31a:e13f:7640:baca:3aff/64'],
|
|
'ipv6autoconf': True,
|
|
'ipv6gateway': 'fe80::f6f2:6dff:fe9c:3967',
|
|
'mtu': '1500',
|
|
'netmask': '255.255.255.0',
|
|
'ports': ['eno1'],
|
|
'stp': 'off',
|
|
'switch': 'legacy'}},
|
|
'kernelArgs': 'BOOT_IMAGE=/vmlinuz-4.5.5-201.fc23.x86_64 ro',
|
|
'bridges': {'ovirtmgmt':
|
|
{'ipv6autoconf': True,
|
|
'addr': '192.168.1.106',
|
|
'ipv6addrs': [],
|
|
'mtu': '1500',
|
|
'dhcpv4': True,
|
|
'netmask': '255.255.255.0',
|
|
'dhcpv6': False,
|
|
'stp': 'off',
|
|
'ipv4addrs': ['192.168.1.106/24'],
|
|
'ipv6gateway': '::',
|
|
'gateway': '192.168.1.1',
|
|
'opts':
|
|
{'multicast_last_member_count': '2',
|
|
'vlan_protocol': '0x8100',
|
|
'hash_elasticity': '4',
|
|
'multicast_query_response_interval': '1000',
|
|
'group_fwd_mask': '0x0',
|
|
'multicast_snooping': '1',
|
|
'multicast_startup_query_interval': '3125',
|
|
'hello_timer': '0',
|
|
'multicast_querier_interval': '25500',
|
|
'max_age': '2000',
|
|
'hash_max': '512',
|
|
'stp_state': '0',
|
|
'topology_change_detected': '0',
|
|
'priority': '32768',
|
|
'multicast_membership_interval': '26000',
|
|
'root_path_cost': '0',
|
|
'root_port': '0',
|
|
'multicast_querier': '0',
|
|
'multicast_startup_query_count': '2',
|
|
'nf_call_iptables': '0',
|
|
'hello_time': '200',
|
|
'topology_change': '0',
|
|
'bridge_id': '8000.b8ca3aa977e2',
|
|
'topology_change_timer': '0',
|
|
'ageing_time': '30000',
|
|
'nf_call_ip6tables': '0',
|
|
'gc_timer': '2191',
|
|
'root_id': '8000.b8ca3aa977e2',
|
|
'nf_call_arptables': '0',
|
|
'group_addr': '1:80:c2:0:0:0',
|
|
'multicast_last_member_interval': '100',
|
|
'default_pvid': '1',
|
|
'multicast_query_interval': '12500',
|
|
'multicast_query_use_ifaddr': '0',
|
|
'tcn_timer': '0',
|
|
'multicast_router': '1',
|
|
'vlan_filtering': '0',
|
|
'forward_delay': '0'},
|
|
'ports': ['eno1']}},
|
|
'uuid': '4C4C4544-0046-4E10-8032-B2C04F385A31',
|
|
'onlineCpus': '0,1,2,3,4,5,6,7',
|
|
'nics': {'eno1': {'ipv6autoconf': False,
|
|
'addr': '',
|
|
'speed': 1000,
|
|
'ipv6addrs': [],
|
|
'mtu': '1500',
|
|
'dhcpv4': False,
|
|
'netmask': '',
|
|
'dhcpv6': False,
|
|
'ipv4addrs': [],
|
|
'hwaddr': 'b8:ca:3a:a9:77:e2',
|
|
'ipv6gateway': '::',
|
|
'gateway': ''}},
|
|
'software_revision': '73',
|
|
'hostdevPassthrough': 'false',
|
|
'clusterLevels': ['3.5', '3.6', '4.0'],
|
|
'cpuFlags': 'fpu,vme,de,pse,tsc,msr,pae,mce,cx8,apic,sep',
|
|
'ISCSIInitiatorName': 'iqn.1994-05.com.redhat:7d366003913',
|
|
'netConfigDirty': 'False',
|
|
'supportedENGINEs': ['3.5', '3.6', '4.0'],
|
|
'autoNumaBalancing': 0,
|
|
'additionalFeatures': ['GLUSTER_SNAPSHOT', 'GLUSTER_GEO_RE'],
|
|
'reservedMem': '321',
|
|
'bondings': {'bond0': {'ipv6autoconf': True,
|
|
'addr': '',
|
|
'ipv6addrs': [],
|
|
'switch': 'legacy',
|
|
'active_slave': '',
|
|
'mtu': '1500',
|
|
'dhcpv4': False,
|
|
'netmask': '',
|
|
'dhcpv6': False,
|
|
'ipv4addrs': [],
|
|
'hwaddr': '3a:02:ff:17:ac:74',
|
|
'slaves': [],
|
|
'ipv6gateway': '::',
|
|
'gateway': '',
|
|
'opts': {'mode': '0'}}},
|
|
'vdsmToCpusAffinity': [1],
|
|
'software_version': '4.18',
|
|
'memSize': '15934',
|
|
'cpuSpeed': '1600.125',
|
|
'numaNodes': {
|
|
'0': {'totalMemory': '15934',
|
|
'cpus': [0, 1, 2, 3, 4, 5, 6, 7],
|
|
'hugepages': {
|
|
4: {
|
|
'totalPages': '2500'},
|
|
2048: {
|
|
'totalPages': '100'}}}},
|
|
'cpuSockets': '1',
|
|
'nameservers': [],
|
|
'vlans': {},
|
|
'lastClientIface': 'ovirtmgmt',
|
|
'cpuCores': '4',
|
|
'kvmEnabled': 'true',
|
|
'guestOverhead': '65',
|
|
'version_name': 'Snow Man',
|
|
'cpuThreads': '8',
|
|
'cpuTopology': [
|
|
{'cpu_id': 0, 'numa_cell_id': 0, 'socket_id': 0,
|
|
'die_id': 0, 'core_id': 0},
|
|
{'cpu_id': 1, 'numa_cell_id': 0, 'socket_id': 0,
|
|
'die_id': 0, 'core_id': 0},
|
|
{'cpu_id': 2, 'numa_cell_id': 0, 'socket_id': 0,
|
|
'die_id': 0, 'core_id': 0},
|
|
{'cpu_id': 3, 'numa_cell_id': 0, 'socket_id': 0,
|
|
'die_id': 0, 'core_id': 0},
|
|
{'cpu_id': 4, 'numa_cell_id': 0, 'socket_id': 0,
|
|
'die_id': 0, 'core_id': 0},
|
|
{'cpu_id': 5, 'numa_cell_id': 0, 'socket_id': 0,
|
|
'die_id': 0, 'core_id': 0},
|
|
{'cpu_id': 6, 'numa_cell_id': 0, 'socket_id': 0,
|
|
'die_id': 0, 'core_id': 0},
|
|
{'cpu_id': 7, 'numa_cell_id': 0, 'socket_id': 0,
|
|
'die_id': 0, 'core_id': 0}],
|
|
'emulatedMachines': ['pc-q35-2.0', 'pc-q35-2.1'],
|
|
'rngSources': ['hwrng', 'random'],
|
|
'operatingSystem': {'release': '1',
|
|
'version': '23',
|
|
'name': 'Fedora',
|
|
'pretty_name': 'Fedora 24 (Workstation)'},
|
|
'vncEncrypted': 'false'}
|
|
|
|
_schema.verify_retval(
|
|
vdsmapi.MethodRep('Host', 'getCapabilities'), ret)
|
|
|
|
def test_create_complex_params(self):
|
|
complex_type = {
|
|
'lease': {
|
|
'sd_id': 'UUID',
|
|
'lease_id': 'UUID'
|
|
},
|
|
'metadata': {
|
|
'LeaseMetadata': [
|
|
[
|
|
'JobMetadata',
|
|
'object'
|
|
]
|
|
]
|
|
}
|
|
}
|
|
|
|
self.assertEqual(
|
|
_schema.get_args_dict('Lease', 'create'),
|
|
json.dumps(complex_type, indent=4))
|
|
|
|
def test_no_params(self):
|
|
self.assertEqual(_schema.get_args_dict(
|
|
'Host', 'getCapabilities'), None)
|
|
|
|
def test_single_param(self):
|
|
complex_type = {'vmID': {'UUID': 'UUID'}}
|
|
self.assertEqual(_schema.get_args_dict(
|
|
'VM', 'getStats'), json.dumps(complex_type, indent=4))
|
|
|
|
|
|
@attr(type='unit')
|
|
class SchemaTypeTest(TestCaseBase):
|
|
|
|
@mock.patch.object(vdsmapi.os.path, "dirname", return_value="/a/b/c")
|
|
def test_schema_dirs(self, dirname_mock):
|
|
schema_dirs = set(vdsmapi.SchemaType.schema_dirs())
|
|
self.assertEqual(schema_dirs, {"/a/b/c", "/a/b/c/../rpc"})
|
|
|
|
@mock.patch.object(vdsmapi.SchemaType, "schema_dirs",
|
|
return_value=("/a/b/c",))
|
|
@mock.patch.object(vdsmapi.os.path, "exists", return_value=False)
|
|
def test_path_should_raise_in(self, exists_mock, schema_dirs_mock):
|
|
expected_msg = ("Unable to find API schema file, tried: "
|
|
"/a/b/c/vdsm-api.pickle")
|
|
with self.assertRaisesRegexp(vdsmapi.SchemaNotFound, expected_msg):
|
|
vdsmapi.SchemaType.VDSM_API.path()
|
|
|
|
@mock.patch.object(vdsmapi.SchemaType, "schema_dirs",
|
|
return_value=("/a/b/c",))
|
|
@mock.patch.object(vdsmapi.os.path, "exists", return_value=True)
|
|
def test_path_should_give_existing_path_in(self, exists_mock,
|
|
schema_dirs_mock):
|
|
expected_path = "/a/b/c/vdsm-api.pickle"
|
|
self.assertEqual(vdsmapi.SchemaType.VDSM_API.path(), expected_path)
|
|
|
|
|
|
@attr(type='unit')
|
|
class MethodArgumentsParsingTests(TestCaseBase):
|
|
|
|
def check_with_own_types(self, types_yaml, arguments_yaml, expected_args):
|
|
"""Checks whether method's arguments representation obtained by calling
|
|
'Schema.get_args_dict' is consistent with 'expected_args' by creating
|
|
a fake schema with types defined as in 'types_yaml' and method
|
|
parameters defined as in 'arguments_yaml'."""
|
|
if types_yaml is None:
|
|
schema = FakeSchema.with_dummy_types(arguments_yaml)
|
|
else:
|
|
schema = FakeSchema.with_types(types_yaml, arguments_yaml)
|
|
actual_args = schema.get_args_dict("Namespace", "Method")
|
|
expected_args = json.dumps(expected_args, indent=4)
|
|
msg = "Expected args:\n{}\nActual args:\n{}".format(expected_args,
|
|
actual_args)
|
|
self.assertEqual(actual_args, expected_args, msg)
|
|
|
|
def check_without_types(self, arguments_yaml, expected_args):
|
|
return self.check_with_own_types(None, arguments_yaml, expected_args)
|
|
|
|
def test_primitive_types(self):
|
|
self.check_without_types(
|
|
"""
|
|
- description: An int argument
|
|
name: some_int
|
|
type: int
|
|
""",
|
|
{"some_int": "int"})
|
|
|
|
self.check_without_types(
|
|
"""
|
|
- description: An uint argument
|
|
name: some_uint
|
|
type: uint
|
|
""",
|
|
{"some_uint": "uint"})
|
|
|
|
self.check_without_types(
|
|
"""
|
|
- description: A long argument
|
|
name: some_long
|
|
type: long
|
|
""",
|
|
{"some_long": "long"})
|
|
|
|
self.check_without_types(
|
|
"""
|
|
- description: An ulong argument
|
|
name: some_ulong
|
|
type: ulong
|
|
""",
|
|
{"some_ulong": "ulong"})
|
|
|
|
self.check_without_types(
|
|
"""
|
|
- description: A boolean argument
|
|
name: some_bool
|
|
type: bool
|
|
""",
|
|
{"some_bool": "bool"})
|
|
|
|
self.check_without_types(
|
|
"""
|
|
- description: A float argument
|
|
name: some_float
|
|
type: float
|
|
""",
|
|
{"some_float": "float"})
|
|
|
|
self.check_without_types(
|
|
"""
|
|
- description: A string argument
|
|
name: some_string
|
|
type: string
|
|
""",
|
|
{"some_string": "string"})
|
|
|
|
@xfail("Enable after fixing argument parsing")
|
|
def test_list_type(self):
|
|
self.check_without_types(
|
|
"""
|
|
- description: A list argument
|
|
name: list_argument
|
|
type:
|
|
- string
|
|
""",
|
|
{"list_argument": ["string"]})
|
|
|
|
self.check_without_types(
|
|
"""
|
|
- description: A list argument
|
|
name: list_argument
|
|
type:
|
|
- int
|
|
""",
|
|
{"list_argument": ["int"]})
|
|
|
|
def test_enum_type(self):
|
|
helper = list({"abra": 0, "ka": 1, "dabra": 2}.keys())
|
|
# "enum ['abra', 'ka', 'dabra']"
|
|
enum_representation = "enum " + str(helper)
|
|
self.check_with_own_types(
|
|
"""
|
|
EnumType: &EnumType
|
|
name: EnumType
|
|
type: enum
|
|
values:
|
|
abra: Abra
|
|
ka: Ka
|
|
dabra: Dabra
|
|
""",
|
|
"""
|
|
- description: An enum argument
|
|
name: enum_argument
|
|
type: *EnumType
|
|
""",
|
|
{"enum_argument": {"EnumType": enum_representation}})
|
|
|
|
def test_union_type(self):
|
|
self.check_with_own_types(
|
|
"""
|
|
UnionType: &UnionType
|
|
name: UnionType
|
|
type: union
|
|
values:
|
|
- int
|
|
- string
|
|
- boolean
|
|
""",
|
|
"""
|
|
- description: An union argument
|
|
name: union_argument
|
|
type: *UnionType
|
|
""",
|
|
{"union_argument": {"UnionType": ["int", "string", "boolean"]}})
|
|
|
|
@xfail("Enable after fixing argument parsing")
|
|
def test_alias_type(self):
|
|
self.check_with_own_types(
|
|
"""
|
|
AliasType: &AliasType
|
|
name: AliasType
|
|
sourcetype: string
|
|
type: alias
|
|
""",
|
|
"""
|
|
- description: An alias argument
|
|
name: alias_argument
|
|
type: *AliasType
|
|
""",
|
|
{"alias_argument": "AliasType"})
|
|
|
|
def test_object_type(self):
|
|
self.check_with_own_types(
|
|
"""
|
|
ObjectType: &ObjectType
|
|
name: ObjectType
|
|
type: object
|
|
properties:
|
|
- some_int: An int property
|
|
name: some_int
|
|
type: int
|
|
- some_bool: A bool property
|
|
name: some_bool
|
|
type: bool
|
|
""",
|
|
"""
|
|
- description: An object argument
|
|
name: object_argument
|
|
type: *ObjectType
|
|
""",
|
|
{"object_argument": {"some_int": "int", "some_bool": "bool"}})
|
|
|
|
@xfail("Enable after fixing argument parsing")
|
|
def test_nested_object_type(self):
|
|
self.check_with_own_types(
|
|
"""
|
|
NestedObjectType: &NestedObjectType
|
|
name: NestedObjectType
|
|
type: object
|
|
properties:
|
|
- some_int: An int property
|
|
name: some_int
|
|
type: int
|
|
- some_bool: A bool property
|
|
name: some_bool
|
|
type: bool
|
|
|
|
ObjectType: &ObjectType
|
|
name: ObjectType
|
|
type: object
|
|
properties:
|
|
- nested_object: A nested object property
|
|
name: nested_object
|
|
type: *NestedObjectType
|
|
""",
|
|
"""
|
|
- description: An object argument
|
|
name: object_argument
|
|
type: *ObjectType
|
|
""",
|
|
{
|
|
"object_argument": {
|
|
"nested_object": {
|
|
"some_int": "int", "some_bool": "bool"
|
|
}
|
|
}
|
|
})
|
|
|
|
@xfail("Enable after fixing argument parsing")
|
|
def test_two_level_nested_object_type(self):
|
|
self.check_with_own_types(
|
|
"""
|
|
InnerObjectType: &InnerObjectType
|
|
name: InnerObjectType
|
|
type: object
|
|
properties:
|
|
- some_int: An int property
|
|
name: some_int
|
|
type: int
|
|
- some_bool: A bool property
|
|
name: some_bool
|
|
type: bool
|
|
|
|
MiddleObjectType: &MiddleObjectType
|
|
name: MiddleObjectType
|
|
type: object
|
|
properties:
|
|
- inner_object: An inner object property
|
|
name: inner_object
|
|
type: *InnerObjectType
|
|
|
|
ObjectType: &ObjectType
|
|
name: ObjectType
|
|
type: object
|
|
properties:
|
|
- middle_object: A middle object property
|
|
name: middle_object
|
|
type: *MiddleObjectType
|
|
""",
|
|
"""
|
|
- description: An object argument
|
|
name: object_argument
|
|
type: *ObjectType
|
|
""",
|
|
{
|
|
"object_argument": {
|
|
"middle_object": {
|
|
"inner_object": {
|
|
"some_int": "int", "some_bool": "bool"
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
|
|
@attr(type='unit')
|
|
class SchemaInconsistencyFormatterTest(TestCaseBase):
|
|
|
|
def run(self, result=None):
|
|
self.buffer = StringIO()
|
|
formatter = SchemaInconsistencyFormatter(fmt="%(message)s")
|
|
handler = logging.StreamHandler(self.buffer)
|
|
handler.setFormatter(formatter)
|
|
self.logger = logging.getLogger(type(self).__name__)
|
|
self.logger.addHandler(handler)
|
|
self.logger.setLevel(logging.DEBUG)
|
|
with mock.patch.object(vdsmapi, "_log_inconsistency",
|
|
new=self.logger.debug):
|
|
super(SchemaInconsistencyFormatterTest, self).run(result)
|
|
|
|
def test_warnings_should_have_no_debugging_info_in(self):
|
|
self.logger.warning(u"zorro")
|
|
self.assertIn(u"zorro", self.buffer.getvalue())
|
|
self.assertNotIn("With message:", self.buffer.getvalue())
|
|
|
|
def log_entries_for(self, types_yaml, parameters_yaml, call_args):
|
|
if types_yaml is None:
|
|
schema = FakeSchema.with_dummy_types(parameters_yaml)
|
|
else:
|
|
schema = FakeSchema.with_types(types_yaml, parameters_yaml)
|
|
schema.verify_args(FakeSchema.METHOD_REP, call_args)
|
|
return self.buffer.getvalue()
|
|
|
|
def test_with_message_only(self):
|
|
types = None
|
|
parameters = """
|
|
- description: An int argument
|
|
name: some_int
|
|
type: int
|
|
"""
|
|
call_args = {"invalid": 6, "some_int": 7}
|
|
log_entries = self.log_entries_for(types, parameters, call_args)
|
|
|
|
self.assertIn(FakeSchema.METHOD_NAME, log_entries)
|
|
self.assertIn(u"With message:", log_entries)
|
|
self.assertIn(u"Following parameters ['invalid'] were not recognized",
|
|
log_entries)
|
|
self.assertNotIn(u"With backtrace:", log_entries)
|
|
|
|
def test_primitive_type_visitor(self):
|
|
types = None
|
|
parameters = """
|
|
- description: An uint argument
|
|
name: some_uint
|
|
type: uint
|
|
"""
|
|
call_args = {"some_uint": -20}
|
|
log_entries = self.log_entries_for(types, parameters, call_args)
|
|
|
|
self.assertIn(FakeSchema.METHOD_NAME, log_entries)
|
|
self.assertIn(u"Parameter some_uint is not uint type", log_entries)
|
|
self.assertIn(u"With backtrace:", log_entries)
|
|
self.assertIn(u"_check_primitive_type", log_entries)
|
|
self.assertIn(u'"schema_type_type":"uint"', log_entries)
|
|
|
|
def test_complex_type_visitor(self):
|
|
types = """
|
|
MyEnum: &MyEnum
|
|
name: MyEnum
|
|
type: enum
|
|
values:
|
|
0: one
|
|
1: two
|
|
2: three
|
|
"""
|
|
parameters = """
|
|
- description: An enum argument
|
|
name: my_enum
|
|
type: *MyEnum
|
|
"""
|
|
call_args = {"my_enum": 42}
|
|
log_entries = self.log_entries_for(types, parameters, call_args)
|
|
|
|
self.assertIn(FakeSchema.METHOD_NAME, log_entries)
|
|
self.assertIn(u'Provided value "42" not defined in MyEnum enum',
|
|
log_entries)
|
|
self.assertIn(u"With backtrace:", log_entries)
|
|
self.assertIn(u"_verify_complex_type", log_entries)
|
|
self.assertIn(u'"schema_type_type":"enum"', log_entries)
|
|
|
|
def test_object_type_visitor(self):
|
|
types = """
|
|
MyObject: &MyObject
|
|
name: MyObject
|
|
properties:
|
|
- name: a
|
|
type: uint
|
|
- name: b
|
|
type: str
|
|
type: object
|
|
"""
|
|
parameters = """
|
|
- description: An object argument
|
|
name: my_object
|
|
type: *MyObject
|
|
"""
|
|
call_args = {"my_object": {"a": "should_be_an_uint", "b": "fine"}}
|
|
log_entries = self.log_entries_for(types, parameters, call_args)
|
|
|
|
self.assertIn(FakeSchema.METHOD_NAME, log_entries)
|
|
self.assertIn(u'Parameter a is not uint type', log_entries)
|
|
self.assertIn(u"With backtrace:", log_entries)
|
|
self.assertIn(u"_verify_object_type", log_entries)
|
|
self.assertIn(u'"schema_type_name":"MyObject"', log_entries)
|
|
self.assertIn(u'call_arg_keys":[', log_entries)
|
|
self.assertIn(u'\t"a",', log_entries)
|
|
self.assertIn(u'\t"b"', log_entries)
|