Example #1
0
    def test_get_bottom_id_by_top_id_region_name(self):
        self._create_pod(0, 'test_az_uuid_0')
        self._create_pod(1, 'test_az_uuid_1')
        self._create_pod(2, 'test_az_uuid_2')
        self._create_resource_mappings()
        region_name = 'test_pod_0'
        bottom_id = api.get_bottom_id_by_top_id_region_name(
            self.context, 'top_uuid', region_name, 'network')
        self.assertIsNone(bottom_id)

        region_name = 'test_pod_1'
        bottom_id = api.get_bottom_id_by_top_id_region_name(
            self.context, 'top_uuid', region_name, 'network')
        self.assertEqual(bottom_id, 'top_uuid')
Example #2
0
    def test_get_bottom_id_by_top_id_region_name(self):
        self._create_pod(0, 'test_az_uuid_0')
        self._create_pod(1, 'test_az_uuid_1')
        self._create_pod(2, 'test_az_uuid_2')
        self._create_resource_mappings()
        region_name = 'test_pod_0'
        bottom_id = api.get_bottom_id_by_top_id_region_name(
            self.context, 'top_uuid', region_name, 'network')
        self.assertIsNone(bottom_id)

        region_name = 'test_pod_1'
        bottom_id = api.get_bottom_id_by_top_id_region_name(
            self.context, 'top_uuid', region_name, 'network')
        self.assertEqual(bottom_id, 'top_uuid')
Example #3
0
    def update_subnet(self, ctx, payload):
        """update bottom subnet

        if bottom pod id equal to POD_NOT_SPECIFIED, dispatch jobs for every
        mapped bottom pod via RPC, otherwise update subnet in the specified
        pod.

        :param ctx: tricircle context
        :param payload: dict whose key is JT_SUBNET_UPDATE and value
        is "top_subnet_id#bottom_pod_id"
        :return: None
        """
        (b_pod_id, t_subnet_id) = payload[
            constants.JT_SUBNET_UPDATE].split('#')
        if b_pod_id == constants.POD_NOT_SPECIFIED:
            mappings = db_api.get_bottom_mappings_by_top_id(
                ctx, t_subnet_id, constants.RT_SUBNET)
            b_pods = [mapping[0] for mapping in mappings]
            for b_pod in b_pods:
                self.xjob_handler.update_subnet(ctx, t_subnet_id,
                                                b_pod['pod_id'])
            return

        t_client = self._get_client()
        t_subnet = t_client.get_subnets(ctx, t_subnet_id)
        if not t_subnet:
            return
        b_pod = db_api.get_pod(ctx, b_pod_id)
        b_region_name = b_pod['region_name']
        b_subnet_id = db_api.get_bottom_id_by_top_id_region_name(
            ctx, t_subnet_id, b_region_name, constants.RT_SUBNET)
        b_client = self._get_client(region_name=b_region_name)
        b_subnet = b_client.get_subnets(ctx, b_subnet_id)
        b_gateway_ip = b_subnet['gateway_ip']

        # we need to remove the bottom subnet gateway ip from the top subnet
        # allaction pools
        b_allocation_pools = helper.NetworkHelper.get_bottom_subnet_pools(
            t_subnet, b_gateway_ip)

        # bottom gateway_ip doesn't need to be updated, because it is reserved
        # by top pod.
        # name is not allowed to be updated, because it is used by
        # lock_handle to retrieve bottom/local resources that have been
        # created but not registered in the resource routing table
        body = {
            'subnet':
                {'description': t_subnet['description'],
                 'enable_dhcp': t_subnet['enable_dhcp'],
                 'allocation_pools': b_allocation_pools,
                 'host_routes': t_subnet['host_routes'],
                 'dns_nameservers': t_subnet['dns_nameservers']}
        }
        try:
            b_client.update_subnets(ctx, b_subnet_id, body)
        except q_cli_exceptions.NotFound:
            LOG.error(_LE('subnet: %(subnet_id)s not found, '
                          'pod name: %(name)s'),
                      {'subnet_id': b_subnet_id, 'name': b_region_name})
Example #4
0
    def update_network(self, ctx, payload):
        """update bottom network

        if bottom pod id equal to POD_NOT_SPECIFIED, dispatch jobs for every
        mapped bottom pod via RPC, otherwise update network in the specified
        pod.

        :param ctx: tricircle context
        :param payload: dict whose key is JT_NETWORK_UPDATE and value
        is "top_network_id#bottom_pod_id"
        :return: None
        """
        (b_pod_id, t_network_id) = payload[
            constants.JT_NETWORK_UPDATE].split('#')
        if b_pod_id == constants.POD_NOT_SPECIFIED:
            mappings = db_api.get_bottom_mappings_by_top_id(
                ctx, t_network_id, constants.RT_NETWORK)
            b_pods = [mapping[0] for mapping in mappings]
            for b_pod in b_pods:
                self.xjob_handler.update_network(ctx, t_network_id,
                                                 b_pod['pod_id'])
            return

        t_client = self._get_client()
        t_network = t_client.get_networks(ctx, t_network_id)
        if not t_network:
            return
        b_pod = db_api.get_pod(ctx, b_pod_id)
        b_region_name = b_pod['region_name']
        b_client = self._get_client(region_name=b_region_name)
        b_network_id = db_api.get_bottom_id_by_top_id_region_name(
            ctx, t_network_id, b_region_name, constants.RT_NETWORK)
        # name is not allowed to be updated, because it is used by
        # lock_handle to retrieve bottom/local resources that have been
        # created but not registered in the resource routing table
        body = {
            'network': {
                'description': t_network['description'],
                'admin_state_up': t_network['admin_state_up'],
                'shared': t_network['shared']
            }
        }

        try:
            b_client.update_networks(ctx, b_network_id, body)
        except q_cli_exceptions.NotFound:
            LOG.error(_LE('network: %(net_id)s not found,'
                          'pod name: %(name)s'),
                      {'net_id': b_network_id, 'name': b_region_name})
Example #5
0
    def ensure_resource_mapping(t_ctx, project_id, pod, entries):
        """Ensure resource mapping

        :param t_ctx: tricircle context
        :param project_id: project id
        :param pod: bottom pod
        :param entries: a list of (top_id, bottom_id, resource_type) tuples.
        :return: None
        """
        for top_id, btm_id, resource_type in entries:
            if db_api.get_bottom_id_by_top_id_region_name(
                    t_ctx, top_id, pod['region_name'], resource_type):
                continue
            db_api.create_resource_mapping(t_ctx, top_id, btm_id,
                                           pod['pod_id'], project_id,
                                           resource_type)
Example #6
0
    def ensure_resource_mapping(t_ctx, project_id, pod, entries):
        """Ensure resource mapping

        :param t_ctx: tricircle context
        :param project_id: project id
        :param pod: bottom pod
        :param entries: a list of (top_id, bottom_id, resource_type) tuples.
        :return: None
        """
        for top_id, btm_id, resource_type in entries:
            if db_api.get_bottom_id_by_top_id_region_name(
                    t_ctx, top_id, pod['region_name'], resource_type):
                continue
            db_api.create_resource_mapping(t_ctx, top_id, btm_id,
                                           pod['pod_id'], project_id,
                                           resource_type)
    def test_get_create_element_create_fail(self):
        pod = self._prepare_pod()
        resource_id = 'fake_resource_id'
        _type = 'fake_resource'
        ele = {'id': resource_id}
        body = {'name': resource_id}

        def fake_create_resource(t_ctx, q_ctx, pod, body, _type):
            raise q_exceptions.ConnectionFailed()

        self.assertRaises(q_exceptions.ConnectionFailed,
                          lock_handle.get_or_create_element, self.t_ctx,
                          self.q_ctx, self.project_id, pod, ele, _type, body,
                          list_resource, fake_create_resource)
        routing = api.get_bottom_id_by_top_id_region_name(
            self.t_ctx, resource_id, pod['region_name'], _type)
        self.assertIsNone(routing)
Example #8
0
    def test_get_create_element_create_fail(self):
        pod = self._prepare_pod()
        resource_id = 'fake_resource_id'
        _type = 'fake_resource'
        ele = {'id': resource_id}
        body = {'name': resource_id}

        def fake_create_resource(t_ctx, q_ctx, pod, body, _type):
            raise q_exceptions.ConnectionFailed()

        self.assertRaises(
            q_exceptions.ConnectionFailed, lock_handle.get_or_create_element,
            self.t_ctx, self.q_ctx, self.project_id, pod, ele, _type, body,
            list_resource, fake_create_resource)
        routing = api.get_bottom_id_by_top_id_region_name(
            self.t_ctx, resource_id, pod['region_name'], _type)
        self.assertIsNone(routing)
