Hello community, here is the log from the commit of package crmsh for openSUSE:Factory checked in at 2018-10-04 19:00:58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/crmsh (Old) and /work/SRC/openSUSE:Factory/.crmsh.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "crmsh" Thu Oct 4 19:00:58 2018 rev:150 rq:639615 version:4.0.0+git.1538492109.4b1170b0 Changes: -------- --- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes 2018-09-28 08:53:13.937743671 +0200 +++ /work/SRC/openSUSE:Factory/.crmsh.new/crmsh.changes 2018-10-04 19:01:02.559231794 +0200 @@ -1,0 +2,9 @@ +Tue Oct 02 14:55:31 UTC 2018 - kgronl...@suse.com + +- Update to version 4.0.0+git.1538492109.4b1170b0: + * medium: bootstrap: Correctly check rrp_mode flag (bsc#1110463) + * medium: bootstrap: Pick first match for multiple routes (bsc#1106946) + * medium: utils: Use cloud metadata service to discover IP (bsc#1106946) + * Fix: bootstrap: change default ip address way for both mcast and unicat(bsc#1109975,bsc#1109974) + +------------------------------------------------------------------- Old: ---- crmsh-4.0.0+git.1537967262.68a0bd1e.tar.bz2 New: ---- crmsh-4.0.0+git.1538492109.4b1170b0.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ crmsh.spec ++++++ --- /var/tmp/diff_new_pack.4stBX7/_old 2018-10-04 19:01:03.075231253 +0200 +++ /var/tmp/diff_new_pack.4stBX7/_new 2018-10-04 19:01:03.075231253 +0200 @@ -36,7 +36,7 @@ Summary: High Availability cluster command-line interface License: GPL-2.0-or-later Group: %{pkg_group} -Version: 4.0.0+git.1537967262.68a0bd1e +Version: 4.0.0+git.1538492109.4b1170b0 Release: 0 Url: http://crmsh.github.io Source0: %{name}-%{version}.tar.bz2 ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.4stBX7/_old 2018-10-04 19:01:03.103231224 +0200 +++ /var/tmp/diff_new_pack.4stBX7/_new 2018-10-04 19:01:03.107231220 +0200 @@ -1,4 +1,4 @@ <servicedata> <service name="tar_scm"> <param name="url">git://github.com/ClusterLabs/crmsh.git</param> - <param name="changesrevision">8ea00b67525414e1682ba1eced82b93281692904</param></service></servicedata> \ No newline at end of file + <param name="changesrevision">16b0d4178d68d1f61377f4db12a53617d21f6e84</param></service></servicedata> \ No newline at end of file ++++++ crmsh-4.0.0+git.1537967262.68a0bd1e.tar.bz2 -> crmsh-4.0.0+git.1538492109.4b1170b0.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.0.0+git.1537967262.68a0bd1e/crmsh/bootstrap.py new/crmsh-4.0.0+git.1538492109.4b1170b0/crmsh/bootstrap.py --- old/crmsh-4.0.0+git.1537967262.68a0bd1e/crmsh/bootstrap.py 2018-09-26 15:07:42.000000000 +0200 +++ new/crmsh-4.0.0+git.1538492109.4b1170b0/crmsh/bootstrap.py 2018-10-02 16:55:09.000000000 +0200 @@ -235,6 +235,21 @@ status_done() +def pick_default_value(default_list, prev_list): + """ + Provide default value for function 'prompt_for_string'. + Make sure give different default value in multi-ring mode. + + Parameters: + * default_list - default value list for config item + * prev_list - previous value for config item in multi-ring mode + """ + for value in default_list: + if value not in prev_list: + return value + return "" + + def start_service(service): """ Start and enable systemd service @@ -886,11 +901,6 @@ def init_corosync_unicast(): - def pick_default_value(vlist, ilist): - # give a different value for second config items - if len(ilist) == 1: - vlist.remove(ilist[0]) - return vlist[0] if _context.yes_to_all: status("Configuring corosync (unicast)") @@ -911,27 +921,18 @@ mcastport_res = [] default_ports = ["5405", "5407"] two_rings = False - default_networks = [] - if _context.ipv6: - network_list = [] - all_ = utils.network_v6_all() - for item in all_.values(): - network_list.extend(item) - default_networks = [utils.get_ipv6_network(x) for x in network_list] - else: - network_list = utils.network_all() - if len(network_list) > 1: - default_networks = [_context.ip_network, network_list.remove(_context.ip_network)] - else: - default_networks = [_context.ip_network] - if not default_networks: + local_iplist = utils.ip_in_local(_context.ipv6) + len_iplist = len(local_iplist) + if len_iplist == 0: error("No network configured at {}!".format(utils.this_node())) + default_ip = [_context.ip_address] + [ip for ip in local_iplist if ip != _context.ip_address] + for i in 0, 1: ringXaddr = prompt_for_string('Address for ring{}'.format(i), r'([0-9]+\.){3}[0-9]+|[0-9a-fA-F]{1,4}:', - _context.ip_address if i == 0 and _context.ip_address else "", + pick_default_value(default_ip, ringXaddr_res), valid_ucastIP, ringXaddr_res) if not ringXaddr: @@ -948,7 +949,7 @@ mcastport_res.append(mcastport) if i == 1 or \ - len(default_networks) == 1 or \ + len_iplist == 1 or \ not _context.second_hb or \ not confirm("\nAdd another heartbeat line?"): break @@ -976,12 +977,6 @@ random.randint(0, 255), random.randint(1, 255)) - def pick_default_value(vlist, ilist): - # give a different value for second config items - if len(ilist) == 1: - vlist.remove(ilist[0]) - return vlist[0] - if _context.yes_to_all: status("Configuring corosync") else: @@ -1013,7 +1008,8 @@ else: network_list = utils.network_all() if len(network_list) > 1: - default_networks = [_context.ip_network, network_list.remove(_context.ip_network)] + network_list.remove(_context.ip_network) + default_networks = [_context.ip_network, network_list[0]] else: default_networks = [_context.ip_network] if not default_networks: @@ -1094,15 +1090,14 @@ """ Configure corosync (unicast or multicast, encrypted?) """ - @utils.memoize - def check_amazon(): - if not utils.is_program("dmidecode"): - return False - _rc, outp = utils.get_stdout("dmidecode -s system-version") - return re.search(r"\<.*\.amazon\>", outp) is not None + def requires_unicast(): + host = utils.detect_cloud() + if host is not None: + status("Detected cloud platform: {}".format(host)) + return host is not None init_corosync_auth() - if _context.unicast or check_amazon(): + if _context.unicast or requires_unicast(): init_corosync_unicast() else: init_corosync_multicast() @@ -1676,7 +1671,7 @@ # check whether have two rings rrp_flag = False rrp = corosync.get_value("totem.rrp_mode") - if rrp: + if rrp in ('active', 'passive'): rrp_flag = True # Need to do this if second (or subsequent) node happens to be up and @@ -1712,12 +1707,19 @@ # if unicast, we need to add our node to $corosync.conf() is_unicast = "nodelist" in open(corosync.conf()).read() if is_unicast: + local_iplist = utils.ip_in_local(_context.ipv6) + len_iplist = len(local_iplist) + if len_iplist == 0: + error("No network configured at {}!".format(utils.this_node())) + + default_ip = [_context.ip_address] + [ip for ip in local_iplist if ip != _context.ip_address] + ringXaddr_res = [] for i in 0, 1: while True: ringXaddr = prompt_for_string('Address for ring{}'.format(i), r'([0-9]+\.){3}[0-9]+|[0-9a-fA-F]{1,4}:', - _context.ip_address if i == 0 and _context.ip_address else "", + pick_default_value(default_ip, ringXaddr_res), valid_ucastIP, ringXaddr_res) if not ringXaddr: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.0.0+git.1537967262.68a0bd1e/crmsh/utils.py new/crmsh-4.0.0+git.1538492109.4b1170b0/crmsh/utils.py --- old/crmsh-4.0.0+git.1537967262.68a0bd1e/crmsh/utils.py 2018-09-26 15:07:42.000000000 +0200 +++ new/crmsh-4.0.0+git.1538492109.4b1170b0/crmsh/utils.py 2018-10-02 16:55:09.000000000 +0200 @@ -110,12 +110,14 @@ if info[0] is None and len(sp) >= 5 and sp[0] == 'default' and sp[1] == 'via': info[0] = sp[4] if info[0] is not None and valfor(sp, 'dev') == info[0] and sp[0] != 'default': + src = valfor(sp, 'src') if sp[0].find('/') >= 0: nw, length = sp[0].split('/') - info[1], info[2], info[3] = valfor(sp, 'src'), nw, length + info[1], info[2], info[3] = src, nw, length + break # we are reading /32 route entry else: - info[1], info[2], info[3] = valfor(sp, 'src'), sp[0], 32 + info[1], info[2], info[3] = src, sp[0], 32 if info[0] is None: raise ValueError("Failed to determine default network interface") return tuple(info) @@ -153,6 +155,13 @@ def ip_in_local(IPv6=False): + # short-circuit ip list handling for + # cloud providers in order to use + # metadata service instead + ips = iplist_for_cloud() + if len(ips) > 0: + return ips + if not IPv6: regex = r' [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]+ ' else: @@ -2056,5 +2065,94 @@ def version(self): return self.addr.version +# Set by detect_cloud() or iplist_for_cloud() +# to avoid multiple requests +_ip_for_cloud = None + + +def _cloud_metadata_request(uri, headers={}): + try: + import urllib2 as urllib + except ImportError: + import urllib.request as urllib + req = urllib.Request(uri) + for header, value in headers.items(): + req.add_header(header, value) + try: + resp = urllib.urlopen(req, timeout=5) + content = resp.read() + if type(content) != str: + return content.decode('utf-8').strip() + return content.strip() + except urllib.URLError: + return None + + +@memoize +def detect_cloud(): + """ + Tries to determine which (if any) cloud environment + the cluster is running on. + + This is mainly done using dmidecode. + + If the host cannot be determined, this function + returns None. Otherwise, it returns a string + identifying the platform. + + These are the currently known platforms: + + * amazon-web-services + * microsoft-azure + * google-cloud-platform + + """ + global _ip_for_cloud + + if not is_program("dmidecode"): + return None + rc, system_version = get_stdout("dmidecode -s system-version") + if re.search(r"\<.*\.amazon\>", system_version) is not None: + return "amazon-web-services" + if rc != 0: + return None + rc, system_manufacturer = get_stdout("dmidecode -s system-manufacturer") + if rc == 0 and "microsoft corporation" in system_manufacturer.lower(): + # To detect azure we also need to make an API request + result = _cloud_metadata_request( + "http://169.254.169.254/metadata/instance/network/interface/0/ipv4/ipAddress/0/privateIpAddress?api-version=2017-08-01&format=text", + headers={"Metadata": "true"}) + if result: + _ip_for_cloud = result + return "microsoft-azure" + rc, bios_vendor = get_stdout("dmidecode -s bios-vendor") + if rc == 0 and "Google" in bios_vendor: + # To detect GCP we also need to make an API request + result = _cloud_metadata_request( + "http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/ip", + headers={"Metadata-Flavor": "Google"}) + if result: + _ip_for_cloud = result + return "google-cloud-platform" + return None + + +def iplist_for_cloud(): + """ + Return list of viable IPs for the + current cloud provider (if any) + """ + global _ip_for_cloud + provider = detect_cloud() + if _ip_for_cloud is not None: + return [_ip_for_cloud] + if provider == "amazon-web-services": + uri = "http://169.254.169.254/latest/meta-data/local-ipv4" + result = _cloud_metadata_request(uri) + if result: + _ip_for_cloud = result + return [_ip_for_cloud] + return [] + # vim:ts=4:sw=4:et: