Commit 2a061e15 authored by Guido Trotter's avatar Guido Trotter
Browse files

Merge branch 'master' into next

* master:
  Update NEWS and version for 2.0.2 release
  Improve the description of node flags in man page
  Change default stripe count to 1
  Use full-stripe size in LVM growth
  RAPI: implement instance reinstall
parents 3df6e710 550a995a
Version 2.0.2
- Added experimental support for stripped logical volumes; this should
enhance performance but comes with a higher complexity in the block
device handling; stripping is only enabled when passing
--with-lvm-stripecount=N to configure, but codepaths are affected
even in the non-stripped mode
- Improved resiliency against transient failures at the end of DRBD
resyncs, and in general of DRBD resync checks
- Fixed a couple of issues with exports and snapshot errors
- Fixed a couple of issues in instance listing
- Added display of the disk size in “gnt-instance info”
- Fixed checking for valid OSes in instance creation
- Fixed handling of the ‘vcpus’ parameter in instance listing and in
general of invalid parameters
- Fixed http server library, and thus RAPI, to handle invalid
username/password combinations correctly; this means that now they
report unauthorized for queries too, not only for modifications,
allowing earlier detect of configuration problems
- Added a new ‘role’ node list field, equivalent to the master/master
candidate/drained/offline flags combinations
- Fixed cluster modify and changes of candidate pool size
- Fixed cluster verify error messages for wrong files on regular nodes
- Fixed a couple of issues with node demotion from master candidate
- Fixed node readd issues
- Added non-interactive mode for “ganeti-masterd --no-voting” startup
- Added a new ‘--no-voting’ option for masterfailover to fix failover
on two-nodes clusters when the former master node is unreachable
- Added instance reinstall over RAPI
Version 2.0.1
- added -H/-B startup parameters to gnt-instance, which will allow
re-adding the start in single-user option (regression from 1.2)
# Configure script for Ganeti
m4_define([gnt_version_major], [2])
m4_define([gnt_version_minor], [0])
m4_define([gnt_version_revision], [1])
m4_define([gnt_version_revision], [2])
m4_define([gnt_version_suffix], [])
......@@ -123,10 +123,10 @@ AC_SUBST(SOCAT_PATH, $socat_path)
[the number of stripes to use for LVM volumes]
[ (default is 3)]
[ (default is 1)]
AC_SUBST(LVM_STRIPECOUNT, $lvm_stripecount)
# Check common programs
......@@ -299,7 +299,7 @@ class LogicalVolume(BlockDev):
self._vg_name, self._lv_name = unique_id
self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
self._degraded = True
self.major = self.minor = None
self.major = self.minor = self.pe_size = self.stripe_count = None
......@@ -411,19 +411,30 @@ class LogicalVolume(BlockDev):
self.attached = False
result = utils.RunCmd(["lvs", "--noheadings", "--separator=,",
"--units=m", "--nosuffix",
"vg_extent_size,stripes", self.dev_path])
if result.failed:
logging.error("Can't find LV %s: %s, %s",
self.dev_path, result.fail_reason, result.output)
return False
out = result.stdout.strip().rstrip(',')
# the output can (and will) have multiple lines for multi-segment
# LVs, as the 'stripes' parameter is a segment one, so we take
# only the last entry, which is the one we're interested in; note
# that with LVM2 anyway the 'stripes' value must be constant
# across segments, so this is a no-op actually
out = result.stdout.splitlines()
if not out: # totally empty result? splitlines() returns at least
# one line for any non-empty string
logging.error("Can't parse LVS output, no lines? Got '%s'", str(out))
return False
out = out[-1].strip().rstrip(',')
out = out.split(",")
if len(out) != 3:
logging.error("Can't parse LVS output, len(%s) != 3", str(out))
if len(out) != 5:
logging.error("Can't parse LVS output, len(%s) != 5", str(out))
return False
status, major, minor = out[:3]
status, major, minor, pe_size, stripes = out
if len(status) != 6:
logging.error("lvs lv_attr is not 6 characters (%s)", status)
return False
......@@ -434,8 +445,22 @@ class LogicalVolume(BlockDev):
except ValueError, err:
logging.error("lvs major/minor cannot be parsed: %s", str(err))
pe_size = int(float(pe_size))
except (TypeError, ValueError), err:
logging.error("Can't parse vg extent size: %s", err)
return False
stripes = int(stripes)
except (TypeError, ValueError), err:
logging.error("Can't parse the number of stripes: %s", err)
return False
self.major = major
self.minor = minor
self.pe_size = pe_size
self.stripe_count = stripes
self._degraded = status[0] == 'v' # virtual volume, i.e. doesn't backing
# storage
self.attached = True
......@@ -554,6 +579,13 @@ class LogicalVolume(BlockDev):
"""Grow the logical volume.
if self.pe_size is None or self.stripe_count is None:
if not self.Attach():
_ThrowError("Can't attach to LV during Grow()")
full_stripe_size = self.pe_size * self.stripe_count
rest = amount % full_stripe_size
if rest != 0:
amount += full_stripe_size - rest
# we try multiple algorithms since the 'best' ones might not have
# space available in the right place, but later ones might (since
# they have less constraints); also note that only recent LVM
......@@ -1609,7 +1641,8 @@ class DRBD8(BaseDRBD):
if len(self._children) != 2 or None in self._children:
_ThrowError("drbd%d: cannot grow diskless device", self.minor)
result = utils.RunCmd(["drbdsetup", self.dev_path, "resize"])
result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
"%dm" % (self.size + amount)])
if result.failed:
_ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
......@@ -250,6 +250,18 @@ class R_Generic(object):
" '%s' parameter" % (name,))
return val
def _checkStringVariable(self, name, default=None):
"""Return the parsed value of an int argument.
val = self.queryargs.get(name, default)
if isinstance(val, list):
if val:
val = val[0]
val = default
return val
def getBodyParameter(self, name, *args):
"""Check and return the value for a given parameter.
......@@ -163,6 +163,8 @@ CONNECTOR.update({
re.compile(r'^/2/instances/([\w\._-]+)/tags$'): rlib2.R_2_instances_name_tags,
......@@ -493,6 +493,36 @@ class R_2_instances_name_shutdown(baserlib.R_Generic):
return baserlib.SubmitJob([op])
class R_2_instances_name_reinstall(baserlib.R_Generic):
"""/2/instances/[instance_name]/reinstall resource.
Implements an instance reinstall.
DOC_URI = "/2/instances/[instance_name]/reinstall"
def POST(self):
"""Reinstall an instance.
The URI takes os=name and nostartup=[0|1] optional
parameters. By default, the instance will be started
instance_name = self.items[0]
ostype = self._checkStringVariable('os')
nostartup = self._checkIntVariable('nostartup')
ops = [
opcodes.OpReinstallInstance(instance_name=instance_name, os_type=ostype),
if not nostartup:
return baserlib.SubmitJob(ops)
class _R_Tags(baserlib.R_Generic):
""" Quasiclass for tagging resources
......@@ -414,13 +414,18 @@
<simpara>whether the node is drained or not</simpara>
<simpara>whether the node is drained or not; the cluster
still communicates with drained nodes but excludes them
from allocation operations</simpara>
<simpara>whether the node is offline or not</simpara>
<simpara>whether the node is offline or not; if offline,
the cluster does not communicate with offline nodes;
useful for nodes that are not reachable in order to
avoid delays</simpara>
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