2
# Licensed under the Apache License, Version 2.0 (the "License"); you may
3
# not use this file except in compliance with the License. You may obtain
4
# a copy of the License at
6
# http://www.apache.org/licenses/LICENSE-2.0
8
# Unless required by applicable law or agreed to in writing, software
9
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11
# License for the specific language governing permissions and limitations
15
from heat.common.i18n import _
16
from heat.engine import attributes
17
from heat.engine import properties
18
from heat.engine.resources.neutron import neutron
19
from heat.engine.resources.neutron import port
20
from heat.engine.resources.neutron import router
21
from heat.engine import support
24
class FloatingIP(neutron.NeutronResource):
26
FLOATING_NETWORK_ID, FLOATING_NETWORK,
27
VALUE_SPECS, PORT_ID, FIXED_IP_ADDRESS,
29
'floating_network_id', 'floating_network',
30
'value_specs', 'port_id', 'fixed_ip_address',
34
ROUTER_ID, TENANT_ID, FLOATING_NETWORK_ID_ATTR, FIXED_IP_ADDRESS_ATTR,
35
FLOATING_IP_ADDRESS_ATTR, PORT_ID_ATTR, SHOW,
37
'router_id', 'tenant_id', 'floating_network_id', 'fixed_ip_address',
38
'floating_ip_address', 'port_id', 'show',
42
FLOATING_NETWORK_ID: properties.Schema(
43
properties.Schema.STRING,
44
support_status=support.SupportStatus(
46
_('Use property %s.') % FLOATING_NETWORK),
49
FLOATING_NETWORK: properties.Schema(
50
properties.Schema.STRING,
51
_('Network to allocate floating IP from.'),
53
support_status=support.SupportStatus(version='2014.2')
55
VALUE_SPECS: properties.Schema(
56
properties.Schema.MAP,
57
_('Extra parameters to include in the "floatingip" object in the '
58
'creation request. Parameters are often specific to installed '
59
'hardware or extensions.'),
62
PORT_ID: properties.Schema(
63
properties.Schema.STRING,
64
_('ID of an existing port with at least one IP address to '
65
'associate with this floating IP.'),
68
FIXED_IP_ADDRESS: properties.Schema(
69
properties.Schema.STRING,
70
_('IP address to use if the port has multiple addresses.'),
76
ROUTER_ID: attributes.Schema(
77
_('ID of the router used as gateway, set when associated with a '
80
TENANT_ID: attributes.Schema(
81
_('The tenant owning this floating IP.')
83
FLOATING_NETWORK_ID_ATTR: attributes.Schema(
84
_('ID of the network in which this IP is allocated.')
86
FIXED_IP_ADDRESS_ATTR: attributes.Schema(
87
_('IP address of the associated port, if specified.')
89
FLOATING_IP_ADDRESS_ATTR: attributes.Schema(
90
_('The allocated address of this IP.')
92
PORT_ID_ATTR: attributes.Schema(
93
_('ID of the port associated with this IP.')
95
SHOW: attributes.Schema(
100
def add_dependencies(self, deps):
101
super(FloatingIP, self).add_dependencies(deps)
103
for resource in self.stack.itervalues():
104
# depend on any RouterGateway in this template with the same
105
# network_id as this floating_network_id
106
if resource.has_interface('OS::Neutron::RouterGateway'):
107
gateway_network = resource.properties.get(
108
router.RouterGateway.NETWORK) or resource.properties.get(
109
router.RouterGateway.NETWORK_ID)
110
floating_network = self.properties.get(
111
self.FLOATING_NETWORK) or self.properties.get(
112
self.FLOATING_NETWORK_ID)
113
if gateway_network == floating_network:
114
deps += (self, resource)
116
# depend on any RouterInterface in this template which interfaces
117
# with the same subnet that this floating IP's port is assigned
119
elif resource.has_interface('OS::Neutron::RouterInterface'):
121
def port_on_subnet(resource, subnet):
122
if not resource.has_interface('OS::Neutron::Port'):
124
for fixed_ip in resource.properties.get(
125
port.Port.FIXED_IPS):
128
fixed_ip.get(port.Port.FIXED_IP_SUBNET)
129
or fixed_ip.get(port.Port.FIXED_IP_SUBNET_ID))
130
return subnet == port_subnet
134
resource.properties.get(router.RouterInterface.SUBNET) or
135
resource.properties.get(router.RouterInterface.SUBNET_ID))
136
for d in deps.graph()[self]:
137
if port_on_subnet(d, interface_subnet):
138
deps += (self, resource)
140
# depend on Router with EXTERNAL_GATEWAY_NETWORK property
141
# this template with the same network_id as this
142
# floating_network_id
143
elif resource.has_interface('OS::Neutron::Router'):
144
gateway = resource.properties.get(
145
router.Router.EXTERNAL_GATEWAY)
147
gateway_network = gateway.get(
148
router.Router.EXTERNAL_GATEWAY_NETWORK)
149
floating_network = self.properties.get(
150
self.FLOATING_NETWORK) or self.properties.get(
151
self.FLOATING_NETWORK_ID)
152
if gateway_network == floating_network:
153
deps += (self, resource)
156
super(FloatingIP, self).validate()
157
self._validate_depr_property_required(
158
self.properties, self.FLOATING_NETWORK, self.FLOATING_NETWORK_ID)
160
def handle_create(self):
161
props = self.prepare_properties(
163
self.physical_resource_name())
164
self.client_plugin().resolve_network(props, self.FLOATING_NETWORK,
165
'floating_network_id')
166
fip = self.neutron().create_floatingip({
167
'floatingip': props})['floatingip']
168
self.resource_id_set(fip['id'])
170
def _show_resource(self):
171
return self.neutron().show_floatingip(self.resource_id)['floatingip']
173
def handle_delete(self):
174
client = self.neutron()
176
client.delete_floatingip(self.resource_id)
177
except Exception as ex:
178
self.client_plugin().ignore_not_found(ex)
180
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
182
neutron_client = self.neutron()
184
port_id = prop_diff.get(self.PORT_ID,
185
self.properties.get(self.PORT_ID))
187
fixed_ip_address = prop_diff.get(
188
self.FIXED_IP_ADDRESS,
189
self.properties.get(self.FIXED_IP_ADDRESS))
194
'fixed_ip_address': fixed_ip_address}}
196
neutron_client.update_floatingip(self.resource_id, request_body)
199
class FloatingIPAssociation(neutron.NeutronResource):
201
FLOATINGIP_ID, PORT_ID, FIXED_IP_ADDRESS,
203
'floatingip_id', 'port_id', 'fixed_ip_address',
206
properties_schema = {
207
FLOATINGIP_ID: properties.Schema(
208
properties.Schema.STRING,
209
_('ID of the floating IP to associate.'),
213
PORT_ID: properties.Schema(
214
properties.Schema.STRING,
215
_('ID of an existing port with at least one IP address to '
216
'associate with this floating IP.'),
220
FIXED_IP_ADDRESS: properties.Schema(
221
properties.Schema.STRING,
222
_('IP address to use if the port has multiple addresses.'),
227
def add_dependencies(self, deps):
228
super(FloatingIPAssociation, self).add_dependencies(deps)
230
for resource in six.itervalues(self.stack):
231
if resource.has_interface('OS::Neutron::RouterInterface'):
233
def port_on_subnet(resource, subnet):
234
if not resource.has_interface('OS::Neutron::Port'):
236
for fixed_ip in resource.properties.get(
237
port.Port.FIXED_IPS):
240
fixed_ip.get(port.Port.FIXED_IP_SUBNET)
241
or fixed_ip.get(port.Port.FIXED_IP_SUBNET_ID))
242
return subnet == port_subnet
246
resource.properties.get(router.RouterInterface.SUBNET) or
247
resource.properties.get(router.RouterInterface.SUBNET_ID))
248
for d in deps.graph()[self]:
249
if port_on_subnet(d, interface_subnet):
250
deps += (self, resource)
253
def handle_create(self):
254
props = self.prepare_properties(self.properties, self.name)
256
floatingip_id = props.pop(self.FLOATINGIP_ID)
258
self.neutron().update_floatingip(floatingip_id, {
259
'floatingip': props})['floatingip']
260
self.resource_id_set('%s:%s' % (floatingip_id, props[self.PORT_ID]))
262
def handle_delete(self):
263
if not self.resource_id:
265
client = self.neutron()
266
(floatingip_id, port_id) = self.resource_id.split(':')
268
client.update_floatingip(
270
{'floatingip': {'port_id': None}})
271
except Exception as ex:
272
self.client_plugin().ignore_not_found(ex)
274
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
276
(floatingip_id, port_id) = self.resource_id.split(':')
277
neutron_client = self.neutron()
278
# if the floatingip_id is changed, disassociate the port which
279
# associated with the old floatingip_id
280
if self.FLOATINGIP_ID in prop_diff:
282
neutron_client.update_floatingip(
284
{'floatingip': {'port_id': None}})
285
except Exception as ex:
286
self.client_plugin().ignore_not_found(ex)
288
# associate the floatingip with the new port
289
floatingip_id = (prop_diff.get(self.FLOATINGIP_ID) or
291
port_id = prop_diff.get(self.PORT_ID) or port_id
293
fixed_ip_address = (prop_diff.get(self.FIXED_IP_ADDRESS) or
294
self.properties.get(self.FIXED_IP_ADDRESS))
299
'fixed_ip_address': fixed_ip_address}}
301
neutron_client.update_floatingip(floatingip_id, request_body)
302
self.resource_id_set('%s:%s' % (floatingip_id, port_id))
305
def resource_mapping():
307
'OS::Neutron::FloatingIP': FloatingIP,
308
'OS::Neutron::FloatingIPAssociation': FloatingIPAssociation,