Make neutron resources available based on service extensions

Change-Id: Ie4780aa9ef81b1decf4a01ca616fa76a2d1b641e
Partially-Closes-Bug: #1506875
This commit is contained in:
Rabi Mishra 2015-10-16 18:10:26 +05:30
parent 20b5e05be7
commit f539f43855
22 changed files with 123 additions and 3 deletions

View File

@ -72,6 +72,11 @@ class NeutronClientPlugin(client_plugin.ClientPlugin):
return neutronV20.find_resourceid_by_name_or_id(
self.client(), key_type, props.get(key))
def has_extension(self, alias):
"""Check if specific extension is present."""
extensions = self.client().list_extensions().get('extensions')
return alias in [extension.get('alias') for extension in extensions]
def _resolve(self, props, key, id_key, key_type):
if props.get(key):
props[id_key] = self.find_neutron_resource(

View File

@ -112,6 +112,9 @@ class Resource(object):
# Default name to use for calls to self.client()
default_client_name = None
# Required service extension for this resource
required_service_extension = None
# no signal actions
no_signal_actions = (SUSPEND, DELETE)
@ -559,9 +562,14 @@ class Resource(object):
# NOTE(kanagaraj-manickam): if one of the service_type does
# exist in the keystone, then considered it as available.
for service_type in service_types:
if client_plugin.does_endpoint_exist(
service_type=service_type,
service_name=cls.default_client_name):
endpoint_exists = client_plugin.does_endpoint_exist(
service_type=service_type,
service_name=cls.default_client_name)
req_extension = cls.required_service_extension
is_ext_available = (
not req_extension or client_plugin.has_extension(
req_extension))
if endpoint_exists and is_ext_available:
return True
except Exception as ex:
LOG.exception(ex)

View File

@ -24,6 +24,8 @@ from heat.engine import support
class ExtraRoute(neutron.NeutronResource):
required_service_extension = 'extraroute'
support_status = support.SupportStatus(
status=support.UNSUPPORTED,
message=_('This resource is not supported, use at your own risk.'))

View File

@ -22,6 +22,8 @@ from heat.engine import support
class Firewall(neutron.NeutronResource):
"""A resource for the Firewall resource in Neutron FWaaS."""
required_service_extension = 'fwaas'
PROPERTIES = (
NAME, DESCRIPTION, ADMIN_STATE_UP, FIREWALL_POLICY_ID,
VALUE_SPECS, SHARED,
@ -143,6 +145,8 @@ class Firewall(neutron.NeutronResource):
class FirewallPolicy(neutron.NeutronResource):
"""A resource for the FirewallPolicy resource in Neutron FWaaS."""
required_service_extension = 'fwaas'
PROPERTIES = (
NAME, DESCRIPTION, SHARED, AUDITED, FIREWALL_RULES,
) = (
@ -248,6 +252,8 @@ class FirewallPolicy(neutron.NeutronResource):
class FirewallRule(neutron.NeutronResource):
"""A resource for the FirewallRule resource in Neutron FWaaS."""
required_service_extension = 'fwaas'
PROPERTIES = (
NAME, DESCRIPTION, SHARED, PROTOCOL, IP_VERSION,
SOURCE_IP_ADDRESS, DESTINATION_IP_ADDRESS, SOURCE_PORT,

View File

@ -27,6 +27,8 @@ from heat.engine import support
class HealthMonitor(neutron.NeutronResource):
"""A resource for managing health monitors for loadbalancers in Neutron."""
required_service_extension = 'lbaas'
PROPERTIES = (
DELAY, TYPE, MAX_RETRIES, TIMEOUT, ADMIN_STATE_UP,
HTTP_METHOD, EXPECTED_CODES, URL_PATH,
@ -173,6 +175,8 @@ class HealthMonitor(neutron.NeutronResource):
class Pool(neutron.NeutronResource):
"""A resource for managing load balancer pools in Neutron."""
required_service_extension = 'lbaas'
PROPERTIES = (
PROTOCOL, SUBNET_ID, SUBNET, LB_METHOD, NAME, DESCRIPTION,
ADMIN_STATE_UP, VIP, MONITORS, PROVIDER,
@ -549,6 +553,8 @@ class Pool(neutron.NeutronResource):
class PoolMember(neutron.NeutronResource):
"""A resource to handle loadbalancer members."""
required_service_extension = 'lbaas'
support_status = support.SupportStatus(version='2014.1')
PROPERTIES = (
@ -672,6 +678,8 @@ class PoolMember(neutron.NeutronResource):
class LoadBalancer(resource.Resource):
"""A resource to link a neutron pool with servers."""
required_service_extension = 'lbaas'
PROPERTIES = (
POOL_ID, PROTOCOL_PORT, MEMBERS,
) = (

View File

@ -24,6 +24,8 @@ from heat.engine import support
class ProviderNet(net.Net):
required_service_extension = 'provider'
support_status = support.SupportStatus(version='2014.1')
PROPERTIES = (

View File

@ -25,6 +25,8 @@ from heat.engine import support
class Router(neutron.NeutronResource):
required_service_extension = 'router'
PROPERTIES = (
NAME, EXTERNAL_GATEWAY, VALUE_SPECS, ADMIN_STATE_UP,
L3_AGENT_ID, L3_AGENT_IDS, DISTRIBUTED, HA,
@ -279,6 +281,9 @@ class Router(neutron.NeutronResource):
class RouterInterface(neutron.NeutronResource):
required_service_extension = 'router'
PROPERTIES = (
ROUTER, ROUTER_ID, SUBNET_ID, SUBNET, PORT_ID, PORT
) = (

View File

@ -21,6 +21,8 @@ from heat.engine import support
class SecurityGroup(neutron.NeutronResource):
required_service_extension = 'security-group'
support_status = support.SupportStatus(version='2014.1')
PROPERTIES = (

View File

@ -22,6 +22,8 @@ from heat.engine import support
class VPNService(neutron.NeutronResource):
"""A resource for VPN service in Neutron."""
required_service_extension = 'vpnaas'
PROPERTIES = (
NAME, DESCRIPTION, ADMIN_STATE_UP,
SUBNET_ID, SUBNET, ROUTER_ID, ROUTER
@ -191,6 +193,8 @@ class VPNService(neutron.NeutronResource):
class IPsecSiteConnection(neutron.NeutronResource):
"""A resource for IPsec site connection in Neutron."""
required_service_extension = 'vpnaas'
PROPERTIES = (
NAME, DESCRIPTION, PEER_ADDRESS, PEER_ID, PEER_CIDRS, MTU,
DPD, PSK, INITIATOR, ADMIN_STATE_UP, IKEPOLICY_ID,
@ -433,6 +437,8 @@ class IPsecSiteConnection(neutron.NeutronResource):
class IKEPolicy(neutron.NeutronResource):
"""A resource for IKE policy in Neutron."""
required_service_extension = 'vpnaas'
PROPERTIES = (
NAME, DESCRIPTION, AUTH_ALGORITHM, ENCRYPTION_ALGORITHM,
PHASE1_NEGOTIATION_MODE, LIFETIME, PFS, IKE_VERSION,
@ -599,6 +605,8 @@ class IKEPolicy(neutron.NeutronResource):
class IPsecPolicy(neutron.NeutronResource):
"""A resource for IPsec policy in Neutron."""
required_service_extension = 'vpnaas'
PROPERTIES = (
NAME, DESCRIPTION, TRANSFORM_PROTOCOL, ENCAPSULATION_MODE,
AUTH_ALGORITHM, ENCRYPTION_ALGORITHM, LIFETIME, PFS,

View File

@ -182,3 +182,22 @@ class NeutronConstraintsValidate(common.HeatTestCase):
self.assertFalse(constraint.validate("bar", ctx))
mock_find.assert_has_calls([mock.call(nc, self.resource_type, 'foo'),
mock.call(nc, self.resource_type, 'bar')])
class NeutronClientPluginExtensionsTests(NeutronClientPluginTestCase):
"""Tests for extensions in neutronclient."""
def test_has_no_extension(self):
mock_extensions = {'extensions': []}
self.neutron_client.list_extensions.return_value = mock_extensions
self.assertFalse(self.neutron_plugin.has_extension('lbaas'))
def test_without_service_extension(self):
mock_extensions = {'extensions': [{'alias': 'router'}]}
self.neutron_client.list_extensions.return_value = mock_extensions
self.assertFalse(self.neutron_plugin.has_extension('lbaas'))
def test_has_service_extension(self):
mock_extensions = {'extensions': [{'alias': 'router'}]}
self.neutron_client.list_extensions.return_value = mock_extensions
self.assertTrue(self.neutron_plugin.has_extension('router'))

View File

@ -14,6 +14,7 @@
from neutronclient.v2_0 import client as neutronclient
from heat.common import template_format
from heat.engine.clients.os import neutron
from heat.engine.resources.openstack.neutron import extraroute
from heat.engine import scheduler
from heat.tests import common
@ -55,6 +56,8 @@ class NeutronExtraRouteTest(common.HeatTestCase):
super(NeutronExtraRouteTest, self).setUp()
self.m.StubOutWithMock(neutronclient.Client, 'show_router')
self.m.StubOutWithMock(neutronclient.Client, 'update_router')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def create_extraroute(self, t, stack, resource_name, properties=None):
properties = properties or {}

View File

@ -19,6 +19,7 @@ import six
from heat.common import exception
from heat.common import template_format
from heat.engine.clients.os import neutron
from heat.engine.resources.openstack.neutron import firewall
from heat.engine import scheduler
from heat.tests import common
@ -81,6 +82,8 @@ class FirewallTest(common.HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client, 'delete_firewall')
self.m.StubOutWithMock(neutronclient.Client, 'show_firewall')
self.m.StubOutWithMock(neutronclient.Client, 'update_firewall')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def create_firewall(self):
neutronclient.Client.create_firewall({
@ -213,6 +216,8 @@ class FirewallPolicyTest(common.HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client, 'delete_firewall_policy')
self.m.StubOutWithMock(neutronclient.Client, 'show_firewall_policy')
self.m.StubOutWithMock(neutronclient.Client, 'update_firewall_policy')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def create_firewall_policy(self):
neutronclient.Client.create_firewall_policy({
@ -340,6 +345,8 @@ class FirewallRuleTest(common.HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client, 'delete_firewall_rule')
self.m.StubOutWithMock(neutronclient.Client, 'show_firewall_rule')
self.m.StubOutWithMock(neutronclient.Client, 'update_firewall_rule')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def create_firewall_rule(self):
neutronclient.Client.create_firewall_rule({

View File

@ -22,6 +22,7 @@ from neutronclient.v2_0 import client as neutronclient
from heat.common import exception
from heat.common import template_format
from heat.engine.cfn import functions as cfn_funcs
from heat.engine.clients.os import neutron
from heat.engine import rsrc_defn
from heat.engine import scheduler
from heat.engine import stack as parser
@ -130,6 +131,8 @@ class NeutronFloatingIPTest(common.HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client, 'show_port')
self.m.StubOutWithMock(neutronV20,
'find_resourceid_by_name_or_id')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def test_floating_ip_validate(self):
t = template_format.parse(neutron_floating_no_assoc_template)

View File

@ -23,6 +23,7 @@ import six
from heat.common import exception
from heat.common.i18n import _
from heat.common import template_format
from heat.engine.clients.os import neutron
from heat.engine.clients.os import nova
from heat.engine.resources.openstack.neutron import loadbalancer
from heat.engine import scheduler
@ -177,6 +178,8 @@ class HealthMonitorTest(common.HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client, 'delete_health_monitor')
self.m.StubOutWithMock(neutronclient.Client, 'show_health_monitor')
self.m.StubOutWithMock(neutronclient.Client, 'update_health_monitor')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def create_health_monitor(self):
neutronclient.Client.create_health_monitor({
@ -311,6 +314,8 @@ class PoolTest(common.HeatTestCase):
self.m.StubOutWithMock(neutronV20, 'find_resourceid_by_name_or_id')
self.m.StubOutWithMock(neutronclient.Client, 'delete_vip')
self.m.StubOutWithMock(neutronclient.Client, 'show_vip')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def create_pool(self, resolve_neutron=True, with_vip_subnet=False):
neutronclient.Client.create_pool({
@ -839,6 +844,8 @@ class PoolMemberTest(common.HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client, 'delete_member')
self.m.StubOutWithMock(neutronclient.Client, 'update_member')
self.m.StubOutWithMock(neutronclient.Client, 'show_member')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def create_member(self):
neutronclient.Client.create_member({
@ -939,6 +946,8 @@ class LoadBalancerTest(common.HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client, 'create_member')
self.m.StubOutWithMock(neutronclient.Client, 'delete_member')
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def create_load_balancer(self):
nova.NovaClientPlugin._create().AndReturn(self.fc)
@ -1036,6 +1045,8 @@ class PoolUpdateHealthMonitorsTest(common.HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client, 'delete_vip')
self.m.StubOutWithMock(neutronclient.Client, 'show_vip')
self.m.StubOutWithMock(neutronV20, 'find_resourceid_by_name_or_id')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def _create_pool_with_health_monitors(self):
neutronclient.Client.create_health_monitor({

View File

@ -20,6 +20,7 @@ from neutronclient.v2_0 import client as neutronclient
from heat.common import exception
from heat.common import template_format
from heat.engine.clients.os import neutron
from heat.engine.resources.openstack.neutron import net
from heat.engine import rsrc_defn
from heat.engine import scheduler
@ -91,6 +92,8 @@ class NeutronNetTest(common.HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client,
'list_dhcp_agent_hosting_networks')
self.m.StubOutWithMock(neutronV20, 'find_resourceid_by_name_or_id')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def create_net(self, t, stack, resource_name):
resource_defns = stack.t.resource_definitions(stack)

View File

@ -18,6 +18,7 @@ from neutronclient.v2_0 import client as neutronclient
from heat.common import exception
from heat.common import template_format
from heat.engine.clients.os import neutron
from heat.engine.resources.openstack.neutron import provider_net
from heat.engine import rsrc_defn
from heat.engine import scheduler
@ -66,6 +67,8 @@ class NeutronProviderNetTest(common.HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client, 'show_network')
self.m.StubOutWithMock(neutronclient.Client, 'delete_network')
self.m.StubOutWithMock(neutronclient.Client, 'update_network')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def create_provider_net(self):
# Create script

View File

@ -21,6 +21,7 @@ import six
from heat.common import exception
from heat.common import template_format
from heat.engine.clients.os import neutron
from heat.engine import properties
from heat.engine.resources.openstack.neutron import router
from heat.engine import rsrc_defn
@ -121,6 +122,8 @@ class NeutronRouterTest(common.HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client,
'list_l3_agent_hosting_routers')
self.m.StubOutWithMock(neutronV20, 'find_resourceid_by_name_or_id')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def create_router(self, t, stack, resource_name):
resource_defns = stack.t.resource_definitions(stack)

View File

@ -18,6 +18,7 @@ from novaclient.v2 import security_groups as nova_sg
from heat.common import exception
from heat.common import template_format
from heat.engine.clients.os import neutron
from heat.engine import scheduler
from heat.engine import stack as parser
from heat.engine import template
@ -100,6 +101,8 @@ resources:
neutronclient.Client, 'delete_security_group_rule')
self.m.StubOutWithMock(neutronclient.Client, 'delete_security_group')
self.m.StubOutWithMock(neutronclient.Client, 'update_security_group')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def create_stack(self, templ):
t = template_format.parse(templ)

View File

@ -20,6 +20,7 @@ import six
from heat.common import exception
from heat.common import template_format
from heat.engine.cfn import functions as cfn_funcs
from heat.engine.clients.os import neutron
from heat.engine.resources.openstack.neutron import subnet
from heat.engine import rsrc_defn
from heat.engine import scheduler
@ -103,6 +104,8 @@ class NeutronSubnetTest(common.HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client, 'show_subnet')
self.m.StubOutWithMock(neutronclient.Client, 'update_subnet')
self.m.StubOutWithMock(neutronV20, 'find_resourceid_by_name_or_id')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def create_subnet(self, t, stack, resource_name):
resource_defns = stack.t.resource_definitions(stack)

View File

@ -21,6 +21,7 @@ import six
from heat.common import exception
from heat.common import template_format
from heat.engine.clients.os import neutron
from heat.engine.resources.openstack.neutron import vpnservice
from heat.engine import scheduler
from heat.tests import common
@ -127,6 +128,8 @@ class VPNServiceTest(common.HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client, 'show_vpnservice')
self.m.StubOutWithMock(neutronclient.Client, 'update_vpnservice')
self.m.StubOutWithMock(neutronV20, 'find_resourceid_by_name_or_id')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def create_vpnservice(self, resolve_neutron=True, resolve_router=True):
self.stub_SubnetConstraint_validate()
@ -317,6 +320,8 @@ class IPsecSiteConnectionTest(common.HeatTestCase):
'show_ipsec_site_connection')
self.m.StubOutWithMock(neutronclient.Client,
'update_ipsec_site_connection')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def create_ipsec_site_connection(self):
neutronclient.Client.create_ipsec_site_connection(
@ -465,6 +470,8 @@ class IKEPolicyTest(common.HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client, 'delete_ikepolicy')
self.m.StubOutWithMock(neutronclient.Client, 'show_ikepolicy')
self.m.StubOutWithMock(neutronclient.Client, 'update_ikepolicy')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def create_ikepolicy(self):
neutronclient.Client.create_ikepolicy(
@ -607,6 +614,8 @@ class IPsecPolicyTest(common.HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client, 'delete_ipsecpolicy')
self.m.StubOutWithMock(neutronclient.Client, 'show_ipsecpolicy')
self.m.StubOutWithMock(neutronclient.Client, 'update_ipsecpolicy')
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
def create_ipsecpolicy(self):
neutronclient.Client.create_ipsecpolicy(

View File

@ -20,6 +20,7 @@ from heat.common import exception
from heat.common import grouputils
from heat.common import short_id
from heat.common import template_format
from heat.engine.clients.os import neutron
from heat.engine.resources.openstack.heat import instance_group as instgrp
from heat.engine import rsrc_defn
from heat.engine import scheduler
@ -247,6 +248,9 @@ class LoadbalancerReloadTest(common.HeatTestCase):
lb.update.assert_called_once_with(expected)
def test_members(self):
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
t = template_format.parse(inline_templates.as_template)
t['Resources']['ElasticLoadBalancer'] = {
'Type': 'OS::Neutron::LoadBalancer',

View File

@ -21,6 +21,7 @@ import yaml
from heat.common import config
from heat.common import exception
from heat.common import template_format
from heat.engine.clients.os import neutron
from heat.tests import common
from heat.tests import utils
@ -205,6 +206,8 @@ class JsonYamlResolvedCompareTest(common.HeatTestCase):
self.assertEqual(stack1[key].t, stack2[key].t)
def test_neutron_resolved(self):
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
self.compare_stacks('Neutron.template', 'Neutron.yaml', {})
def test_wordpress_resolved(self):