Example #9
0
    def _setup_router_one_pod(self, ctx, t_pod, b_pod, t_client, t_net,
                              t_router, t_bridge_net, t_bridge_subnet,
                              is_ext_net_pod):
        # NOTE(zhiyuan) after the bridge network combination, external network
        # is attached to a separate router, which is created in central plugin,
        # so is_ext_net_pod is not used in the current implementation, but we
        # choose to keep this parameter since it's an important attribute of a
        # pod and we may need to use it later.
        b_client = self._get_client(b_pod['region_name'])

        is_distributed = t_router.get('distributed', False)
        router_body = {'router': {'name': t_router['id'],
                                  'distributed': is_distributed}}
        project_id = t_router['tenant_id']

        # create bottom router in target bottom pod
        _, b_router_id = self.helper.prepare_bottom_element(
            ctx, project_id, b_pod, t_router, constants.RT_ROUTER, router_body)

        # create top bridge port
        q_ctx = None  # no need to pass neutron context when using client
        t_bridge_port_id = self.helper.get_bridge_interface(
            ctx, q_ctx, project_id, t_pod, t_bridge_net['id'], b_router_id)

        # create bottom bridge port
        # if target bottom pod is hosting real external network, we create
        # another bottom router and attach the bridge network as internal
        # network, but this work is done by central plugin when user sets
        # router gateway.
        t_bridge_port = t_client.get_ports(ctx, t_bridge_port_id)
        (is_new, b_bridge_port_id, b_bridge_subnet_id,
         b_bridge_net_id) = self.helper.get_bottom_bridge_elements(
            ctx, project_id, b_pod, t_bridge_net, True, t_bridge_subnet, None)

        # we attach the bridge port as router gateway
        # add_gateway is update operation, which can run multiple times
        gateway_ip = t_bridge_port['fixed_ips'][0]['ip_address']
        b_client.action_routers(
            ctx, 'add_gateway', b_router_id,
            {'network_id': b_bridge_net_id,
             'enable_snat': False,
             'external_fixed_ips': [{'subnet_id': b_bridge_subnet_id,
                                     'ip_address': gateway_ip}]})

        # attach internal port to bottom router
        t_ports = self._get_router_interfaces(t_client, ctx, t_router['id'],
                                              t_net['id'])
        b_net_id = db_api.get_bottom_id_by_top_id_region_name(
            ctx, t_net['id'], b_pod['region_name'], constants.RT_NETWORK)
        if b_net_id:
            b_ports = self._get_router_interfaces(b_client, ctx, b_router_id,
                                                  b_net_id)
        else:
            b_ports = []
        if not t_ports and b_ports:
            # remove redundant bottom interface
            b_port = b_ports[0]
            request_body = {'port_id': b_port['id']}
            b_client.action_routers(ctx, 'remove_interface', b_router_id,
                                    request_body)
        elif t_ports and not b_ports:
            # create new bottom interface
            t_port = t_ports[0]

            # only consider ipv4 address currently
            t_subnet_id = t_port['fixed_ips'][0]['subnet_id']
            t_subnet = t_client.get_subnets(ctx, t_subnet_id)

            if CONF.enable_api_gateway:
                (b_net_id,
                 subnet_map) = self.helper.prepare_bottom_network_subnets(
                    ctx, q_ctx, project_id, b_pod, t_net, [t_subnet])
            else:
                (b_net_id,
                 subnet_map) = (t_net['id'], {t_subnet['id']: t_subnet['id']})

            # the gateway ip of bottom subnet is set to the ip of t_port, so
            # we just attach the bottom subnet to the bottom router and neutron
            # server in the bottom pod will create the interface for us, using
            # the gateway ip.
            b_client.action_routers(ctx, 'add_interface', b_router_id,
                                    {'subnet_id': subnet_map[t_subnet_id]})

        if not t_router['external_gateway_info']:
            return

        # handle floatingip
        t_ext_net_id = t_router['external_gateway_info']['network_id']
        t_fips = t_client.list_floatingips(ctx, [{'key': 'floating_network_id',
                                                  'comparator': 'eq',
                                                  'value': t_ext_net_id}])
        # skip unbound top floatingip
        t_ip_fip_map = dict([(fip['floating_ip_address'],
                              fip) for fip in t_fips if fip['port_id']])
        mappings = db_api.get_bottom_mappings_by_top_id(ctx, t_ext_net_id,
                                                        constants.RT_NETWORK)
        # bottom external network should exist
        b_ext_pod, b_ext_net_id = mappings[0]
        b_ext_client = self._get_client(b_ext_pod['region_name'])
        b_fips = b_ext_client.list_floatingips(
            ctx, [{'key': 'floating_network_id', 'comparator': 'eq',
                   'value': b_ext_net_id}])
        b_ip_fip_map = dict([(fip['floating_ip_address'],
                              fip) for fip in b_fips])
        add_fips = [ip for ip in t_ip_fip_map if ip not in b_ip_fip_map]
        del_fips = [ip for ip in b_ip_fip_map if ip not in t_ip_fip_map]

        for add_fip in add_fips:
            fip = t_ip_fip_map[add_fip]
            t_int_port_id = fip['port_id']
            b_int_port_id = db_api.get_bottom_id_by_top_id_region_name(
                ctx, t_int_port_id, b_pod['region_name'], constants.RT_PORT)
            if not b_int_port_id:
                LOG.warning(_LW('Port %(port_id)s associated with floating ip '
                                '%(fip)s is not mapped to bottom pod'),
                            {'port_id': t_int_port_id, 'fip': add_fip})
                continue
            t_int_port = t_client.get_ports(ctx, t_int_port_id)
            if t_int_port['network_id'] != t_net['id']:
                # only handle floating ip association for the given top network
                continue

            if b_ext_pod['pod_id'] != b_pod['pod_id']:
                # if the internal port is not located in the external network
                # pod, we need to create a copied port in that pod for floating
                # ip association purpose
                t_int_net_id = t_int_port['network_id']
                t_int_subnet_id = t_int_port['fixed_ips'][0]['subnet_id']
                port_body = {
                    'port': {
                        'tenant_id': project_id,
                        'admin_state_up': True,
                        'name': constants.shadow_port_name % t_int_port['id'],
                        'network_id': t_int_net_id,
                        'fixed_ips': [{'ip_address': t_int_port[
                            'fixed_ips'][0]['ip_address']}]
                    }
                }
                self.helper.prepare_bottom_element(
                    ctx, project_id, b_ext_pod, t_int_port,
                    constants.RT_SD_PORT, port_body)
                # create routing entries for copied network and subnet so we
                # can easily find them during central network and subnet
                # deletion, create_resource_mapping will catch DBDuplicateEntry
                # exception and ignore it so it's safe to call this function
                # multiple times
                db_api.create_resource_mapping(ctx, t_int_net_id, t_int_net_id,
                                               b_ext_pod['pod_id'], project_id,
                                               constants.RT_SD_NETWORK)
                db_api.create_resource_mapping(ctx, t_int_subnet_id,
                                               t_int_subnet_id,
                                               b_ext_pod['pod_id'], project_id,
                                               constants.RT_SD_SUBNET)

            self._safe_create_bottom_floatingip(
                ctx, b_pod, b_ext_client, b_ext_net_id, add_fip,
                b_int_port_id)

        for del_fip in del_fips:
            fip = b_ip_fip_map[del_fip]
            if b_ext_pod['pod_id'] != b_pod['pod_id'] and fip['port_id']:
                # expire the routing entry for copy port
                with ctx.session.begin():
                    core.update_resources(
                        ctx, models.ResourceRouting,
                        [{'key': 'bottom_id', 'comparator': 'eq',
                          'value': fip['port_id']},
                         {'key': 'resource_type', 'comparator': 'eq',
                          'value': constants.RT_SD_PORT}],
                        {'bottom_id': None,
                         'created_at': constants.expire_time,
                         'updated_at': constants.expire_time})
                # delete copy port
                b_ext_client.delete_ports(ctx, fip['port_id'])
                # delete the expired entry, even if this deletion fails, we
                # still have a chance that lock_handle module will delete it
                with ctx.session.begin():
                    core.delete_resources(ctx, models.ResourceRouting,
                                          [{'key': 'top_id',
                                            'comparator': 'eq',
                                            'value': fip['port_id']},
                                           {'key': 'resource_type',
                                            'comparator': 'eq',
                                            'value': constants.RT_SD_PORT}])
                    # delete port before floating ip disassociation, copy
                    # network and copy subnet are deleted during central
                    # network and subnet deletion
            b_ext_client.delete_floatingips(ctx, fip['id'])