예제 #1
0
def get_mtu():
    ''' Get user-specified MTU size, adjusted to make room for encapsulation
    headers. https://docs.projectcalico.org/networking/mtu
    '''
    mtu = charm_config('veth-mtu')
    if not mtu:
        return None

    if charm_config('vxlan') != 'Never':
        return mtu - 50
    elif charm_config('ipip') != 'Never':
        return mtu - 20
    return mtu
예제 #2
0
def configure_bgp_globals():
    status.maintenance('Configuring BGP globals')
    config = charm_config()

    try:
        try:
            bgp_config = calicoctl_get('bgpconfig', 'default')
        except CalledProcessError as e:
            if b'resource does not exist' in e.output:
                log('default BGPConfiguration does not exist')
                bgp_config = {
                    'apiVersion': 'projectcalico.org/v3',
                    'kind': 'BGPConfiguration',
                    'metadata': {
                        'name': 'default'
                    },
                    'spec': {}
                }
            else:
                raise

        spec = bgp_config['spec']
        spec['asNumber'] = config['global-as-number']
        spec['nodeToNodeMeshEnabled'] = config['node-to-node-mesh']
        calicoctl_apply(bgp_config)
    except CalledProcessError:
        log(traceback.format_exc())
        status.waiting('Waiting to retry BGP global configuration')
        return

    set_state('calico.bgp.globals.configured')
예제 #3
0
def install_calico_service():
    ''' Install the calico-node systemd service. '''
    status.maintenance('Installing calico-node service.')

    with open(kubernetes_common.kubeclientconfig_path) as f:
        kubeconfig = yaml.safe_load(f)
    any_file_changed([kubernetes_common.kubeclientconfig_path])
    kubeconfig['users'] = [{
        'name': 'calico-node',
        'user': {
            'token': leader_get('calico-node-token')
        }
    }]
    kubeconfig['contexts'][0]['context']['user'] = '******'
    with open('/opt/calicoctl/kubeconfig', 'w') as f:
        yaml.dump(kubeconfig, f)

    etcd = endpoint_from_flag('etcd.available')
    service_path = os.path.join(os.sep, 'lib', 'systemd', 'system',
                                'calico-node.service')
    ip_versions = {net.version for net in get_networks(charm_config('cidr'))}
    ip4 = get_bind_address() if 4 in ip_versions else "none"
    ip6 = "autodetect" if 6 in ip_versions else "none"
    render(
        'calico-node.service',
        service_path,
        {
            'connection_string': etcd.get_connection_string(),
            'etcd_key_path': ETCD_KEY_PATH,
            'etcd_ca_path': ETCD_CA_PATH,
            'etcd_cert_path': ETCD_CERT_PATH,
            'nodename': gethostname(),
            # specify IP so calico doesn't grab a silly one from, say, lxdbr0
            'ip': ip4,
            'ip6': ip6,
            'mtu': get_mtu(),
            'calico_node_image': charm_config('calico-node-image'),
            'ignore_loose_rpf': charm_config('ignore-loose-rpf'),
            'lc_all': os.environ.get('LC_ALL', 'C.UTF-8'),
            'lang': os.environ.get('LANG', 'C.UTF-8')
        })
    check_call(['systemctl', 'daemon-reload'])
    service_restart('calico-node')
    service('enable', 'calico-node')
    remove_state('cni.kubeconfig.changed')
    set_state('calico.service.installed')
예제 #4
0
def is_rpf_config_mismatched():
    with open('/proc/sys/net/ipv4/conf/all/rp_filter') as f:
        rp_filter = int(f.read())
    ignore_loose_rpf = charm_config('ignore-loose-rpf')
    if rp_filter == 2 and not ignore_loose_rpf:
        # calico says this is invalid
        # https://github.com/kubernetes-sigs/kind/issues/891
        return True
    return False
