diff --git a/doc/design-2.0.rst b/doc/design-2.0.rst index 91c10f79a27019b3dc81299ddbab307f17d870f6..75d1859e2fcba6a1dd207d7f2ffc0f25cc17e291 100644 --- a/doc/design-2.0.rst +++ b/doc/design-2.0.rst @@ -1685,7 +1685,7 @@ response body. Typical examples of queries would be: list of nodes, instances, cluster info, etc. In the case of job submission, the client receive a job ID, the -identifier which allows to query the job progress in the job queue +identifier which allows one to query the job progress in the job queue (see `Job Queue`_). Internally, each exported object has an version identifier, which is diff --git a/doc/design-2.2.rst b/doc/design-2.2.rst index 66f13b020807786b2b895579d1c526ead468efa5..5af014ad91518463414a6fc671ea51afb390bd66 100644 --- a/doc/design-2.2.rst +++ b/doc/design-2.2.rst @@ -286,8 +286,8 @@ level only, allowing us to easily support non-DRBD instance moves. Intra-cluster instance moves will re-use the existing export and import scripts supplied by instance OS definitions. Unlike simply copying the -raw data, this allows to use filesystem-specific utilities to dump only -used parts of the disk and to exclude certain disks from the move. +raw data, this allows one to use filesystem-specific utilities to dump +only used parts of the disk and to exclude certain disks from the move. Compression should be used to further reduce the amount of data transferred. diff --git a/doc/rapi.rst b/doc/rapi.rst index 7ba33608371807075351b3e49afa01de49cca776..49e82342ca9bb320c132b277421272fe80a5ed40 100644 --- a/doc/rapi.rst +++ b/doc/rapi.rst @@ -1225,11 +1225,14 @@ It supports the following commands: ``GET``, ``PUT``. The role is always one of the following: - drained - - master - master-candidate - offline - regular +Note that the 'master' role is a special, and currently it can't be +modified via RAPI, only via the command line (``gnt-cluster +master-failover``). + ``GET`` ~~~~~~~ diff --git a/doc/security.rst b/doc/security.rst index a02a72e23c7981e60339746be37f951809b161fa..10010ab4a710892a3bc778555d8f6cef284d0d49 100644 --- a/doc/security.rst +++ b/doc/security.rst @@ -122,11 +122,11 @@ only one available before Ganeti 2.1.2. Under security model 'user' an instance is run as the user specified by the hypervisor parameter 'security_domain'. This makes it easy to run -all instances as non privileged users, and allows to manually allocate -specific users to specific instances or sets of instances. If the -specified user doesn't have permissions a jail broken instance will need -some local privilege escalation before being able to take over the node -and the cluster. It's possible though for a jail broken instance to +all instances as non privileged users, and allows one to manually +allocate specific users to specific instances or sets of instances. If +the specified user doesn't have permissions a jail broken instance will +need some local privilege escalation before being able to take over the +node and the cluster. It's possible though for a jail broken instance to affect other ones running under the same user. Under security model 'pool' a global cluster-level uid pool is used to diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 8b9fa17805951b70d798793f8d15cf5a9d837190..3c4701fd629fcb69bc4a5d197561d7bc94b526c4 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -6062,31 +6062,44 @@ class LUInstanceRecreateDisks(LogicalUnit): """Recreate the disks. """ - # change primary node, if needed - if self.op.nodes: - self.instance.primary_node = self.op.nodes[0] - self.LogWarning("Changing the instance's nodes, you will have to" - " remove any disks left on the older nodes manually") + instance = self.instance to_skip = [] - for idx, disk in enumerate(self.instance.disks): + mods = [] # keeps track of needed logical_id changes + + for idx, disk in enumerate(instance.disks): if idx not in self.op.disks: # disk idx has not been passed in to_skip.append(idx) continue # update secondaries for disks, if needed if self.op.nodes: if disk.dev_type == constants.LD_DRBD8: - # need to update the nodes + # need to update the nodes and minors assert len(self.op.nodes) == 2 - logical_id = list(disk.logical_id) - logical_id[0] = self.op.nodes[0] - logical_id[1] = self.op.nodes[1] - disk.logical_id = tuple(logical_id) + assert len(disk.logical_id) == 6 # otherwise disk internals + # have changed + (_, _, old_port, _, _, old_secret) = disk.logical_id + new_minors = self.cfg.AllocateDRBDMinor(self.op.nodes, instance.name) + new_id = (self.op.nodes[0], self.op.nodes[1], old_port, + new_minors[0], new_minors[1], old_secret) + assert len(disk.logical_id) == len(new_id) + mods.append((idx, new_id)) + + # now that we have passed all asserts above, we can apply the mods + # in a single run (to avoid partial changes) + for idx, new_id in mods: + instance.disks[idx].logical_id = new_id + # change primary node, if needed if self.op.nodes: - self.cfg.Update(self.instance, feedback_fn) + instance.primary_node = self.op.nodes[0] + self.LogWarning("Changing the instance's nodes, you will have to" + " remove any disks left on the older nodes manually") + + if self.op.nodes: + self.cfg.Update(instance, feedback_fn) - _CreateDisks(self, self.instance, to_skip=to_skip) + _CreateDisks(self, instance, to_skip=to_skip) class LUInstanceRename(LogicalUnit): @@ -9341,6 +9354,12 @@ class TLReplaceDisks(Tasklet): (node_name, self.instance.name)) def _CreateNewStorage(self, node_name): + """Create new storage on the primary or secondary node. + + This is only used for same-node replaces, not for changing the + secondary node, hence we don't want to modify the existing disk. + + """ iv_names = {} for idx, dev in enumerate(self.instance.disks): @@ -9362,7 +9381,7 @@ class TLReplaceDisks(Tasklet): logical_id=(vg_meta, names[1])) new_lvs = [lv_data, lv_meta] - old_lvs = dev.children + old_lvs = [child.Copy() for child in dev.children] iv_names[dev.iv_name] = (dev, old_lvs, new_lvs) # we pass force_create=True to force the LVM creation @@ -9400,7 +9419,7 @@ class TLReplaceDisks(Tasklet): self.lu.LogWarning("Can't remove old LV: %s" % msg, hint="remove unused LVs manually") - def _ExecDrbd8DiskOnly(self, feedback_fn): + def _ExecDrbd8DiskOnly(self, feedback_fn): # pylint: disable-msg=W0613 """Replace a disk on the primary or secondary for DRBD 8. The algorithm for replace is quite complicated: @@ -9483,10 +9502,14 @@ class TLReplaceDisks(Tasklet): rename_new_to_old) result.Raise("Can't rename new LVs on node %s" % self.target_node) + # Intermediate steps of in memory modifications for old, new in zip(old_lvs, new_lvs): new.logical_id = old.logical_id self.cfg.SetDiskID(new, self.target_node) + # We need to modify old_lvs so that removal later removes the + # right LVs, not the newly added ones; note that old_lvs is a + # copy here for disk in old_lvs: disk.logical_id = ren_fn(disk, temp_suffix) self.cfg.SetDiskID(disk, self.target_node) @@ -9506,10 +9529,6 @@ class TLReplaceDisks(Tasklet): "volumes")) raise errors.OpExecError("Can't add local storage to drbd: %s" % msg) - dev.children = new_lvs - - self.cfg.Update(self.instance, feedback_fn) - cstep = 5 if self.early_release: self.lu.LogStep(cstep, steps_total, "Removing old storage") diff --git a/lib/ht.py b/lib/ht.py index 935f4ed4a66e0b168fc3df07a959ef83d4f03fa9..c97486c8060cb3d4a01bbac35c2079089a9676bb 100644 --- a/lib/ht.py +++ b/lib/ht.py @@ -296,6 +296,9 @@ TPositiveFloat = \ TJobId = TOr(TPositiveInt, TRegex(re.compile("^%s$" % constants.JOB_ID_TEMPLATE))) +#: Number +TNumber = TOr(TInt, TFloat) + def TListOf(my_type): """Checks if a given value is a list with all elements of the same type. diff --git a/lib/hypervisor/hv_kvm.py b/lib/hypervisor/hv_kvm.py index ae7cb2fc4cf1cb820c130bdd04f10fc4abb94ee4..1840381a138a894ec1013c739d4304c76acd8e9a 100644 --- a/lib/hypervisor/hv_kvm.py +++ b/lib/hypervisor/hv_kvm.py @@ -866,11 +866,13 @@ class KVMHypervisor(hv_base.BaseHypervisor): utils.EnsureDirs([(self._InstanceChrootDir(name), constants.SECURE_DIR_MODE)]) - if not incoming: - # Configure the network now for starting instances, during - # FinalizeMigration for incoming instances - for nic_seq, nic in enumerate(kvm_nics): - self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq]) + # Configure the network now for starting instances and bridged interfaces, + # during FinalizeMigration for incoming instances' routed interfaces + for nic_seq, nic in enumerate(kvm_nics): + if (incoming and + nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED): + continue + self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq]) if security_model == constants.HT_SM_POOL: ss = ssconf.SimpleStore() @@ -1036,6 +1038,9 @@ class KVMHypervisor(hv_base.BaseHypervisor): kvm_nics = kvm_runtime[1] for nic_seq, nic in enumerate(kvm_nics): + if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: + # Bridged interfaces have already been configured + continue try: tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq)) except EnvironmentError, err: diff --git a/lib/jqueue.py b/lib/jqueue.py index 2180af527c3dcea878b423c70beeba56d1502889..c72e5ad2de10bc7a01f006375acc6968236e24c8 100644 --- a/lib/jqueue.py +++ b/lib/jqueue.py @@ -1537,10 +1537,13 @@ class JobQueue(object): "%s\n" % serial, True) result = [self._FormatJobID(v) - for v in range(self._last_serial, serial + 1)] + for v in range(self._last_serial + 1, serial + 1)] + # Keep it only if we were able to write the file self._last_serial = serial + assert len(result) == count + return result @staticmethod diff --git a/lib/opcodes.py b/lib/opcodes.py index 75ff7a0aeb8b372f568cee74c85075791f69142a..e1d40ad28bb75172e4b0413f16f8698aa148d930 100644 --- a/lib/opcodes.py +++ b/lib/opcodes.py @@ -1427,7 +1427,7 @@ class OpTestDelay(OpCode): """ OP_DSC_FIELD = "duration" OP_PARAMS = [ - ("duration", ht.NoDefault, ht.TFloat, None), + ("duration", ht.NoDefault, ht.TNumber, None), ("on_master", True, ht.TBool, None), ("on_nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None), ("repeat", 0, ht.TPositiveInt, None), diff --git a/lib/query.py b/lib/query.py index 51448bab035848bef3aa5e65a49d738861cf2702..4c847743bd1f47400ad0728e5f1e4a136405ab05 100644 --- a/lib/query.py +++ b/lib/query.py @@ -110,7 +110,7 @@ _VERIFY_FN = { QFT_BOOL: ht.TBool, QFT_NUMBER: ht.TInt, QFT_UNIT: ht.TInt, - QFT_TIMESTAMP: ht.TOr(ht.TInt, ht.TFloat), + QFT_TIMESTAMP: ht.TNumber, QFT_OTHER: lambda _: True, } diff --git a/man/ganeti-listrunner.rst b/man/ganeti-listrunner.rst index 12fddda92d9c9a2128d2de6396fabd96f6b45ddb..91a9c124a438323dd5239fbf1ca78f61a349d6e0 100644 --- a/man/ganeti-listrunner.rst +++ b/man/ganeti-listrunner.rst @@ -89,7 +89,9 @@ Run a command on a list of hosts:: Upload a script, some auxiliary files and run the script:: - listrunner -l logdir -x runme.sh -a seed.dat -a golden.dat -h host1,host2,host3 + listrunner -l logdir -x runme.sh \ + -a seed.dat -a golden.dat \ + -h host1,host2,host3 SEE ALSO diff --git a/man/ganeti-os-interface.rst b/man/ganeti-os-interface.rst index 21bc66d5ca1eb654458b47244ee4a366e366536f..a8aff1fefcb993f95d7cc0fc0d8cd0b96636d0e5 100644 --- a/man/ganeti-os-interface.rst +++ b/man/ganeti-os-interface.rst @@ -225,12 +225,12 @@ script could be:: #!/bin/sh case $OSP_DHCP in - ""|yes|no) - ;; - *) - echo "Invalid value '$OSP_DHCP' for the dhcp parameter" 1>&2 - exit 1; - ;; + ""|yes|no) + ;; + *) + echo "Invalid value '$OSP_DHCP' for the dhcp parameter" 1>&2 + exit 1; + ;; esac exit 0 diff --git a/man/ganeti-watcher.rst b/man/ganeti-watcher.rst index 3a55595fe1de2f96a76d6b7ec3ee2847f77167b6..6f2b69238ad99421c6d32fa38cfb43814435f518 100644 --- a/man/ganeti-watcher.rst +++ b/man/ganeti-watcher.rst @@ -23,7 +23,7 @@ that runs on every node. If the watcher is disabled at cluster level (via the **gnt-cluster watcher pause** command), it will exit without doing -anything. The cluster-level pause can be overriden via the +anything. The cluster-level pause can be overridden via the ``--ignore-pause`` option, for example if during a maintenance the watcher needs to be disabled in general, but the administrator wants to run it just once. diff --git a/man/gnt-cluster.rst b/man/gnt-cluster.rst index d3fcecd15c5aa0f5a894d859a2e7f1066c915686..a90f5fd96704b480f73a53063f79a86396e520fe 100644 --- a/man/gnt-cluster.rst +++ b/man/gnt-cluster.rst @@ -308,7 +308,7 @@ link network script it is interpreted as a routing table number or name. -The option ``--maintain-node-health`` allows to enable/disable +The option ``--maintain-node-health`` allows one to enable/disable automatic maintenance actions on nodes. Currently these include automatic shutdown of instances and deactivation of DRBD devices on offline nodes; in the future it might be extended to automatic diff --git a/man/gnt-debug.rst b/man/gnt-debug.rst index 70ddd198e729fcfa17a5b045be23ab3c3da2dc7b..7989631eb898cde30d637dc5b3029ef73d61c94d 100644 --- a/man/gnt-debug.rst +++ b/man/gnt-debug.rst @@ -112,7 +112,7 @@ The available fields and their meaning are: @QUERY_FIELDS_LOCK@ If the value of the option starts with the character ``+``, the new -fields will be added to the default list. This allows to quickly +fields will be added to the default list. This allows one to quickly see the default list plus a few other fields, instead of retyping the entire list of fields. diff --git a/man/gnt-group.rst b/man/gnt-group.rst index 2569bc0230cf6bb00bf8d1ce94433add87bd0e6e..03ac5c790f26a6f3fdfa387d6207512df682f850 100644 --- a/man/gnt-group.rst +++ b/man/gnt-group.rst @@ -107,7 +107,7 @@ special field states (see **ganeti(7)**). The ``-o`` option takes a comma-separated list of output fields. If the value of the option starts with the character ``+``, the new -fields will be added to the default list. This allows to quickly +fields will be added to the default list. This allows one to quickly see the default list plus a few other fields, instead of retyping the entire list of fields. diff --git a/man/gnt-instance.rst b/man/gnt-instance.rst index 02edb1fb61a6a54f6ff4cb3f611d484962901d57..147edec1bb512304924d6e762c0ff98c36a0f038 100644 --- a/man/gnt-instance.rst +++ b/man/gnt-instance.rst @@ -182,8 +182,8 @@ boot\_order blockdev\_prefix Valid for the Xen HVM and PVM hypervisors. - Relevant to nonpvops guest kernels, in which the disk device names - are given by the host. Allows to specify 'xvd', which helps run + Relevant to non-pvops guest kernels, in which the disk device names + are given by the host. Allows one to specify 'xvd', which helps run Red Hat based installers, driven by anaconda. floppy\_image\_path @@ -670,8 +670,8 @@ fields. The available fields and their meaning are: @QUERY_FIELDS_INSTANCE@ If the value of the option starts with the character ``+``, the new -field(s) will be added to the default list. This allows to quickly see -the default list plus a few other fields, instead of retyping the +field(s) will be added to the default list. This allows one to quickly +see the default list plus a few other fields, instead of retyping the entire list of fields. There is a subtle grouping about the available output fields: all diff --git a/man/gnt-job.rst b/man/gnt-job.rst index 6b31cb022cd4111d04fa3dad7c799f5bb76aa109..ac547149f5214aa768d90bacc6931e1f6f6c4dc1 100644 --- a/man/gnt-job.rst +++ b/man/gnt-job.rst @@ -124,7 +124,7 @@ oppriority If the value of the option starts with the character ``+``, the new -fields will be added to the default list. This allows to quickly +fields will be added to the default list. This allows one to quickly see the default list plus a few other fields, instead of retyping the entire list of fields. diff --git a/man/gnt-node.rst b/man/gnt-node.rst index 747322fca4d8b941ff72c836ee04d6486bb1c3ae..71995c5526c2352fa004d9e139bbf0e71c035d7c 100644 --- a/man/gnt-node.rst +++ b/man/gnt-node.rst @@ -183,7 +183,7 @@ fields. The available fields and their meaning are: @QUERY_FIELDS_NODE@ If the value of the option starts with the character ``+``, the new -fields will be added to the default list. This allows to quickly +fields will be added to the default list. This allows one to quickly see the default list plus a few other fields, instead of retyping the entire list of fields. diff --git a/test/ganeti.ht_unittest.py b/test/ganeti.ht_unittest.py index a0abdbcec0f19ea67ed835a514dffd36795a5576..567c53c24db871833c9a92570e711668f5ed80b1 100755 --- a/test/ganeti.ht_unittest.py +++ b/test/ganeti.ht_unittest.py @@ -53,6 +53,7 @@ class TestTypeChecks(unittest.TestCase): def testInt(self): for val in [-100, -3, 0, 16, 128, 923874]: self.assertTrue(ht.TInt(val)) + self.assertTrue(ht.TNumber(val)) for val in [False, True, None, "", [], "Hello", 0.0, 0.23, -3818.163]: self.assertFalse(ht.TInt(val)) @@ -76,10 +77,19 @@ class TestTypeChecks(unittest.TestCase): def testFloat(self): for val in [-100.21, -3.0, 0.0, 16.12, 128.3433, 923874.928]: self.assertTrue(ht.TFloat(val)) + self.assertTrue(ht.TNumber(val)) for val in [False, True, None, "", [], "Hello", 0, 28, -1, -3281]: self.assertFalse(ht.TFloat(val)) + def testNumber(self): + for val in [-100, -3, 0, 16, 128, 923874, + -100.21, -3.0, 0.0, 16.12, 128.3433, 923874.928]: + self.assertTrue(ht.TNumber(val)) + + for val in [False, True, None, "", [], "Hello", "1"]: + self.assertFalse(ht.TNumber(val)) + def testString(self): for val in ["", "abc", "Hello World", "123", u"", u"\u272C", u"abc"]: