¡@

Home 

OpenStack Study: midonet_lib.py

OpenStack Index

**** CubicPower OpenStack Study ****

# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright (C) 2012 Midokura Japan K.K.

# Copyright (C) 2013 Midokura PTE LTD

# All Rights Reserved.

#

# Licensed under the Apache License, Version 2.0 (the "License"); you may

# not use this file except in compliance with the License. You may obtain

# a copy of the License at

#

# http://www.apache.org/licenses/LICENSE-2.0

#

# Unless required by applicable law or agreed to in writing, software

# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT

# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the

# License for the specific language governing permissions and limitations

# under the License.

#

# @author: Tomoe Sugihara, Midokura Japan KK

# @author: Ryu Ishimoto, Midokura Japan KK

# @author: Rossella Sblendido, Midokura Japan KK

# @author: Duarte Nunes, Midokura Japan KK

from midonetclient import exc

from webob import exc as w_exc

from neutron.common import exceptions as n_exc

from neutron.openstack.common import log as logging

from neutron.plugins.midonet.common import net_util

LOG = logging.getLogger(__name__)

**** CubicPower OpenStack Study ****

def handle_api_error(fn):

    """Wrapper for methods that throws custom exceptions."""

    def wrapped(*args, **kwargs):

        try:

            return fn(*args, **kwargs)

        except (w_exc.HTTPException,

                exc.MidoApiConnectionError) as ex:

            raise MidonetApiException(msg=ex)

    return wrapped

**** CubicPower OpenStack Study ****

 def wrapped(*args, **kwargs):

        try:

            return fn(*args, **kwargs)

        except (w_exc.HTTPException,

                exc.MidoApiConnectionError) as ex:

            raise MidonetApiException(msg=ex)

    return wrapped

**** CubicPower OpenStack Study ****

class MidonetResourceNotFound(n_exc.NotFound):

message = _('MidoNet %(resource_type)s %(id)s could not be found')

**** CubicPower OpenStack Study ****

class MidonetApiException(n_exc.NeutronException):

message = _("MidoNet API error: %(msg)s")

**** CubicPower OpenStack Study ****

class MidoClient:

**** CubicPower OpenStack Study ****

    def __init__(self, mido_api):

        self.mido_api = mido_api

    @classmethod

**** CubicPower OpenStack Study ****

    def _fill_dto(cls, dto, fields):

        for field_name, field_value in fields.iteritems():

            # We assume the setters are named the

            # same way as the attributes themselves.

            try:

                getattr(dto, field_name)(field_value)

            except AttributeError:

                pass

        return dto

    @classmethod

**** CubicPower OpenStack Study ****

    def _create_dto(cls, dto, fields):

        return cls._fill_dto(dto, fields).create()

    @classmethod

**** CubicPower OpenStack Study ****

    def _update_dto(cls, dto, fields):

        return cls._fill_dto(dto, fields).update()

    @handle_api_error

**** CubicPower OpenStack Study ****

    def create_bridge(self, **kwargs):

        """Create a new bridge

        :param \**kwargs: configuration of the new bridge

        :returns: newly created bridge

        """

        LOG.debug(_("MidoClient.create_bridge called: "

                    "kwargs=%(kwargs)s"), {'kwargs': kwargs})

        return self._create_dto(self.mido_api.add_bridge(), kwargs)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def delete_bridge(self, id):

        """Delete a bridge

        :param id: id of the bridge

        """

        LOG.debug(_("MidoClient.delete_bridge called: id=%(id)s"), {'id': id})

        return self.mido_api.delete_bridge(id)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def get_bridge(self, id):

        """Get a bridge

        :param id: id of the bridge

        :returns: requested bridge. None if bridge does not exist.

        """

        LOG.debug(_("MidoClient.get_bridge called: id=%s"), id)

        try:

            return self.mido_api.get_bridge(id)

        except w_exc.HTTPNotFound:

            raise MidonetResourceNotFound(resource_type='Bridge', id=id)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def update_bridge(self, id, **kwargs):

        """Update a bridge of the given id with the new fields

        :param id: id of the bridge

        :param \**kwargs: the fields to update and their values

        :returns: bridge object

        """

        LOG.debug(_("MidoClient.update_bridge called: "

                    "id=%(id)s, kwargs=%(kwargs)s"),

                  {'id': id, 'kwargs': kwargs})

        try:

            return self._update_dto(self.mido_api.get_bridge(id), kwargs)

        except w_exc.HTTPNotFound:

            raise MidonetResourceNotFound(resource_type='Bridge', id=id)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def create_dhcp(self, bridge, gateway_ip, cidr, host_rts=None,

                    dns_servers=None):

        """Create a new DHCP entry

        :param bridge: bridge object to add dhcp to

        :param gateway_ip: IP address of gateway

        :param cidr: subnet represented as x.x.x.x/y

        :param host_rts: list of routes set in the host

        :param dns_servers: list of dns servers

        :returns: newly created dhcp

        """

        LOG.debug(_("MidoClient.create_dhcp called: bridge=%(bridge)s, "

                    "cidr=%(cidr)s, gateway_ip=%(gateway_ip)s, "

                    "host_rts=%(host_rts)s, dns_servers=%(dns_servers)s"),

                  {'bridge': bridge, 'cidr': cidr, 'gateway_ip': gateway_ip,

                   'host_rts': host_rts, 'dns_servers': dns_servers})

        self.mido_api.add_bridge_dhcp(bridge, gateway_ip, cidr,

                                      host_rts=host_rts,

                                      dns_nservers=dns_servers)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def add_dhcp_host(self, bridge, cidr, ip, mac):

        """Add DHCP host entry

        :param bridge: bridge the DHCP is configured for

        :param cidr: subnet represented as x.x.x.x/y

        :param ip: IP address

        :param mac: MAC address

        """

        LOG.debug(_("MidoClient.add_dhcp_host called: bridge=%(bridge)s, "

                    "cidr=%(cidr)s, ip=%(ip)s, mac=%(mac)s"),

                  {'bridge': bridge, 'cidr': cidr, 'ip': ip, 'mac': mac})

        subnet = bridge.get_dhcp_subnet(net_util.subnet_str(cidr))

        if subnet is None:

            raise MidonetApiException(msg=_("Tried to add to"

                                            "non-existent DHCP"))

        subnet.add_dhcp_host().ip_addr(ip).mac_addr(mac).create()

    @handle_api_error

**** CubicPower OpenStack Study ****

    def remove_dhcp_host(self, bridge, cidr, ip, mac):

        """Remove DHCP host entry

        :param bridge: bridge the DHCP is configured for

        :param cidr: subnet represented as x.x.x.x/y

        :param ip: IP address

        :param mac: MAC address

        """

        LOG.debug(_("MidoClient.remove_dhcp_host called: bridge=%(bridge)s, "

                    "cidr=%(cidr)s, ip=%(ip)s, mac=%(mac)s"),

                  {'bridge': bridge, 'cidr': cidr, 'ip': ip, 'mac': mac})

        subnet = bridge.get_dhcp_subnet(net_util.subnet_str(cidr))

        if subnet is None:

            LOG.warn(_("Tried to delete mapping from non-existent subnet"))

            return

        for dh in subnet.get_dhcp_hosts():

            if dh.get_mac_addr() == mac and dh.get_ip_addr() == ip:

                LOG.debug(_("MidoClient.remove_dhcp_host: Deleting %(dh)r"),

                          {"dh": dh})

                dh.delete()

    @handle_api_error

