Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for source groups by name #39

Merged
merged 1 commit into from
Mar 17, 2015
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 78 additions & 39 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,17 +234,17 @@ 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)
Expand Down Expand Up @@ -287,7 +301,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 @@ -301,7 +315,7 @@ def _update_sg_rules(module, network_client, sg, wanted_rules):
return changed


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 +325,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 +342,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