예제 #5
0
def configure_calico_pool():
    ''' Configure Calico IP pool. '''
    config = charm_config()
    if not config['manage-pools']:
        log('Skipping pool configuration')
        set_state('calico.pool.configured')
        return

    status.maintenance('Configuring Calico IP pool')

    try:
        # remove unrecognized pools, and default pool if CIDR doesn't match
        pools = calicoctl_get('pool')['items']

        cidrs = tuple(cidr.strip() for cidr in config['cidr'].split(','))
        names = tuple('ipv{}'.format(get_network(cidr).version)
                      for cidr in cidrs)
        pool_names_to_delete = [
            pool['metadata']['name'] for pool in pools
            if pool['metadata']['name'] not in names
            or pool['spec']['cidr'] not in cidrs
        ]

        for pool_name in pool_names_to_delete:
            log('Deleting pool: %s' % pool_name)
            calicoctl('delete', 'pool', pool_name, '--skip-not-exists')

        for cidr, name in zip(cidrs, names):
            # configure the default pool
            pool = {
                'apiVersion': 'projectcalico.org/v3',
                'kind': 'IPPool',
                'metadata': {
                    'name': name,
                },
                'spec': {
                    'cidr': cidr,
                    'ipipMode': config['ipip'],
                    'vxlanMode': config['vxlan'],
                    'natOutgoing': config['nat-outgoing'],
                }
            }

            calicoctl_apply(pool)
    except CalledProcessError:
        log(traceback.format_exc())
        if config['ipip'] != 'Never' and config['vxlan'] != 'Never':
            status.blocked('ipip and vxlan configs are in conflict')
        else:
            status.waiting('Waiting to retry calico pool configuration')
        return

    set_state('calico.pool.configured')
예제 #6
0
def configure_cni():
    ''' Configure Calico CNI. '''
    status.maintenance('Configuring Calico CNI')
    cni = endpoint_from_flag('cni.connected')
    etcd = endpoint_from_flag('etcd.available')
    os.makedirs('/etc/cni/net.d', exist_ok=True)
    ip_versions = {net.version for net in get_networks(charm_config('cidr'))}
    context = {
        'connection_string': etcd.get_connection_string(),
        'etcd_key_path': ETCD_KEY_PATH,
        'etcd_cert_path': ETCD_CERT_PATH,
        'etcd_ca_path': ETCD_CA_PATH,
        'kubeconfig_path': '/opt/calicoctl/kubeconfig',
        'mtu': get_mtu(),
        'assign_ipv4': 'true' if 4 in ip_versions else 'false',
        'assign_ipv6': 'true' if 6 in ip_versions else 'false',
    }
    render('10-calico.conflist', '/etc/cni/net.d/10-calico.conflist', context)
    config = charm_config()
    cni.set_config(cidr=config['cidr'], cni_conf_file='10-calico.conflist')
    set_state('calico.cni.configured')
예제 #7
0
def disable_vxlan_tx_checksumming():
    '''Workaround for https://github.com/projectcalico/calico/issues/3145'''
    config = charm_config()

    if config['disable-vxlan-tx-checksumming'] and config['vxlan'] != 'Never':
        cmd = [
            'ethtool', '-K', 'vxlan.calico', 'tx-checksum-ip-generic', 'off'
        ]
        try:
            check_call(cmd)
        except CalledProcessError:
            msg = 'Waiting to retry disabling VXLAN TX checksumming'
            log(msg)
            status.waiting(msg)
예제 #8
0
def pull_calico_node_image():
    image = resource_get('calico-node-image')

    if not image or os.path.getsize(image) == 0:
        status_set('maintenance', 'Pulling calico-node image')
        image = charm_config('calico-node-image')
        CTL.pull(image)
    else:
        status_set('maintenance', 'Loading calico-node image')
        unzipped = '/tmp/calico-node-image.tar'
        with gzip.open(image, 'rb') as f_in:
            with open(unzipped, 'wb') as f_out:
                f_out.write(f_in.read())
        CTL.load(unzipped)

    set_state('calico.image.pulled')