**** CubicPower OpenStack Study ****

    def delete_dhcp_host(self, bridge_id, cidr, ip, mac):

        """Delete DHCP host entry

        :param bridge_id: id of the bridge of the DHCP

        :param cidr: subnet represented as x.x.x.x/y

        :param ip: IP address

        :param mac: MAC address

        """

        LOG.debug(_("MidoClient.delete_dhcp_host called: "

                    "bridge_id=%(bridge_id)s, cidr=%(cidr)s, ip=%(ip)s, "

                    "mac=%(mac)s"), {'bridge_id': bridge_id,

                                     'cidr': cidr,

                                     'ip': ip, 'mac': mac})

        bridge = self.get_bridge(bridge_id)

        self.remove_dhcp_host(bridge, net_util.subnet_str(cidr), ip, mac)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def delete_dhcp(self, bridge, cidr):

        """Delete a DHCP entry

        :param bridge: bridge to remove DHCP from

        :param cidr: subnet represented as x.x.x.x/y

        """

        LOG.debug(_("MidoClient.delete_dhcp called: bridge=%(bridge)s, "

                    "cidr=%(cidr)s"),

                  {'bridge': bridge, 'cidr': cidr})

        dhcp_subnets = bridge.get_dhcp_subnets()

        net_addr, net_len = net_util.net_addr(cidr)

        if not dhcp_subnets:

            raise MidonetApiException(

                msg=_("Tried to delete non-existent DHCP"))

        for dhcp in dhcp_subnets:

            if dhcp.get_subnet_prefix() == net_addr:

                dhcp.delete()

                break

    @handle_api_error

**** CubicPower OpenStack Study ****

    def delete_port(self, id, delete_chains=False):

        """Delete a port

        :param id: id of the port

        """

        LOG.debug(_("MidoClient.delete_port called: id=%(id)s, "

                    "delete_chains=%(delete_chains)s"),

                  {'id': id, 'delete_chains': delete_chains})

        if delete_chains:

            self.delete_port_chains(id)

        self.mido_api.delete_port(id)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def get_port(self, id):

        """Get a port

        :param id: id of the port

        :returns: requested port. None if it does not exist

        """

        LOG.debug(_("MidoClient.get_port called: id=%(id)s"), {'id': id})

        try:

            return self.mido_api.get_port(id)

        except w_exc.HTTPNotFound:

            raise MidonetResourceNotFound(resource_type='Port', id=id)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def add_bridge_port(self, bridge, **kwargs):

        """Add a port on a bridge

        :param bridge: bridge to add a new port to

        :param \**kwargs: configuration of the new port

        :returns: newly created port

        """

        LOG.debug(_("MidoClient.add_bridge_port called: "

                    "bridge=%(bridge)s, kwargs=%(kwargs)s"),

                  {'bridge': bridge, 'kwargs': kwargs})

        return self._create_dto(self.mido_api.add_bridge_port(bridge), kwargs)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def update_port(self, id, **kwargs):

        """Update a port of the given id with the new fields

        :param id: id of the port

        :param \**kwargs: the fields to update and their values

        """

        LOG.debug(_("MidoClient.update_port called: "

                    "id=%(id)s, kwargs=%(kwargs)s"),

                  {'id': id, 'kwargs': kwargs})

        try:

            return self._update_dto(self.mido_api.get_port(id), kwargs)

        except w_exc.HTTPNotFound:

            raise MidonetResourceNotFound(resource_type='Port', id=id)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def add_router_port(self, router, **kwargs):

        """Add a new port to an existing router.

        :param router: router to add a new port to

        :param \**kwargs: configuration of the new port

        :returns: newly created port

        """

        return self._create_dto(self.mido_api.add_router_port(router), kwargs)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def create_router(self, **kwargs):

        """Create a new router

        :param \**kwargs: configuration of the new router

        :returns: newly created router

        """

        LOG.debug(_("MidoClient.create_router called: "

                    "kwargs=%(kwargs)s"), {'kwargs': kwargs})

        return self._create_dto(self.mido_api.add_router(), kwargs)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def delete_router(self, id):

        """Delete a router

        :param id: id of the router

        """

        LOG.debug(_("MidoClient.delete_router called: id=%(id)s"), {'id': id})

        return self.mido_api.delete_router(id)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def get_router(self, id):

        """Get a router with the given id

        :param id: id of the router

        :returns: requested router object.  None if it does not exist.

        """

        LOG.debug(_("MidoClient.get_router called: id=%(id)s"), {'id': id})

        try:

            return self.mido_api.get_router(id)

        except w_exc.HTTPNotFound:

            raise MidonetResourceNotFound(resource_type='Router', id=id)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def update_router(self, id, **kwargs):

        """Update a router of the given id with the new name

        :param id: id of the router

        :param \**kwargs: the fields to update and their values

        :returns: router object

        """

        LOG.debug(_("MidoClient.update_router called: "

                    "id=%(id)s, kwargs=%(kwargs)s"),

                  {'id': id, 'kwargs': kwargs})

        try:

            return self._update_dto(self.mido_api.get_router(id), kwargs)

        except w_exc.HTTPNotFound:

            raise MidonetResourceNotFound(resource_type='Router', id=id)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def delete_route(self, id):

        return self.mido_api.delete_route(id)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def add_dhcp_route_option(self, bridge, cidr, gw_ip, dst_ip):

        """Add Option121 route to subnet

        :param bridge: Bridge to add the option route to

        :param cidr: subnet represented as x.x.x.x/y

        :param gw_ip: IP address of the next hop

        :param dst_ip: IP address of the destination, in x.x.x.x/y format

        """

        LOG.debug(_("MidoClient.add_dhcp_route_option called: "

                    "bridge=%(bridge)s, cidr=%(cidr)s, gw_ip=%(gw_ip)s"

                    "dst_ip=%(dst_ip)s"),

                  {"bridge": bridge, "cidr": cidr, "gw_ip": gw_ip,

                   "dst_ip": dst_ip})

        subnet = bridge.get_dhcp_subnet(net_util.subnet_str(cidr))

        if subnet is None:

            raise MidonetApiException(

                msg=_("Tried to access non-existent DHCP"))

        prefix, length = dst_ip.split("/")

        routes = [{'destinationPrefix': prefix, 'destinationLength': length,

                   'gatewayAddr': gw_ip}]

        cur_routes = subnet.get_opt121_routes()

        if cur_routes:

            routes = routes + cur_routes

        subnet.opt121_routes(routes).update()

    @handle_api_error

**** CubicPower OpenStack Study ****

    def link(self, port, peer_id):

        """Link a port to a given peerId."""

        self.mido_api.link(port, peer_id)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def delete_port_routes(self, routes, port_id):

        """Remove routes whose next hop port is the given port ID."""

        for route in routes:

            if route.get_next_hop_port() == port_id:

                self.mido_api.delete_route(route.get_id())

    @handle_api_error

**** CubicPower OpenStack Study ****

    def get_router_routes(self, router_id):

        """Get all routes for the given router."""

        return self.mido_api.get_router_routes(router_id)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def unlink(self, port):

        """Unlink a port

        :param port: port object

        """

        LOG.debug(_("MidoClient.unlink called: port=%(port)s"),

                  {'port': port})

        if port.get_peer_id():

            self.mido_api.unlink(port)

        else:

            LOG.warn(_("Attempted to unlink a port that was not linked. %s"),

                     port.get_id())

    @handle_api_error

**** CubicPower OpenStack Study ****

    def remove_rules_by_property(self, tenant_id, chain_name, key, value):

        """Remove all the rules that match the provided key and value."""

        LOG.debug(_("MidoClient.remove_rules_by_property called: "

                    "tenant_id=%(tenant_id)s, chain_name=%(chain_name)s"

                    "key=%(key)s, value=%(value)s"),

                  {'tenant_id': tenant_id, 'chain_name': chain_name,

                   'key': key, 'value': value})

        chain = self.get_chain_by_name(tenant_id, chain_name)

        if chain is None:

            raise MidonetResourceNotFound(resource_type='Chain',

                                          id=chain_name)

        for r in chain.get_rules():

            if key in r.get_properties():

                if r.get_properties()[key] == value:

                    self.mido_api.delete_rule(r.get_id())

    @handle_api_error

