summaryrefslogtreecommitdiff
path: root/neutron_sec_group
diff options
context:
space:
mode:
authorJoris Roovers <jroovers@cisco.com>2014-07-25 14:27:12 +0200
committerJoris Roovers <jroovers@cisco.com>2014-07-25 14:27:12 +0200
commitcf1bf7c259458fba76f0d441e04e657ea73b14c8 (patch)
tree7acd6bb071bd6f436fbacacdb94d9467e072d722 /neutron_sec_group
parent1b34a7a2f777340f07e9ff19f2d39e9f4d1c26bd (diff)
downloadopenstack-ansible-modules-cf1bf7c259458fba76f0d441e04e657ea73b14c8.tar.gz
Openstack security groups module
Allows users to easily create, update or delete security groups, as well as managing security group rules within a security group. Works for both admin users (they can add security groups to all projects), as well as regular users (they can only add security groups to their own projects).
Diffstat (limited to 'neutron_sec_group')
-rw-r--r--neutron_sec_group323
1 files changed, 323 insertions, 0 deletions
diff --git a/neutron_sec_group b/neutron_sec_group
new file mode 100644
index 0000000..9e273ea
--- /dev/null
+++ b/neutron_sec_group
@@ -0,0 +1,323 @@
+#!/usr/bin/python
+#
+# (c) Cisco Systems, 2014
+#
+# This module is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This software is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this software. If not, see <http://www.gnu.org/licenses/>.
+
+try:
+ import neutronclient.v2_0.client
+ import keystoneclient.v2_0.client
+except ImportError:
+ print "failed=True msg='neutronclient and keystoneclient are required'"
+
+DOCUMENTATION = '''
+---
+module: neutron_sec_group
+short_description: Create, Remove or Update Openstack security groups
+description:
+ - Create, Remove or Update Openstack security groups
+options:
+ login_username:
+ description:
+ - login username to authenticate to keystone
+ required: true
+ login_password:
+ description:
+ - Password of login user
+ required: true
+ login_tenant_name:
+ description:
+ - The tenant name of the login user
+ required: true
+ auth_url:
+ description:
+ - The keystone url for authentication
+ required: false
+ default: 'http://127.0.0.1:5000/v2.0/'
+ region_name:
+ description:
+ - Name of the region
+ required: false
+ default: None
+ state:
+ description:
+ - Indicate desired state of the security group
+ choices: ['present', 'absent']
+ default: present
+ name:
+ description:
+ - Name to be given to the security group
+ required: true
+ default: None
+ tenant_name:
+ description:
+ - Name of the tenant for which the security group has to be created,
+ if none, the security group would be created for the login tenant.
+ required: false
+ default: None
+ rules:
+ description:
+ - "List of security group rules. Available parameters of a rule:
+ direction, port_range_min, port_range_max, ethertype, protocol,
+ remote_ip_prefix/remote_ip_group"
+ required: false
+ default: none
+requirements: ["neutronclient", "keystoneclient"]
+'''
+
+EXAMPLES = '''
+# Creates a security group with a number of rules
+neutron_sec_group:
+ login_username: "demo"
+ login_password: "password"
+ login_tenant_name: "demo"
+ auth_url: "http://127.0.0.1:5000/v2.0"
+ name: "sg-test"
+ description: "Description of the security group"
+ state: "present"
+ rules:
+ - { direction: "ingress",
+ port_range_min: "80",
+ port_range_max: "80",
+ ethertype: "IPv4",
+ protocol: "tcp",
+ remote_ip_prefix: "10.0.0.1/24"
+ }
+ - { direction: "ingress",
+ port_range_min: "22",
+ port_range_max: "22",
+ ethertype: "IPv4",
+ protocol: "tcp",
+ remote_ip_prefix: "10.0.0.1/24"
+ }
+'''
+
+
+def main():
+ """
+ Main function - entry point. The magic starts here ;-)
+ """
+ module = AnsibleModule(
+ argument_spec=dict(
+ auth_url=dict(default="http://127.0.0.1:5000/v2.0/"),
+ login_username=dict(required=True),
+ login_password=dict(required=True),
+ login_tenant_name=dict(required=True),
+ name=dict(required=True),
+ description=dict(default=None),
+ region_name=dict(default=None),
+ rules=dict(default=None),
+ tenant_name=dict(required=False),
+ state=dict(default="present", choices=['present', 'absent'])
+ )
+ )
+ network_client = _get_network_client(module.params)
+ identity_client = _get_identity_client(module.params)
+ try:
+ # Get id of security group (as a result check whether it exists)
+ sec_groups = network_client.list_security_groups()["security_groups"]
+ sec_group = next((sg for sg in sec_groups
+ if sg["name"] == module.params['name']), None)
+ sec_group_exists = True if sec_group else False
+
+ # state=present -> create or update depending on whether sg exists.
+ if module.params['state'] == "present":
+ # UPDATE
+ if sec_group_exists:
+ sg = _update_sg(module, network_client, sec_group)
+ module.exit_json(sec_group=sg, updated=True, changed=True)
+ # CREATE
+ else:
+ sg = _create_sg(module, network_client, identity_client)
+ module.exit_json(sec_group=sg, created=True, changed=True)
+ # DELETE
+ elif module.params['state'] == "absent" and sec_group_exists:
+ _delete_sg(module, network_client, sec_group)
+ module.exit_json(changed=True)
+
+ module.exit_json(changed=False)
+
+ except Exception, e:
+ _handle_exception(module, e)
+
+
+def _delete_sg(module, network_client, sec_group):
+ """
+ Deletes a security group.
+ :param module: module to get security group params from.
+ :param network_client: network client to use.
+ :param sec_group: security group to delete.
+ """
+ network_client.delete_security_group(sec_group['id'])
+
+
+def _create_sg(module, network_client, identity_client):
+ """
+ Creates a security group.
+ :param module: module to get security group params from.
+ :param network_client: network client to use.
+ :param: identity_client: identity_client used if an admin performs the
+ operation for a different tenant.
+ :return: newly created security group.
+ """
+ # NOTE: we don't do explicit rule validation, the API server will take
+ # care of that for us :-)
+ rules = module.params['rules']
+
+ data = {
+ "security_group": {
+ "name": module.params['name'],
+ "description": module.params['description'],
+ }
+ }
+ _add_tenant_id(identity_client, module.params, data['security_group'])
+
+ sg = network_client.create_security_group(data)
+ sg = sg["security_group"]
+
+ sg = _create_sg_rules(network_client, sg, rules)
+ return sg
+
+
+def _update_sg(module, network_client, sg):
+ """
+ Updates a security group.
+ :param module: module to get updated security group param from.
+ :param network_client: network client to use.
+ :param sg: security group that needs to be updated.
+ :return: the updated security group.
+ """
+ # We only allow description updating, no name updating
+ if module.params["description"]:
+ body = {
+ "security_group": {
+ "description": module.params["description"]
+ }
+ }
+ sg = network_client.update_security_group(sg['id'], body)
+ sg = sg['security_group']
+
+ # Security rules group update
+ # We keep things simple: first remove all rules, then insert the new
+ # rules. Not terribly efficient, but easy to implement.
+ existing_rules = network_client.list_security_group_rules(sg['id'])
+ existing_rules = existing_rules['security_group_rules']
+
+ for rule in existing_rules:
+ network_client.delete_security_group_rule(rule['id'])
+
+ sg = _create_sg_rules(network_client, sg, module.params['rules'])
+
+ return sg
+
+
+def _create_sg_rules(network_client, sg, rules):
+ """
+ Creates a set of security group rules in a given security group.
+ :param network_client: network client to use to create rules.
+ :param sg: security group to create rules in.
+ :param rules: rules to create.
+ :return: the updated security group.
+ """
+ if rules:
+ for rule in rules:
+ rule['security_group_id'] = sg['id']
+ data = {
+ "security_group_rule": rule
+ }
+ network_client.create_security_group_rule(data)
+
+ # fetch security group again to show end result
+ return network_client.show_security_group(sg['id'])['security_group']
+ return sg
+
+
+def _handle_exception(module, e):
+ """
+ Convenience method to deal with exceptions.
+ :param module: module object
+ :param e: exception to deal with
+ """
+ if type(e) is neutronclient.common.exceptions.Unauthorized:
+ module.fail_json(msg="Authenticated error: %s" % str(e))
+ else:
+ module.fail_json(msg="An error occured: %s" % str(e))
+
+
+def _add_tenant_id(identity_client, module_params, data):
+ """
+ Adds the tenant_id to the given data dictionary if tenant_name
+ is specified in the module params.
+ :param: identity_client: identity_client used to get the tenant_id from its
+ name.
+ :param module_params: module parameters.
+ :param data: data dictionary to add tenant id to.
+ """
+ tenant_name = module_params.get('tenant_name')
+ if tenant_name:
+ tenant = _get_tenant(identity_client, tenant_name)
+ data['tenant_id'] = tenant.id
+
+
+def _get_tenant(identity_client, tenant_name):
+ """
+ Returns the tenant, given the tenant_name.
+ :param: identity_client: identity client to use to do the required requests.
+ :param: tenant_name: name of the tenant.
+ :return: tenant for which the name was given.
+ """
+ tenants = identity_client.tenants.list()
+ tenant = next((t for t in tenants if t.name == tenant_name), None)
+ if not tenant:
+ raise Exception("Tenant with name '%s' not found." % tenant_name)
+
+ return tenant
+
+
+def _get_network_client(module_params):
+ """
+ :param module_params: module params containing the openstack credentials
+ used to authenticate.
+ :return: a neutron client.
+ """
+ client = neutronclient.v2_0.client.Client(
+ username=module_params.get('login_username'),
+ password=module_params.get('login_password'),
+ tenant_name=module_params.get('login_tenant_name'),
+ auth_url=module_params.get('auth_url'),
+ region_name=module_params.get('region_name'))
+
+ return client
+
+
+def _get_identity_client(module_params):
+ """
+ :param module_params: module params containing the openstack credentials
+ used to authenticate.
+ :return: a keystone client.
+ """
+ client = keystoneclient.v2_0.client.Client(
+ username=module_params.get('login_username'),
+ password=module_params.get('login_password'),
+ tenant_name=module_params.get('login_tenant_name'),
+ auth_url=module_params.get('auth_url'),
+ region_name=module_params.get('region_name'))
+
+ return client
+
+
+# Let's get the party started!
+from ansible.module_utils.basic import *
+
+main() \ No newline at end of file