Merge "Implements Nova FloatingIP resources"
This commit is contained in:
commit
1b8c1b2091
125
heat/engine/resources/nova_floatingip.py
Normal file
125
heat/engine/resources/nova_floatingip.py
Normal file
@ -0,0 +1,125 @@
|
||||
# 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.
|
||||
|
||||
from heat.engine import clients
|
||||
from heat.engine import properties
|
||||
from heat.engine import resource
|
||||
|
||||
from heat.openstack.common import excutils
|
||||
from heat.openstack.common import log as logging
|
||||
from heat.openstack.common.gettextutils import _
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NovaFloatingIp(resource.Resource):
|
||||
PROPERTIES = (POOL,) = ('pool',)
|
||||
|
||||
properties_schema = {
|
||||
POOL: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
description=_('Allocate a floating IP from a given '
|
||||
'floating IP pool.')
|
||||
),
|
||||
}
|
||||
|
||||
attributes_schema = {
|
||||
'pool': _('Pool from which floating IP is allocated.'),
|
||||
'ip': _('Allocated floating IP address.')
|
||||
}
|
||||
|
||||
def __init__(self, name, json_snippet, stack):
|
||||
super(NovaFloatingIp, self).__init__(name, json_snippet, stack)
|
||||
self._floating_ip = None
|
||||
|
||||
def _get_resource(self):
|
||||
if self._floating_ip is None and self.resource_id is not None:
|
||||
self._floating_ip = self.nova().floating_ips.get(self.resource_id)
|
||||
|
||||
return self._floating_ip
|
||||
|
||||
def handle_create(self):
|
||||
try:
|
||||
pool = self.properties.get(self.POOL)
|
||||
floating_ip = self.nova().floating_ips.create(pool=pool)
|
||||
except clients.novaclient.exceptions.NotFound:
|
||||
with excutils.save_and_reraise_exception():
|
||||
if pool is None:
|
||||
msg = _('Could not allocate floating IP. Probably there '
|
||||
'is no default floating IP pool is configured.')
|
||||
logger.error(msg)
|
||||
|
||||
self.resource_id_set(floating_ip.id)
|
||||
self._floating_ip = floating_ip
|
||||
|
||||
def handle_delete(self):
|
||||
if self.resource_id is not None:
|
||||
try:
|
||||
self.nova().floating_ips.delete(self.resource_id)
|
||||
except clients.novaclient.exceptions.NotFound:
|
||||
pass
|
||||
|
||||
def _resolve_attribute(self, key):
|
||||
floating_ip = self._get_resource()
|
||||
attributes = {
|
||||
'pool': getattr(floating_ip, 'pool', None),
|
||||
'ip': floating_ip.ip
|
||||
}
|
||||
return unicode(attributes[key])
|
||||
|
||||
|
||||
class NovaFloatingIpAssociation(resource.Resource):
|
||||
PROPERTIES = (
|
||||
SERVER, FLOATING_IP
|
||||
) = (
|
||||
'server_id', 'floating_ip'
|
||||
)
|
||||
|
||||
properties_schema = {
|
||||
SERVER: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Server to assign floating IP to.'),
|
||||
required=True
|
||||
),
|
||||
FLOATING_IP: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('ID of the floating IP to assign to the server.'),
|
||||
required=True
|
||||
),
|
||||
}
|
||||
|
||||
def FnGetRefId(self):
|
||||
return unicode(self.physical_resource_name())
|
||||
|
||||
def handle_create(self):
|
||||
server = self.nova().servers.get(self.properties[self.SERVER])
|
||||
fl_ip = self.nova().floating_ips.get(self.properties[self.FLOATING_IP])
|
||||
|
||||
self.nova().servers.add_floating_ip(server, fl_ip.ip)
|
||||
self.resource_id_set('%s-%s' % (fl_ip.id, fl_ip.ip))
|
||||
|
||||
def handle_delete(self):
|
||||
try:
|
||||
server = self.nova().servers.get(self.properties[self.SERVER])
|
||||
if server:
|
||||
self.nova().servers.remove_floating_ip(
|
||||
server, self.properties[self.FLOATING_IP]
|
||||
)
|
||||
except clients.novaclient.exceptions.NotFound:
|
||||
pass
|
||||
|
||||
|
||||
def resource_mapping():
|
||||
return {
|
||||
'OS::Nova::FloatingIP': NovaFloatingIp,
|
||||
'OS::Nova::FloatingIPAssociation': NovaFloatingIpAssociation,
|
||||
}
|
169
heat/tests/test_nova_floatingip.py
Normal file
169
heat/tests/test_nova_floatingip.py
Normal file
@ -0,0 +1,169 @@
|
||||
# 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.
|
||||
|
||||
from heat.common import template_format
|
||||
from heat.engine.resources.nova_floatingip import NovaFloatingIp
|
||||
from heat.engine.resources.nova_floatingip import NovaFloatingIpAssociation
|
||||
from heat.engine import clients
|
||||
from heat.engine import scheduler
|
||||
from heat.tests.common import HeatTestCase
|
||||
from heat.tests import utils
|
||||
|
||||
from novaclient.v1_1 import client as novaclient
|
||||
|
||||
|
||||
floating_ip_template = '''
|
||||
{
|
||||
"heat_template_version": "2013-05-23",
|
||||
"Resources": {
|
||||
"MyFloatingIP": {
|
||||
"Type": "OS::Nova::FloatingIP",
|
||||
"Properties": {
|
||||
"pool": "public"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
'''
|
||||
|
||||
floating_ip_template_with_assoc = '''
|
||||
{
|
||||
"heat_template_version": "2013-05-23",
|
||||
"Resources": {
|
||||
"MyFloatingIPAssociation": {
|
||||
"Type": "OS::Nova::FloatingIPAssociation",
|
||||
"Properties": {
|
||||
"server_id": "67dc62f9-efde-4c8b-94af-013e00f5dc57",
|
||||
"floating_ip": "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
'''
|
||||
|
||||
|
||||
class NovaFloatingIPTest(HeatTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(NovaFloatingIPTest, self).setUp()
|
||||
|
||||
self.novaclient = novaclient.Client('user', 'pass', 'project', 'uri')
|
||||
self.m.StubOutWithMock(clients.OpenStackClients, 'nova')
|
||||
self.m.StubOutWithMock(self.novaclient.floating_ips, 'create')
|
||||
self.m.StubOutWithMock(self.novaclient.floating_ips, 'get')
|
||||
self.m.StubOutWithMock(self.novaclient.floating_ips, 'delete')
|
||||
self.m.StubOutWithMock(self.novaclient.servers, 'get')
|
||||
self.m.StubOutWithMock(self.novaclient.servers, 'add_floating_ip')
|
||||
self.m.StubOutWithMock(self.novaclient.servers, 'remove_floating_ip')
|
||||
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
|
||||
|
||||
utils.setup_dummy_db()
|
||||
|
||||
def _make_obj(self, **kwargs):
|
||||
mock = self.m.CreateMockAnything()
|
||||
for k, v in kwargs.iteritems():
|
||||
setattr(mock, k, v)
|
||||
return mock
|
||||
|
||||
def prepare_floating_ip(self):
|
||||
clients.OpenStackClients.nova('compute').AndReturn(self.novaclient)
|
||||
self.novaclient.floating_ips.create(pool='public').AndReturn(
|
||||
self._make_obj(**{
|
||||
'id': '1',
|
||||
'ip': '11.0.0.1',
|
||||
'pool': 'public'
|
||||
})
|
||||
)
|
||||
|
||||
template = template_format.parse(floating_ip_template)
|
||||
stack = utils.parse_stack(template)
|
||||
floating_ip = template['Resources']['MyFloatingIP']
|
||||
|
||||
return NovaFloatingIp('MyFloatingIP', floating_ip, stack)
|
||||
|
||||
def prepare_floating_ip_assoc(self):
|
||||
clients.OpenStackClients.nova('compute').MultipleTimes().AndReturn(
|
||||
self.novaclient)
|
||||
self.novaclient.servers.get('67dc62f9-efde-4c8b-94af-013e00f5dc57')
|
||||
self.novaclient.floating_ips.get('1').AndReturn(
|
||||
self._make_obj(**{
|
||||
'id': '1',
|
||||
'ip': '11.0.0.1',
|
||||
'pool': 'public'
|
||||
})
|
||||
)
|
||||
self.novaclient.servers.add_floating_ip(None, '11.0.0.1')
|
||||
|
||||
template = template_format.parse(floating_ip_template_with_assoc)
|
||||
stack = utils.parse_stack(template)
|
||||
floating_ip_assoc = template['Resources']['MyFloatingIPAssociation']
|
||||
|
||||
return NovaFloatingIpAssociation('MyFloatingIPAssociation',
|
||||
floating_ip_assoc, stack)
|
||||
|
||||
def test_floating_ip_create(self):
|
||||
rsrc = self.prepare_floating_ip()
|
||||
self.m.ReplayAll()
|
||||
|
||||
rsrc.validate()
|
||||
scheduler.TaskRunner(rsrc.create)()
|
||||
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
self.assertEqual('1', rsrc.FnGetRefId())
|
||||
self.assertEqual('11.0.0.1', rsrc.FnGetAtt('ip'))
|
||||
self.assertEqual('public', rsrc.FnGetAtt('pool'))
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_floating_ip_delete(self):
|
||||
rsrc = self.prepare_floating_ip()
|
||||
rsrc.validate()
|
||||
|
||||
clients.OpenStackClients.nova('compute').AndReturn(self.novaclient)
|
||||
self.novaclient.floating_ips.delete('1')
|
||||
|
||||
self.m.ReplayAll()
|
||||
|
||||
scheduler.TaskRunner(rsrc.create)()
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
scheduler.TaskRunner(rsrc.delete)()
|
||||
self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_floating_ip_assoc_create(self):
|
||||
rsrc = self.prepare_floating_ip_assoc()
|
||||
self.m.ReplayAll()
|
||||
|
||||
rsrc.validate()
|
||||
scheduler.TaskRunner(rsrc.create)()
|
||||
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_floating_ip_assoc_delete(self):
|
||||
rsrc = self.prepare_floating_ip_assoc()
|
||||
|
||||
self.novaclient.servers.get(
|
||||
'67dc62f9-efde-4c8b-94af-013e00f5dc57').AndReturn('server')
|
||||
self.novaclient.servers.remove_floating_ip('server', '1')
|
||||
|
||||
self.m.ReplayAll()
|
||||
|
||||
rsrc.validate()
|
||||
scheduler.TaskRunner(rsrc.create)()
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
scheduler.TaskRunner(rsrc.delete)()
|
||||
self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
|
||||
|
||||
self.m.VerifyAll()
|
Loading…
Reference in New Issue
Block a user