**** CubicPower OpenStack Study ****

    def add_router_chains(self, router, inbound_chain_name,

                          outbound_chain_name):

        """Create chains for a new router.

        Creates inbound and outbound chains for the router with the given

        names, and the new chains are set on the router.

        :param router: router to set chains for

        :param inbound_chain_name: Name of the inbound chain

        :param outbound_chain_name: Name of the outbound chain

        """

        LOG.debug(_("MidoClient.create_router_chains called: "

                    "router=%(router)s, inbound_chain_name=%(in_chain)s, "

                    "outbound_chain_name=%(out_chain)s"),

                  {"router": router, "in_chain": inbound_chain_name,

                   "out_chain": outbound_chain_name})

        tenant_id = router.get_tenant_id()

        inbound_chain = self.mido_api.add_chain().tenant_id(tenant_id).name(

            inbound_chain_name,).create()

        outbound_chain = self.mido_api.add_chain().tenant_id(tenant_id).name(

            outbound_chain_name).create()

        # set chains to in/out filters

        router.inbound_filter_id(inbound_chain.get_id()).outbound_filter_id(

            outbound_chain.get_id()).update()

        return inbound_chain, outbound_chain

    @handle_api_error

**** CubicPower OpenStack Study ****

    def delete_router_chains(self, id):

        """Deletes chains of a router.

        :param id: router ID to delete chains of

        """

        LOG.debug(_("MidoClient.delete_router_chains called: "

                    "id=%(id)s"), {'id': id})

        router = self.get_router(id)

        if (router.get_inbound_filter_id()):

            self.mido_api.delete_chain(router.get_inbound_filter_id())

        if (router.get_outbound_filter_id()):

            self.mido_api.delete_chain(router.get_outbound_filter_id())

    @handle_api_error

**** CubicPower OpenStack Study ****

    def delete_port_chains(self, id):

        """Deletes chains of a port.

        :param id: port ID to delete chains of

        """

        LOG.debug(_("MidoClient.delete_port_chains called: "

                    "id=%(id)s"), {'id': id})

        port = self.get_port(id)

        if (port.get_inbound_filter_id()):

            self.mido_api.delete_chain(port.get_inbound_filter_id())

        if (port.get_outbound_filter_id()):

            self.mido_api.delete_chain(port.get_outbound_filter_id())

    @handle_api_error

**** CubicPower OpenStack Study ****

    def get_link_port(self, router, peer_router_id):

        """Setup a route on the router to the next hop router."""

        LOG.debug(_("MidoClient.get_link_port called: "

                    "router=%(router)s, peer_router_id=%(peer_router_id)s"),

                  {'router': router, 'peer_router_id': peer_router_id})

        # Find the port linked between the two routers

        link_port = None

        for p in router.get_peer_ports():

            if p.get_device_id() == peer_router_id:

                link_port = p

                break

        return link_port

    @handle_api_error

**** CubicPower OpenStack Study ****

    def add_router_route(self, router, type='Normal',

                         src_network_addr=None, src_network_length=None,

                         dst_network_addr=None, dst_network_length=None,

                         next_hop_port=None, next_hop_gateway=None,

                         weight=100):

        """Setup a route on the router."""

        return self.mido_api.add_router_route(

            router, type=type, src_network_addr=src_network_addr,

            src_network_length=src_network_length,

            dst_network_addr=dst_network_addr,

            dst_network_length=dst_network_length,

            next_hop_port=next_hop_port, next_hop_gateway=next_hop_gateway,

            weight=weight)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def add_static_nat(self, tenant_id, chain_name, from_ip, to_ip, port_id,

                       nat_type='dnat', **kwargs):

        """Add a static NAT entry

        :param tenant_id: owner fo the chain to add a NAT to

        :param chain_name: name of the chain to add a NAT to

        :param from_ip: IP to translate from

        :param from_ip: IP to translate from

        :param to_ip: IP to translate to

        :param port_id: port to match on

        :param nat_type: 'dnat' or 'snat'

        """

        LOG.debug(_("MidoClient.add_static_nat called: "

                    "tenant_id=%(tenant_id)s, chain_name=%(chain_name)s, "

                    "from_ip=%(from_ip)s, to_ip=%(to_ip)s, "

                    "port_id=%(port_id)s, nat_type=%(nat_type)s"),

                  {'tenant_id': tenant_id, 'chain_name': chain_name,

                   'from_ip': from_ip, 'to_ip': to_ip,

                   'portid': port_id, 'nat_type': nat_type})

        if nat_type not in ['dnat', 'snat']:

            raise ValueError(_("Invalid NAT type passed in %s") % nat_type)

        chain = self.get_chain_by_name(tenant_id, chain_name)

        nat_targets = []

        nat_targets.append(

            {'addressFrom': to_ip, 'addressTo': to_ip,

             'portFrom': 0, 'portTo': 0})

        rule = chain.add_rule().type(nat_type).flow_action('accept').position(

            1).nat_targets(nat_targets).properties(kwargs)

        if nat_type == 'dnat':

            rule = rule.nw_dst_address(from_ip).nw_dst_length(32).in_ports(

                [port_id])

        else:

            rule = rule.nw_src_address(from_ip).nw_src_length(32).out_ports(

                [port_id])

        return rule.create()

    @handle_api_error

