예제 #1
0
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))
예제 #2
0
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)
예제 #3
0
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
예제 #4
0
            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)
예제 #5
0
            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)
예제 #6
0
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)