diff --git a/mistral/actions/generator_factory.py b/mistral/actions/generator_factory.py index 65d2b2652..c7770b490 100644 --- a/mistral/actions/generator_factory.py +++ b/mistral/actions/generator_factory.py @@ -20,5 +20,6 @@ def all_generators(): generators.NovaActionGenerator, generators.GlanceActionGenerator, generators.KeystoneActionGenerator, - generators.HeatActionGenerator + generators.HeatActionGenerator, + generators.NeutronActionGenerator ] \ No newline at end of file diff --git a/mistral/actions/openstack/action_generator/generators.py b/mistral/actions/openstack/action_generator/generators.py index f43f53a80..b4afb2613 100644 --- a/mistral/actions/openstack/action_generator/generators.py +++ b/mistral/actions/openstack/action_generator/generators.py @@ -34,3 +34,8 @@ class KeystoneActionGenerator(base.OpenStackActionGenerator): class HeatActionGenerator(base.OpenStackActionGenerator): action_namespace = "heat" base_action_class = actions.HeatAction + + +class NeutronActionGenerator(base.OpenStackActionGenerator): + action_namespace = "neutron" + base_action_class = actions.NeutronAction diff --git a/mistral/actions/openstack/actions.py b/mistral/actions/openstack/actions.py index 6977eb4c6..4bd2aa36c 100644 --- a/mistral/actions/openstack/actions.py +++ b/mistral/actions/openstack/actions.py @@ -17,6 +17,7 @@ import inspect from glanceclient.v2 import client as glanceclient from heatclient.v1 import client as heatclient from keystoneclient.v3 import client as keystoneclient +from neutronclient.v2_0 import client as neutronclient from novaclient.v1_1 import client as novaclient from oslo.config import cfg @@ -84,4 +85,17 @@ class HeatAction(base.OpenStackAction): return result except Exception as e: raise exc.ActionException("%s failed: %s" - % (self.__class__.__name__, e)) \ No newline at end of file + % (self.__class__.__name__, e)) + + +class NeutronAction(base.OpenStackAction): + _client_class = neutronclient.Client + + def _get_client(self): + ctx = context.ctx() + auth_url = CONF.keystone_authtoken.auth_uri + endpoint_url = keystone_utils.get_endpoint_for_project('neutron') + + return self._client_class(endpoint_url=endpoint_url, + token=ctx.auth_token, + auth_url=auth_url) \ No newline at end of file diff --git a/mistral/actions/openstack/mapping.json b/mistral/actions/openstack/mapping.json index e15a8a419..b33adc473 100644 --- a/mistral/actions/openstack/mapping.json +++ b/mistral/actions/openstack/mapping.json @@ -338,5 +338,173 @@ "stacks_template": "stacks.template", "stacks_update": "stacks.update", "stacks_validate": "stacks.validate" + }, + "neutron": { + "add_gateway_router": "add_gateway_router", + "add_interface_router": "add_interface_router", + "add_network_to_dhcp_agent": "add_network_to_dhcp_agent", + "add_router_to_l3_agent": "add_router_to_l3_agent", + "associate_health_monitor": "associate_health_monitor", + "connect_network_gateway": "connect_network_gateway", + "create_credential": "create_credential", + "create_firewall": "create_firewall", + "create_firewall_policy": "create_firewall_policy", + "create_firewall_rule": "create_firewall_rule", + "create_floatingip": "create_floatingip", + "create_gateway_device": "create_gateway_device", + "create_health_monitor": "create_health_monitor", + "create_ikepolicy": "create_ikepolicy", + "create_ipsec_site_connection": "create_ipsec_site_connection", + "create_ipsecpolicy": "create_ipsecpolicy", + "create_member": "create_member", + "create_metering_label": "create_metering_label", + "create_metering_label_rule": "create_metering_label_rule", + "create_net_partition": "create_net_partition", + "create_network": "create_network", + "create_network_gateway": "create_network_gateway", + "create_network_profile": "create_network_profile", + "create_packet_filter": "create_packet_filter", + "create_pool": "create_pool", + "create_port": "create_port", + "create_qos_queue": "create_qos_queue", + "create_router": "create_router", + "create_security_group": "create_security_group", + "create_security_group_rule": "create_security_group_rule", + "create_subnet": "create_subnet", + "create_vip": "create_vip", + "create_vpnservice": "create_vpnservice", + "delete_agent": "delete_agent", + "delete_credential": "delete_credential", + "delete_firewall": "delete_firewall", + "delete_firewall_policy": "delete_firewall_policy", + "delete_firewall_rule": "delete_firewall_rule", + "delete_floatingip": "delete_floatingip", + "delete_gateway_device": "delete_gateway_device", + "delete_health_monitor": "delete_health_monitor", + "delete_ikepolicy": "delete_ikepolicy", + "delete_ipsec_site_connection": "delete_ipsec_site_connection", + "delete_ipsecpolicy": "delete_ipsecpolicy", + "delete_member": "delete_member", + "delete_metering_label": "delete_metering_label", + "delete_metering_label_rule": "delete_metering_label_rule", + "delete_net_partition": "delete_net_partition", + "delete_network": "delete_network", + "delete_network_gateway": "delete_network_gateway", + "delete_network_profile": "delete_network_profile", + "delete_packet_filter": "delete_packet_filter", + "delete_pool": "delete_pool", + "delete_port": "delete_port", + "delete_qos_queue": "delete_qos_queue", + "delete_quota": "delete_quota", + "delete_router": "delete_router", + "delete_security_group": "delete_security_group", + "delete_security_group_rule": "delete_security_group_rule", + "delete_subnet": "delete_subnet", + "delete_vip": "delete_vip", + "delete_vpnservice": "delete_vpnservice", + "disassociate_health_monitor": "disassociate_health_monitor", + "disconnect_network_gateway": "disconnect_network_gateway", + "firewall_policy_insert_rule": "firewall_policy_insert_rule", + "firewall_policy_remove_rule": "firewall_policy_remove_rule", + "get_lbaas_agent_hosting_pool": "get_lbaas_agent_hosting_pool", + "get_quotas_tenant": "get_quotas_tenant", + "list_agents": "list_agents", + "list_dhcp_agent_hosting_networks": "list_dhcp_agent_hosting_networks", + "list_extensions": "list_extensions", + "list_firewall_policies": "list_firewall_policies", + "list_firewall_rules": "list_firewall_rules", + "list_firewalls": "list_firewalls", + "list_floatingips": "list_floatingips", + "list_gateway_devices": "list_gateway_devices", + "list_health_monitors": "list_health_monitors", + "list_ikepolicies": "list_ikepolicies", + "list_ipsec_site_connections": "list_ipsec_site_connections", + "list_ipsecpolicies": "list_ipsecpolicies", + "list_l3_agent_hosting_routers": "list_l3_agent_hosting_routers", + "list_members": "list_members", + "list_metering_label_rules": "list_metering_label_rules", + "list_metering_labels": "list_metering_labels", + "list_net_partitions": "list_net_partitions", + "list_network_gateways": "list_network_gateways", + "list_network_profiles": "list_network_profiles", + "list_networks": "list_networks", + "list_networks_on_dhcp_agent": "list_networks_on_dhcp_agent", + "list_packet_filters": "list_packet_filters", + "list_policy_profile_bindings": "list_policy_profile_bindings", + "list_policy_profiles": "list_policy_profiles", + "list_pools": "list_pools", + "list_pools_on_lbaas_agent": "list_pools_on_lbaas_agent", + "list_ports": "list_ports", + "list_qos_queues": "list_qos_queues", + "list_quotas": "list_quotas", + "list_routers": "list_routers", + "list_routers_on_l3_agent": "list_routers_on_l3_agent", + "list_security_group_rules": "list_security_group_rules", + "list_security_groups": "list_security_groups", + "list_service_providers": "list_service_providers", + "list_subnets": "list_subnets", + "list_vips": "list_vips", + "list_vpnservices": "list_vpnservices", + "remove_gateway_router": "remove_gateway_router", + "remove_interface_router": "remove_interface_router", + "remove_network_from_dhcp_agent": "remove_network_from_dhcp_agent", + "remove_router_from_l3_agent": "remove_router_from_l3_agent", + "retrieve_pool_stats": "retrieve_pool_stats", + "show_agent": "show_agent", + "show_credential": "show_credential", + "show_extension": "show_extension", + "show_firewall": "show_firewall", + "show_firewall_policy": "show_firewall_policy", + "show_firewall_rule": "show_firewall_rule", + "show_floatingip": "show_floatingip", + "show_gateway_device": "show_gateway_device", + "show_health_monitor": "show_health_monitor", + "show_ikepolicy": "show_ikepolicy", + "show_ipsec_site_connection": "show_ipsec_site_connection", + "show_ipsecpolicy": "show_ipsecpolicy", + "show_member": "show_member", + "show_metering_label": "show_metering_label", + "show_metering_label_rule": "show_metering_label_rule", + "show_net_partition": "show_net_partition", + "show_network": "show_network", + "show_network_gateway": "show_network_gateway", + "show_network_profile": "show_network_profile", + "show_packet_filter": "show_packet_filter", + "show_policy_profile": "show_policy_profile", + "show_pool": "show_pool", + "show_port": "show_port", + "show_qos_queue": "show_qos_queue", + "show_quota": "show_quota", + "show_router": "show_router", + "show_security_group": "show_security_group", + "show_security_group_rule": "show_security_group_rule", + "show_subnet": "show_subnet", + "show_vip": "show_vip", + "show_vpnservice": "show_vpnservice", + "update_agent": "update_agent", + "update_credential": "update_credential", + "update_firewall": "update_firewall", + "update_firewall_policy": "update_firewall_policy", + "update_firewall_rule": "update_firewall_rule", + "update_floatingip": "update_floatingip", + "update_gateway_device": "update_gateway_device", + "update_health_monitor": "update_health_monitor", + "update_ikepolicy": "update_ikepolicy", + "update_ipsec_site_connection": "update_ipsec_site_connection", + "update_ipsecpolicy": "update_ipsecpolicy", + "update_member": "update_member", + "update_network": "update_network", + "update_network_gateway": "update_network_gateway", + "update_network_profile": "update_network_profile", + "update_packet_filter": "update_packet_filter", + "update_policy_profile": "update_policy_profile", + "update_pool": "update_pool", + "update_port": "update_port", + "update_quota": "update_quota", + "update_router": "update_router", + "update_security_group": "update_security_group", + "update_subnet": "update_subnet", + "update_vip": "update_vip", + "update_vpnservice": "update_vpnservice" } } \ No newline at end of file diff --git a/mistral/tests/resources/openstack_tasks/neutron.yaml b/mistral/tests/resources/openstack_tasks/neutron.yaml new file mode 100644 index 000000000..8bea9d259 --- /dev/null +++ b/mistral/tests/resources/openstack_tasks/neutron.yaml @@ -0,0 +1,4 @@ +Workflow: + tasks: + neutron_list_networks: + action: neutron.list_networks diff --git a/mistral/tests/unit/actions/openstack/test_neutron_generator.py b/mistral/tests/unit/actions/openstack/test_neutron_generator.py new file mode 100644 index 000000000..1581d9352 --- /dev/null +++ b/mistral/tests/unit/actions/openstack/test_neutron_generator.py @@ -0,0 +1,32 @@ +# Copyright 2014 - Mirantis, Inc. +# +# 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 oslotest import base + +from mistral.actions.openstack.action_generator import generators +from mistral.actions.openstack import actions + + +class NeutronGeneratorTest(base.BaseTestCase): + def test_generator(self): + action_name = "neutron.show_network" + generator = generators.NeutronActionGenerator + action_classes = generator.create_action_classes() + short_action_name = action_name.split(".")[1] + action_class = action_classes[short_action_name] + + self.assertIsNotNone(generator) + self.assertIn(short_action_name, action_classes) + self.assertTrue(issubclass(action_class, actions.NeutronAction)) + self.assertEqual("show_network", action_class.client_method_name) diff --git a/mistral/tests/unit/actions/openstack/test_openstack_actions.py b/mistral/tests/unit/actions/openstack/test_openstack_actions.py index 7d213d893..08f28089f 100644 --- a/mistral/tests/unit/actions/openstack/test_openstack_actions.py +++ b/mistral/tests/unit/actions/openstack/test_openstack_actions.py @@ -66,3 +66,15 @@ class OpenStackActionTest(base.BaseTestCase): self.assertTrue(mocked().stacks.get.called) mocked().stacks.get.assert_called_once_with(id="1234-abcd") + + @mock.patch.object(actions.NeutronAction, '_get_client') + def test_neutron_action(self, mocked): + method_name = "show_network" + action_class = actions.NeutronAction + action_class.client_method_name = method_name + params = {'id': '1234-abcd'} + action = action_class(**params) + action.run() + + self.assertTrue(mocked().show_network.called) + mocked().show_network.assert_called_once_with(id="1234-abcd") diff --git a/mistral/tests/unit/engine/test_openstack_actions.py b/mistral/tests/unit/engine/test_openstack_actions.py index 5685077f2..760cdff28 100644 --- a/mistral/tests/unit/engine/test_openstack_actions.py +++ b/mistral/tests/unit/engine/test_openstack_actions.py @@ -169,3 +169,28 @@ class OpenStackActionsEngineTest(base.EngineTestCase): self.assertEqual(states.SUCCESS, task['state']) self.assertEqual("stacks", task['output']['task'][task_name]) + + @mock.patch.object(actions.NeutronAction, 'run', + mock.Mock(return_value="networks")) + def test_neutron_action(self): + context = {} + wb = create_workbook('openstack_tasks/neutron.yaml') + task_name = 'neutron_list_networks' + execution = self.engine.start_workflow_execution(wb['name'], + task_name, + context) + + # We have to reread execution to get its latest version. + execution = db_api.execution_get(execution['id']) + + self.assertEqual(states.SUCCESS, execution['state']) + + tasks = db_api.tasks_get(workbook_name=wb['name'], + execution_id=execution['id']) + + self.assertEqual(1, len(tasks)) + + task = self._assert_single_item(tasks, name=task_name) + + self.assertEqual(states.SUCCESS, task['state']) + self.assertEqual("networks", task['output']['task'][task_name]) diff --git a/requirements.txt b/requirements.txt index ba10b3465..afcc4b842 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,6 +17,7 @@ oslo.messaging>=1.3.0 paramiko>=1.13.0 python-heatclient>=0.2.9 python-keystoneclient>=0.9.0 +python-neutronclient>=2.3.6,<3 python-novaclient>=2.17 python-glanceclient>=0.13 networkx>=1.8