**** CubicPower OpenStack Study ****

    def add_dynamic_snat(self, tenant_id, pre_chain_name, post_chain_name,

                         snat_ip, port_id, **kwargs):

        """Add SNAT masquerading rule

        MidoNet requires two rules on the router, one to do NAT to a range of

        ports, and another to retrieve back the original IP in the return

        flow.

        """

        pre_chain = self.get_chain_by_name(tenant_id, pre_chain_name)

        post_chain = self.get_chain_by_name(tenant_id, post_chain_name)

        pre_chain.add_rule().nw_dst_address(snat_ip).nw_dst_length(

            32).type('rev_snat').flow_action('accept').in_ports(

                [port_id]).properties(kwargs).position(1).create()

        nat_targets = []

        nat_targets.append(

            {'addressFrom': snat_ip, 'addressTo': snat_ip,

             'portFrom': 1, 'portTo': 65535})

        post_chain.add_rule().type('snat').flow_action(

            'accept').nat_targets(nat_targets).out_ports(

                [port_id]).properties(kwargs).position(1).create()

    @handle_api_error

**** CubicPower OpenStack Study ****

    def remove_static_route(self, router, ip):

        """Remove static route for the IP

        :param router: next hop router to remove the routes to

        :param ip: IP address of the route to remove

        """

        LOG.debug(_("MidoClient.remote_static_route called: "

                    "router=%(router)s, ip=%(ip)s"),

                  {'router': router, 'ip': ip})

        for r in router.get_routes():

            if (r.get_dst_network_addr() == ip and

                    r.get_dst_network_length() == 32):

                self.mido_api.delete_route(r.get_id())

    @handle_api_error

**** CubicPower OpenStack Study ****

    def update_port_chains(self, port, inbound_chain_id, outbound_chain_id):

        """Bind inbound and outbound chains to the port."""

        LOG.debug(_("MidoClient.update_port_chains called: port=%(port)s"

                    "inbound_chain_id=%(inbound_chain_id)s, "

                    "outbound_chain_id=%(outbound_chain_id)s"),

                  {"port": port, "inbound_chain_id": inbound_chain_id,

                   "outbound_chain_id": outbound_chain_id})

        port.inbound_filter_id(inbound_chain_id).outbound_filter_id(

            outbound_chain_id).update()

    @handle_api_error

**** CubicPower OpenStack Study ****

    def create_chain(self, tenant_id, name):

        """Create a new chain."""

        LOG.debug(_("MidoClient.create_chain called: tenant_id=%(tenant_id)s "

                    " name=%(name)s"), {"tenant_id": tenant_id, "name": name})

        return self.mido_api.add_chain().tenant_id(tenant_id).name(

            name).create()

    @handle_api_error

**** CubicPower OpenStack Study ****

    def delete_chain(self, id):

        """Delete chain matching the ID."""

        LOG.debug(_("MidoClient.delete_chain called: id=%(id)s"), {"id": id})

        self.mido_api.delete_chain(id)

    @handle_api_error

