def delete_port(self, context, id, l3_port_check=True): if l3_port_check: self.prevent_l3_port_deletion(context, id) port = self._get_port(context, id) port_mapping = nuagedb.get_port_mapping_by_id(context.session, id) # This is required for to pass ut test_floatingip_port_delete self.disassociate_floatingips(context, id) if not port["fixed_ips"]: return super(NuagePlugin, self).delete_port(context, id) sub_id = port["fixed_ips"][0]["subnet_id"] subnet_mapping = nuagedb.get_subnet_l2dom_by_id(context.session, sub_id) if not subnet_mapping: return super(NuagePlugin, self).delete_port(context, id) netpart_id = subnet_mapping["net_partition_id"] net_partition = nuagedb.get_net_partition_by_id(context.session, netpart_id) # Need to call this explicitly to delete vport_vporttag_mapping if constants.NOVA_PORT_OWNER_PREF in port["device_owner"]: # This was a VM Port filters = {"device_id": [port["device_id"]]} ports = self.get_ports(context, filters) params = { "no_of_ports": len(ports), "net_partition": net_partition, "tenant": port["tenant_id"], "mac": port["mac_address"], "nuage_vif_id": port_mapping["nuage_vif_id"], "id": port["device_id"], } self.nuageclient.delete_vms(params) super(NuagePlugin, self).delete_port(context, id)
def create_port(self, context, port): session = context.session with session.begin(subtransactions=True): p = port["port"] port = super(NuagePlugin, self).create_port(context, port) device_owner = port.get("device_owner", None) if device_owner and device_owner not in constants.AUTO_CREATE_PORT_OWNERS: if "fixed_ips" not in port or len(port["fixed_ips"]) == 0: return self._extend_port_dict_binding(context, port) subnet_id = port["fixed_ips"][0]["subnet_id"] subnet_mapping = nuagedb.get_subnet_l2dom_by_id(session, subnet_id) if subnet_mapping: static_ip = False if attributes.is_attr_set(p["fixed_ips"]) and "ip_address" in p["fixed_ips"][0]: static_ip = True nuage_vport_id = None nuage_vif_id = None port_mapping = nuagedb.add_port_vport_mapping( session, port["id"], nuage_vport_id, nuage_vif_id, static_ip ) port_prefix = constants.NOVA_PORT_OWNER_PREF if port["device_owner"].startswith(port_prefix): # This request is coming from nova try: self._create_update_port(context, port, port_mapping, subnet_mapping) except Exception: with excutils.save_and_reraise_exception(): super(NuagePlugin, self).delete_port(context, port["id"]) return self._extend_port_dict_binding(context, port)
def delete_port(self, context, id, l3_port_check=True): if l3_port_check: self.prevent_l3_port_deletion(context, id) port = self._get_port(context, id) port_mapping = nuagedb.get_port_mapping_by_id(context.session, id) # This is required for to pass ut test_floatingip_port_delete self.disassociate_floatingips(context, id) if not port['fixed_ips']: return super(NuagePlugin, self).delete_port(context, id) sub_id = port['fixed_ips'][0]['subnet_id'] subnet_mapping = nuagedb.get_subnet_l2dom_by_id(context.session, sub_id) if not subnet_mapping: return super(NuagePlugin, self).delete_port(context, id) netpart_id = subnet_mapping['net_partition_id'] net_partition = nuagedb.get_net_partition_by_id(context.session, netpart_id) # Need to call this explicitly to delete vport_vporttag_mapping if constants.NOVA_PORT_OWNER_PREF in port['device_owner']: # This was a VM Port filters = {'device_id': [port['device_id']]} ports = self.get_ports(context, filters) params = { 'no_of_ports': len(ports), 'net_partition': net_partition, 'tenant': port['tenant_id'], 'mac': port['mac_address'], 'nuage_vif_id': port_mapping['nuage_vif_id'], 'id': port['device_id'] } self.nuageclient.delete_vms(params) super(NuagePlugin, self).delete_port(context, id)
def create_port(self, context, port): session = context.session with session.begin(subtransactions=True): port = super(NuagePlugin, self).create_port(context, port) device_owner = port.get('device_owner', None) if (device_owner and device_owner not in constants.AUTO_CREATE_PORT_OWNERS): if 'fixed_ips' not in port or len(port['fixed_ips']) == 0: return self._extend_port_dict_binding(context, port) subnet_id = port['fixed_ips'][0]['subnet_id'] subnet_mapping = nuagedb.get_subnet_l2dom_by_id( session, subnet_id) if subnet_mapping: port_prefix = constants.NOVA_PORT_OWNER_PREF if port['device_owner'].startswith(port_prefix): #This request is coming from nova try: self._create_update_port( context, port, subnet_mapping['net_partition_id'], subnet_mapping['nuage_subnet_id']) except Exception: with excutils.save_and_reraise_exception(): super(NuagePlugin, self).delete_port(context, port['id']) return self._extend_port_dict_binding(context, port)
def delete_port(self, context, id, l3_port_check=True): if l3_port_check: self.prevent_l3_port_deletion(context, id) port = self._get_port(context, id) port_mapping = nuagedb.get_port_mapping_by_id(context.session, id) # This is required for to pass ut test_floatingip_port_delete self.disassociate_floatingips(context, id) if not port['fixed_ips']: return super(NuagePlugin, self).delete_port(context, id) sub_id = port['fixed_ips'][0]['subnet_id'] subnet_mapping = nuagedb.get_subnet_l2dom_by_id( context.session, sub_id) if not subnet_mapping: return super(NuagePlugin, self).delete_port(context, id) netpart_id = subnet_mapping['net_partition_id'] net_partition = nuagedb.get_net_partition_by_id( context.session, netpart_id) # Need to call this explicitly to delete vport_vporttag_mapping if constants.NOVA_PORT_OWNER_PREF in port['device_owner']: # This was a VM Port filters = {'device_id': [port['device_id']]} ports = self.get_ports(context, filters) params = { 'no_of_ports': len(ports), 'net_partition': net_partition, 'tenant': port['tenant_id'], 'mac': port['mac_address'], 'nuage_vif_id': port_mapping['nuage_vif_id'], 'id': port['device_id'] } self.nuageclient.delete_vms(params) super(NuagePlugin, self).delete_port(context, id)
def create_port(self, context, port): session = context.session with session.begin(subtransactions=True): p = port['port'] port = super(NuagePlugin, self).create_port(context, port) device_owner = port.get('device_owner', None) if (device_owner and device_owner not in constants.AUTO_CREATE_PORT_OWNERS): if 'fixed_ips' not in port or len(port['fixed_ips']) == 0: return self._extend_port_dict_binding(context, port) subnet_id = port['fixed_ips'][0]['subnet_id'] subnet_mapping = nuagedb.get_subnet_l2dom_by_id( session, subnet_id) if subnet_mapping: static_ip = False if (attributes.is_attr_set(p['fixed_ips']) and 'ip_address' in p['fixed_ips'][0]): static_ip = True nuage_vport_id = None nuage_vif_id = None port_mapping = nuagedb.add_port_vport_mapping( session, port['id'], nuage_vport_id, nuage_vif_id, static_ip) port_prefix = constants.NOVA_PORT_OWNER_PREF if port['device_owner'].startswith(port_prefix): #This request is coming from nova try: self._create_update_port(context, port, port_mapping, subnet_mapping) except Exception: with excutils.save_and_reraise_exception(): super(NuagePlugin, self).delete_port(context, port['id']) return self._extend_port_dict_binding(context, port)
def update_port(self, context, id, port): p = port['port'] if p.get('device_owner', '').startswith(constants.NOVA_PORT_OWNER_PREF): session = context.session with session.begin(subtransactions=True): port = self._get_port(context, id) port.update(p) if 'fixed_ips' not in port or len(port['fixed_ips']) == 0: return self._make_port_dict(port) subnet_id = port['fixed_ips'][0]['subnet_id'] subnet_mapping = nuagedb.get_subnet_l2dom_by_id( session, subnet_id) if not subnet_mapping: msg = (_("Subnet %s not found on VSD") % subnet_id) raise q_exc.BadRequest(resource='port', msg=msg) port_mapping = nuagedb.get_port_mapping_by_id(session, id) if not port_mapping: msg = (_("Port-Mapping for port %s not " " found on VSD") % id) raise q_exc.BadRequest(resource='port', msg=msg) if not port_mapping['nuage_vport_id']: self._create_update_port(context, port, port_mapping, subnet_mapping) updated_port = self._make_port_dict(port) else: updated_port = super(NuagePlugin, self).update_port(context, id, port) return updated_port
def update_network(self, context, id, network): with context.session.begin(subtransactions=True): is_external_set, subnet = self._validate_update_network(context, id, network) net = super(NuagePlugin, self).update_network(context, id, network) self._process_l3_update(context, net, network['network']) if subnet and is_external_set: subn = subnet[0] subnet_l2dom = nuagedb.get_subnet_l2dom_by_id(context.session, subn['id']) if subnet_l2dom: nuage_subnet_id = subnet_l2dom['nuage_subnet_id'] nuage_l2dom_tid = subnet_l2dom['nuage_l2dom_tmplt_id'] user_id = subnet_l2dom['nuage_user_id'] group_id = subnet_l2dom['nuage_group_id'] self.nuageclient.delete_subnet(nuage_subnet_id, nuage_l2dom_tid) self.nuageclient.delete_user(user_id) self.nuageclient.delete_group(group_id) nuagedb.delete_subnetl2dom_mapping(context.session, subnet_l2dom) self._add_nuage_sharedresource(context, subnet[0], id, constants.SR_TYPE_FLOATING) return net
def create_port(self, context, port): session = context.session with session.begin(subtransactions=True): port = super(NuagePlugin, self).create_port(context, port) device_owner = port.get('device_owner', None) if (device_owner and device_owner not in constants.AUTO_CREATE_PORT_OWNERS): if 'fixed_ips' not in port or len(port['fixed_ips']) == 0: return self._extend_port_dict_binding(context, port) subnet_id = port['fixed_ips'][0]['subnet_id'] subnet_mapping = nuagedb.get_subnet_l2dom_by_id(session, subnet_id) if subnet_mapping: port_prefix = constants.NOVA_PORT_OWNER_PREF if port['device_owner'].startswith(port_prefix): #This request is coming from nova try: self._create_update_port( context, port, subnet_mapping['net_partition_id'], subnet_mapping['nuage_subnet_id']) except Exception: with excutils.save_and_reraise_exception(): super(NuagePlugin, self).delete_port( context, port['id']) return self._extend_port_dict_binding(context, port)
def update_port(self, context, id, port): p = port['port'] if p.get('device_owner', '').startswith( constants.NOVA_PORT_OWNER_PREF): session = context.session with session.begin(subtransactions=True): port = self._get_port(context, id) port.update(p) if 'fixed_ips' not in port or len(port['fixed_ips']) == 0: return self._make_port_dict(port) subnet_id = port['fixed_ips'][0]['subnet_id'] subnet_mapping = nuagedb.get_subnet_l2dom_by_id(session, subnet_id) if not subnet_mapping: msg = (_("Subnet %s not found on VSD") % subnet_id) raise n_exc.BadRequest(resource='port', msg=msg) port_mapping = nuagedb.get_port_mapping_by_id(session, id) if not port_mapping: msg = (_("Port-Mapping for port %s not " " found on VSD") % id) raise n_exc.BadRequest(resource='port', msg=msg) if not port_mapping['nuage_vport_id']: self._create_update_port(context, port, port_mapping, subnet_mapping) updated_port = self._make_port_dict(port) else: updated_port = super(NuagePlugin, self).update_port(context, id, port) return updated_port
def add_router_interface(self, context, router_id, interface_info): session = context.session with session.begin(subtransactions=True): rtr_if_info = super(NuagePlugin, self).add_router_interface(context, router_id, interface_info) subnet_id = rtr_if_info["subnet_id"] subn = self.get_subnet(context, subnet_id) rtr_zone_mapping = nuagedb.get_rtr_zone_mapping(session, router_id) ent_rtr_mapping = nuagedb.get_ent_rtr_mapping_by_rtrid(session, router_id) subnet_l2dom = nuagedb.get_subnet_l2dom_by_id(session, subnet_id) if not rtr_zone_mapping or not ent_rtr_mapping: super(NuagePlugin, self).remove_router_interface(context, router_id, interface_info) msg = ( _("Router %s does not hold default zone OR " "net_partition mapping. Router-IF add failed") % router_id ) raise q_exc.BadRequest(resource="router", msg=msg) if not subnet_l2dom: super(NuagePlugin, self).remove_router_interface(context, router_id, interface_info) msg = _("Subnet %s does not hold Nuage VSD reference. " "Router-IF add failed") % subnet_id raise q_exc.BadRequest(resource="subnet", msg=msg) if subnet_l2dom["net_partition_id"] != ent_rtr_mapping["net_partition_id"]: super(NuagePlugin, self).remove_router_interface(context, router_id, interface_info) msg = _( "Subnet %(subnet)s and Router %(router)s belong to " "different net_partition Router-IF add " "not permitted" ) % {"subnet": subnet_id, "router": router_id} raise q_exc.BadRequest(resource="subnet", msg=msg) nuage_subnet_id = subnet_l2dom["nuage_subnet_id"] nuage_l2dom_tmplt_id = subnet_l2dom["nuage_l2dom_tmplt_id"] if self.nuageclient.vms_on_l2domain(nuage_subnet_id): super(NuagePlugin, self).remove_router_interface(context, router_id, interface_info) msg = _("Subnet %s has one or more active VMs " "Router-IF add not permitted") % subnet_id raise q_exc.BadRequest(resource="subnet", msg=msg) self.nuageclient.delete_subnet(nuage_subnet_id, nuage_l2dom_tmplt_id) net = netaddr.IPNetwork(subn["cidr"]) params = {"net": net, "zone_id": rtr_zone_mapping["nuage_zone_id"]} if not attributes.is_attr_set(subn["gateway_ip"]): subn["gateway_ip"] = str(netaddr.IPAddress(net.first + 1)) try: nuage_subnet = self.nuageclient.create_domain_subnet(subn, params) except Exception: with excutils.save_and_reraise_exception(): super(NuagePlugin, self).remove_router_interface(context, router_id, interface_info) if nuage_subnet: ns_dict = {} ns_dict["nuage_subnet_id"] = nuage_subnet["nuage_subnetid"] ns_dict["nuage_l2dom_tmplt_id"] = None nuagedb.update_subnetl2dom_mapping(subnet_l2dom, ns_dict) return rtr_if_info
def delete_subnet(self, context, id): subnet = self.get_subnet(context, id) if self._network_is_external(context, subnet['network_id']): return super(NuagePlugin, self).delete_subnet(context, id) subnet_l2dom = nuagedb.get_subnet_l2dom_by_id(context.session, id) if subnet_l2dom: template_id = subnet_l2dom['nuage_l2dom_tmplt_id'] try: self.nuageclient.delete_subnet(subnet_l2dom['nuage_subnet_id'], template_id) except Exception: msg = (_('Unable to complete operation on subnet %s.' 'One or more ports have an IP allocation ' 'from this subnet.') % id) raise q_exc.BadRequest(resource='subnet', msg=msg) super(NuagePlugin, self).delete_subnet(context, id) if subnet_l2dom and not self._check_router_subnet_for_tenant(context): self.nuageclient.delete_user(subnet_l2dom['nuage_user_id']) self.nuageclient.delete_group(subnet_l2dom['nuage_group_id'])
def delete_subnet(self, context, id): subnet = self.get_subnet(context, id) if self._network_is_external(context, subnet['network_id']): return super(NuagePlugin, self).delete_subnet(context, id) subnet_l2dom = nuagedb.get_subnet_l2dom_by_id(context.session, id) if subnet_l2dom: template_id = subnet_l2dom['nuage_l2dom_tmplt_id'] try: self.nuageclient.delete_subnet(subnet_l2dom['nuage_subnet_id'], template_id) except Exception: msg = (_('Unable to complete operation on subnet %s.' 'One or more ports have an IP allocation ' 'from this subnet.') % id) raise n_exc.BadRequest(resource='subnet', msg=msg) super(NuagePlugin, self).delete_subnet(context, id) if subnet_l2dom and not self._check_router_subnet_for_tenant(context): self.nuageclient.delete_user(subnet_l2dom['nuage_user_id']) self.nuageclient.delete_group(subnet_l2dom['nuage_group_id'])
def remove_router_interface(self, context, router_id, interface_info): if 'subnet_id' in interface_info: subnet_id = interface_info['subnet_id'] subnet = self.get_subnet(context, subnet_id) found = False try: filters = { 'device_id': [router_id], 'device_owner': [os_constants.DEVICE_OWNER_ROUTER_INTF], 'network_id': [subnet['network_id']] } ports = self.get_ports(context, filters) for p in ports: if p['fixed_ips'][0]['subnet_id'] == subnet_id: found = True break except exc.NoResultFound: msg = (_("No router interface found for Router %s. " "Router-IF delete failed") % router_id) raise q_exc.BadRequest(resource='router', msg=msg) if not found: msg = (_("No router interface found for Router %s. " "Router-IF delete failed") % router_id) raise q_exc.BadRequest(resource='router', msg=msg) elif 'port_id' in interface_info: port_db = self._get_port(context, interface_info['port_id']) if not port_db: msg = (_("No router interface found for Router %s. " "Router-IF delete failed") % router_id) raise q_exc.BadRequest(resource='router', msg=msg) subnet_id = port_db['fixed_ips'][0]['subnet_id'] session = context.session with session.begin(subtransactions=True): subnet_l2dom = nuagedb.get_subnet_l2dom_by_id(session, subnet_id) nuage_subn_id = subnet_l2dom['nuage_subnet_id'] if self.nuageclient.vms_on_l2domain(nuage_subn_id): msg = (_("Subnet %s has one or more active VMs " "Router-IF delete not permitted") % subnet_id) raise q_exc.BadRequest(resource='subnet', msg=msg) neutron_subnet = self.get_subnet(context, subnet_id) ent_rtr_mapping = nuagedb.get_ent_rtr_mapping_by_rtrid( context.session, router_id) if not ent_rtr_mapping: msg = (_("Router %s does not hold net_partition " "assoc on Nuage VSD. Router-IF delete failed") % router_id) raise q_exc.BadRequest(resource='router', msg=msg) net = netaddr.IPNetwork(neutron_subnet['cidr']) net_part_id = ent_rtr_mapping['net_partition_id'] net_partition = self.get_net_partition(context, net_part_id) params = { 'net_partition': net_partition, 'tenant_id': neutron_subnet['tenant_id'], 'net': net } nuage_subnet = self.nuageclient.create_subnet( neutron_subnet, params) self.nuageclient.delete_domain_subnet(nuage_subn_id) info = super(NuagePlugin, self).remove_router_interface(context, router_id, interface_info) if nuage_subnet: tmplt_id = str(nuage_subnet['nuage_l2template_id']) ns_dict = {} ns_dict['nuage_subnet_id'] = nuage_subnet['nuage_l2domain_id'] ns_dict['nuage_l2dom_tmplt_id'] = tmplt_id nuagedb.update_subnetl2dom_mapping(subnet_l2dom, ns_dict) return info
def add_router_interface(self, context, router_id, interface_info): session = context.session with session.begin(subtransactions=True): rtr_if_info = super(NuagePlugin, self).add_router_interface( context, router_id, interface_info) subnet_id = rtr_if_info['subnet_id'] subn = self.get_subnet(context, subnet_id) rtr_zone_mapping = nuagedb.get_rtr_zone_mapping(session, router_id) ent_rtr_mapping = nuagedb.get_ent_rtr_mapping_by_rtrid( session, router_id) subnet_l2dom = nuagedb.get_subnet_l2dom_by_id(session, subnet_id) if not rtr_zone_mapping or not ent_rtr_mapping: super(NuagePlugin, self).remove_router_interface(context, router_id, interface_info) msg = (_("Router %s does not hold default zone OR " "net_partition mapping. Router-IF add failed") % router_id) raise q_exc.BadRequest(resource='router', msg=msg) if not subnet_l2dom: super(NuagePlugin, self).remove_router_interface(context, router_id, interface_info) msg = (_("Subnet %s does not hold Nuage VSD reference. " "Router-IF add failed") % subnet_id) raise q_exc.BadRequest(resource='subnet', msg=msg) if (subnet_l2dom['net_partition_id'] != ent_rtr_mapping['net_partition_id']): super(NuagePlugin, self).remove_router_interface(context, router_id, interface_info) msg = (_("Subnet %(subnet)s and Router %(router)s belong to " "different net_partition Router-IF add " "not permitted") % { 'subnet': subnet_id, 'router': router_id }) raise q_exc.BadRequest(resource='subnet', msg=msg) nuage_subnet_id = subnet_l2dom['nuage_subnet_id'] nuage_l2dom_tmplt_id = subnet_l2dom['nuage_l2dom_tmplt_id'] if self.nuageclient.vms_on_l2domain(nuage_subnet_id): super(NuagePlugin, self).remove_router_interface(context, router_id, interface_info) msg = (_("Subnet %s has one or more active VMs " "Router-IF add not permitted") % subnet_id) raise q_exc.BadRequest(resource='subnet', msg=msg) self.nuageclient.delete_subnet(nuage_subnet_id, nuage_l2dom_tmplt_id) net = netaddr.IPNetwork(subn['cidr']) params = {'net': net, 'zone_id': rtr_zone_mapping['nuage_zone_id']} if not attributes.is_attr_set(subn['gateway_ip']): subn['gateway_ip'] = str(netaddr.IPAddress(net.first + 1)) try: nuage_subnet = self.nuageclient.create_domain_subnet( subn, params) except Exception: with excutils.save_and_reraise_exception(): super(NuagePlugin, self).remove_router_interface( context, router_id, interface_info) if nuage_subnet: ns_dict = {} ns_dict['nuage_subnet_id'] = nuage_subnet['nuage_subnetid'] ns_dict['nuage_l2dom_tmplt_id'] = None nuagedb.update_subnetl2dom_mapping(subnet_l2dom, ns_dict) return rtr_if_info
def remove_router_interface(self, context, router_id, interface_info): if 'subnet_id' in interface_info: subnet_id = interface_info['subnet_id'] subnet = self.get_subnet(context, subnet_id) found = False try: filters = {'device_id': [router_id], 'device_owner': [os_constants.DEVICE_OWNER_ROUTER_INTF], 'network_id': [subnet['network_id']]} ports = self.get_ports(context, filters) for p in ports: if p['fixed_ips'][0]['subnet_id'] == subnet_id: found = True break except exc.NoResultFound: msg = (_("No router interface found for Router %s. " "Router-IF delete failed") % router_id) raise n_exc.BadRequest(resource='router', msg=msg) if not found: msg = (_("No router interface found for Router %s. " "Router-IF delete failed") % router_id) raise n_exc.BadRequest(resource='router', msg=msg) elif 'port_id' in interface_info: port_db = self._get_port(context, interface_info['port_id']) if not port_db: msg = (_("No router interface found for Router %s. " "Router-IF delete failed") % router_id) raise n_exc.BadRequest(resource='router', msg=msg) subnet_id = port_db['fixed_ips'][0]['subnet_id'] session = context.session with session.begin(subtransactions=True): subnet_l2dom = nuagedb.get_subnet_l2dom_by_id(session, subnet_id) if not subnet_l2dom: return super(NuagePlugin, self).remove_router_interface(context, router_id, interface_info) nuage_subn_id = subnet_l2dom['nuage_subnet_id'] if self.nuageclient.vms_on_l2domain(nuage_subn_id): msg = (_("Subnet %s has one or more active VMs " "Router-IF delete not permitted") % subnet_id) raise n_exc.BadRequest(resource='subnet', msg=msg) neutron_subnet = self.get_subnet(context, subnet_id) ent_rtr_mapping = nuagedb.get_ent_rtr_mapping_by_rtrid( context.session, router_id) if not ent_rtr_mapping: msg = (_("Router %s does not hold net_partition " "assoc on Nuage VSD. Router-IF delete failed") % router_id) raise n_exc.BadRequest(resource='router', msg=msg) net = netaddr.IPNetwork(neutron_subnet['cidr']) net_part_id = ent_rtr_mapping['net_partition_id'] net_partition = self.get_net_partition(context, net_part_id) params = { 'net_partition': net_partition, 'tenant_id': neutron_subnet['tenant_id'], 'net': net } nuage_subnet = self.nuageclient.create_subnet(neutron_subnet, params) self.nuageclient.delete_domain_subnet(nuage_subn_id) info = super(NuagePlugin, self).remove_router_interface(context, router_id, interface_info) if nuage_subnet: tmplt_id = str(nuage_subnet['nuage_l2template_id']) ns_dict = {} ns_dict['nuage_subnet_id'] = nuage_subnet['nuage_l2domain_id'] ns_dict['nuage_l2dom_tmplt_id'] = tmplt_id nuagedb.update_subnetl2dom_mapping(subnet_l2dom, ns_dict) return info