abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
+# Helper values for calling builtin functions
+empty :=
+space := $(empty) $(empty)
+comma := ,
+
# Use bash in order to be able to use pipefail
SHELL=/bin/bash
BUILT_SOURCES = \
ganeti \
stamp-srclinks \
- lib/_autoconf.py \
- lib/_vcsversion.py \
$(all_dirfiles) \
- $(PYTHON_BOOTSTRAP)
+ $(PYTHON_BOOTSTRAP) \
+ $(BUILT_PYTHON_SOURCES)
-nodist_pkgpython_PYTHON = \
+BUILT_PYTHON_SOURCES = \
lib/_autoconf.py \
lib/_vcsversion.py
+nodist_pkgpython_PYTHON = \
+ $(BUILT_PYTHON_SOURCES)
+
noinst_PYTHON = \
lib/build/__init__.py \
lib/build/sphinx_ext.py
# it changes
doc/html/index.html: $(docrst) $(docpng) doc/conf.py configure.ac \
$(RUN_IN_TEMPDIR) lib/build/sphinx_ext.py lib/opcodes.py lib/ht.py \
- | lib/_autoconf.py lib/_vcsversion.py
+ | $(BUILT_PYTHON_SOURCES)
@test -n "$(SPHINX)" || \
{ echo 'sphinx-build' not found during configure; exit 1; }
@mkdir_p@ $(dir $@)
@rm -f test.tix
./htools/test
+# E111: indentation is not a multiple of four
+# E261: at least two spaces before inline comment
+# E501: line too long (80 characters)
+PEP8_IGNORE = E111,E261,E501
+
+# For excluding pep8 expects filenames only, not whole paths
+PEP8_EXCLUDE = $(subst $(space),$(comma),$(strip $(notdir $(BUILT_PYTHON_SOURCES))))
+
.PHONY: lint
lint: $(BUILT_SOURCES)
@test -n "$(PYLINT)" || { echo 'pylint' not found during configure; exit 1; }
+ if test -z "$(PEP8)"; then \
+ echo '"pep8" not found during configure' >&2; \
+ else \
+ $(PEP8) --repeat --ignore='$(PEP8_IGNORE)' --exclude='$(PEP8_EXCLUDE)' \
+ $(lint_python_code); \
+ fi
$(PYLINT) $(LINT_OPTS) $(lint_python_code)
cd $(top_srcdir)/qa && \
PYTHONPATH=$(abs_top_srcdir) $(PYLINT) $(LINT_OPTS) \
from ganeti import errors
from ganeti import ssh
from ganeti import utils
+from ganeti import netutils
_GROUPS_MERGE = "merge"
"""Container class to hold data used for merger.
"""
- def __init__(self, cluster, key_path, nodes, instances, config_path=None):
+ def __init__(self, cluster, key_path, nodes, instances, master_node,
+ master_ip, config_path=None):
"""Initialize the container.
@param cluster: The name of the cluster
@param key_path: Path to the ssh private key used for authentication
@param nodes: List of online nodes in the merging cluster
@param instances: List of instances running on merging cluster
+ @param master_node: Name of the master node
+ @param master_ip: Cluster IP
@param config_path: Path to the merging cluster config
"""
self.key_path = key_path
self.nodes = nodes
self.instances = instances
+ self.master_node = master_node
+ self.master_ip = master_ip
self.config_path = config_path
(cluster, result.fail_reason, result.output))
instances = result.stdout.splitlines()
- self.merger_data.append(MergerData(cluster, key_path, nodes, instances))
+ path = utils.PathJoin(constants.DATA_DIR, "ssconf_%s" %
+ constants.SS_MASTER_NODE)
+ result = self._RunCmd(cluster, "cat %s" % path, private_key=key_path)
+ if result.failed:
+ raise errors.RemoteError("Unable to retrieve the master node name from"
+ " %s. Fail reason: %s; output: %s" %
+ (cluster, result.fail_reason, result.output))
+ master_node = result.stdout.strip()
+
+ path = utils.PathJoin(constants.DATA_DIR, "ssconf_%s" %
+ constants.SS_MASTER_IP)
+ result = self._RunCmd(cluster, "cat %s" % path, private_key=key_path)
+ if result.failed:
+ raise errors.RemoteError("Unable to retrieve the master IP from"
+ " %s. Fail reason: %s; output: %s" %
+ (cluster, result.fail_reason, result.output))
+ master_ip = result.stdout.strip()
+
+ self.merger_data.append(MergerData(cluster, key_path, nodes, instances,
+ master_node, master_ip))
def _PrepareAuthorizedKeys(self):
"""Prepare the authorized_keys on every merging node.
" Fail reason: %s; output: %s" %
(cluster, result.fail_reason, result.output))
+ def _RemoveMasterIps(self):
+ """Removes the master IPs from the master nodes of each cluster.
+
+ """
+ for data in self.merger_data:
+ master_ip_family = netutils.IPAddress.GetAddressFamily(data.master_ip)
+ master_ip_len = netutils.IP4Address.iplen
+ if master_ip_family == netutils.IP6Address.family:
+ master_ip_len = netutils.IP6Address.iplen
+ # Not using constants.IP_COMMAND_PATH because the command might run on a
+ # machine in which the ip path is different, so it's better to rely on
+ # $PATH.
+ cmd = "ip address del %s/%s dev $(cat %s)" % (
+ data.master_ip,
+ master_ip_len,
+ utils.PathJoin(constants.DATA_DIR, "ssconf_%s" %
+ constants.SS_MASTER_NETDEV))
+ result = self._RunCmd(data.master_node, cmd, max_attempts=3)
+ if result.failed:
+ raise errors.RemoteError("Unable to remove master IP on %s."
+ " Fail reason: %s; output: %s" %
+ (data.master_node,
+ result.fail_reason,
+ result.output))
+
def _StopDaemons(self):
"""Stop all daemons on merging nodes.
self._StopDaemons()
logging.info("Merging config")
self._FetchRemoteConfig()
+ logging.info("Removing master IPs on mergee master nodes")
+ self._RemoveMasterIps()
logging.info("Stopping master daemon")
self._KillMasterDaemon()