1c4f0ff13c2ff05ead8e64d19553345cb1dbe5aa
[openstack-build/neutron-build.git] / neutron / extensions / l3.py
1 # Copyright 2012 VMware, Inc.
2 # All rights reserved.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15
16 import abc
17
18 from oslo_config import cfg
19
20 from neutron._i18n import _
21 from neutron.api import extensions
22 from neutron.api.v2 import attributes as attr
23 from neutron.api.v2 import resource_helper
24 from neutron.common import exceptions as nexception
25 from neutron.plugins.common import constants
26
27
28 # L3 Exceptions
29 class RouterNotFound(nexception.NotFound):
30     message = _("Router %(router_id)s could not be found")
31
32
33 class RouterInUse(nexception.InUse):
34     message = _("Router %(router_id)s %(reason)s")
35
36     def __init__(self, **kwargs):
37         if 'reason' not in kwargs:
38             kwargs['reason'] = "still has ports"
39         super(RouterInUse, self).__init__(**kwargs)
40
41
42 class RouterInterfaceNotFound(nexception.NotFound):
43     message = _("Router %(router_id)s does not have "
44                 "an interface with id %(port_id)s")
45
46
47 class RouterInterfaceNotFoundForSubnet(nexception.NotFound):
48     message = _("Router %(router_id)s has no interface "
49                 "on subnet %(subnet_id)s")
50
51
52 class RouterInterfaceInUseByFloatingIP(nexception.InUse):
53     message = _("Router interface for subnet %(subnet_id)s on router "
54                 "%(router_id)s cannot be deleted, as it is required "
55                 "by one or more floating IPs.")
56
57
58 class FloatingIPNotFound(nexception.NotFound):
59     message = _("Floating IP %(floatingip_id)s could not be found")
60
61
62 class ExternalGatewayForFloatingIPNotFound(nexception.NotFound):
63     message = _("External network %(external_network_id)s is not reachable "
64                 "from subnet %(subnet_id)s.  Therefore, cannot associate "
65                 "Port %(port_id)s with a Floating IP.")
66
67
68 class FloatingIPPortAlreadyAssociated(nexception.InUse):
69     message = _("Cannot associate floating IP %(floating_ip_address)s "
70                 "(%(fip_id)s) with port %(port_id)s "
71                 "using fixed IP %(fixed_ip)s, as that fixed IP already "
72                 "has a floating IP on external network %(net_id)s.")
73
74
75 class RouterExternalGatewayInUseByFloatingIp(nexception.InUse):
76     message = _("Gateway cannot be updated for router %(router_id)s, since a "
77                 "gateway to external network %(net_id)s is required by one or "
78                 "more floating IPs.")
79
80 ROUTERS = 'routers'
81 EXTERNAL_GW_INFO = 'external_gateway_info'
82 FLOATINGIPS = 'floatingips'
83
84 RESOURCE_ATTRIBUTE_MAP = {
85     ROUTERS: {
86         'id': {'allow_post': False, 'allow_put': False,
87                'validate': {'type:uuid': None},
88                'is_visible': True,
89                'primary_key': True},
90         'name': {'allow_post': True, 'allow_put': True,
91                  'validate': {'type:string': attr.NAME_MAX_LEN},
92                  'is_visible': True, 'default': ''},
93         'admin_state_up': {'allow_post': True, 'allow_put': True,
94                            'default': True,
95                            'convert_to': attr.convert_to_boolean,
96                            'is_visible': True},
97         'status': {'allow_post': False, 'allow_put': False,
98                    'is_visible': True},
99         'tenant_id': {'allow_post': True, 'allow_put': False,
100                       'required_by_policy': True,
101                       'validate': {'type:string': attr.TENANT_ID_MAX_LEN},
102                       'is_visible': True},
103         EXTERNAL_GW_INFO: {'allow_post': True, 'allow_put': True,
104                            'is_visible': True, 'default': None,
105                            'enforce_policy': True,
106                            'validate': {
107                                'type:dict_or_nodata': {
108                                    'network_id': {'type:uuid': None,
109                                                   'required': True},
110                                    'external_fixed_ips': {
111                                        'convert_list_to':
112                                        attr.convert_kvp_list_to_dict,
113                                        'type:fixed_ips': None,
114                                        'default': None,
115                                        'required': False,
116                                    }
117                                }
118                            }}
119     },
120     FLOATINGIPS: {
121         'id': {'allow_post': False, 'allow_put': False,
122                'validate': {'type:uuid': None},
123                'is_visible': True,
124                'primary_key': True},
125         'floating_ip_address': {'allow_post': True, 'allow_put': False,
126                                 'validate': {'type:ip_address_or_none': None},
127                                 'is_visible': True, 'default': None,
128                                 'enforce_policy': True},
129         'subnet_id': {'allow_post': True, 'allow_put': False,
130                       'validate': {'type:uuid_or_none': None},
131                       'is_visible': False,  # Use False for input only attr
132                       'default': None},
133         'floating_network_id': {'allow_post': True, 'allow_put': False,
134                                 'validate': {'type:uuid': None},
135                                 'is_visible': True},
136         'router_id': {'allow_post': False, 'allow_put': False,
137                       'validate': {'type:uuid_or_none': None},
138                       'is_visible': True, 'default': None},
139         'port_id': {'allow_post': True, 'allow_put': True,
140                     'validate': {'type:uuid_or_none': None},
141                     'is_visible': True, 'default': None,
142                     'required_by_policy': True},
143         'fixed_ip_address': {'allow_post': True, 'allow_put': True,
144                              'validate': {'type:ip_address_or_none': None},
145                              'is_visible': True, 'default': None},
146         'tenant_id': {'allow_post': True, 'allow_put': False,
147                       'required_by_policy': True,
148                       'validate': {'type:string': attr.TENANT_ID_MAX_LEN},
149                       'is_visible': True},
150         'status': {'allow_post': False, 'allow_put': False,
151                    'is_visible': True},
152     },
153 }
154
155 l3_quota_opts = [
156     cfg.IntOpt('quota_router',
157                default=10,
158                help=_('Number of routers allowed per tenant. '
159                       'A negative value means unlimited.')),
160     cfg.IntOpt('quota_floatingip',
161                default=50,
162                help=_('Number of floating IPs allowed per tenant. '
163                       'A negative value means unlimited.')),
164 ]
165 cfg.CONF.register_opts(l3_quota_opts, 'QUOTAS')
166
167
168 class L3(extensions.ExtensionDescriptor):
169
170     @classmethod
171     def get_name(cls):
172         return "Neutron L3 Router"
173
174     @classmethod
175     def get_alias(cls):
176         return "router"
177
178     @classmethod
179     def get_description(cls):
180         return ("Router abstraction for basic L3 forwarding"
181                 " between L2 Neutron networks and access to external"
182                 " networks via a NAT gateway.")
183
184     @classmethod
185     def get_updated(cls):
186         return "2012-07-20T10:00:00-00:00"
187
188     @classmethod
189     def get_resources(cls):
190         """Returns Ext Resources."""
191         plural_mappings = resource_helper.build_plural_mappings(
192             {}, RESOURCE_ATTRIBUTE_MAP)
193         plural_mappings['external_fixed_ips'] = 'external_fixed_ip'
194         attr.PLURALS.update(plural_mappings)
195         action_map = {'router': {'add_router_interface': 'PUT',
196                                  'remove_router_interface': 'PUT'}}
197         return resource_helper.build_resource_info(plural_mappings,
198                                                    RESOURCE_ATTRIBUTE_MAP,
199                                                    constants.L3_ROUTER_NAT,
200                                                    action_map=action_map,
201                                                    register_quota=True)
202
203     def update_attributes_map(self, attributes):
204         super(L3, self).update_attributes_map(
205             attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP)
206
207     def get_extended_resources(self, version):
208         if version == "2.0":
209             return RESOURCE_ATTRIBUTE_MAP
210         else:
211             return {}
212
213
214 class RouterPluginBase(object):
215
216     @abc.abstractmethod
217     def create_router(self, context, router):
218         pass
219
220     @abc.abstractmethod
221     def update_router(self, context, id, router):
222         pass
223
224     @abc.abstractmethod
225     def get_router(self, context, id, fields=None):
226         pass
227
228     @abc.abstractmethod
229     def delete_router(self, context, id):
230         pass
231
232     @abc.abstractmethod
233     def get_routers(self, context, filters=None, fields=None,
234                     sorts=None, limit=None, marker=None, page_reverse=False):
235         pass
236
237     @abc.abstractmethod
238     def add_router_interface(self, context, router_id, interface_info):
239         pass
240
241     @abc.abstractmethod
242     def remove_router_interface(self, context, router_id, interface_info):
243         pass
244
245     @abc.abstractmethod
246     def create_floatingip(self, context, floatingip):
247         pass
248
249     @abc.abstractmethod
250     def update_floatingip(self, context, id, floatingip):
251         pass
252
253     @abc.abstractmethod
254     def get_floatingip(self, context, id, fields=None):
255         pass
256
257     @abc.abstractmethod
258     def delete_floatingip(self, context, id):
259         pass
260
261     @abc.abstractmethod
262     def get_floatingips(self, context, filters=None, fields=None,
263                         sorts=None, limit=None, marker=None,
264                         page_reverse=False):
265         pass
266
267     def get_routers_count(self, context, filters=None):
268         raise NotImplementedError()
269
270     def get_floatingips_count(self, context, filters=None):
271         raise NotImplementedError()