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