def get_endpoints(with_id=False, skip_this=False, skip_removed=False, port=ETCD_CLIENT_PORT, sep=','): ''' Build a comma-separated list of etcd endpoints It will skip * current node, when `skip_this=True` * nodes with G@node_removal_in_progress=true, when `skip_removed=True` ''' expr = 'G@roles:etcd' if skip_removed: expr += ' and not G@node_removal_in_progress:true' etcd_members_lst = [] for (node_id, name) in __salt__['caasp_grains.get'](expr).items(): if skip_this and name == __salt__['caasp_net.get_nodename'](): continue member_endpoint = 'https://{}:{}'.format(name, port) if with_id: member_endpoint = "{}={}".format(node_id, member_endpoint) etcd_members_lst.append(member_endpoint) if len(etcd_members_lst) == 0: error('no etcd members available!!') raise NoEtcdServersException() etcd_members_lst.sort() return sep.join(etcd_members_lst)
def get_member_id(nodename=None): ''' Return the member ID (different from the node ID) for a etcd member of the cluster. Arguments: * `nodename`: (optional) the nodename for the member we want the ID for. if no name is provided (or empty), the local node will be used. ''' command = ["etcdctl"] + get_etcdctl_args() + ["member", "list"] target_nodename = nodename or __salt__['caasp_net.get_nodename']() debug("getting etcd member ID with: %s", command) try: target_url = 'https://{}:{}'.format(target_nodename, ETCD_CLIENT_PORT) members_output = subprocess.check_output(command) for member_line in members_output.splitlines(): if target_url in member_line: return member_line.split(':')[0] except Exception as e: error('cannot get member ID for "%s": %s', e, target_nodename) error('output: %s', members_output) return ''
def get_additional_etcd_members(num_wanted=None, **kwargs): ''' Taking into account 1) the current number of etcd members, and 2) the number of etcd nodes we should be running in the cluster (obtained with `get_cluster_size()`) get a list of additional nodes (IDs) that should run `etcd` too. Optional arguments: * `etcd_members`: list of current etcd members * `excluded`: list of nodes to exclude ''' excluded = kwargs.get('excluded', []) current_etcd_members = __salt__['caasp_nodes.get_from_args_or_with_expr']( 'etcd_members', kwargs, 'G@roles:etcd') num_current_etcd_members = len(current_etcd_members) # the number of etcd masters that should be in the cluster num_wanted_etcd_members = num_wanted or get_cluster_size(**kwargs) # ... and the number we are missing num_additional_etcd_members = num_wanted_etcd_members - num_current_etcd_members if num_additional_etcd_members <= 0: debug('get_additional_etcd_members: we dont need more etcd members') return [] debug('get_additional_etcd_members: curr:%d wanted:%d -> %d missing', num_current_etcd_members, num_wanted_etcd_members, num_additional_etcd_members) # Get a list of `num_additional_etcd_members` nodes that could be used # for running etcd. A valid node is a node that: # # 1) is not the `admin` or `ca` # 2) has no `etcd` role (bootstrapped or not) # 2) is not being removed/added/updated # 3) (in preference order, first for non bootstrapped nodes) # 1) has no role assigned # 2) is a master # 3) is a minion # new_etcd_members = __salt__['caasp_nodes.get_with_prio_for_role']( num_additional_etcd_members, 'etcd', excluded=current_etcd_members + excluded) if len(new_etcd_members) < num_additional_etcd_members: error( 'get_additional_etcd_members: cannot satisfy the %s members missing', num_additional_etcd_members) return new_etcd_members
def get_primary_ips_for(compound, **kwargs): """ given a compound expression 'compound', return the primary IPs for all the nodes that match that expression """ try: all_ifaces = __salt__["caasp_grains.get"](compound, "network.interfaces") return [ get_primary_ip(host=host, **kwargs) for host in all_ifaces.keys() ] except Exception as e: error("could not get primary IPs for %s: %s", compound, e) return []
def get_nodename(host=None, **kwargs): """ (given some optional 'host') return the `nodename` """ try: if not host: assert __opts__["__role"] != "master" return __salt__["grains.get"](NODENAME_GRAIN) else: all_nodenames = __salt__["caasp_grains.get"](host, grain=NODENAME_GRAIN, type="glob") return all_nodenames[host] except Exception as e: error("could not get nodename: %s", e) return ""
def get_nodename(host=None, **kwargs): ''' (given some optional 'host') return the `nodename` ''' try: if not host: assert __opts__['__role'] != 'master' return __salt__['grains.get']('nodename') else: all_nodenames = __salt__['caasp_grains.get'](host, 'nodename', type='glob') return all_nodenames[host] except Exception as e: error('could not get nodename: %s', e) return ''
def get_primary_iface(host=None): ''' (given some optional 'host') return the name of the primary iface (the iface associated with the default route) ''' try: if not host or host == get_nodename(): default_route_lst = __salt__['network.default_route']() return default_route_lst[0]['interface'] else: all_routes = __salt__['caasp_grains.get'](host, 'network.default_route', type='glob') return all_routes[host][0]['interface'] except Exception as e: error('could not get the primary interface: %s', e) return __salt__['caasp_pillar.get']('hw:netiface', DEFAULT_INTERFACE)
def get_iface_ip(iface, host=None, ifaces=None): """ given an 'iface' (and an optional 'host' and list of 'ifaces'), return the IP address associated with 'iface' """ try: if not ifaces: if not host or host == get_nodename(): ifaces = __salt__["network.interfaces"]() else: ifaces = __salt__["caasp_grains.get"](host, "network.interfaces", type="glob") iface = ifaces.get(iface) ipv4addr = iface.get("inet", [{}]) return ipv4addr[0].get("address") except Exception as e: error("could not get IP for interface %s: %s", iface, e) return ""
def get_iface_ip(iface, host=None, ifaces=None): ''' given an 'iface' (and an optional 'host' and list of 'ifaces'), return the IP address associated with 'iface' ''' try: if not ifaces: if not host or host == get_nodename(): ifaces = __salt__['network.interfaces']() else: ifaces = __salt__['caasp_grains.get'](host, 'network.interfaces', type='glob') iface = ifaces.get(iface) ipv4addr = iface.get('inet', [{}]) return ipv4addr[0].get('address') except Exception as e: error('could not get IP for interface %s: %s', iface, e) return ''
def get_registries_certs(lst, default_port=5000): ''' Given a list of "valid" items, return a dictionay of "<HOST>[:<PORT>]" -> <CERT> "valid" items must be get'able objects, with attributes "url", "cert" and (optional) "mirrors" "url"s can be [<PROTO>://]<HOST>[:<PORT>] ''' certs = {} debug('Finding certificates in: %s', lst) for registry in lst: try: url = registry.get('url') cert = registry.get('cert', '') if cert: # parse the name as an URL or "host:port", and return <HOST>[:<PORT>] hostname, port = _get_hostname_and_port(url) host_port = hostname if port: host_port += ":" + str(port) debug('Adding certificate for: %s', host_port) certs[host_port] = cert if port: if port == default_port: # When using the standar port (5000), if the user introduces # "my-registry:5000" as a trusted registry, he/she will be able # to do "docker pull my-registry:5000/some/image" but not # "docker pull my-registry/some/image". # So we must also create the "ca.crt" for "my-registry" # as he/she could just access "docker pull my-registry/some/image", # and Docker would fail to find "my-registry/ca.crt" name = hostname debug( 'Using default port: adding certificate for "%s" too', name) certs[name] = cert else: # the same happens if the user introduced a certificate for # "my-registry": we must fix the "docker pull my-registry:5000/some/image" case... name = hostname + ':' + str(default_port) debug( 'Adding certificate for default port, "%s", too', name) certs[name] = cert except Exception as e: error('Could not parse certificate: %s', e) try: mirrors = registry.get('mirrors', []) if mirrors: debug('Looking recursively for certificates in mirrors') certs_mirrors = get_registries_certs(mirrors, default_port=default_port) certs.update(certs_mirrors) except Exception as e: error('Could not parse mirrors: %s', e) return certs