**** CubicPower OpenStack Study ****

    def delete_chains_by_names(self, tenant_id, names):

        """Delete chains matching the names given for a tenant."""

        LOG.debug(_("MidoClient.delete_chains_by_names called: "

                    "tenant_id=%(tenant_id)s names=%(names)s "),

                  {"tenant_id": tenant_id, "names": names})

        chains = self.mido_api.get_chains({'tenant_id': tenant_id})

        for c in chains:

            if c.get_name() in names:

                self.mido_api.delete_chain(c.get_id())

    @handle_api_error

**** CubicPower OpenStack Study ****

    def get_chain_by_name(self, tenant_id, name):

        """Get the chain by its name."""

        LOG.debug(_("MidoClient.get_chain_by_name called: "

                    "tenant_id=%(tenant_id)s name=%(name)s "),

                  {"tenant_id": tenant_id, "name": name})

        for c in self.mido_api.get_chains({'tenant_id': tenant_id}):

            if c.get_name() == name:

                return c

        return None

    @handle_api_error

**** CubicPower OpenStack Study ****

    def get_port_group_by_name(self, tenant_id, name):

        """Get the port group by name."""

        LOG.debug(_("MidoClient.get_port_group_by_name called: "

                    "tenant_id=%(tenant_id)s name=%(name)s "),

                  {"tenant_id": tenant_id, "name": name})

        for p in self.mido_api.get_port_groups({'tenant_id': tenant_id}):

            if p.get_name() == name:

                return p

        return None

    @handle_api_error

**** CubicPower OpenStack Study ****

    def create_port_group(self, tenant_id, name):

        """Create a port group

        Create a new port group for a given name and ID.

        """

        LOG.debug(_("MidoClient.create_port_group called: "

                    "tenant_id=%(tenant_id)s name=%(name)s"),

                  {"tenant_id": tenant_id, "name": name})

        return self.mido_api.add_port_group().tenant_id(tenant_id).name(

            name).create()

    @handle_api_error

**** CubicPower OpenStack Study ****

    def delete_port_group_by_name(self, tenant_id, name):

        """Delete port group matching the name given for a tenant."""

        LOG.debug(_("MidoClient.delete_port_group_by_name called: "

                    "tenant_id=%(tenant_id)s name=%(name)s "),

                  {"tenant_id": tenant_id, "name": name})

        pgs = self.mido_api.get_port_groups({'tenant_id': tenant_id})

        for pg in pgs:

            if pg.get_name() == name:

                LOG.debug(_("Deleting pg %(id)s"), {"id": pg.get_id()})

                self.mido_api.delete_port_group(pg.get_id())

    @handle_api_error

**** CubicPower OpenStack Study ****

    def add_port_to_port_group_by_name(self, tenant_id, name, port_id):

        """Add a port to a port group with the given name."""

        LOG.debug(_("MidoClient.add_port_to_port_group_by_name called: "

                    "tenant_id=%(tenant_id)s name=%(name)s "

                    "port_id=%(port_id)s"),

                  {"tenant_id": tenant_id, "name": name, "port_id": port_id})

        pg = self.get_port_group_by_name(tenant_id, name)

        if pg is None:

            raise MidonetResourceNotFound(resource_type='PortGroup', id=name)

        pg = pg.add_port_group_port().port_id(port_id).create()

        return pg

    @handle_api_error

**** CubicPower OpenStack Study ****

    def remove_port_from_port_groups(self, port_id):

        """Remove a port binding from all the port groups."""

        LOG.debug(_("MidoClient.remove_port_from_port_groups called: "

                    "port_id=%(port_id)s"), {"port_id": port_id})

        port = self.get_port(port_id)

        for pg in port.get_port_groups():

            pg.delete()

    @handle_api_error

**** CubicPower OpenStack Study ****

    def add_chain_rule(self, chain, action='accept', **kwargs):

        """Create a new accept chain rule."""

        self.mido_api.add_chain_rule(chain, action, **kwargs)