def _assign_host_tunnel_addr(ipip_pools): """ Claims an IPIP-enabled IP address from the first pool with some space. Stores the result in the host's config as its tunnel address. Exits on failure. :param ipip_pools: List of IPPools to search for an address. """ for ipip_pool in ipip_pools: v4_addrs, _ = client.auto_assign_ips( num_v4=1, num_v6=0, handle_id=None, attributes={}, pool=(ipip_pool, None), host=hostname ) if v4_addrs: # Successfully allocated an address. Unpack the list. [ip_addr] = v4_addrs break else: # Failed to allocate an address, the pools must be full. print_paragraph( "Failed to allocate an IP address from an IPIP-enabled pool " "for the host's IPIP tunnel device. Pools are likely " "exhausted." ) sys.exit(1) # If we get here, we've allocated a new IPIP-enabled address, # Store it in etcd so that Felix will pick it up. client.set_per_host_config(hostname, "IpInIpTunnelAddr", str(ip_addr))
def warn_if_hostname_conflict(ip): """ Prints a warning message if it seems like an existing host is already running calico using this hostname. :param ip: User-provided or detected IP address to start this node with. :return: Nothing """ try: current_ipv4, _ = client.get_host_bgp_ips(hostname) except KeyError: # No other machine has registered configuration under this hostname. # This must be a new host with a unique hostname, which is the # expected behavior. pass else: if current_ipv4 != "" and current_ipv4 != ip: hostname_warning = "WARNING: Hostname '%s' is already in use " \ "with IP address %s. Calico requires each " \ "compute host to have a unique hostname. " \ "If this is your first time running " \ "the Calico node on this host, ensure " \ "that another host is not already using the " \ "same hostname." % (hostname, current_ipv4) print_paragraph(hostname_warning)
def error_if_bgp_ip_conflict(ip, ip6): """ Prints an error message and exits if either of the IPv4 or IPv6 addresses is already in use by another calico BGP host. :param ip: User-provided IPv4 address to start this node with. :param ip6: User-provided IPv6 address to start this node with. :return: Nothing """ ip_list = [] if ip: ip_list.append(ip) if ip6: ip_list.append(ip6) try: # Get hostname of host that already uses the given IP, if it exists ip_conflicts = client.get_hostnames_from_ips(ip_list) except KeyError: # No hosts have been configured in etcd, so there cannot be a conflict return if ip_conflicts.keys(): ip_error = "ERROR: IP address %s is already in use by host %s. " \ "Calico requires each compute host to have a unique IP. " \ "If this is your first time running the Calico node on " \ "this host, ensure that another host is not already using " \ "the same IP address." try: if ip_conflicts[ip] != hostname: ip_error = ip_error % (ip, str(ip_conflicts[ip])) print_paragraph(ip_error) sys.exit(1) except KeyError: # IP address was not found in ip-host dictionary pass try: if ip6 and ip_conflicts[ip6] != hostname: ip_error = ip_error % (ip6, str(ip_conflicts[ip6])) print_paragraph(ip_error) sys.exit(1) except KeyError: # IP address was not found in ip-host dictionary pass
command_module = getattr(calico_ctl, command) # The command module should have a function the same name as the # command. This protects against trying to run an invalid command that # happens to have the same name as a non-command module. if not hasattr(command_module, command): docopt(docstring, options_first=True, argv=["--help"]) sys.exit(1) # docopt the arguments through that module's docstring arguments = docopt(command_module.__doc__, argv=argv) # Call the dispatch function in that module which should also have # the same name cmd = getattr(command_module, command) except AttributeError: # Unrecognized submodule. Show main help message docopt(docstring, options_first=True, argv=["--help"]) sys.exit(1) else: cmd(arguments) except SystemExit: raise except DataStoreError as e: print_paragraph(e.message) sys.exit(1) except BaseException as e: print "{0}: {1}\n".format(type(e).__name__, e) traceback.print_exc() sys.exit(1)
command_module = getattr(calico_ctl, command) # The command module should have a function the same name as the # command. This protects against trying to run an invalid command that # happens to have the same name as a non-command module. if not hasattr(command_module, command): docopt(__doc__, options_first=True, argv=['--help']) sys.exit(1) # docopt the arguments through that module's docstring arguments = docopt(command_module.__doc__, argv=argv) # Call the dispatch function in that module which should also have # the same name cmd = getattr(command_module, command) except AttributeError: # Unrecognized submodule. Show main help message docopt(__doc__, options_first=True, argv=['--help']) sys.exit(1) else: cmd(arguments) except SystemExit: raise except DataStoreError as e: print_paragraph(e.message) sys.exit(1) except BaseException as e: print "{0}: {1}\n".format(type(e).__name__, e) traceback.print_exc() sys.exit(1)
def validate_arguments(arguments): """ Validate common argument values. :param arguments: Docopt processed arguments. """ # List of valid characters that Felix permits valid_chars = '[a-zA-Z0-9_\.\-]' # Validate Profiles profile_ok = True if "<PROFILES>" in arguments or "<PROFILE>" in arguments: profiles = arguments.get("<PROFILES>") or arguments.get("<PROFILE>") if profiles: for profile in profiles: if not re.match("^%s+$" % valid_chars, profile): profile_ok = False break # Validate tags tag_ok = (arguments.get("<TAG>") is None or re.match("^%s+$" % valid_chars, arguments["<TAG>"])) # Validate IPs ip_ok = arguments.get("--ip") is None or netaddr.valid_ipv4( arguments.get("--ip")) ip6_ok = arguments.get("--ip6") is None or \ netaddr.valid_ipv6(arguments.get("--ip6")) container_ip_ok = arguments.get("<IP>") is None or \ netaddr.valid_ipv4(arguments["<IP>"]) or \ netaddr.valid_ipv6(arguments["<IP>"]) peer_ip_ok = arguments.get("<PEER_IP>") is None or \ netaddr.valid_ipv4(arguments["<PEER_IP>"]) or \ netaddr.valid_ipv6(arguments["<PEER_IP>"]) cidr_ok = True for arg in ["<CIDR>", "<SRCCIDR>", "<DSTCIDR>"]: if arguments.get(arg): try: arguments[arg] = str(netaddr.IPNetwork(arguments[arg])) except (AddrFormatError, ValueError): # Some versions of Netaddr have a bug causing them to return a # ValueError rather than an AddrFormatError, so catch both. cidr_ok = False icmp_ok = True for arg in ["<ICMPCODE>", "<ICMPTYPE>"]: if arguments.get(arg) is not None: try: value = int(arguments[arg]) if not (0 <= value < 255): # Felix doesn't support 255 raise ValueError("Invalid %s: %s" % (arg, value)) except ValueError: icmp_ok = False asnum_ok = True if arguments.get("<AS_NUM>") or arguments.get("--as"): try: asnum = int(arguments["<AS_NUM>"] or arguments["--as"]) asnum_ok = 0 <= asnum <= 4294967295 except ValueError: asnum_ok = False if not profile_ok: print_paragraph("Profile names must be < 40 character long and can " "only contain numbers, letters, dots, dashes and " "underscores.") if not tag_ok: print_paragraph("Tags names can only contain numbers, letters, dots, " "dashes and underscores.") if not ip_ok: print "Invalid IPv4 address specified with --ip argument." if not ip6_ok: print "Invalid IPv6 address specified with --ip6 argument." if not container_ip_ok or not peer_ip_ok: print "Invalid IP address specified." if not cidr_ok: print "Invalid CIDR specified." if not icmp_ok: print "Invalid ICMP type or code specified." if not asnum_ok: print "Invalid AS Number specified." if not (profile_ok and ip_ok and ip6_ok and tag_ok and peer_ip_ok and container_ip_ok and cidr_ok and icmp_ok and asnum_ok): sys.exit(1)