예제 #9
0
def configure_calico_pool():
    ''' Configure Calico IP pool. '''
    config = charm_config()
    if not config['manage-pools']:
        log('Skipping pool configuration')
        set_state('calico.pool.configured')
        return

    status.maintenance('Configuring Calico IP pool')

    try:
        # remove unrecognized pools, and default pool if CIDR doesn't match
        pools = calicoctl_get('pool')['items']

        pool_names_to_delete = [
            pool['metadata']['name'] for pool in pools
            if pool['metadata']['name'] != 'default'
            or pool['spec']['cidr'] != config['cidr']
        ]

        for pool_name in pool_names_to_delete:
            log('Deleting pool: %s' % pool_name)
            calicoctl('delete', 'pool', pool_name, '--skip-not-exists')

        # configure the default pool
        pool = {
            'apiVersion': 'projectcalico.org/v3',
            'kind': 'IPPool',
            'metadata': {
                'name': 'default'
            },
            'spec': {
                'cidr': config['cidr'],
                'ipipMode': config['ipip'],
                'natOutgoing': config['nat-outgoing']
            }
        }

        calicoctl_apply(pool)
    except CalledProcessError:
        log(traceback.format_exc())
        status.waiting('Waiting to retry calico pool configuration')
        return

    set_state('calico.pool.configured')
예제 #10
0
def configure_cni():
    ''' Configure Calico CNI. '''
    status.maintenance('Configuring Calico CNI')
    cni = endpoint_from_flag('cni.is-worker')
    etcd = endpoint_from_flag('etcd.available')
    os.makedirs('/etc/cni/net.d', exist_ok=True)
    cni_config = cni.get_config()
    context = {
        'connection_string': etcd.get_connection_string(),
        'etcd_key_path': ETCD_KEY_PATH,
        'etcd_cert_path': ETCD_CERT_PATH,
        'etcd_ca_path': ETCD_CA_PATH,
        'kubeconfig_path': cni_config['kubeconfig_path']
    }
    render('10-calico.conflist', '/etc/cni/net.d/10-calico.conflist', context)
    config = charm_config()
    cni.set_config(cidr=config['cidr'], cni_conf_file='10-calico.conflist')
    set_state('calico.cni.configured')
예제 #11
0
def deploy_network_policy_controller():
    ''' Deploy the Calico network policy controller. '''
    status.maintenance('Deploying network policy controller.')
    etcd = endpoint_from_flag('etcd.available')
    context = {
        'connection_string': etcd.get_connection_string(),
        'etcd_key_path': ETCD_KEY_PATH,
        'etcd_cert_path': ETCD_CERT_PATH,
        'etcd_ca_path': ETCD_CA_PATH,
        'calico_policy_image': charm_config('calico-policy-image'),
        'etcd_cert_last_modified': os.path.getmtime(ETCD_CERT_PATH)
    }
    render('policy-controller.yaml', '/tmp/policy-controller.yaml', context)
    try:
        kubectl('apply', '-f', '/tmp/policy-controller.yaml')
        set_state('calico.npc.deployed')
    except CalledProcessError as e:
        status.waiting('Waiting for kubernetes')
        log(str(e))
예제 #12
0
def get_unit_as_number():
    config = charm_config()

    # Check for matching unit rule
    unit_id = get_unit_id()
    unit_as_numbers = yaml.safe_load(config['unit-as-numbers'])
    if unit_id in unit_as_numbers:
        as_number = unit_as_numbers[unit_id]
        return as_number

    # Check for matching subnet rule
    subnet_as_numbers = yaml.safe_load(config['subnet-as-numbers'])
    subnets = filter_local_subnets(subnet_as_numbers)
    if subnets:
        subnets.sort(key=lambda subnet: -subnet.prefixlen)
        subnet = subnets[0]
        as_number = subnet_as_numbers[str(subnet)]
        return as_number

    # No AS number specified for this unit.
    return None
