Commit 5a93930f authored by Klaus Aehlig's avatar Klaus Aehlig

Merge branch 'stable-2.8' into stable-2.9

* stable-2.8
  Version bump for 2.8.4 and NEWS update
  Update NEWS file with news about job cancellation bugfix
  Fix QA flakiness
  Linting fix: remove unused import
  Add missing parameter entry to man file
  Add QA test for job cancellation
  Add correct locking of master node to gnt-debug delay
  Add job id type assert to jqueue.py
  Add job id transformation/check to Luxi Python client
  Start-master/stop-master always fail if confd is disabled
  Improve backwards compatibility of Issue 649 fix
  Add missing NEWS entries from stable-2.8
  Change usb_devices separator to whitespace

Conflicts:
	NEWS: take both additions
	configure.ac: ignore revision bump
	lib/cmdlib/test.py: manually redo changes of stable-2.8
	    on stable-2.9 version
Signed-off-by: default avatarKlaus Aehlig <aehlig@google.com>
Reviewed-by: default avatarGuido Trotter <ultrotter@google.com>
parents 091034b2 9f7413ab
......@@ -180,6 +180,22 @@ This was the first beta release of the 2.9 series. All important changes
are listed in the latest 2.9 entry.
Version 2.8.4
-------------
*(Released Thu, 23 Jan 2014)*
- Change the list separator for the usb_devices parameter from comma to space.
Commas could not work because they are already the hypervisor option
separator (Issue 649)
- Add support for blktap2 file-driver (Issue 638)
- Add network tag definitions to the haskell codebase (Issue 641)
- Fix RAPI network tag handling
- Add the network tags to the tags searched by gnt-cluster search-tags
- Fix caching bug preventing jobs from being cancelled
- Start-master/stop-master was always failing of ConfD was disabled. (Issue 685)
Version 2.8.3
-------------
......
......@@ -292,14 +292,20 @@ check_and_start() {
start_master() {
start ganeti-masterd
start ganeti-rapi
_confd_enabled && start ganeti-luxid
if _confd_enabled; then
start ganeti-luxid
else
return 0
fi
}
# Stops the master role
stop_master() {
if _confd_enabled ; then
stop ganeti-luxid
fi
stop ganeti-rapi
stop ganeti-masterd
_confd_enabled && stop ganeti-luxid
}
# Start all daemons
......
......@@ -286,6 +286,7 @@ __all__ = [
"OPT_COMPL_ONE_OS",
"OPT_COMPL_ONE_EXTSTORAGE",
"cli_option",
"FixHvParams",
"SplitNodeOption",
"CalculateOSNames",
"ParseFields",
......@@ -2600,6 +2601,21 @@ def ParseNicOption(optvalue):
return nics
def FixHvParams(hvparams):
# In Ganeti 2.8.4 the separator for the usb_devices hvparam was changed from
# comma to space because commas cannot be accepted on the command line
# (they already act as the separator between different hvparams). Still,
# RAPI should be able to accept commas for backwards compatibility.
# Therefore, we convert spaces into commas here, and we keep the old
# parsing logic everywhere else.
try:
new_usb_devices = hvparams[constants.HV_USB_DEVICES].replace(" ", ",")
hvparams[constants.HV_USB_DEVICES] = new_usb_devices
except KeyError:
#No usb_devices, no modification required
pass
def GenericInstanceCreate(mode, opts, args):
"""Add an instance to the cluster via either creation or import.
......@@ -2692,6 +2708,7 @@ def GenericInstanceCreate(mode, opts, args):
utils.ForceDictType(opts.beparams, constants.BES_PARAMETER_COMPAT)
utils.ForceDictType(hvparams, constants.HVS_PARAMETER_TYPES)
FixHvParams(hvparams)
if mode == constants.INSTANCE_CREATE:
start = opts.start
......
......@@ -1317,6 +1317,7 @@ def SetInstanceParams(opts, args):
utils.ForceDictType(opts.hvparams, constants.HVS_PARAMETER_TYPES,
allowed_values=[constants.VALUE_DEFAULT])
FixHvParams(opts.hvparams)
nics = _ConvertNicDiskModifications(opts.nics)
disks = _ParseDiskSizes(_ConvertNicDiskModifications(opts.disks))
......
#
#
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Google 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
......@@ -53,13 +53,21 @@ class LUTestDelay(NoHooksLU):
"""
self.needed_locks = {}
if self.op.on_nodes or self.op.on_master:
self.needed_locks[locking.LEVEL_NODE] = []
if self.op.on_nodes:
# _GetWantedNodes can be used here, but is not always appropriate to use
# this way in ExpandNames. Check LogicalUnit.ExpandNames docstring for
# more information.
(self.op.on_node_uuids, self.op.on_nodes) = \
GetWantedNodes(self, self.op.on_nodes)
self.needed_locks[locking.LEVEL_NODE] = self.op.on_node_uuids
self.needed_locks[locking.LEVEL_NODE].extend(self.op.on_node_uuids)
if self.op.on_master:
# The node lock should be acquired for the master as well.
self.needed_locks[locking.LEVEL_NODE].append(self.cfg.GetMasterNode())
def _TestDelay(self):
"""Do the actual sleep.
......
#
#
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc.
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2014 Google 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
......@@ -2043,6 +2043,8 @@ class JobQueue(object):
@return: either None or the job object
"""
assert isinstance(job_id, int), "Job queue: Supplied job id is not an int!"
job = self._memcache.get(job_id, None)
if job:
logging.debug("Found job %s in memcache", job_id)
......
#
#
# Copyright (C) 2006, 2007, 2011, 2012 Google Inc.
# Copyright (C) 2006, 2007, 2011, 2012, 2014 Google 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
......@@ -487,13 +487,25 @@ class Client(object):
jobs_state.append([op.__getstate__() for op in ops])
return self.CallMethod(REQ_SUBMIT_MANY_JOBS, (jobs_state, ))
@staticmethod
def _PrepareJobId(request_name, job_id):
try:
return int(job_id)
except ValueError:
raise RequestError("Invalid parameter passed to %s as job id: "
" expected integer, got value %s" %
(request_name, job_id))
def CancelJob(self, job_id):
job_id = Client._PrepareJobId(REQ_CANCEL_JOB, job_id)
return self.CallMethod(REQ_CANCEL_JOB, (job_id, ))
def ArchiveJob(self, job_id):
job_id = Client._PrepareJobId(REQ_ARCHIVE_JOB, job_id)
return self.CallMethod(REQ_ARCHIVE_JOB, (job_id, ))
def ChangeJobPriority(self, job_id, priority):
job_id = Client._PrepareJobId(REQ_CHANGE_JOB_PRIORITY, job_id)
return self.CallMethod(REQ_CHANGE_JOB_PRIORITY, (job_id, priority))
def AutoArchiveJobs(self, age):
......@@ -524,6 +536,7 @@ class Client(object):
min(WFJC_TIMEOUT, timeout)))
def WaitForJobChange(self, job_id, fields, prev_job_info, prev_log_serial):
job_id = Client._PrepareJobId(REQ_WAIT_FOR_JOB_CHANGE, job_id)
while True:
result = self.WaitForJobChangeOnce(job_id, fields,
prev_job_info, prev_log_serial)
......
......@@ -886,6 +886,27 @@ class R_2_groups_name_assign_nodes(baserlib.OpcodeResource):
})
def _ConvertUsbDevices(data):
"""Convert in place the usb_devices string to the proper format.
In Ganeti 2.8.4 the separator for the usb_devices hvparam was changed from
comma to space because commas cannot be accepted on the command line
(they already act as the separator between different hvparams). RAPI
should be able to accept commas for backwards compatibility, but we want
it to also accept the new space separator. Therefore, we convert
spaces into commas here and keep the old parsing logic elsewhere.
"""
try:
hvparams = data["hvparams"]
usb_devices = hvparams[constants.HV_USB_DEVICES]
hvparams[constants.HV_USB_DEVICES] = usb_devices.replace(" ", ",")
data["hvparams"] = hvparams
except KeyError:
#No usb_devices, no modification required
pass
class R_2_instances(baserlib.OpcodeResource):
"""/2/instances resource.
......@@ -935,6 +956,8 @@ class R_2_instances(baserlib.OpcodeResource):
# Remove "__version__"
data.pop(_REQ_DATA_VERSION, None)
_ConvertUsbDevices(data)
return (data, {
"dry_run": self.dryRun(),
})
......@@ -1328,7 +1351,10 @@ class R_2_instances_name_modify(baserlib.OpcodeResource):
"""Changes parameters of an instance.
"""
return (self.request_body, {
data = self.request_body.copy()
_ConvertUsbDevices(data)
return (data, {
"instance_name": self.items[0],
})
......
......@@ -338,6 +338,14 @@ vnc\_bind\_address
or specify the address of one of the interfaces on the node to
restrict listening to that interface.
vnc\_password\_file
Valid for the Xen HVM and KVM hypervisors.
Specifies the location of the file containing the password for
connections using VNC. The default is a file named
vnc-cluster-password which can be found in the configuration
directory.
vnc\_tls
Valid for the KVM hypervisor.
......@@ -727,10 +735,13 @@ soundhw
usb\_devices
Valid for the KVM hypervisor.
Comma separated list of usb devices. These can be emulated devices
Space separated list of usb devices. These can be emulated devices
or passthrough ones, and each one gets passed to kvm with its own
``-usbdevice`` option. See the **qemu**\(1) manpage for the syntax
of the possible components.
of the possible components. Note that values set with this
parameter are split on a space character and currently don't support
quoting. For backwards compatibility reasons, the RAPI interface keeps
accepting comma separated lists too.
vga
Valid for the KVM hypervisor.
......
......@@ -180,6 +180,7 @@ def SetupCluster(rapi_user):
qa_node.MarkNodeAddedAll()
RunTestIf("test-jobqueue", qa_cluster.TestJobqueue)
RunTestIf("test-jobqueue", qa_job.TestJobCancellation)
# enable the watcher (unconditionally)
RunTest(qa_daemon.TestResumeWatcher)
......
#
#
# Copyright (C) 2012 Google Inc.
# Copyright (C) 2012, 2014 Google 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
......@@ -23,10 +23,19 @@
"""
from ganeti.utils import retry
from ganeti import constants
from ganeti import query
import functools
import re
import qa_config
import qa_error
import qa_utils
from qa_utils import AssertCommand, GetCommandOutput
def TestJobList():
"""gnt-job list"""
......@@ -37,3 +46,96 @@ def TestJobList():
def TestJobListFields():
"""gnt-node list-fields"""
qa_utils.GenericQueryFieldsTest("gnt-job", query.JOB_FIELDS.keys())
def _GetJobStatuses():
""" Invokes gnt-job list and extracts an id to status dictionary.
@rtype: dict of string to string
@return: A dictionary mapping job ids to matching statuses
"""
master = qa_config.GetMasterNode()
list_output = GetCommandOutput(
master.primary, "gnt-job list --no-headers --output=id,status"
)
return dict(map(lambda s: s.split(), list_output.splitlines()))
def _GetJobStatus(job_id):
""" Retrieves the status of a job.
@type job_id: string
@param job_id: The job id, represented as a string.
@rtype: string or None
@return: The job status, or None if not present.
"""
return _GetJobStatuses().get(job_id, None)
def _RetryingFetchJobStatus(retry_status, job_id):
""" Used with C{retry.Retry}, waits for a status other than the one given.
@type retry_status: string
@param retry_status: The old job status, expected to change.
@type job_id: string
@param job_id: The job id, represented as a string.
@rtype: string or None
@return: The new job status, or None if none could be retrieved.
"""
status = _GetJobStatus(job_id)
if status == retry_status:
raise retry.RetryAgain()
return status
def TestJobCancellation():
"""gnt-job cancel"""
# The delay used for the first command should be large enough for the next
# command and the cancellation command to complete before the first job is
# done. The second delay should be small enough that not too much time is
# spend waiting in the case of a failed cancel and a running command.
FIRST_COMMAND_DELAY = 10.0
AssertCommand(["gnt-debug", "delay", "--submit", str(FIRST_COMMAND_DELAY)])
SECOND_COMMAND_DELAY = 1.0
master = qa_config.GetMasterNode()
# Forcing tty usage does not work on buildbot, so force all output of this
# command to be redirected to stdout
job_id_output = GetCommandOutput(
master.primary, "gnt-debug delay --submit %s 2>&1" % SECOND_COMMAND_DELAY
)
possible_job_ids = re.findall("JobID: ([0-9]+)", job_id_output)
if len(possible_job_ids) != 1:
raise qa_error.Error("Cannot parse gnt-debug delay output to find job id")
job_id = possible_job_ids[0]
AssertCommand(["gnt-job", "cancel", job_id])
# Now wait until the second job finishes, and expect the watch to fail due to
# job cancellation
AssertCommand(["gnt-job", "watch", job_id], fail=True)
# Then check for job cancellation
job_status = _GetJobStatus(job_id)
if job_status != constants.JOB_STATUS_CANCELED:
# Try and see if the job is being cancelled, and wait until the status
# changes or we hit a timeout
if job_status == constants.JOB_STATUS_CANCELING:
retry_fn = functools.partial(_RetryingFetchJobStatus,
constants.JOB_STATUS_CANCELING, job_id)
try:
job_status = retry.Retry(retry_fn, 2.0, 2 * FIRST_COMMAND_DELAY)
except retry.RetryTimeout:
# The job status remains the same
pass
if job_status != constants.JOB_STATUS_CANCELED:
raise qa_error.Error("Job was not successfully cancelled, status "
"found: %s" % job_status)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment