Skip to content

Commit

Permalink
Add support for source groups by name
Browse files Browse the repository at this point in the history
  • Loading branch information
Etrik Patricella committed Mar 17, 2015
1 parent bfe8dcc commit c52f3d5
Showing 1 changed file with 81 additions and 54 deletions.
135 changes: 81 additions & 54 deletions neutron_sec_group
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ options:
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"
remote_ip_prefix|remote_group_id|remote_group_name"
required: false
default: none
requirements: ["neutronclient", "keystoneclient"]
Expand Down Expand Up @@ -93,6 +93,18 @@ neutron_sec_group:
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_group_id: UUID_OF_GROUP
- direction: "ingress"
port_range_min: "22"
port_range_max: "22"
ethertype: "IPv4"
protocol: "tcp"
remote_group_name: 'default'
'''

try:
Expand All @@ -102,13 +114,14 @@ try:
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/"),
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),
Expand All @@ -117,7 +130,7 @@ def main():
region_name=dict(default=None),
rules=dict(default=None),
tenant_name=dict(required=False),
state=dict(default="present", choices=['present', 'absent'])
state=dict(default='present', choices=['present', 'absent'])
),
supports_check_mode=True
)
Expand All @@ -126,35 +139,38 @@ def main():

try:
# Get id of security group (as a result check whether it exists)
tenant_id = _get_tenant_id(module, identity_client)

params = {
'name': module.params['name'],
'tenant_id': _get_tenant_id(module, identity_client),
'fields': 'id'
'name': module.params['name'],
'tenant_id': tenant_id,
'fields': 'id',
}
sec_groups = network_client.list_security_groups(**params)["security_groups"]

sec_groups = network_client.list_security_groups(**params)['security_groups']
if len(sec_groups) > 1:
raise exceptions.NeutronClientNoUniqueMatch(resource='security_group',name=name)
raise exceptions.NeutronClientNoUniqueMatch(resource='security_group', name=name)
elif len(sec_groups) == 0:
sec_group_exists = False
sec_group_exists = False
else:
sec_group = sec_groups[0]
sec_group_exists = True
sec_group = sec_groups[0]
sec_group_exists = True

# state=present -> create or update depending on whether sg exists.
if module.params['state'] == "present":
if module.params['state'] == 'present':
# UPDATE
if sec_group_exists:
changed, sg = _update_sg(module, network_client, sec_group)
changed, sg = _update_sg(module, network_client, sec_group, tenant_id)
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)
sg = _create_sg(module, network_client, tenant_id)
module.exit_json(sec_group=sg, created=True, changed=True)
# DELETE
elif module.params['state'] == "absent" and sec_group_exists:
elif module.params['state'] == 'absent' and sec_group_exists:
_delete_sg(module, network_client, sec_group)
module.exit_json(changed=True)

Expand All @@ -165,6 +181,7 @@ def main():
except Exception as exc:
module.fail_json(msg="Error: %s" % str(exc))


def _delete_sg(module, network_client, sec_group):
"""
Deletes a security group.
Expand All @@ -177,7 +194,7 @@ def _delete_sg(module, network_client, sec_group):
network_client.delete_security_group(sec_group['id'])


def _create_sg(module, network_client, identity_client):
def _create_sg(module, network_client, tenant_id):
"""
Creates a security group.
:param module: module to get security group params from.
Expand All @@ -188,26 +205,23 @@ def _create_sg(module, network_client, identity_client):
"""
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)
'security_group': {
'name': module.params['name'],
'description': module.params['description'],
'tenant_id': tenant_id
}
}

sg = network_client.create_security_group(data)
sg = sg["security_group"]
sg = sg['security_group']

changed, sg = _update_sg(module, network_client, sg)
changed, sg = _update_sg(module, network_client, sg, tenant_id)
return sg


def _update_sg(module, network_client, sg):
def _update_sg(module, network_client, sg, tenant_id):
"""
Updates a security group.
:param module: module to get updated security group param from.
Expand All @@ -220,38 +234,26 @@ def _update_sg(module, network_client, sg):
sg = sg['security_group']

# We only allow description updating, no name updating
if module.params["description"] \
if module.params['description'] \
and not module.params['description'] == sg['description'] \
and module.check_mode:

changed = True
elif module.params["description"] \
elif module.params['description'] \
and not module.params['description'] == sg['description'] \
and not module.check_mode:
body = {
"security_group": {
"description": module.params["description"]
'security_group': {
'description': module.params['description']
}
}
sg = network_client.update_security_group(sg['id'], body)
sg = sg['security_group']
changed = True

if module.params['rules'] is not None:
rules_changed = _update_sg_rules(module, network_client, sg,
module.params['rules'])
changed |= rules_changed

return changed, sg


def _update_sg_rules(module, network_client, sg, wanted_rules):
"""
Updates rules of a security group.
"""

changed = False
# Security rules group update
existing_rules = sg['security_group_rules']
wanted_rules = module.params['rules']

#check ok
ok_rules = []
Expand Down Expand Up @@ -287,7 +289,7 @@ def _update_sg_rules(module, network_client, sg, wanted_rules):
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)
sg = _create_sg_rules(network_client, sg, new_rules, tenant_id)
changed = True

#then delete not ok
Expand All @@ -298,10 +300,10 @@ def _update_sg_rules(module, network_client, sg, wanted_rules):
sg = network_client.delete_security_group_rule(rule['id'])
changed = True

return changed
return changed, sg


def _create_sg_rules(network_client, sg, rules):
def _create_sg_rules(network_client, sg, rules, tenant_id):
"""
Creates a set of security group rules in a given security group.
:param network_client: network client to use to create rules.
Expand All @@ -311,10 +313,15 @@ def _create_sg_rules(network_client, sg, rules):
"""
if rules:
for rule in rules:
if 'remote_group_name' in rule:
rule['remote_group_id'] = _get_security_group_id(network_client,
rule['remote_group_name'],
tenant_id)
rule.pop('remote_group_name', None)
rule['tenant_id'] = sg['tenant_id']
rule['security_group_id'] = sg['id']
data = {
"security_group_rule": rule
'security_group_rule': rule
}
network_client.create_security_group_rule(data)

Expand All @@ -323,23 +330,43 @@ def _create_sg_rules(network_client, sg, rules):
return sg


def _get_security_group_id(network_client, group_name, tenant_id):
"""
Lookup the UUID for a named security group. This provides the ability to
specify a SourceGroup via remote_group_id.
http://docs.openstack.org/openstack-ops/content/security_groups.html
This will return the first match to a group name.
:param network_client: network client ot use to lookup group_id
:param group_name: The name of the security group to lookup
"""

params = {
'name': group_name,
'tenant_id': tenant_id,
'fields': 'id'
}

return network_client.list_security_groups(**params)['security_groups'][0]['id']


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
if tenant_name is not specified in the module params uses login_tenant_name
: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
tenant_name = module.params['login_tenant_name']
else:
tenant_name = module.params['tenant_name']
tenant = _get_tenant(identity_client, tenant_name)
tenant_id = tenant.id

return tenant_id
tenant = _get_tenant(identity_client, tenant_name)

return tenant.id


def _get_tenant(identity_client, tenant_name):
Expand Down

0 comments on commit c52f3d5

Please sign in to comment.