예제 #13
0
def install_calico_service():
    ''' Install the calico-node systemd service. '''
    status.maintenance('Installing calico-node service.')
    etcd = endpoint_from_flag('etcd.available')
    service_path = os.path.join(os.sep, 'lib', 'systemd', 'system',
                                'calico-node.service')
    render(
        'calico-node.service',
        service_path,
        {
            'connection_string': etcd.get_connection_string(),
            'etcd_key_path': ETCD_KEY_PATH,
            'etcd_ca_path': ETCD_CA_PATH,
            'etcd_cert_path': ETCD_CERT_PATH,
            'nodename': gethostname(),
            # specify IP so calico doesn't grab a silly one from, say, lxdbr0
            'ip': get_bind_address(),
            'calico_node_image': charm_config('calico-node-image')
        })
    check_call(['systemctl', 'daemon-reload'])
    service_restart('calico-node')
    service('enable', 'calico-node')
    set_state('calico.service.installed')
예제 #14
0
def configure_master_cni():
    status_set('maintenance', 'Configuring Calico CNI')
    cni = endpoint_from_flag('cni.is-master')
    config = charm_config()
    cni.set_config(cidr=config['cidr'])
    set_state('calico.cni.configured')
예제 #15
0
def get_route_reflector_cluster_id():
    config = charm_config()
    route_reflector_cluster_ids = yaml.safe_load(
        config['route-reflector-cluster-ids'])
    unit_id = get_unit_id()
    return route_reflector_cluster_ids.get(unit_id)
예제 #16
0
def configure_bgp_peers():
    status.maintenance('Configuring BGP peers')

    peers = []

    # Global BGP peers
    config = charm_config()
    peers += yaml.safe_load(config['global-bgp-peers'])

    # Subnet-scoped BGP peers
    subnet_bgp_peers = yaml.safe_load(config['subnet-bgp-peers'])
    subnets = filter_local_subnets(subnet_bgp_peers)
    for subnet in subnets:
        peers += subnet_bgp_peers[str(subnet)]

    # Unit-scoped BGP peers
    unit_id = get_unit_id()
    unit_bgp_peers = yaml.safe_load(config['unit-bgp-peers'])
    if unit_id in unit_bgp_peers:
        peers += unit_bgp_peers[unit_id]

    # Give names to peers
    safe_unit_name = local_unit().replace('/', '-')
    named_peers = {
        # name must consist of lower case alphanumeric characters, '-' or '.'
        '%s-%s-%s' %
        (safe_unit_name, peer['address'].replace(':', '-'), peer['as-number']):
        peer
        for peer in peers
    }

    try:
        node_name = gethostname()
        for peer_name, peer in named_peers.items():
            peer_def = {
                'apiVersion': 'projectcalico.org/v3',
                'kind': 'BGPPeer',
                'metadata': {
                    'name': peer_name,
                },
                'spec': {
                    'node': node_name,
                    'peerIP': peer['address'],
                    'asNumber': peer['as-number']
                }
            }
            calicoctl_apply(peer_def)

        # Delete unrecognized peers
        existing_peers = calicoctl_get('bgppeers')['items']
        existing_peers = [peer['metadata']['name'] for peer in existing_peers]
        peers_to_delete = [
            peer for peer in existing_peers
            if peer.startswith(safe_unit_name +
                               '-') and peer not in named_peers
        ]

        for peer in peers_to_delete:
            calicoctl('delete', 'bgppeer', peer)
    except CalledProcessError:
        log(traceback.format_exc())
        status.waiting('Waiting to retry BGP peer configuration')
        return

    set_state('calico.bgp.peers.configured')
예제 #17
0
def configure_master_cni():
    status.maintenance('Configuring Calico CNI')
    cni = endpoint_from_flag('cni.is-master')
    config = charm_config()
    cni.set_config(cidr=config['cidr'], cni_conf_file='10-calico.conflist')
    set_state('calico.cni.configured')