Change in vdsm[master]: GuestIF Refactoring
by Vinzenz Feenstra
Vinzenz Feenstra has uploaded a new change for review.
Change subject: GuestIF Refactoring
......................................................................
GuestIF Refactoring
Change-Id: Ib357d770a26ef1dc80b89a32bf6808551a7d622d
Signed-off-by: Vinzenz Feenstra <vfeenstr(a)redhat.com>
---
M vdsm/guestIF.py
1 file changed, 114 insertions(+), 76 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/18/24618/1
diff --git a/vdsm/guestIF.py b/vdsm/guestIF.py
index 229a55d..96ad68c 100644
--- a/vdsm/guestIF.py
+++ b/vdsm/guestIF.py
@@ -39,6 +39,115 @@
union(set(range(0x86, 0x9F + 1)))
+class UnknownMessageError(Exception):
+ def __init__(self, message, args):
+ Exception.__init__(self, 'Unknown or unsupported guest agent message '
+ '"%s" received with args "%s"' % (message,
+ str(args)))
+
+
+class MessageHandler(object):
+ def __init__(self, agent):
+ self.log = agent.log
+ self._agent = agent
+
+ def __call__(self, message, args):
+ handler = self.getattr(self, message.replace('-', '_'), None)
+ if handler:
+ handler(args)
+ else:
+ raise UnknownMessageError(message, args)
+
+ def applications(self, args):
+ self._agent.guestInfo['appsList'] = args['applications']
+
+ def fqdn(self, args):
+ self._agent.guestInfo['guestFQDN'] = args['fqdn']
+
+ def host_name(self, args):
+ self._agent.guestInfo['guestName'] = args['name']
+
+ def os_version(self, args):
+ self._agent.guestInfo['guestOs'] = args['version']
+
+ def session_lock(self, args):
+ self.agent.guestInfo['session'] = 'Locked'
+
+ def session_logoff(self, args):
+ self.agent.guestInfo['session'] = 'LoggedOff'
+
+ def session_logon(self, args):
+ self.agent.guestInfo['session'] = 'UserLoggedOn'
+
+ def session_unlock(self, args):
+ self.agent.guestInfo['session'] = 'Active'
+
+ def session_shutdown(self, args):
+ self.log.debug('Guest system shuts down')
+
+ def session_startup(self, args):
+ self.log.debug('Guest system started or restarted')
+
+ def uninstalled(self, args):
+ self.log.debug('Guest agent was uninstalled')
+ self._agent.guestInfo['appsList'] = []
+
+ def heartbeat(self, args):
+ self._agent.guestStatus = 'Up'
+ self._agent.guestInfo['memUsage'] = int(args['free-ram'])
+ # ovirt-guest-agent reports the following fields in 'memory-stat':
+ # 'mem_total', 'mem_free', 'mem_unused', 'swap_in', 'swap_out',
+ # 'pageflt' and 'majflt'
+ if 'memory-stat' in args:
+ for (k, v) in args['memory-stat'].iteritems():
+ # Convert the value to string since 64-bit integer is not
+ # supported in XMLRPC
+ self._agent.guestInfo['memoryStats'][k] = str(v)
+
+ if 'apiVersion' in args:
+ # The guest agent supports API Versioning
+ self._agent._handleAPIVersion(args['apiVersion'])
+ elif self._agent.effectiveApiVersion != _IMPLICIT_API_VERSION_ZERO:
+ # Older versions of the guest agent (before the introduction
+ # of API versioning) do not report this field
+ # Disable the API if not already disabled (e.g. after
+ # downgrade of the guest agent)
+ self.log.debug("API versioning no longer reported by guest.")
+ self._agent.effectiveApiVersion = _IMPLICIT_API_VERSION_ZERO
+
+ def network_interfaces(self, args):
+ interfaces = []
+ old_ips = ''
+ for iface in args['interfaces']:
+ iface['inet'] = iface.get('inet', [])
+ iface['inet6'] = iface.get('inet6', [])
+ interfaces.append(iface)
+ # Provide the old information which includes
+ # only the IP addresses.
+ old_ips += ' '.join(iface['inet']) + ' '
+ self._agent.guestInfo['netIfaces'] = interfaces
+ self._agent.guestInfo['guestIPs'] = old_ips.strip()
+
+ def active_user(self, args):
+ currentUser = args['name']
+ if ((currentUser != self._agent.guestInfo['username']) and
+ not (currentUser == 'Unknown' and
+ self._agent.guestInfo['username'] == 'None')):
+ self._agent.guestInfo['username'] = currentUser
+ self._agent.guestInfo['lastLogin'] = time.time()
+ self.log.debug("username: %s", repr(self.guestInfo['username']))
+
+ def disks_usage(self, args):
+ disks = []
+ for disk in args['disks']:
+ # Converting to string because XML-RPC doesn't support 64-bit
+ # integers.
+ disk['total'] = str(disk['total'])
+ disk['used'] = str(disk['used'])
+ disks.append(disk)
+ self._agent.guestInfo['disksUsage'] = disks
+
+
def _filterXmlChars(u):
"""
The set of characters allowed in XML documents is described in
@@ -109,6 +218,7 @@
def __init__(self, socketName, channelListener, log, user='Unknown',
ips='', connect=True):
+ self.handler = MessageHandler(self)
self.effectiveApiVersion = _IMPLICIT_API_VERSION_ZERO
self.log = log
self._socketName = socketName
@@ -223,82 +333,10 @@
self.log.log(logging.TRACE, "Guest's message %s: %s", message, args)
if self.guestStatus is None:
self.guestStatus = 'Up'
- if message == 'heartbeat':
- self.guestStatus = 'Up'
- self.guestInfo['memUsage'] = int(args['free-ram'])
- # ovirt-guest-agent reports the following fields in 'memory-stat':
- # 'mem_total', 'mem_free', 'mem_unused', 'swap_in', 'swap_out',
- # 'pageflt' and 'majflt'
- if 'memory-stat' in args:
- for (k, v) in args['memory-stat'].iteritems():
- # Convert the value to string since 64-bit integer is not
- # supported in XMLRPC
- self.guestInfo['memoryStats'][k] = str(v)
-
- if 'apiVersion' in args:
- # The guest agent supports API Versioning
- self._handleAPIVersion(args['apiVersion'])
- elif self.effectiveApiVersion != _IMPLICIT_API_VERSION_ZERO:
- # Older versions of the guest agent (before the introduction
- # of API versioning) do not report this field
- # Disable the API if not already disabled (e.g. after
- # downgrade of the guest agent)
- self.log.debug("API versioning no longer reported by guest.")
- self.effectiveApiVersion = _IMPLICIT_API_VERSION_ZERO
- elif message == 'host-name':
- self.guestInfo['guestName'] = args['name']
- elif message == 'os-version':
- self.guestInfo['guestOs'] = args['version']
- elif message == 'network-interfaces':
- interfaces = []
- old_ips = ''
- for iface in args['interfaces']:
- iface['inet'] = iface.get('inet', [])
- iface['inet6'] = iface.get('inet6', [])
- interfaces.append(iface)
- # Provide the old information which includes
- # only the IP addresses.
- old_ips += ' '.join(iface['inet']) + ' '
- self.guestInfo['netIfaces'] = interfaces
- self.guestInfo['guestIPs'] = old_ips.strip()
- elif message == 'applications':
- self.guestInfo['appsList'] = args['applications']
- elif message == 'active-user':
- currentUser = args['name']
- if ((currentUser != self.guestInfo['username']) and
- not (currentUser == 'Unknown' and
- self.guestInfo['username'] == 'None')):
- self.guestInfo['username'] = currentUser
- self.guestInfo['lastLogin'] = time.time()
- self.log.debug("username: %s", repr(self.guestInfo['username']))
- elif message == 'session-logon':
- self.guestInfo['session'] = "UserLoggedOn"
- elif message == 'session-lock':
- self.guestInfo['session'] = "Locked"
- elif message == 'session-unlock':
- self.guestInfo['session'] = "Active"
- elif message == 'session-logoff':
- self.guestInfo['session'] = "LoggedOff"
- elif message == 'uninstalled':
- self.log.debug("RHEV agent was uninstalled.")
- self.guestInfo['appsList'] = []
- elif message == 'session-startup':
- self.log.debug("Guest system is started or restarted.")
- elif message == 'fqdn':
- self.guestInfo['guestFQDN'] = args['fqdn']
- elif message == 'session-shutdown':
- self.log.debug("Guest system shuts down.")
- elif message == 'disks-usage':
- disks = []
- for disk in args['disks']:
- # Converting to string because XML-RPC doesn't support 64-bit
- # integers.
- disk['total'] = str(disk['total'])
- disk['used'] = str(disk['used'])
- disks.append(disk)
- self.guestInfo['disksUsage'] = disks
- else:
- self.log.error('Unknown message type %s', message)
+ try:
+ self.handler(message, args)
+ except UnknownMessageError as e:
+ self.log.error(e)
def stop(self):
self._stopped = True
--
To view, visit http://gerrit.ovirt.org/24618
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ib357d770a26ef1dc80b89a32bf6808551a7d622d
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Vinzenz Feenstra <vfeenstr(a)redhat.com>
6 years, 5 months
Change in vdsm[master]: yml: return type fixes for StoragePool.getSpmStatus
by Piotr Kliczewski
Piotr Kliczewski has uploaded a new change for review.
Change subject: yml: return type fixes for StoragePool.getSpmStatus
......................................................................
yml: return type fixes for StoragePool.getSpmStatus
Change-Id: Ieb9fbae250507d4cf02ef9b73eee6403a06cd9d2
Signed-off-by: Piotr Kliczewski <piotr.kliczewski(a)gmail.com>
---
M lib/api/vdsm-api.yml
M tests/vdsmapi_test.py
2 files changed, 7 insertions(+), 1 deletion(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/06/59706/1
diff --git a/lib/api/vdsm-api.yml b/lib/api/vdsm-api.yml
index 20de0a6..7dd95d1 100644
--- a/lib/api/vdsm-api.yml
+++ b/lib/api/vdsm-api.yml
@@ -5468,7 +5468,7 @@
- description: The lock version of the Storage Pool
name: spmLver
- type: int
+ type: long
type: object
StorageDomainCreateArgumentsBlock: &StorageDomainCreateArgumentsBlock
diff --git a/tests/vdsmapi_test.py b/tests/vdsmapi_test.py
index c3cacf0..eddb735 100644
--- a/tests/vdsmapi_test.py
+++ b/tests/vdsmapi_test.py
@@ -601,3 +601,9 @@
_schema.schema().verify_retval(
vdsmapi.MethodRep('Task', 'getStatus'), ret)
+
+ def test_spm_status(self):
+ ret = {'spmId': 1, 'spmStatus': 'SPM', 'spmLver': 10}
+
+ _schema.schema().verify_retval(
+ vdsmapi.MethodRep('StoragePool', 'getSpmStatus'), ret)
--
To view, visit https://gerrit.ovirt.org/59706
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ieb9fbae250507d4cf02ef9b73eee6403a06cd9d2
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Piotr Kliczewski <piotr.kliczewski(a)gmail.com>
6 years, 6 months
Change in vdsm[master]: yml: return type fixes for Task.getStatus
by Piotr Kliczewski
Piotr Kliczewski has uploaded a new change for review.
Change subject: yml: return type fixes for Task.getStatus
......................................................................
yml: return type fixes for Task.getStatus
Change-Id: I7ce3e3ab2e21e5d1f6da6bdf5dc89a3db40e2160
Signed-off-by: Piotr Kliczewski <piotr.kliczewski(a)gmail.com>
---
M lib/api/vdsm-api.yml
M tests/vdsmapi_test.py
2 files changed, 11 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/05/59705/1
diff --git a/lib/api/vdsm-api.yml b/lib/api/vdsm-api.yml
index 670eed7..20de0a6 100644
--- a/lib/api/vdsm-api.yml
+++ b/lib/api/vdsm-api.yml
@@ -5818,6 +5818,7 @@
cleanFailure: The task failed and recovery also failed
cleanSuccess: The task failed but was successfully recovered
success: The task was successful
+ '': Task is in progress
TaskState: &TaskState
added: '3.1'
diff --git a/tests/vdsmapi_test.py b/tests/vdsmapi_test.py
index 04297b1..c3cacf0 100644
--- a/tests/vdsmapi_test.py
+++ b/tests/vdsmapi_test.py
@@ -591,3 +591,13 @@
_schema.schema().verify_args(
vdsmapi.MethodRep('StoragePool', 'spmStart'), params)
+
+ def test_task_status(self):
+ ret = {'code': 0,
+ 'message': 'running job 1 of 1',
+ 'taskState': 'running',
+ 'taskResult': '',
+ 'taskID': '72fdf1ca-8826-4688-9c8c-e175d4253e2a'}
+
+ _schema.schema().verify_retval(
+ vdsmapi.MethodRep('Task', 'getStatus'), ret)
--
To view, visit https://gerrit.ovirt.org/59705
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I7ce3e3ab2e21e5d1f6da6bdf5dc89a3db40e2160
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Piotr Kliczewski <piotr.kliczewski(a)gmail.com>
6 years, 6 months
Change in vdsm[master]: yml: parameter type fixes for StoragePool.spmStart
by Piotr Kliczewski
Piotr Kliczewski has uploaded a new change for review.
Change subject: yml: parameter type fixes for StoragePool.spmStart
......................................................................
yml: parameter type fixes for StoragePool.spmStart
Change-Id: I49072827b8ac04f720d50aca8e5a24b4be7582b7
Signed-off-by: Piotr Kliczewski <piotr.kliczewski(a)gmail.com>
---
M lib/api/vdsm-api.yml
M tests/vdsmapi_test.py
2 files changed, 17 insertions(+), 3 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/04/59704/1
diff --git a/lib/api/vdsm-api.yml b/lib/api/vdsm-api.yml
index a289b7f..670eed7 100644
--- a/lib/api/vdsm-api.yml
+++ b/lib/api/vdsm-api.yml
@@ -8602,11 +8602,13 @@
- description: Deprecated. The lver of the previous SPM
name: prevLver
- type: int
+ type: string
+ datatype: int
- description: This parameter is not used
name: enableScsiFencing
- type: boolean
+ type: string
+ datatype: boolean
- defaultvalue: null
description: The maximum number of hosts that could be in the cluster
@@ -8616,7 +8618,8 @@
- defaultvalue: null
description: The expected Storage Domain version of the master domain
name: domVersion
- type: int
+ type: string
+ datatype: int
return:
description: A task UUID
type: *UUID
diff --git a/tests/vdsmapi_test.py b/tests/vdsmapi_test.py
index bff578c..04297b1 100644
--- a/tests/vdsmapi_test.py
+++ b/tests/vdsmapi_test.py
@@ -580,3 +580,14 @@
_schema.schema().verify_retval(
vdsmapi.MethodRep('Host', 'hostdevListByCaps'), ret)
+
+ def test_start_spm(self):
+ params = {u'prevLver': u'-1',
+ u'enableScsiFencing': u'false',
+ u'storagepoolID': u'636d9c59-f7ba-4115-87a1-44d6563a9610',
+ u'prevID': -1,
+ u'domVersion': u'3',
+ u'maxHostID': 250}
+
+ _schema.schema().verify_args(
+ vdsmapi.MethodRep('StoragePool', 'spmStart'), params)
--
To view, visit https://gerrit.ovirt.org/59704
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I49072827b8ac04f720d50aca8e5a24b4be7582b7
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Piotr Kliczewski <piotr.kliczewski(a)gmail.com>
6 years, 6 months
Change in vdsm[master]: core: Unlink image run directory when deleting a snapshot
by ahino@redhat.com
Ala Hino has uploaded a new change for review.
Change subject: core: Unlink image run directory when deleting a snapshot
......................................................................
core: Unlink image run directory when deleting a snapshot
Unlink image run directory,
/run/vdsm/storage/sdUUID/imgUUID/volUUID, when removing a
snapshot.
Change-Id: Ib88bf92e702ac6c324b87c9459b01adf165eaca4
Bug-Url: https://bugzilla.redhat.com/1321018
Signed-off-by: Ala Hino <ahino(a)redhat.com>
---
M vdsm/storage/blockVolume.py
1 file changed, 11 insertions(+), 1 deletion(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/25/59725/1
diff --git a/vdsm/storage/blockVolume.py b/vdsm/storage/blockVolume.py
index 4476a9e..080a858 100644
--- a/vdsm/storage/blockVolume.py
+++ b/vdsm/storage/blockVolume.py
@@ -604,12 +604,22 @@
try:
self.log.debug("Unlinking %s", vol_path)
os.unlink(vol_path)
- return True
except Exception as e:
eFound = e
self.log.error("cannot delete volume's %s/%s link path: %s",
self.sdUUID, self.volUUID, vol_path, exc_info=True)
+ try:
+ imgRundir = os.path.join(constants.P_VDSM_STORAGE, self.sdUUID,
+ self.imgUUID, self.volUUID)
+ self.log.debug("Unlinking %s", imgRundir)
+ os.unlink(imgRundir)
+ return True
+ except Exception as e:
+ eFound = e
+ self.log.error("cannot delete volume's %s/%s link path: %s",
+ self.sdUUID, self.volUUID, imgRundir, exc_info=True)
+
raise eFound
def extend(self, newSize):
--
To view, visit https://gerrit.ovirt.org/59725
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ib88bf92e702ac6c324b87c9459b01adf165eaca4
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Ala Hino <ahino(a)redhat.com>
6 years, 6 months
Change in vdsm[ovirt-3.5]: spec: Require sanlock 2.8-3
by Nir Soffer
Nir Soffer has uploaded a new change for review.
Change subject: spec: Require sanlock 2.8-3
......................................................................
spec: Require sanlock 2.8-3
Sanlock 2.8-3 added missing dependency on /usr/sbin/useradd and
/usr/sbin/groupadd, used to add the sanlock user and group during
installation. Without these dependencies, sanlock installation fail to
add the user and group, which fail vdsm-tool configure later.
Change-Id: I83ad11eda2695f161ee294571bbacbac11586b83
Bug-Url: https://bugzilla.redhat.com/1349068
Signed-off-by: Nir Soffer <nsoffer(a)redhat.com>
---
M vdsm.spec.in
1 file changed, 1 insertion(+), 1 deletion(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/56/59656/1
diff --git a/vdsm.spec.in b/vdsm.spec.in
index 607532f..7f968bd 100644
--- a/vdsm.spec.in
+++ b/vdsm.spec.in
@@ -216,7 +216,7 @@
Requires: iscsi-initiator-utils >= 6.2.0.873-21
%endif
-Requires: sanlock >= 2.8, sanlock-python
+Requires: sanlock >= 2.8-3, sanlock-python
%if 0%{?rhel}
Requires: python-ethtool >= 0.6-3
--
To view, visit https://gerrit.ovirt.org/59656
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I83ad11eda2695f161ee294571bbacbac11586b83
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: ovirt-3.5
Gerrit-Owner: Nir Soffer <nsoffer(a)redhat.com>
6 years, 6 months
Change in vdsm[master]: network: Use new concurrent.thread() utility
by Nir Soffer
Nir Soffer has uploaded a new change for review.
Change subject: network: Use new concurrent.thread() utility
......................................................................
network: Use new concurrent.thread() utility
This patch updates the networking subsystem to use the new utility.
Behavior changes:
- dhclient.DhcpClient threads are protected from silent failures
- configurators/ifcfg._ifup threads are proected from silent
failures.
Change-Id: I62e80bbbb9354d3173cce631ed5579532cf7cdcb
Relates-To: https://bugzilla.redhat.com/1141422
Signed-off-by: Nir Soffer <nsoffer(a)redhat.com>
---
M vdsm/network/configurators/dhclient.py
M vdsm/network/configurators/ifcfg.py
M vdsm/network/sourceroutethread.py
3 files changed, 8 insertions(+), 14 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/53/45553/1
diff --git a/vdsm/network/configurators/dhclient.py b/vdsm/network/configurators/dhclient.py
index 181c302..a25ae8f 100644
--- a/vdsm/network/configurators/dhclient.py
+++ b/vdsm/network/configurators/dhclient.py
@@ -23,8 +23,8 @@
import logging
import os
import signal
-import threading
+from vdsm import concurrent
from vdsm import cmdutils
from vdsm import ipwrapper
from vdsm import netinfo
@@ -76,9 +76,8 @@
rc, _, _ = self._dhclient()
return rc
else:
- t = threading.Thread(target=self._dhclient, name='vdsm-dhclient-%s'
- % self.iface)
- t.daemon = True
+ t = concurrent.thread(self._dhclient,
+ name='vdsm-dhclient-%s' % self.iface)
t.start()
def shutdown(self):
diff --git a/vdsm/network/configurators/ifcfg.py b/vdsm/network/configurators/ifcfg.py
index e1d3e94..f676f83 100644
--- a/vdsm/network/configurators/ifcfg.py
+++ b/vdsm/network/configurators/ifcfg.py
@@ -28,11 +28,11 @@
import re
import selinux
import shutil
-import threading
from libvirt import libvirtError, VIR_ERR_NO_NETWORK
from vdsm.config import config
+from vdsm import concurrent
from vdsm import cmdutils
from vdsm import constants
from vdsm import ipwrapper
@@ -782,9 +782,8 @@
if not iface.blockingdhcp and (iface.ipv4.bootproto == 'dhcp' or
iface.ipv6.dhcpv6):
# wait for dhcp in another thread, so vdsm won't get stuck (BZ#498940)
- t = threading.Thread(target=_exec_ifup, name='ifup-waiting-on-dhcp',
- args=(iface.name, cgroup))
- t.daemon = True
+ t = concurrent.thread(_exec_ifup, name='ifup-waiting-on-dhcp',
+ args=(iface.name, cgroup))
t.start()
else:
_exec_ifup(iface.name, cgroup)
diff --git a/vdsm/network/sourceroutethread.py b/vdsm/network/sourceroutethread.py
index 0a49760..042e5bd 100644
--- a/vdsm/network/sourceroutethread.py
+++ b/vdsm/network/sourceroutethread.py
@@ -19,12 +19,11 @@
from __future__ import absolute_import
import logging
import os
-import threading
import pyinotify
from vdsm.constants import P_VDSM_RUN
-from vdsm import utils
+from vdsm import concurrent
from .configurators.iproute2 import Iproute2
from .sourceroute import DynamicSourceRoute
@@ -68,13 +67,10 @@
def start():
- thread = threading.Thread(target=_subscribeToInotifyLoop,
- name='sourceRoute')
- thread.daemon = True
+ thread = concurrent.thread(_subscribeToInotifyLoop, name='sourceRoute')
thread.start()
-(a)utils.traceback()
def _subscribeToInotifyLoop():
logging.debug("sourceRouteThread.subscribeToInotifyLoop started")
--
To view, visit https://gerrit.ovirt.org/45553
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I62e80bbbb9354d3173cce631ed5579532cf7cdcb
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Nir Soffer <nsoffer(a)redhat.com>
6 years, 6 months
Change in vdsm[master]: libvirtconnection: Replace assert with AssertionError
by Nir Soffer
Nir Soffer has uploaded a new change for review.
Change subject: libvirtconnection: Replace assert with AssertionError
......................................................................
libvirtconnection: Replace assert with AssertionError
The code wrongly assumed that assert always exists. When running in
optimized mode, the check would be skipped, and instead of getting an
AssertionError, which is the expected error for programmer error
(starting the eventloop twice), we could get a confusing
RuntimeException or RuntimeError from Thread.start (depending on Python
version).
RuntimeError misused in the standard library for all kinds of errors
that do not have builtin errors. It is particularry bad option when used
for usage error.
Change-Id: Icf1564f81f4c1fbf77ccaff6d93c047a02d946da
Signed-off-by: Nir Soffer <nsoffer(a)redhat.com>
---
M lib/vdsm/libvirtconnection.py
1 file changed, 2 insertions(+), 1 deletion(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/64/34364/1
diff --git a/lib/vdsm/libvirtconnection.py b/lib/vdsm/libvirtconnection.py
index 5430c82..009f8b7 100644
--- a/lib/vdsm/libvirtconnection.py
+++ b/lib/vdsm/libvirtconnection.py
@@ -37,7 +37,8 @@
self.__thread = None
def start(self):
- assert not self.run
+ if self.run:
+ raise AssertionError("EventLoop is running")
self.__thread = threading.Thread(target=self.__run,
name="libvirtEventLoop")
self.__thread.setDaemon(True)
--
To view, visit http://gerrit.ovirt.org/34364
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Icf1564f81f4c1fbf77ccaff6d93c047a02d946da
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Nir Soffer <nsoffer(a)redhat.com>
6 years, 6 months
Change in vdsm[master]: vm: Remove useless volume size monitoring
by Nir Soffer
Nir Soffer has uploaded a new change for review.
Change subject: vm: Remove useless volume size monitoring
......................................................................
vm: Remove useless volume size monitoring
We used to check drive volume size every 60 seconds but we do not use
the result of this check for anything. According old comments, the
result was used in the past for extending disks. Since 3.6 we are using
the capacity value from libvirt.
This check may block on storage apis for minutes when using NFS
unresponsive storage, blocking executor workers and harming unrelated
vms using other storage.
Change-Id: Ib1436c2968f3e408ce38a913c6ca3146a25a312d
Signed-off-by: Nir Soffer <nsoffer(a)redhat.com>
---
M lib/vdsm/config.py.in
M lib/vdsm/virt/periodic.py
M vdsm/virt/vm.py
M vdsm/virt/vmdevices/storage.py
4 files changed, 19 insertions(+), 89 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/01/59801/1
diff --git a/lib/vdsm/config.py.in b/lib/vdsm/config.py.in
index 8bcb09c..ad2f8c7 100644
--- a/lib/vdsm/config.py.in
+++ b/lib/vdsm/config.py.in
@@ -287,9 +287,6 @@
'volume_utilization_percent, set the free space limit. Use higher '
'values to extend in bigger chunks.'),
- ('vol_size_sample_interval', '60',
- 'How often should the volume size be checked (seconds).'),
-
('scsi_rescan_maximal_timeout', '30',
'The maximal number of seconds to wait for scsi scan to return.'),
diff --git a/lib/vdsm/virt/periodic.py b/lib/vdsm/virt/periodic.py
index 8ed5d67..72f33a9 100644
--- a/lib/vdsm/virt/periodic.py
+++ b/lib/vdsm/virt/periodic.py
@@ -71,12 +71,6 @@
return Operation(disp, period, scheduler)
_operations = [
- # needs dispatching becuse updating the volume stats needs the
- # access the storage, thus can block.
- per_vm_operation(
- UpdateVolumes,
- config.getint('irs', 'vol_size_sample_interval')),
-
# needs dispatching becuse access FS and libvirt data
per_vm_operation(
NumaInfoMonitor,
@@ -316,22 +310,6 @@
return '<%s vm=%s at 0x%x>' % (
self.__class__.__name__, self._vm.id, id(self)
)
-
-
-class UpdateVolumes(_RunnableOnVm):
-
- @property
- def required(self):
- return (super(UpdateVolumes, self).required and
- # Avoid queries from storage during recovery process
- self._vm.isDisksStatsCollectionEnabled())
-
- def _execute(self):
- for drive in self._vm.getDiskDevices():
- # TODO: If this block (it is actually possible?)
- # we must make sure we don't overwrite good data
- # with stale old data.
- self._vm.updateDriveVolume(drive)
class NumaInfoMonitor(_RunnableOnVm):
diff --git a/vdsm/virt/vm.py b/vdsm/virt/vm.py
index b5eec87..db2bec0 100644
--- a/vdsm/virt/vm.py
+++ b/vdsm/virt/vm.py
@@ -378,15 +378,6 @@
if 'device' not in drv:
drv['device'] = 'disk'
- if drv['device'] == 'disk':
- volsize = self._getVolumeSize(drv['domainID'], drv['poolID'],
- drv['imageID'], drv['volumeID'])
- drv['truesize'] = str(volsize.truesize)
- drv['apparentsize'] = str(volsize.apparentsize)
- else:
- drv['truesize'] = 0
- drv['apparentsize'] = 0
-
def __legacyDrives(self):
"""
Backward compatibility for qa scripts that specify direct paths.
@@ -398,8 +389,7 @@
if path:
legacies.append({'type': hwclass.DISK,
'device': 'disk', 'path': path,
- 'iface': 'ide', 'index': index,
- 'truesize': 0})
+ 'iface': 'ide', 'index': index})
return legacies
def __removableDrives(self):
@@ -408,8 +398,7 @@
'device': 'cdrom',
'iface': vmdevices.storage.DEFAULT_INTERFACE_FOR_ARCH[self.arch],
'path': self.conf.get('cdrom', ''),
- 'index': 2,
- 'truesize': 0}]
+ 'index': 2}]
floppyPath = self.conf.get('floppy')
if floppyPath:
removables.append({
@@ -417,8 +406,7 @@
'device': 'floppy',
'path': floppyPath,
'iface': 'fdc',
- 'index': 0,
- 'truesize': 0})
+ 'index': 0})
return removables
def _devMapFromDevSpecMap(self, dev_spec_map):
@@ -999,10 +987,9 @@
for drive, volumeID, capacity, alloc, physical in extend:
self.log.info(
- "Requesting extension for volume %s on domain %s (apparent: "
- "%s, capacity: %s, allocated: %s, physical: %s)",
- volumeID, drive.domainID, drive.apparentsize, capacity,
- alloc, physical)
+ "Requesting extension for volume %s on domain %s (capacity: "
+ "%s, allocated: %s, physical: %s)",
+ volumeID, drive.domainID, capacity, alloc, physical)
self.extendDriveVolume(drive, volumeID, physical, capacity)
return len(extend) > 0
@@ -1044,8 +1031,6 @@
raise RuntimeError(
"Volume extension failed for %s (domainID: %s, volumeID: %s)" %
(volInfo['name'], volInfo['domainID'], volInfo['volumeID']))
-
- return volSize
def __afterReplicaExtension(self, volInfo):
self.__verifyVolumeExtension(volInfo)
@@ -1093,14 +1078,7 @@
def __afterVolumeExtension(self, volInfo):
# Check if the extension succeeded. On failure an exception is raised
# TODO: Report failure to the engine.
- volSize = self.__verifyVolumeExtension(volInfo)
-
- # Only update apparentsize and truesize if we've resized the leaf
- if not volInfo['internal']:
- vmDrive = self._findDriveByName(volInfo['name'])
- vmDrive.apparentsize = volSize.apparentsize
- vmDrive.truesize = volSize.truesize
-
+ self.__verifyVolumeExtension(volInfo)
try:
self.cont()
except libvirt.libvirtError:
@@ -3235,22 +3213,6 @@
return device
raise LookupError("No such disk %r" % name)
- def updateDriveVolume(self, vmDrive):
- if not vmDrive.device == 'disk' or not isVdsmImage(vmDrive):
- return
-
- try:
- volSize = self._getVolumeSize(
- vmDrive.domainID, vmDrive.poolID, vmDrive.imageID,
- vmDrive.volumeID)
- except StorageUnavailableError as e:
- self.log.error("Unable to update drive %s volume size: %s",
- vmDrive.name, e)
- return
-
- vmDrive.truesize = volSize.truesize
- vmDrive.apparentsize = volSize.apparentsize
-
def updateDriveParameters(self, driveParams):
"""Update the drive with the new volume information"""
@@ -3259,7 +3221,6 @@
if vmDrive.name == driveParams["name"]:
for k, v in driveParams.iteritems():
setattr(vmDrive, k, v)
- self.updateDriveVolume(vmDrive)
break
else:
self.log.error("Unable to update the drive object for: %s",
@@ -3499,11 +3460,8 @@
# see the XML even with 'info' as default level.
self.log.info(snapxml)
- # We need to stop the collection of the stats for two reasons, one
- # is to prevent spurious libvirt errors about missing drive paths
- # (since we're changing them), and also to prevent to trigger a drive
- # extension for the new volume with the apparent size of the old one
- # (the apparentsize is updated as last step in updateDriveParameters)
+ # Prevent spurious libvirt errors about missing drive paths (since
+ # we're changing them).
self.stopDisksStatsCollection()
try:
diff --git a/vdsm/virt/vmdevices/storage.py b/vdsm/virt/vmdevices/storage.py
index 78bf00c..eb8b2ae 100644
--- a/vdsm/virt/vmdevices/storage.py
+++ b/vdsm/virt/vmdevices/storage.py
@@ -56,13 +56,14 @@
class Drive(Base):
- __slots__ = ('iface', '_path', 'readonly', 'bootOrder', 'domainID',
- 'poolID', 'imageID', 'UUID', 'volumeID', 'format',
- 'propagateErrors', 'address', 'apparentsize', 'volumeInfo',
- 'index', 'name', 'optional', 'shared', 'truesize',
- 'volumeChain', 'baseVolumeID', 'serial', 'reqsize', 'cache',
- '_blockDev', 'extSharedState', 'drv', 'sgio', 'GUID',
- 'diskReplicate', '_diskType', 'hosts', 'protocol', 'auth')
+ __slots__ = (
+ 'iface', '_path', 'readonly', 'bootOrder', 'domainID', 'poolID',
+ 'imageID', 'UUID', 'volumeID', 'format', 'propagateErrors', 'address',
+ 'volumeInfo', 'index', 'name', 'optional', 'shared', 'volumeChain',
+ 'baseVolumeID', 'serial', 'reqsize', 'cache', '_blockDev',
+ 'extSharedState', 'drv', 'sgio', 'GUID', 'diskReplicate', '_diskType',
+ 'hosts', 'protocol', 'auth'
+ )
VOLWM_CHUNK_SIZE = (config.getint('irs', 'volume_utilization_chunk_mb') *
constants.MEGAB)
VOLWM_FREE_PCT = 100 - config.getint('irs', 'volume_utilization_percent')
@@ -165,8 +166,6 @@
self.device = getattr(self, 'device', 'disk')
# Keep sizes as int
self.reqsize = int(kwargs.get('reqsize', '0')) # Backward compatible
- self.truesize = int(kwargs.get('truesize', '0'))
- self.apparentsize = int(kwargs.get('apparentsize', '0'))
self.name = makeName(self.iface, self.index)
self.cache = config.get('vars', 'qemu_drive_cache')
@@ -247,10 +246,8 @@
Returns the next volume size in bytes. This value is based on the
volExtensionChunk property and it's the size that should be requested
for the next LV extension. curSize is the current size of the volume
- to be extended. For the leaf volume curSize == self.apparentsize.
- For internal volumes it is discovered by calling irs.getVolumeSize().
- capacity is the maximum size of the volume. It can be discovered using
- libvirt.virDomain.blockInfo() or qemuimg.info().
+ to be extended. capacity is the maximum size of the volume. It can be
+ discovered using libvirt.virDomain.blockInfo() or qemuimg.info().
"""
nextSize = utils.round(curSize + self.volExtensionChunk,
constants.MEGAB)
--
To view, visit https://gerrit.ovirt.org/59801
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ib1436c2968f3e408ce38a913c6ca3146a25a312d
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Nir Soffer <nsoffer(a)redhat.com>
6 years, 6 months
Change in vdsm[master]: cache: Add caching decorator with invalidation
by Nir Soffer
Nir Soffer has uploaded a new change for review.
Change subject: cache: Add caching decorator with invalidation
......................................................................
cache: Add caching decorator with invalidation
The new cache.memoized extends utils.memoized, adding invalidation
support.
Features added:
- An optional "validate" argument. This is a callable invoked each time
the memoized function is called. When the callable returns False, the
cache is invalidated.
- Memoized functions have an "invalidate" method, used to invalidate the
cache during testing.
- file_validator - invalidates the cache when a file changes.
Example usage:
from vdsm.cache import memoized, file_validator
@memoized(file_validator('/bigfile'))
def parse_bigfile():
# Expensive code processing '/bigfile' contents
Change-Id: I6dd8fb29d94286e3e3a3e29b8218501cbdc5c018
Signed-off-by: Nir Soffer <nsoffer(a)redhat.com>
---
M lib/vdsm/Makefile.am
A lib/vdsm/cache.py
M tests/Makefile.am
A tests/cacheTests.py
4 files changed, 366 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/09/34709/1
diff --git a/lib/vdsm/Makefile.am b/lib/vdsm/Makefile.am
index b862e71..6f0040d 100644
--- a/lib/vdsm/Makefile.am
+++ b/lib/vdsm/Makefile.am
@@ -23,6 +23,7 @@
dist_vdsmpylib_PYTHON = \
__init__.py \
+ cache.py \
compat.py \
define.py \
exception.py \
diff --git a/lib/vdsm/cache.py b/lib/vdsm/cache.py
new file mode 100644
index 0000000..9806e40
--- /dev/null
+++ b/lib/vdsm/cache.py
@@ -0,0 +1,98 @@
+#
+# Copyright 2014 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
+#
+
+import errno
+import os
+import functools
+
+
+def memoized(validate=None):
+ """
+ Return a caching decorator supporting invalidation.
+
+ The decorator accepts an optional validate callable, called each time the
+ memoized function is called. If the validate callable return True, the
+ memoized function will use the cache. If the validate callable return
+ False, the memoized cache is cleared.
+
+ The memoized function may accept multiple positional arguments. The
+ cache store the result for each combination of arguments. Functions with
+ kwargs are not supported.
+
+ Memoized functions have an "invalidate" method, used to invalidate the
+ memoized cache during testing.
+
+ To invalidate the cache when a file changes, use the file_validator from
+ this module.
+
+ Example usage:
+
+ from vdsm.cache import memoized, file_validator
+
+ @memoized(file_validator('/bigfile'))
+ def parse_bigfile():
+ # Expensive code processing '/bigfile' contents
+
+ """
+ def decorator(f):
+ cache = {}
+
+ @functools.wraps(f)
+ def wrapper(*args):
+ if validate is not None and not validate():
+ cache.clear()
+ try:
+ value = cache[args]
+ except KeyError:
+ value = cache[args] = f(*args)
+ return value
+
+ wrapper.invalidate = cache.clear
+ return wrapper
+
+ return decorator
+
+
+class file_validator(object):
+ """
+ I'm a validator returning False when a file has changed since the last
+ validation.
+ """
+
+ UNKNOWN = 0
+ MISSING = 1
+
+ def __init__(self, path):
+ self.path = path
+ self.stats = self.UNKNOWN
+
+ def __call__(self):
+ try:
+ stats = os.stat(self.path)
+ except OSError as e:
+ if e.errno != errno.ENOENT:
+ raise
+ stats = self.MISSING
+ else:
+ stats = stats.st_ino, stats.st_size, stats.st_mtime
+ if stats != self.stats:
+ self.stats = stats
+ return False
+ return True
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 36a1cdd..6fa7e64 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -26,6 +26,7 @@
alignmentScanTests.py \
blocksdTests.py \
bridgeTests.py \
+ cacheTests.py \
cPopenTests.py \
capsTests.py \
clientifTests.py \
diff --git a/tests/cacheTests.py b/tests/cacheTests.py
new file mode 100644
index 0000000..8927b39
--- /dev/null
+++ b/tests/cacheTests.py
@@ -0,0 +1,266 @@
+#
+# Copyright 2014 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
+#
+
+import os
+from vdsm.cache import memoized
+from vdsm.cache import file_validator
+from testlib import VdsmTestCase
+from testlib import namedTemporaryDir
+
+
+class Validator(object):
+ """ I'm a callable returning a boolean value (self.valid) """
+
+ def __init__(self):
+ self.valid = True
+ self.count = 0
+
+ def __call__(self):
+ self.count += 1
+ return self.valid
+
+
+class Accessor(object):
+ """ I'm recording how many times a dict was accessed. """
+
+ def __init__(self, d):
+ self.d = d
+ self.count = 0
+
+ def get(self, key):
+ self.count += 1
+ return self.d[key]
+
+
+class MemoizedTests(VdsmTestCase):
+
+ def setUp(self):
+ self.values = {'a': 0, 'b': 10, ('a',): 20, ('a', 'b'): 30}
+
+ def test_no_args(self):
+ accessor = Accessor(self.values)
+
+ @memoized()
+ def func(key):
+ return accessor.get(key)
+
+ # Fill the cache
+ self.assertEqual(func('a'), self.values['a'])
+ self.assertEqual(func('b'), self.values['b'])
+ self.assertEqual(accessor.count, 2)
+
+ # Values served now from the cache
+ self.assertEqual(func('a'), self.values['a'])
+ self.assertEqual(func('b'), self.values['b'])
+ self.assertEqual(accessor.count, 2)
+
+ def test_validation(self):
+ accessor = Accessor(self.values)
+ validator = Validator()
+
+ @memoized(validator)
+ def func(key):
+ return accessor.get(key)
+
+ # Fill the cache
+ self.assertEqual(func('a'), self.values['a'])
+ self.assertEqual(func('b'), self.values['b'])
+ self.assertEqual(accessor.count, 2)
+ self.assertEqual(validator.count, 2)
+
+ # Values served now from the cache
+ self.assertEqual(func('a'), self.values['a'])
+ self.assertEqual(func('b'), self.values['b'])
+ self.assertEqual(accessor.count, 2)
+ self.assertEqual(validator.count, 4)
+
+ # Values has changed
+ self.values['a'] += 1
+ self.values['b'] += 1
+
+ # Next call should clear the cache
+ validator.valid = False
+ self.assertEqual(func('a'), self.values['a'])
+ self.assertEqual(accessor.count, 3)
+ self.assertEqual(validator.count, 5)
+
+ # Next call should add next value to cache
+ validator.valid = True
+ self.assertEqual(func('b'), self.values['b'])
+ self.assertEqual(accessor.count, 4)
+ self.assertEqual(validator.count, 6)
+
+ # Values served now from the cache
+ self.assertEqual(func('a'), self.values['a'])
+ self.assertEqual(func('b'), self.values['b'])
+ self.assertEqual(accessor.count, 4)
+ self.assertEqual(validator.count, 8)
+
+ def test_raise_errors_in_memoized_func(self):
+ accessor = Accessor(self.values)
+ validator = Validator()
+
+ @memoized(validator)
+ def func(key):
+ return accessor.get(key)
+
+ # First run should fail, second shold fill the cache
+ self.assertRaises(KeyError, func, 'no such key')
+ self.assertEqual(func('a'), self.values['a'])
+ self.assertEqual(accessor.count, 2)
+ self.assertEqual(validator.count, 2)
+
+ def test_multiple_args(self):
+ accessor = Accessor(self.values)
+
+ @memoized()
+ def func(*args):
+ return accessor.get(args)
+
+ # Fill the cache
+ self.assertEqual(func('a'), self.values[('a',)])
+ self.assertEqual(func('a', 'b'), self.values[('a', 'b')])
+ self.assertEqual(accessor.count, 2)
+
+ # Values served now from the cache
+ self.assertEqual(func('a'), self.values[('a',)])
+ self.assertEqual(func('a', 'b'), self.values[('a', 'b')])
+ self.assertEqual(accessor.count, 2)
+
+ def test_kwargs_not_supported(self):
+ @memoized()
+ def func(a=None, b=None):
+ pass
+ self.assertRaises(TypeError, func, a=1, b=2)
+
+ def test_invalidate(self):
+ accessor = Accessor(self.values)
+
+ @memoized()
+ def func(key):
+ return accessor.get(key)
+
+ self.assertEqual(func('a'), self.values['a'])
+ self.assertEqual(accessor.count, 1)
+
+ func.invalidate()
+
+ self.assertEqual(func('a'), self.values['a'])
+ self.assertEqual(accessor.count, 2)
+
+
+class FileValidatorTests(VdsmTestCase):
+
+ def test_no_file(self):
+ with namedTemporaryDir() as tempdir:
+ path = os.path.join(tempdir, 'data')
+ validator = file_validator(path)
+
+ # Must be False so memoise call the decorated function
+ self.assertEqual(validator(), False)
+
+ # Since file state did not change, must remain True
+ self.assertEqual(validator(), True)
+
+ def test_file_created(self):
+ with namedTemporaryDir() as tempdir:
+ path = os.path.join(tempdir, 'data')
+ validator = file_validator(path)
+
+ self.assertEqual(validator(), False)
+ self.assertEqual(validator(), True)
+
+ with open(path, 'w') as f:
+ f.write('data')
+
+ self.assertEqual(validator(), False)
+ self.assertEqual(validator(), True)
+
+ def test_file_removed(self):
+ with namedTemporaryDir() as tempdir:
+ path = os.path.join(tempdir, 'data')
+ validator = file_validator(path)
+
+ with open(path, 'w') as f:
+ f.write('data')
+
+ self.assertEqual(validator(), False)
+ self.assertEqual(validator(), True)
+
+ os.unlink(path)
+
+ self.assertEqual(validator(), False)
+ self.assertEqual(validator(), True)
+
+ def test_size_changed(self):
+ with namedTemporaryDir() as tempdir:
+ path = os.path.join(tempdir, 'data')
+ validator = file_validator(path)
+ data = 'old data'
+ with open(path, 'w') as f:
+ f.write(data)
+
+ self.assertEqual(validator(), False)
+ self.assertEqual(validator(), True)
+
+ with open(path, 'w') as f:
+ f.write(data + ' new data')
+
+ self.assertEqual(validator(), False)
+ self.assertEqual(validator(), True)
+
+ def test_mtime_changed(self):
+ with namedTemporaryDir() as tempdir:
+ path = os.path.join(tempdir, 'data')
+ validator = file_validator(path)
+ data = 'old data'
+ with open(path, 'w') as f:
+ f.write(data)
+
+ self.assertEqual(validator(), False)
+ self.assertEqual(validator(), True)
+
+ # Fake timestamp change, as timestamp resolution may not be good
+ # enough when comparing changes during the test.
+ atime = mtime = os.path.getmtime(path) + 1
+ os.utime(path, (atime, mtime))
+
+ self.assertEqual(validator(), False)
+ self.assertEqual(validator(), True)
+
+ def test_ino_changed(self):
+ with namedTemporaryDir() as tempdir:
+ path = os.path.join(tempdir, 'data')
+ validator = file_validator(path)
+ data = 'old data'
+ with open(path, 'w') as f:
+ f.write(data)
+
+ self.assertEqual(validator(), False)
+ self.assertEqual(validator(), True)
+
+ tmp = path + '.tmp'
+ with open(tmp, 'w') as f:
+ f.write(data)
+ os.rename(tmp, path)
+
+ self.assertEqual(validator(), False)
+ self.assertEqual(validator(), True)
--
To view, visit http://gerrit.ovirt.org/34709
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I6dd8fb29d94286e3e3a3e29b8218501cbdc5c018
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Nir Soffer <nsoffer(a)redhat.com>
6 years, 6 months