1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 """Logical units dealing with the cluster."""
32
33 import copy
34 import itertools
35 import logging
36 import operator
37 import os
38 import re
39 import time
40
41 from ganeti import compat
42 from ganeti import constants
43 from ganeti import errors
44 from ganeti import hypervisor
45 from ganeti import locking
46 from ganeti import masterd
47 from ganeti import netutils
48 from ganeti import objects
49 from ganeti import opcodes
50 from ganeti import pathutils
51 from ganeti import query
52 import ganeti.rpc.node as rpc
53 from ganeti import runtime
54 from ganeti import ssh
55 from ganeti import uidpool
56 from ganeti import utils
57 from ganeti import vcluster
58
59 from ganeti.cmdlib.base import NoHooksLU, QueryBase, LogicalUnit, \
60 ResultWithJobs
61 from ganeti.cmdlib.common import ShareAll, RunPostHook, \
62 ComputeAncillaryFiles, RedistributeAncillaryFiles, UploadHelper, \
63 GetWantedInstances, MergeAndVerifyHvState, MergeAndVerifyDiskState, \
64 GetUpdatedIPolicy, ComputeNewInstanceViolations, GetUpdatedParams, \
65 CheckOSParams, CheckHVParams, AdjustCandidatePool, CheckNodePVs, \
66 ComputeIPolicyInstanceViolation, AnnotateDiskParams, SupportsOob, \
67 CheckIpolicyVsDiskTemplates, CheckDiskAccessModeValidity, \
68 CheckDiskAccessModeConsistency, GetClientCertDigest, \
69 AddInstanceCommunicationNetworkOp, ConnectInstanceCommunicationNetworkOp, \
70 CheckImageValidity, CheckDiskAccessModeConsistency, EnsureKvmdOnNodes
71
72 import ganeti.masterd.instance
76 """Renew the cluster's crypto tokens.
77
78 """
79
80 _MAX_NUM_RETRIES = 3
81 REQ_BGL = False
82
89
91 """Check prerequisites.
92
93 Notably the compatibility of specified key bits and key type.
94
95 """
96 cluster_info = self.cfg.GetClusterInfo()
97
98 self.ssh_key_type = self.op.ssh_key_type
99 if self.ssh_key_type is None:
100 self.ssh_key_type = cluster_info.ssh_key_type
101
102 self.ssh_key_bits = ssh.DetermineKeyBits(self.ssh_key_type,
103 self.op.ssh_key_bits,
104 cluster_info.ssh_key_type,
105 cluster_info.ssh_key_bits)
106
108 """Renews the nodes' SSL certificates.
109
110 Note that most of this operation is done in gnt_cluster.py, this LU only
111 takes care of the renewal of the client SSL certificates.
112
113 """
114 master_uuid = self.cfg.GetMasterNode()
115 cluster = self.cfg.GetClusterInfo()
116
117 logging.debug("Renewing the master's SSL node certificate."
118 " Master's UUID: %s.", master_uuid)
119
120
121 digest_map = {}
122 master_digest = utils.GetCertificateDigest(
123 cert_filename=pathutils.NODED_CLIENT_CERT_FILE)
124 digest_map[master_uuid] = master_digest
125 logging.debug("Adding the master's SSL node certificate digest to the"
126 " configuration. Master's UUID: %s, Digest: %s",
127 master_uuid, master_digest)
128
129 node_errors = {}
130 nodes = self.cfg.GetAllNodesInfo()
131 logging.debug("Renewing non-master nodes' node certificates.")
132 for (node_uuid, node_info) in nodes.items():
133 if node_info.offline:
134 logging.info("* Skipping offline node %s", node_info.name)
135 continue
136 if node_uuid != master_uuid:
137 logging.debug("Adding certificate digest of node '%s'.", node_uuid)
138 last_exception = None
139 for i in range(self._MAX_NUM_RETRIES):
140 try:
141 if node_info.master_candidate:
142 node_digest = GetClientCertDigest(self, node_uuid)
143 digest_map[node_uuid] = node_digest
144 logging.debug("Added the node's certificate to candidate"
145 " certificate list. Current list: %s.",
146 str(cluster.candidate_certs))
147 break
148 except errors.OpExecError as e:
149 last_exception = e
150 logging.error("Could not fetch a non-master node's SSL node"
151 " certificate at attempt no. %s. The node's UUID"
152 " is %s, and the error was: %s.",
153 str(i), node_uuid, e)
154 else:
155 if last_exception:
156 node_errors[node_uuid] = last_exception
157
158 if node_errors:
159 msg = ("Some nodes' SSL client certificates could not be fetched."
160 " Please make sure those nodes are reachable and rerun"
161 " the operation. The affected nodes and their errors are:\n")
162 for uuid, e in node_errors.items():
163 msg += "Node %s: %s\n" % (uuid, e)
164 feedback_fn(msg)
165
166 self.cfg.SetCandidateCerts(digest_map)
167
169 """Renew all nodes' SSH keys.
170
171 @type feedback_fn: function
172 @param feedback_fn: logging function, see L{ganeti.cmdlist.base.LogicalUnit}
173
174 """
175 master_uuid = self.cfg.GetMasterNode()
176
177 nodes = self.cfg.GetAllNodesInfo()
178 nodes_uuid_names = [(node_uuid, node_info.name) for (node_uuid, node_info)
179 in nodes.items() if not node_info.offline]
180 node_names = [name for (_, name) in nodes_uuid_names]
181 node_uuids = [uuid for (uuid, _) in nodes_uuid_names]
182 potential_master_candidates = self.cfg.GetPotentialMasterCandidates()
183 master_candidate_uuids = self.cfg.GetMasterCandidateUuids()
184
185 cluster_info = self.cfg.GetClusterInfo()
186
187 result = self.rpc.call_node_ssh_keys_renew(
188 [master_uuid],
189 node_uuids, node_names,
190 master_candidate_uuids,
191 potential_master_candidates,
192 cluster_info.ssh_key_type,
193 self.ssh_key_type,
194 self.ssh_key_bits)
195 result[master_uuid].Raise("Could not renew the SSH keys of all nodes")
196
197
198
199 cluster_info.ssh_key_type = self.ssh_key_type
200 cluster_info.ssh_key_bits = self.ssh_key_bits
201 self.cfg.Update(cluster_info, feedback_fn)
202
203 - def Exec(self, feedback_fn):
204 if self.op.node_certificates:
205 feedback_fn("Renewing Node SSL certificates")
206 self._RenewNodeSslCertificates(feedback_fn)
207
208 if self.op.renew_ssh_keys:
209 if self.cfg.GetClusterInfo().modify_ssh_setup:
210 feedback_fn("Renewing SSH keys")
211 self._RenewSshKeys(feedback_fn)
212 else:
213 feedback_fn("Cannot renew SSH keys if the cluster is configured to not"
214 " modify the SSH setup.")
215
218 """Activate the master IP on the master node.
219
220 """
221 - def Exec(self, feedback_fn):
230
233 """Deactivate the master IP on the master node.
234
235 """
236 - def Exec(self, feedback_fn):
245
248 """Return configuration values.
249
250 """
251 REQ_BGL = False
252
254 self.cq = ClusterQuery(None, self.op.output_fields, False)
255
258
261
262 - def Exec(self, feedback_fn):
263 result = self.cq.OldStyleQuery(self)
264
265 assert len(result) == 1
266
267 return result[0]
268
271 """Logical unit for destroying the cluster.
272
273 """
274 HPATH = "cluster-destroy"
275 HTYPE = constants.HTYPE_CLUSTER
276
277
278
279
280 clusterHasBeenDestroyed = False
281
283 """Build hooks env.
284
285 """
286 return {
287 "OP_TARGET": self.cfg.GetClusterName(),
288 }
289
291 """Build hooks nodes.
292
293 """
294 return ([], [])
295
297 """Check prerequisites.
298
299 This checks whether the cluster is empty.
300
301 Any errors are signaled by raising errors.OpPrereqError.
302
303 """
304 master = self.cfg.GetMasterNode()
305
306 nodelist = self.cfg.GetNodeList()
307 if len(nodelist) != 1 or nodelist[0] != master:
308 raise errors.OpPrereqError("There are still %d node(s) in"
309 " this cluster." % (len(nodelist) - 1),
310 errors.ECODE_INVAL)
311 instancelist = self.cfg.GetInstanceList()
312 if instancelist:
313 raise errors.OpPrereqError("There are still %d instance(s) in"
314 " this cluster." % len(instancelist),
315 errors.ECODE_INVAL)
316
317 - def Exec(self, feedback_fn):
337
338
339 -class LUClusterPostInit(LogicalUnit):
340 """Logical unit for running hooks after cluster initialization.
341
342 """
343 HPATH = "cluster-init"
344 HTYPE = constants.HTYPE_CLUSTER
345
346 - def CheckArguments(self):
347 self.master_uuid = self.cfg.GetMasterNode()
348 self.master_ndparams = self.cfg.GetNdParams(self.cfg.GetMasterNodeInfo())
349
350
351
352
353
354
355 if (self.master_ndparams[constants.ND_OVS] and not
356 self.master_ndparams.get(constants.ND_OVS_LINK, None)):
357 self.LogInfo("No physical interface for OpenvSwitch was given."
358 " OpenvSwitch will not have an outside connection. This"
359 " might not be what you want.")
360
361 - def BuildHooksEnv(self):
362 """Build hooks env.
363
364 """
365 return {
366 "OP_TARGET": self.cfg.GetClusterName(),
367 }
368
369 - def BuildHooksNodes(self):
370 """Build hooks nodes.
371
372 """
373 return ([], [self.cfg.GetMasterNode()])
374
375 - def Exec(self, feedback_fn):
376 """Create and configure Open vSwitch
377
378 """
379 if self.master_ndparams[constants.ND_OVS]:
380 result = self.rpc.call_node_configure_ovs(
381 self.master_uuid,
382 self.master_ndparams[constants.ND_OVS_NAME],
383 self.master_ndparams.get(constants.ND_OVS_LINK, None))
384 result.Raise("Could not successully configure Open vSwitch")
385
386 return True
387
437
440 """Query cluster configuration.
441
442 """
443 REQ_BGL = False
444
446 self.needed_locks = {}
447
448 - def Exec(self, feedback_fn):
449 """Return cluster config.
450
451 """
452 cluster = self.cfg.GetClusterInfo()
453 os_hvp = {}
454
455
456 for os_name, hv_dict in cluster.os_hvp.items():
457 os_hvp[os_name] = {}
458 for hv_name, hv_params in hv_dict.items():
459 if hv_name in cluster.enabled_hypervisors:
460 os_hvp[os_name][hv_name] = hv_params
461
462
463 primary_ip_version = constants.IP4_VERSION
464 if cluster.primary_ip_family == netutils.IP6Address.family:
465 primary_ip_version = constants.IP6_VERSION
466
467 result = {
468 "software_version": constants.RELEASE_VERSION,
469 "protocol_version": constants.PROTOCOL_VERSION,
470 "config_version": constants.CONFIG_VERSION,
471 "os_api_version": max(constants.OS_API_VERSIONS),
472 "export_version": constants.EXPORT_VERSION,
473 "vcs_version": constants.VCS_VERSION,
474 "architecture": runtime.GetArchInfo(),
475 "name": cluster.cluster_name,
476 "master": self.cfg.GetMasterNodeName(),
477 "default_hypervisor": cluster.primary_hypervisor,
478 "enabled_hypervisors": cluster.enabled_hypervisors,
479 "hvparams": dict([(hypervisor_name, cluster.hvparams[hypervisor_name])
480 for hypervisor_name in cluster.enabled_hypervisors]),
481 "os_hvp": os_hvp,
482 "beparams": cluster.beparams,
483 "osparams": cluster.osparams,
484 "ipolicy": cluster.ipolicy,
485 "nicparams": cluster.nicparams,
486 "ndparams": cluster.ndparams,
487 "diskparams": cluster.diskparams,
488 "candidate_pool_size": cluster.candidate_pool_size,
489 "max_running_jobs": cluster.max_running_jobs,
490 "max_tracked_jobs": cluster.max_tracked_jobs,
491 "mac_prefix": cluster.mac_prefix,
492 "master_netdev": cluster.master_netdev,
493 "master_netmask": cluster.master_netmask,
494 "use_external_mip_script": cluster.use_external_mip_script,
495 "volume_group_name": cluster.volume_group_name,
496 "drbd_usermode_helper": cluster.drbd_usermode_helper,
497 "file_storage_dir": cluster.file_storage_dir,
498 "shared_file_storage_dir": cluster.shared_file_storage_dir,
499 "maintain_node_health": cluster.maintain_node_health,
500 "ctime": cluster.ctime,
501 "mtime": cluster.mtime,
502 "uuid": cluster.uuid,
503 "tags": list(cluster.GetTags()),
504 "uid_pool": cluster.uid_pool,
505 "default_iallocator": cluster.default_iallocator,
506 "default_iallocator_params": cluster.default_iallocator_params,
507 "reserved_lvs": cluster.reserved_lvs,
508 "primary_ip_version": primary_ip_version,
509 "prealloc_wipe_disks": cluster.prealloc_wipe_disks,
510 "hidden_os": cluster.hidden_os,
511 "blacklisted_os": cluster.blacklisted_os,
512 "enabled_disk_templates": cluster.enabled_disk_templates,
513 "install_image": cluster.install_image,
514 "instance_communication_network": cluster.instance_communication_network,
515 "compression_tools": cluster.compression_tools,
516 "enabled_user_shutdown": cluster.enabled_user_shutdown,
517 }
518
519 return result
520
523 """Force the redistribution of cluster configuration.
524
525 This is a very simple LU.
526
527 """
528 REQ_BGL = False
529
535
536 - def Exec(self, feedback_fn):
542
545 """Rename the cluster.
546
547 """
548 HPATH = "cluster-rename"
549 HTYPE = constants.HTYPE_CLUSTER
550
552 """Build hooks env.
553
554 """
555 return {
556 "OP_TARGET": self.cfg.GetClusterName(),
557 "NEW_NAME": self.op.name,
558 }
559
565
588
589 - def Exec(self, feedback_fn):
590 """Rename the cluster.
591
592 """
593 clustername = self.op.name
594 new_ip = self.ip
595
596
597 master_params = self.cfg.GetMasterNetworkParameters()
598 ems = self.cfg.GetUseExternalMipScript()
599 result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
600 master_params, ems)
601 result.Raise("Could not disable the master role")
602
603 try:
604 cluster = self.cfg.GetClusterInfo()
605 cluster.cluster_name = clustername
606 cluster.master_ip = new_ip
607 self.cfg.Update(cluster, feedback_fn)
608
609
610 ssh.WriteKnownHostsFile(self.cfg, pathutils.SSH_KNOWN_HOSTS_FILE)
611 node_list = self.cfg.GetOnlineNodeList()
612 try:
613 node_list.remove(master_params.uuid)
614 except ValueError:
615 pass
616 UploadHelper(self, node_list, pathutils.SSH_KNOWN_HOSTS_FILE)
617 finally:
618 master_params.ip = new_ip
619 result = self.rpc.call_node_activate_master_ip(master_params.uuid,
620 master_params, ems)
621 result.Warn("Could not re-enable the master role on the master,"
622 " please restart manually", self.LogWarning)
623
624 return clustername
625
628 """Verifies the cluster disks sizes.
629
630 """
631 REQ_BGL = False
632
654
656 if level == locking.LEVEL_NODE_RES and self.wanted_names is not None:
657 self._LockInstancesNodes(primary_only=True, level=level)
658
660 """Check prerequisites.
661
662 This only checks the optional instance list against the existing names.
663
664 """
665 if self.wanted_names is None:
666 self.wanted_names = self.owned_locks(locking.LEVEL_INSTANCE)
667
668 self.wanted_instances = \
669 map(compat.snd, self.cfg.GetMultiInstanceInfoByName(self.wanted_names))
670
672 """Ensure children of the disk have the needed disk size.
673
674 This is valid mainly for DRBD8 and fixes an issue where the
675 children have smaller disk size.
676
677 @param disk: an L{ganeti.objects.Disk} object
678
679 """
680 if disk.dev_type == constants.DT_DRBD8:
681 assert disk.children, "Empty children for DRBD8?"
682 fchild = disk.children[0]
683 mismatch = fchild.size < disk.size
684 if mismatch:
685 self.LogInfo("Child disk has size %d, parent %d, fixing",
686 fchild.size, disk.size)
687 fchild.size = disk.size
688
689
690 return self._EnsureChildSizes(fchild) or mismatch
691 else:
692 return False
693
694 - def Exec(self, feedback_fn):
695 """Verify the size of cluster disks.
696
697 """
698
699
700 per_node_disks = {}
701 for instance in self.wanted_instances:
702 pnode = instance.primary_node
703 if pnode not in per_node_disks:
704 per_node_disks[pnode] = []
705 for idx, disk in enumerate(self.cfg.GetInstanceDisks(instance.uuid)):
706 per_node_disks[pnode].append((instance, idx, disk))
707
708 assert not (frozenset(per_node_disks.keys()) -
709 frozenset(self.owned_locks(locking.LEVEL_NODE_RES))), \
710 "Not owning correct locks"
711 assert not self.owned_locks(locking.LEVEL_NODE)
712
713 es_flags = rpc.GetExclusiveStorageForNodes(self.cfg,
714 per_node_disks.keys())
715
716 changed = []
717 for node_uuid, dskl in per_node_disks.items():
718 if not dskl:
719
720 continue
721
722 newl = [([v[2].Copy()], v[0]) for v in dskl]
723 node_name = self.cfg.GetNodeName(node_uuid)
724 result = self.rpc.call_blockdev_getdimensions(node_uuid, newl)
725 if result.fail_msg:
726 self.LogWarning("Failure in blockdev_getdimensions call to node"
727 " %s, ignoring", node_name)
728 continue
729 if len(result.payload) != len(dskl):
730 logging.warning("Invalid result from node %s: len(dksl)=%d,"
731 " result.payload=%s", node_name, len(dskl),
732 result.payload)
733 self.LogWarning("Invalid result from node %s, ignoring node results",
734 node_name)
735 continue
736 for ((instance, idx, disk), dimensions) in zip(dskl, result.payload):
737 if dimensions is None:
738 self.LogWarning("Disk %d of instance %s did not return size"
739 " information, ignoring", idx, instance.name)
740 continue
741 if not isinstance(dimensions, (tuple, list)):
742 self.LogWarning("Disk %d of instance %s did not return valid"
743 " dimension information, ignoring", idx,
744 instance.name)
745 continue
746 (size, spindles) = dimensions
747 if not isinstance(size, (int, long)):
748 self.LogWarning("Disk %d of instance %s did not return valid"
749 " size information, ignoring", idx, instance.name)
750 continue
751 size = size >> 20
752 if size != disk.size:
753 self.LogInfo("Disk %d of instance %s has mismatched size,"
754 " correcting: recorded %d, actual %d", idx,
755 instance.name, disk.size, size)
756 disk.size = size
757 self.cfg.Update(disk, feedback_fn)
758 changed.append((instance.name, idx, "size", size))
759 if es_flags[node_uuid]:
760 if spindles is None:
761 self.LogWarning("Disk %d of instance %s did not return valid"
762 " spindles information, ignoring", idx,
763 instance.name)
764 elif disk.spindles is None or disk.spindles != spindles:
765 self.LogInfo("Disk %d of instance %s has mismatched spindles,"
766 " correcting: recorded %s, actual %s",
767 idx, instance.name, disk.spindles, spindles)
768 disk.spindles = spindles
769 self.cfg.Update(disk, feedback_fn)
770 changed.append((instance.name, idx, "spindles", disk.spindles))
771 if self._EnsureChildSizes(disk):
772 self.cfg.Update(disk, feedback_fn)
773 changed.append((instance.name, idx, "size", disk.size))
774 return changed
775
796
801 """Checks whether the given file-based storage directory is acceptable.
802
803 Note: This function is public, because it is also used in bootstrap.py.
804
805 @type logging_warn_fn: function
806 @param logging_warn_fn: function which accepts a string and logs it
807 @type file_storage_dir: string
808 @param file_storage_dir: the directory to be used for file-based instances
809 @type enabled_disk_templates: list of string
810 @param enabled_disk_templates: the list of enabled disk templates
811 @type file_disk_template: string
812 @param file_disk_template: the file-based disk template for which the
813 path should be checked
814
815 """
816 assert (file_disk_template in utils.storage.GetDiskTemplatesOfStorageTypes(
817 constants.ST_FILE, constants.ST_SHARED_FILE, constants.ST_GLUSTER
818 ))
819
820 file_storage_enabled = file_disk_template in enabled_disk_templates
821 if file_storage_dir is not None:
822 if file_storage_dir == "":
823 if file_storage_enabled:
824 raise errors.OpPrereqError(
825 "Unsetting the '%s' storage directory while having '%s' storage"
826 " enabled is not permitted." %
827 (file_disk_template, file_disk_template),
828 errors.ECODE_INVAL)
829 else:
830 if not file_storage_enabled:
831 logging_warn_fn(
832 "Specified a %s storage directory, although %s storage is not"
833 " enabled." % (file_disk_template, file_disk_template))
834 else:
835 raise errors.ProgrammerError("Received %s storage dir with value"
836 " 'None'." % file_disk_template)
837
849
861
873
901
904 """Change the parameters of the cluster.
905
906 """
907 HPATH = "cluster-modify"
908 HTYPE = constants.HTYPE_CLUSTER
909 REQ_BGL = False
910
944
956
958 """Build hooks env.
959
960 """
961 return {
962 "OP_TARGET": self.cfg.GetClusterName(),
963 "NEW_VG_NAME": self.op.vg_name,
964 }
965
967 """Build hooks nodes.
968
969 """
970 mn = self.cfg.GetMasterNode()
971 return ([mn], [mn])
972
973 - def _CheckVgName(self, node_uuids, enabled_disk_templates,
974 new_enabled_disk_templates):
975 """Check the consistency of the vg name on all nodes and in case it gets
976 unset whether there are instances still using it.
977
978 """
979 lvm_is_enabled = utils.IsLvmEnabled(enabled_disk_templates)
980 lvm_gets_enabled = utils.LvmGetsEnabled(enabled_disk_templates,
981 new_enabled_disk_templates)
982 current_vg_name = self.cfg.GetVGName()
983
984 if self.op.vg_name == '':
985 if lvm_is_enabled:
986 raise errors.OpPrereqError("Cannot unset volume group if lvm-based"
987 " disk templates are or get enabled.",
988 errors.ECODE_INVAL)
989
990 if self.op.vg_name is None:
991 if current_vg_name is None and lvm_is_enabled:
992 raise errors.OpPrereqError("Please specify a volume group when"
993 " enabling lvm-based disk-templates.",
994 errors.ECODE_INVAL)
995
996 if self.op.vg_name is not None and not self.op.vg_name:
997 if self.cfg.DisksOfType(constants.DT_PLAIN):
998 raise errors.OpPrereqError("Cannot disable lvm storage while lvm-based"
999 " instances exist", errors.ECODE_INVAL)
1000
1001 if (self.op.vg_name is not None and lvm_is_enabled) or \
1002 (self.cfg.GetVGName() is not None and lvm_gets_enabled):
1003 self._CheckVgNameOnNodes(node_uuids)
1004
1025
1026 @staticmethod
1029 """Computes three sets of disk templates.
1030
1031 @see: C{_GetDiskTemplateSets} for more details.
1032
1033 """
1034 enabled_disk_templates = None
1035 new_enabled_disk_templates = []
1036 disabled_disk_templates = []
1037 if op_enabled_disk_templates:
1038 enabled_disk_templates = op_enabled_disk_templates
1039 new_enabled_disk_templates = \
1040 list(set(enabled_disk_templates)
1041 - set(old_enabled_disk_templates))
1042 disabled_disk_templates = \
1043 list(set(old_enabled_disk_templates)
1044 - set(enabled_disk_templates))
1045 else:
1046 enabled_disk_templates = old_enabled_disk_templates
1047 return (enabled_disk_templates, new_enabled_disk_templates,
1048 disabled_disk_templates)
1049
1051 """Computes three sets of disk templates.
1052
1053 The three sets are:
1054 - disk templates that will be enabled after this operation (no matter if
1055 they were enabled before or not)
1056 - disk templates that get enabled by this operation (thus haven't been
1057 enabled before.)
1058 - disk templates that get disabled by this operation
1059
1060 """
1061 return self._GetDiskTemplateSetsInner(self.op.enabled_disk_templates,
1062 cluster.enabled_disk_templates)
1063
1065 """Checks the ipolicy.
1066
1067 @type cluster: C{objects.Cluster}
1068 @param cluster: the cluster's configuration
1069 @type enabled_disk_templates: list of string
1070 @param enabled_disk_templates: list of (possibly newly) enabled disk
1071 templates
1072
1073 """
1074
1075 if self.op.ipolicy:
1076 self.new_ipolicy = GetUpdatedIPolicy(cluster.ipolicy, self.op.ipolicy,
1077 group_policy=False)
1078
1079 CheckIpolicyVsDiskTemplates(self.new_ipolicy,
1080 enabled_disk_templates)
1081
1082 all_instances = self.cfg.GetAllInstancesInfo().values()
1083 violations = set()
1084 for group in self.cfg.GetAllNodeGroupsInfo().values():
1085 instances = frozenset(
1086 [inst for inst in all_instances
1087 if compat.any(nuuid in group.members
1088 for nuuid in self.cfg.GetInstanceNodes(inst.uuid))])
1089 new_ipolicy = objects.FillIPolicy(self.new_ipolicy, group.ipolicy)
1090 ipol = masterd.instance.CalculateGroupIPolicy(cluster, group)
1091 new = ComputeNewInstanceViolations(ipol, new_ipolicy, instances,
1092 self.cfg)
1093 if new:
1094 violations.update(new)
1095
1096 if violations:
1097 self.LogWarning("After the ipolicy change the following instances"
1098 " violate them: %s",
1099 utils.CommaJoin(utils.NiceSort(violations)))
1100 else:
1101 CheckIpolicyVsDiskTemplates(cluster.ipolicy,
1102 enabled_disk_templates)
1103
1105 """Checks whether the set DRBD helper actually exists on the nodes.
1106
1107 @type drbd_helper: string
1108 @param drbd_helper: path of the drbd usermode helper binary
1109 @type node_uuids: list of strings
1110 @param node_uuids: list of node UUIDs to check for the helper
1111
1112 """
1113
1114 helpers = self.rpc.call_drbd_helper(node_uuids)
1115 for (_, ninfo) in self.cfg.GetMultiNodeInfo(node_uuids):
1116 if ninfo.offline:
1117 self.LogInfo("Not checking drbd helper on offline node %s",
1118 ninfo.name)
1119 continue
1120 msg = helpers[ninfo.uuid].fail_msg
1121 if msg:
1122 raise errors.OpPrereqError("Error checking drbd helper on node"
1123 " '%s': %s" % (ninfo.name, msg),
1124 errors.ECODE_ENVIRON)
1125 node_helper = helpers[ninfo.uuid].payload
1126 if node_helper != drbd_helper:
1127 raise errors.OpPrereqError("Error on node '%s': drbd helper is %s" %
1128 (ninfo.name, node_helper),
1129 errors.ECODE_ENVIRON)
1130
1132 """Check the DRBD usermode helper.
1133
1134 @type node_uuids: list of strings
1135 @param node_uuids: a list of nodes' UUIDs
1136 @type drbd_enabled: boolean
1137 @param drbd_enabled: whether DRBD will be enabled after this operation
1138 (no matter if it was disabled before or not)
1139 @type drbd_gets_enabled: boolen
1140 @param drbd_gets_enabled: true if DRBD was disabled before this
1141 operation, but will be enabled afterwards
1142
1143 """
1144 if self.op.drbd_helper == '':
1145 if drbd_enabled:
1146 raise errors.OpPrereqError("Cannot disable drbd helper while"
1147 " DRBD is enabled.", errors.ECODE_STATE)
1148 if self.cfg.DisksOfType(constants.DT_DRBD8):
1149 raise errors.OpPrereqError("Cannot disable drbd helper while"
1150 " drbd-based instances exist",
1151 errors.ECODE_INVAL)
1152
1153 else:
1154 if self.op.drbd_helper is not None and drbd_enabled:
1155 self._CheckDrbdHelperOnNodes(self.op.drbd_helper, node_uuids)
1156 else:
1157 if drbd_gets_enabled:
1158 current_drbd_helper = self.cfg.GetClusterInfo().drbd_usermode_helper
1159 if current_drbd_helper is not None:
1160 self._CheckDrbdHelperOnNodes(current_drbd_helper, node_uuids)
1161 else:
1162 raise errors.OpPrereqError("Cannot enable DRBD without a"
1163 " DRBD usermode helper set.",
1164 errors.ECODE_STATE)
1165
1168 """Check whether we try to disable a disk template that is in use.
1169
1170 @type disabled_disk_templates: list of string
1171 @param disabled_disk_templates: list of disk templates that are going to
1172 be disabled by this operation
1173
1174 """
1175 for disk_template in disabled_disk_templates:
1176 disks_with_type = self.cfg.DisksOfType(disk_template)
1177 if disks_with_type:
1178 disk_desc = []
1179 for disk in disks_with_type:
1180 instance_uuid = self.cfg.GetInstanceForDisk(disk.uuid)
1181 instance = self.cfg.GetInstanceInfo(instance_uuid)
1182 if instance:
1183 instance_desc = "on " + instance.name
1184 else:
1185 instance_desc = "detached"
1186 disk_desc.append("%s (%s)" % (disk, instance_desc))
1187 raise errors.OpPrereqError(
1188 "Cannot disable disk template '%s', because there is at least one"
1189 " disk using it:\n * %s" % (disk_template, "\n * ".join(disk_desc)),
1190 errors.ECODE_STATE)
1191 if constants.DT_DISKLESS in disabled_disk_templates:
1192 instances = self.cfg.GetAllInstancesInfo()
1193 for inst in instances.values():
1194 if not inst.disks:
1195 raise errors.OpPrereqError(
1196 "Cannot disable disk template 'diskless', because there is at"
1197 " least one instance using it:\n * %s" % inst.name,
1198 errors.ECODE_STATE)
1199
1200 @staticmethod
1202 """Check whether an existing network is configured for instance
1203 communication.
1204
1205 Checks whether an existing network is configured with the
1206 parameters that are advisable for instance communication, and
1207 otherwise issue security warnings.
1208
1209 @type network: L{ganeti.objects.Network}
1210 @param network: L{ganeti.objects.Network} object whose
1211 configuration is being checked
1212 @type warning_fn: function
1213 @param warning_fn: function used to print warnings
1214 @rtype: None
1215 @return: None
1216
1217 """
1218 def _MaybeWarn(err, val, default):
1219 if val != default:
1220 warning_fn("Supplied instance communication network '%s' %s '%s',"
1221 " this might pose a security risk (default is '%s').",
1222 network.name, err, val, default)
1223
1224 if network.network is None:
1225 raise errors.OpPrereqError("Supplied instance communication network '%s'"
1226 " must have an IPv4 network address.",
1227 network.name)
1228
1229 _MaybeWarn("has an IPv4 gateway", network.gateway, None)
1230 _MaybeWarn("has a non-standard IPv4 network address", network.network,
1231 constants.INSTANCE_COMMUNICATION_NETWORK4)
1232 _MaybeWarn("has an IPv6 gateway", network.gateway6, None)
1233 _MaybeWarn("has a non-standard IPv6 network address", network.network6,
1234 constants.INSTANCE_COMMUNICATION_NETWORK6)
1235 _MaybeWarn("has a non-standard MAC prefix", network.mac_prefix,
1236 constants.INSTANCE_COMMUNICATION_MAC_PREFIX)
1237
1239 """Check prerequisites.
1240
1241 This checks whether the given params don't conflict and
1242 if the given volume group is valid.
1243
1244 """
1245 node_uuids = self.owned_locks(locking.LEVEL_NODE)
1246 self.cluster = cluster = self.cfg.GetClusterInfo()
1247
1248 vm_capable_node_uuids = [node.uuid
1249 for node in self.cfg.GetAllNodesInfo().values()
1250 if node.uuid in node_uuids and node.vm_capable]
1251
1252 (enabled_disk_templates, new_enabled_disk_templates,
1253 disabled_disk_templates) = self._GetDiskTemplateSets(cluster)
1254 self._CheckInstancesOfDisabledDiskTemplates(disabled_disk_templates)
1255
1256 self._CheckVgName(vm_capable_node_uuids, enabled_disk_templates,
1257 new_enabled_disk_templates)
1258
1259 if self.op.file_storage_dir is not None:
1260 CheckFileStoragePathVsEnabledDiskTemplates(
1261 self.LogWarning, self.op.file_storage_dir, enabled_disk_templates)
1262
1263 if self.op.shared_file_storage_dir is not None:
1264 CheckSharedFileStoragePathVsEnabledDiskTemplates(
1265 self.LogWarning, self.op.shared_file_storage_dir,
1266 enabled_disk_templates)
1267
1268 drbd_enabled = constants.DT_DRBD8 in enabled_disk_templates
1269 drbd_gets_enabled = constants.DT_DRBD8 in new_enabled_disk_templates
1270 self._CheckDrbdHelper(vm_capable_node_uuids,
1271 drbd_enabled, drbd_gets_enabled)
1272
1273
1274 if self.op.beparams:
1275 objects.UpgradeBeParams(self.op.beparams)
1276 utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES)
1277 self.new_beparams = cluster.SimpleFillBE(self.op.beparams)
1278
1279 if self.op.ndparams:
1280 utils.ForceDictType(self.op.ndparams, constants.NDS_PARAMETER_TYPES)
1281 self.new_ndparams = cluster.SimpleFillND(self.op.ndparams)
1282
1283
1284
1285 if self.new_ndparams["oob_program"] == "":
1286 self.new_ndparams["oob_program"] = \
1287 constants.NDC_DEFAULTS[constants.ND_OOB_PROGRAM]
1288
1289 if self.op.hv_state:
1290 new_hv_state = MergeAndVerifyHvState(self.op.hv_state,
1291 self.cluster.hv_state_static)
1292 self.new_hv_state = dict((hv, cluster.SimpleFillHvState(values))
1293 for hv, values in new_hv_state.items())
1294
1295 if self.op.disk_state:
1296 new_disk_state = MergeAndVerifyDiskState(self.op.disk_state,
1297 self.cluster.disk_state_static)
1298 self.new_disk_state = \
1299 dict((storage, dict((name, cluster.SimpleFillDiskState(values))
1300 for name, values in svalues.items()))
1301 for storage, svalues in new_disk_state.items())
1302
1303 self._CheckIpolicy(cluster, enabled_disk_templates)
1304
1305 if self.op.nicparams:
1306 utils.ForceDictType(self.op.nicparams, constants.NICS_PARAMETER_TYPES)
1307 self.new_nicparams = cluster.SimpleFillNIC(self.op.nicparams)
1308 objects.NIC.CheckParameterSyntax(self.new_nicparams)
1309 nic_errors = []
1310
1311
1312 for instance in self.cfg.GetAllInstancesInfo().values():
1313 for nic_idx, nic in enumerate(instance.nics):
1314 params_copy = copy.deepcopy(nic.nicparams)
1315 params_filled = objects.FillDict(self.new_nicparams, params_copy)
1316
1317
1318 try:
1319 objects.NIC.CheckParameterSyntax(params_filled)
1320 except errors.ConfigurationError, err:
1321 nic_errors.append("Instance %s, nic/%d: %s" %
1322 (instance.name, nic_idx, err))
1323
1324
1325 target_mode = params_filled[constants.NIC_MODE]
1326 if target_mode == constants.NIC_MODE_ROUTED and not nic.ip:
1327 nic_errors.append("Instance %s, nic/%d: routed NIC with no ip"
1328 " address" % (instance.name, nic_idx))
1329 if nic_errors:
1330 raise errors.OpPrereqError("Cannot apply the change, errors:\n%s" %
1331 "\n".join(nic_errors), errors.ECODE_INVAL)
1332
1333
1334 self.new_hvparams = new_hvp = objects.FillDict(cluster.hvparams, {})
1335 if self.op.hvparams:
1336 for hv_name, hv_dict in self.op.hvparams.items():
1337 if hv_name not in self.new_hvparams:
1338 self.new_hvparams[hv_name] = hv_dict
1339 else:
1340 self.new_hvparams[hv_name].update(hv_dict)
1341
1342
1343 self.new_diskparams = objects.FillDict(cluster.diskparams, {})
1344 if self.op.diskparams:
1345 for dt_name, dt_params in self.op.diskparams.items():
1346 if dt_name not in self.new_diskparams:
1347 self.new_diskparams[dt_name] = dt_params
1348 else:
1349 self.new_diskparams[dt_name].update(dt_params)
1350 CheckDiskAccessModeConsistency(self.op.diskparams, self.cfg)
1351
1352
1353 self.new_os_hvp = objects.FillDict(cluster.os_hvp, {})
1354 if self.op.os_hvp:
1355 for os_name, hvs in self.op.os_hvp.items():
1356 if os_name not in self.new_os_hvp:
1357 self.new_os_hvp[os_name] = hvs
1358 else:
1359 for hv_name, hv_dict in hvs.items():
1360 if hv_dict is None:
1361
1362 self.new_os_hvp[os_name].pop(hv_name, None)
1363 elif hv_name not in self.new_os_hvp[os_name]:
1364 self.new_os_hvp[os_name][hv_name] = hv_dict
1365 else:
1366 self.new_os_hvp[os_name][hv_name].update(hv_dict)
1367
1368
1369 self._BuildOSParams(cluster)
1370
1371
1372 if self.op.enabled_hypervisors is not None:
1373 for hv in self.op.enabled_hypervisors:
1374
1375
1376
1377
1378
1379 if hv not in new_hvp:
1380 new_hvp[hv] = {}
1381 new_hvp[hv] = objects.FillDict(constants.HVC_DEFAULTS[hv], new_hvp[hv])
1382 utils.ForceDictType(new_hvp[hv], constants.HVS_PARAMETER_TYPES)
1383
1384 if self.op.hvparams or self.op.enabled_hypervisors is not None:
1385
1386 for hv_name, hv_params in self.new_hvparams.items():
1387 if ((self.op.hvparams and hv_name in self.op.hvparams) or
1388 (self.op.enabled_hypervisors and
1389 hv_name in self.op.enabled_hypervisors)):
1390
1391 hv_class = hypervisor.GetHypervisorClass(hv_name)
1392 utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1393 hv_class.CheckParameterSyntax(hv_params)
1394 CheckHVParams(self, node_uuids, hv_name, hv_params)
1395
1396 if self.op.os_hvp:
1397
1398
1399 for os_name, os_hvp in self.new_os_hvp.items():
1400 for hv_name, hv_params in os_hvp.items():
1401 utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1402
1403 cluster_defaults = self.new_hvparams.get(hv_name, {})
1404 new_osp = objects.FillDict(cluster_defaults, hv_params)
1405 hv_class = hypervisor.GetHypervisorClass(hv_name)
1406 hv_class.CheckParameterSyntax(new_osp)
1407 CheckHVParams(self, node_uuids, hv_name, new_osp)
1408
1409 if self.op.default_iallocator:
1410 alloc_script = utils.FindFile(self.op.default_iallocator,
1411 constants.IALLOCATOR_SEARCH_PATH,
1412 os.path.isfile)
1413 if alloc_script is None:
1414 raise errors.OpPrereqError("Invalid default iallocator script '%s'"
1415 " specified" % self.op.default_iallocator,
1416 errors.ECODE_INVAL)
1417
1418 if self.op.instance_communication_network:
1419 network_name = self.op.instance_communication_network
1420
1421 try:
1422 network_uuid = self.cfg.LookupNetwork(network_name)
1423 except errors.OpPrereqError:
1424 network_uuid = None
1425
1426 if network_uuid is not None:
1427 network = self.cfg.GetNetwork(network_uuid)
1428 self._CheckInstanceCommunicationNetwork(network, self.LogWarning)
1429
1430 if self.op.compression_tools:
1431 CheckCompressionTools(self.op.compression_tools)
1432
1434 "Calculate the new OS parameters for this operation."
1435
1436 def _GetNewParams(source, new_params):
1437 "Wrapper around GetUpdatedParams."
1438 if new_params is None:
1439 return source
1440 result = objects.FillDict(source, {})
1441 for os_name in new_params:
1442 result[os_name] = GetUpdatedParams(result.get(os_name, {}),
1443 new_params[os_name],
1444 use_none=True)
1445 if not result[os_name]:
1446 del result[os_name]
1447 return result
1448
1449 self.new_osp = _GetNewParams(cluster.osparams,
1450 self.op.osparams)
1451 self.new_osp_private = _GetNewParams(cluster.osparams_private_cluster,
1452 self.op.osparams_private_cluster)
1453
1454
1455 changed_oses = (set(self.new_osp.keys()) | set(self.new_osp_private.keys()))
1456 for os_name in changed_oses:
1457 os_params = cluster.SimpleFillOS(
1458 os_name,
1459 self.new_osp.get(os_name, {}),
1460 os_params_private=self.new_osp_private.get(os_name, {})
1461 )
1462
1463 CheckOSParams(self, False, [self.cfg.GetMasterNode()],
1464 os_name, os_params, False)
1465
1467 """Determines and sets the new volume group name.
1468
1469 """
1470 if self.op.vg_name is not None:
1471 new_volume = self.op.vg_name
1472 if not new_volume:
1473 new_volume = None
1474 if new_volume != self.cfg.GetVGName():
1475 self.cfg.SetVGName(new_volume)
1476 else:
1477 feedback_fn("Cluster LVM configuration already in desired"
1478 " state, not changing")
1479
1481 """Set the file storage directory.
1482
1483 """
1484 if self.op.file_storage_dir is not None:
1485 if self.cluster.file_storage_dir == self.op.file_storage_dir:
1486 feedback_fn("Global file storage dir already set to value '%s'"
1487 % self.cluster.file_storage_dir)
1488 else:
1489 self.cluster.file_storage_dir = self.op.file_storage_dir
1490
1492 """Set the shared file storage directory.
1493
1494 """
1495 if self.op.shared_file_storage_dir is not None:
1496 if self.cluster.shared_file_storage_dir == \
1497 self.op.shared_file_storage_dir:
1498 feedback_fn("Global shared file storage dir already set to value '%s'"
1499 % self.cluster.shared_file_storage_dir)
1500 else:
1501 self.cluster.shared_file_storage_dir = self.op.shared_file_storage_dir
1502
1504 """Set the DRBD usermode helper.
1505
1506 """
1507 if self.op.drbd_helper is not None:
1508 if not constants.DT_DRBD8 in self.cluster.enabled_disk_templates:
1509 feedback_fn("Note that you specified a drbd user helper, but did not"
1510 " enable the drbd disk template.")
1511 new_helper = self.op.drbd_helper
1512 if not new_helper:
1513 new_helper = None
1514 if new_helper != self.cfg.GetDRBDHelper():
1515 self.cfg.SetDRBDHelper(new_helper)
1516 else:
1517 feedback_fn("Cluster DRBD helper already in desired state,"
1518 " not changing")
1519
1520 @staticmethod
1522 """Ensure that the instance communication network exists and is
1523 connected to all groups.
1524
1525 The instance communication network given by L{network_name} it is
1526 created, if necessary, via the opcode 'OpNetworkAdd'. Also, the
1527 instance communication network is connected to all existing node
1528 groups, if necessary, via the opcode 'OpNetworkConnect'.
1529
1530 @type cfg: L{config.ConfigWriter}
1531 @param cfg: cluster configuration
1532
1533 @type network_name: string
1534 @param network_name: instance communication network name
1535
1536 @rtype: L{ganeti.cmdlib.ResultWithJobs} or L{None}
1537 @return: L{ganeti.cmdlib.ResultWithJobs} if the instance
1538 communication needs to be created or it needs to be
1539 connected to a group, otherwise L{None}
1540
1541 """
1542 jobs = []
1543
1544 try:
1545 network_uuid = cfg.LookupNetwork(network_name)
1546 network_exists = True
1547 except errors.OpPrereqError:
1548 network_exists = False
1549
1550 if not network_exists:
1551 jobs.append(AddInstanceCommunicationNetworkOp(network_name))
1552
1553 for group_uuid in cfg.GetNodeGroupList():
1554 group = cfg.GetNodeGroup(group_uuid)
1555
1556 if network_exists:
1557 network_connected = network_uuid in group.networks
1558 else:
1559
1560
1561
1562
1563 network_connected = False
1564
1565 if not network_connected:
1566 op = ConnectInstanceCommunicationNetworkOp(group_uuid, network_name)
1567 jobs.append(op)
1568
1569 if jobs:
1570 return ResultWithJobs([jobs])
1571 else:
1572 return None
1573
1574 @staticmethod
1576 """Update the instance communication network stored in the cluster
1577 configuration.
1578
1579 Compares the user-supplied instance communication network against
1580 the one stored in the Ganeti cluster configuration. If there is a
1581 change, the instance communication network may be possibly created
1582 and connected to all groups (see
1583 L{LUClusterSetParams._EnsureInstanceCommunicationNetwork}).
1584
1585 @type cfg: L{config.ConfigWriter}
1586 @param cfg: cluster configuration
1587
1588 @type network_name: string
1589 @param network_name: instance communication network name
1590
1591 @type feedback_fn: function
1592 @param feedback_fn: see L{ganeti.cmdlist.base.LogicalUnit}
1593
1594 @rtype: L{LUClusterSetParams._EnsureInstanceCommunicationNetwork} or L{None}
1595 @return: see L{LUClusterSetParams._EnsureInstanceCommunicationNetwork}
1596
1597 """
1598 config_network_name = cfg.GetInstanceCommunicationNetwork()
1599
1600 if network_name == config_network_name:
1601 feedback_fn("Instance communication network already is '%s', nothing to"
1602 " do." % network_name)
1603 else:
1604 try:
1605 cfg.LookupNetwork(config_network_name)
1606 feedback_fn("Previous instance communication network '%s'"
1607 " should be removed manually." % config_network_name)
1608 except errors.OpPrereqError:
1609 pass
1610
1611 if network_name:
1612 feedback_fn("Changing instance communication network to '%s', only new"
1613 " instances will be affected."
1614 % network_name)
1615 else:
1616 feedback_fn("Disabling instance communication network, only new"
1617 " instances will be affected.")
1618
1619 cfg.SetInstanceCommunicationNetwork(network_name)
1620
1621 if network_name:
1622 return LUClusterSetParams._EnsureInstanceCommunicationNetwork(
1623 cfg,
1624 network_name)
1625 else:
1626 return None
1627
1628 - def Exec(self, feedback_fn):
1629 """Change the parameters of the cluster.
1630
1631 """
1632
1633 self.cluster = self.cfg.GetClusterInfo()
1634 if self.op.enabled_disk_templates:
1635 self.cluster.enabled_disk_templates = \
1636 list(self.op.enabled_disk_templates)
1637
1638 self.cfg.Update(self.cluster, feedback_fn)
1639
1640 self._SetVgName(feedback_fn)
1641
1642 self.cluster = self.cfg.GetClusterInfo()
1643 self._SetFileStorageDir(feedback_fn)
1644 self._SetSharedFileStorageDir(feedback_fn)
1645 self.cfg.Update(self.cluster, feedback_fn)
1646 self._SetDrbdHelper(feedback_fn)
1647
1648
1649 self.cluster = self.cfg.GetClusterInfo()
1650
1651 ensure_kvmd = False
1652
1653 active = constants.DATA_COLLECTOR_STATE_ACTIVE
1654 if self.op.enabled_data_collectors is not None:
1655 for name, val in self.op.enabled_data_collectors.items():
1656 self.cluster.data_collectors[name][active] = val
1657
1658 if self.op.data_collector_interval:
1659 internal = constants.DATA_COLLECTOR_PARAMETER_INTERVAL
1660 for name, val in self.op.data_collector_interval.items():
1661 self.cluster.data_collectors[name][internal] = int(val)
1662
1663 if self.op.hvparams:
1664 self.cluster.hvparams = self.new_hvparams
1665 if self.op.os_hvp:
1666 self.cluster.os_hvp = self.new_os_hvp
1667 if self.op.enabled_hypervisors is not None:
1668 self.cluster.hvparams = self.new_hvparams
1669 self.cluster.enabled_hypervisors = self.op.enabled_hypervisors
1670 ensure_kvmd = True
1671 if self.op.beparams:
1672 self.cluster.beparams[constants.PP_DEFAULT] = self.new_beparams
1673 if self.op.nicparams:
1674 self.cluster.nicparams[constants.PP_DEFAULT] = self.new_nicparams
1675 if self.op.ipolicy:
1676 self.cluster.ipolicy = self.new_ipolicy
1677 if self.op.osparams:
1678 self.cluster.osparams = self.new_osp
1679 if self.op.osparams_private_cluster:
1680 self.cluster.osparams_private_cluster = self.new_osp_private
1681 if self.op.ndparams:
1682 self.cluster.ndparams = self.new_ndparams
1683 if self.op.diskparams:
1684 self.cluster.diskparams = self.new_diskparams
1685 if self.op.hv_state:
1686 self.cluster.hv_state_static = self.new_hv_state
1687 if self.op.disk_state:
1688 self.cluster.disk_state_static = self.new_disk_state
1689
1690 if self.op.candidate_pool_size is not None:
1691 self.cluster.candidate_pool_size = self.op.candidate_pool_size
1692
1693 master_node = self.cfg.GetMasterNode()
1694 potential_master_candidates = self.cfg.GetPotentialMasterCandidates()
1695 modify_ssh_setup = self.cfg.GetClusterInfo().modify_ssh_setup
1696 AdjustCandidatePool(
1697 self, [], master_node, potential_master_candidates, feedback_fn,
1698 modify_ssh_setup)
1699
1700 if self.op.max_running_jobs is not None:
1701 self.cluster.max_running_jobs = self.op.max_running_jobs
1702
1703 if self.op.max_tracked_jobs is not None:
1704 self.cluster.max_tracked_jobs = self.op.max_tracked_jobs
1705
1706 if self.op.maintain_node_health is not None:
1707 self.cluster.maintain_node_health = self.op.maintain_node_health
1708
1709 if self.op.modify_etc_hosts is not None:
1710 self.cluster.modify_etc_hosts = self.op.modify_etc_hosts
1711
1712 if self.op.prealloc_wipe_disks is not None:
1713 self.cluster.prealloc_wipe_disks = self.op.prealloc_wipe_disks
1714
1715 if self.op.add_uids is not None:
1716 uidpool.AddToUidPool(self.cluster.uid_pool, self.op.add_uids)
1717
1718 if self.op.remove_uids is not None:
1719 uidpool.RemoveFromUidPool(self.cluster.uid_pool, self.op.remove_uids)
1720
1721 if self.op.uid_pool is not None:
1722 self.cluster.uid_pool = self.op.uid_pool
1723
1724 if self.op.default_iallocator is not None:
1725 self.cluster.default_iallocator = self.op.default_iallocator
1726
1727 if self.op.default_iallocator_params is not None:
1728 self.cluster.default_iallocator_params = self.op.default_iallocator_params
1729
1730 if self.op.reserved_lvs is not None:
1731 self.cluster.reserved_lvs = self.op.reserved_lvs
1732
1733 if self.op.use_external_mip_script is not None:
1734 self.cluster.use_external_mip_script = self.op.use_external_mip_script
1735
1736 if self.op.enabled_user_shutdown is not None and \
1737 self.cluster.enabled_user_shutdown != self.op.enabled_user_shutdown:
1738 self.cluster.enabled_user_shutdown = self.op.enabled_user_shutdown
1739 ensure_kvmd = True
1740
1741 def helper_os(aname, mods, desc):
1742 desc += " OS list"
1743 lst = getattr(self.cluster, aname)
1744 for key, val in mods:
1745 if key == constants.DDM_ADD:
1746 if val in lst:
1747 feedback_fn("OS %s already in %s, ignoring" % (val, desc))
1748 else:
1749 lst.append(val)
1750 elif key == constants.DDM_REMOVE:
1751 if val in lst:
1752 lst.remove(val)
1753 else:
1754 feedback_fn("OS %s not found in %s, ignoring" % (val, desc))
1755 else:
1756 raise errors.ProgrammerError("Invalid modification '%s'" % key)
1757
1758 if self.op.hidden_os:
1759 helper_os("hidden_os", self.op.hidden_os, "hidden")
1760
1761 if self.op.blacklisted_os:
1762 helper_os("blacklisted_os", self.op.blacklisted_os, "blacklisted")
1763
1764 if self.op.mac_prefix:
1765 self.cluster.mac_prefix = self.op.mac_prefix
1766
1767 if self.op.master_netdev:
1768 master_params = self.cfg.GetMasterNetworkParameters()
1769 ems = self.cfg.GetUseExternalMipScript()
1770 feedback_fn("Shutting down master ip on the current netdev (%s)" %
1771 self.cluster.master_netdev)
1772 result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
1773 master_params, ems)
1774 if not self.op.force:
1775 result.Raise("Could not disable the master ip")
1776 else:
1777 if result.fail_msg:
1778 msg = ("Could not disable the master ip (continuing anyway): %s" %
1779 result.fail_msg)
1780 feedback_fn(msg)
1781 feedback_fn("Changing master_netdev from %s to %s" %
1782 (master_params.netdev, self.op.master_netdev))
1783 self.cluster.master_netdev = self.op.master_netdev
1784
1785 if self.op.master_netmask:
1786 master_params = self.cfg.GetMasterNetworkParameters()
1787 feedback_fn("Changing master IP netmask to %s" % self.op.master_netmask)
1788 result = self.rpc.call_node_change_master_netmask(
1789 master_params.uuid, master_params.netmask,
1790 self.op.master_netmask, master_params.ip,
1791 master_params.netdev)
1792 result.Warn("Could not change the master IP netmask", feedback_fn)
1793 self.cluster.master_netmask = self.op.master_netmask
1794
1795 if self.op.install_image:
1796 self.cluster.install_image = self.op.install_image
1797
1798 if self.op.zeroing_image is not None:
1799 CheckImageValidity(self.op.zeroing_image,
1800 "Zeroing image must be an absolute path or a URL")
1801 self.cluster.zeroing_image = self.op.zeroing_image
1802
1803 self.cfg.Update(self.cluster, feedback_fn)
1804
1805 if self.op.master_netdev:
1806 master_params = self.cfg.GetMasterNetworkParameters()
1807 feedback_fn("Starting the master ip on the new master netdev (%s)" %
1808 self.op.master_netdev)
1809 ems = self.cfg.GetUseExternalMipScript()
1810 result = self.rpc.call_node_activate_master_ip(master_params.uuid,
1811 master_params, ems)
1812 result.Warn("Could not re-enable the master ip on the master,"
1813 " please restart manually", self.LogWarning)
1814
1815
1816
1817
1818
1819 if ensure_kvmd:
1820 EnsureKvmdOnNodes(self, feedback_fn)
1821
1822 if self.op.compression_tools is not None:
1823 self.cfg.SetCompressionTools(self.op.compression_tools)
1824
1825 network_name = self.op.instance_communication_network
1826 if network_name is not None:
1827 return self._ModifyInstanceCommunicationNetwork(self.cfg,
1828 network_name, feedback_fn)
1829 else:
1830 return None
1831