summaryrefslogtreecommitdiff
path: root/openstack/usr/share/openstack/modules/neutron_sec_group
diff options
context:
space:
mode:
Diffstat (limited to 'openstack/usr/share/openstack/modules/neutron_sec_group')
-rw-r--r--openstack/usr/share/openstack/modules/neutron_sec_group382
1 files changed, 382 insertions, 0 deletions
diff --git a/openstack/usr/share/openstack/modules/neutron_sec_group b/openstack/usr/share/openstack/modules/neutron_sec_group
new file mode 100644
index 00000000..518c50e7
--- /dev/null
+++ b/openstack/usr/share/openstack/modules/neutron_sec_group
@@ -0,0 +1,382 @@
+#!/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/>.
+
+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"
+'''
+
+try:
+ import neutronclient.v2_0.client
+ import keystoneclient.v2_0.client
+ from neutronclient.common import exceptions
+except ImportError:
+ print "failed=True msg='neutronclient and keystoneclient are required'"
+
+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'])
+ ),
+ supports_check_mode=True
+ )
+ 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)
+ params = {
+ 'name': module.params['name'],
+ 'tenant_id': _get_tenant_id(module, identity_client),
+ 'fields': 'id'
+ }
+ sec_groups = network_client.list_security_groups(**params)["security_groups"]
+ if len(sec_groups) > 1:
+ raise exceptions.NeutronClientNoUniqueMatch(resource='security_group',name=name)
+ elif len(sec_groups) == 0:
+ sec_group_exists = False
+ else:
+ sec_group = sec_groups[0]
+ sec_group_exists = True
+
+ # state=present -> create or update depending on whether sg exists.
+ if module.params['state'] == "present":
+ # UPDATE
+ if sec_group_exists:
+ changed, sg = _update_sg(module, network_client, sec_group)
+ if changed:
+ module.exit_json(sec_group=sg, updated=True, changed=changed)
+ else:
+ module.exit_json(sec_group=sg, changed=changed)
+ # 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 exceptions.Unauthorized as exc:
+ module.fail_json(msg="Authentication error: %s" % str(exc))
+ except Exception as exc:
+ module.fail_json(msg="Error: %s" % str(exc))
+
+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.
+ """
+ if module.check_mode:
+ return
+ 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.
+ """
+ if module.check_mode:
+ return None
+ # 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'],
+ 'tenant_id': _get_tenant_id(module, identity_client)
+ }
+ }
+
+ sg = network_client.create_security_group(data)
+ sg = sg["security_group"]
+
+ changed, sg = _update_sg(module, network_client, sg)
+ 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: True/False, the updated security group.
+ """
+ changed = False
+ sg = network_client.show_security_group(sg['id'])
+ sg = sg['security_group']
+
+ # We only allow description updating, no name updating
+ if module.params["description"] \
+ and not module.params['description'] == sg['description'] \
+ and module.check_mode:
+
+ changed = True
+ elif module.params["description"] \
+ and not module.params['description'] == sg['description'] \
+ and not module.check_mode:
+ body = {
+ "security_group": {
+ "description": module.params["description"]
+ }
+ }
+ sg = network_client.update_security_group(sg['id'], body)
+ sg = sg['security_group']
+ changed = True
+
+ # Security rules group update
+ existing_rules = sg['security_group_rules']
+ wanted_rules = module.params['rules']
+
+ #check ok
+ ok_rules = []
+ for new_rule in wanted_rules:
+ # Ugly: define tenant also here so that matches
+ new_rule['tenant_id'] = sg['tenant_id']
+ # protocol is in lowercase
+ if 'protocol' in new_rule:
+ new_rule['protocol'] = new_rule['protocol'].lower()
+
+ matched_id = None
+ for old_rule in existing_rules:
+ clean_new_rule = new_rule.copy()
+ clean_old_rule = old_rule.copy()
+ old_id = clean_old_rule.pop('id')
+ clean_old_rule.pop('security_group_id')
+ for key in clean_old_rule.keys():
+ if key not in clean_new_rule:
+ clean_new_rule[key] = None
+ continue
+ value = clean_new_rule[key]
+ if isinstance(value, (str, unicode)) and value.isdigit():
+ clean_new_rule[key] = int(value)
+ if cmp(clean_old_rule, clean_new_rule) == 0:
+ matched_id = old_id
+ break
+
+ if matched_id:
+ new_rule['done'] = True
+ ok_rules.append(matched_id)
+
+ #apply new first
+ new_rules = [rule for rule in wanted_rules if 'done' not in rule]
+ if len(new_rules):
+ if not module.check_mode:
+ sg = _create_sg_rules(network_client, sg, new_rules)
+ changed = True
+
+ #then delete not ok
+ for rule in existing_rules:
+ if rule['id'] in ok_rules:
+ continue
+ if not module.check_mode:
+ sg = network_client.delete_security_group_rule(rule['id'])
+ changed = True
+
+ return changed, 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['tenant_id'] = sg['tenant_id']
+ 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 _get_tenant_id(module, identity_client):
+ """
+ Returns the tenant_id, given tenant_name.
+ if tenant_name is not specified in the module params uses tenant_id
+ from Keystone session
+ :param identity_client: identity_client used to get the tenant_id from its
+ name.
+ :param module_params: module parameters.
+ """
+ if not module.params['tenant_name']:
+ tenant_id = identity_client.tenant_id
+ else:
+ tenant_name = module.params['tenant_name']
+ tenant = _get_tenant(identity_client, tenant_name)
+ tenant_id = tenant.id
+
+ return 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()