mirror of https://github.com/ansible/ansible.git
Remove old deprecated OpenStack modules (#23002)
These were deprecated back in the 1.x timeframe, so have been deprecated for the 2 cycles required. They also do things wrong and should be avoided anyway. Go ahead and remove them.pull/22264/head
parent
42383656e7
commit
443102c9f0
@ -1,283 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Benno Joy <benno@ansible.com>
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.0',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: glance_image
|
||||
author: "Benno Joy (@bennojoy)"
|
||||
version_added: "1.2"
|
||||
deprecated: Deprecated in 1.10. Use M(os_image) instead.
|
||||
short_description: Add/Delete images from glance
|
||||
description:
|
||||
- Add or Remove images from the glance repository.
|
||||
options:
|
||||
login_username:
|
||||
description:
|
||||
- login username to authenticate to keystone
|
||||
required: true
|
||||
default: admin
|
||||
login_password:
|
||||
description:
|
||||
- Password of login user
|
||||
required: true
|
||||
default: 'yes'
|
||||
login_tenant_name:
|
||||
description:
|
||||
- The tenant name of the login user
|
||||
required: true
|
||||
default: 'yes'
|
||||
auth_url:
|
||||
description:
|
||||
- The keystone url for authentication
|
||||
required: false
|
||||
default: http://127.0.0.1:35357/v2.0/
|
||||
region_name:
|
||||
description:
|
||||
- Name of the region
|
||||
required: false
|
||||
default: None
|
||||
state:
|
||||
description:
|
||||
- Indicate desired state of the resource
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
name:
|
||||
description:
|
||||
- Name that has to be given to the image
|
||||
required: true
|
||||
default: None
|
||||
disk_format:
|
||||
description:
|
||||
- The format of the disk that is getting uploaded
|
||||
required: false
|
||||
default: qcow2
|
||||
container_format:
|
||||
description:
|
||||
- The format of the container
|
||||
required: false
|
||||
default: bare
|
||||
owner:
|
||||
description:
|
||||
- The owner of the image
|
||||
required: false
|
||||
default: None
|
||||
min_disk:
|
||||
description:
|
||||
- The minimum disk space required to deploy this image
|
||||
required: false
|
||||
default: None
|
||||
min_ram:
|
||||
description:
|
||||
- The minimum ram required to deploy this image
|
||||
required: false
|
||||
default: None
|
||||
is_public:
|
||||
description:
|
||||
- Whether the image can be accessed publicly
|
||||
required: false
|
||||
default: 'yes'
|
||||
copy_from:
|
||||
description:
|
||||
- A url from where the image can be downloaded, mutually exclusive with file parameter
|
||||
required: false
|
||||
default: None
|
||||
timeout:
|
||||
description:
|
||||
- The time to wait for the image process to complete in seconds
|
||||
required: false
|
||||
default: 180
|
||||
file:
|
||||
description:
|
||||
- The path to the file which has to be uploaded, mutually exclusive with copy_from
|
||||
required: false
|
||||
default: None
|
||||
endpoint_type:
|
||||
description:
|
||||
- The name of the glance service's endpoint URL type
|
||||
choices: [publicURL, internalURL]
|
||||
required: false
|
||||
default: publicURL
|
||||
version_added: "1.7"
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python-glanceclient"
|
||||
- "python-keystoneclient"
|
||||
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Upload an image from an HTTP URL
|
||||
glance_image:
|
||||
login_username: admin
|
||||
login_password: passme
|
||||
login_tenant_name: admin
|
||||
name: cirros
|
||||
container_format: bare
|
||||
disk_format: qcow2
|
||||
state: present
|
||||
copy_from: http://launchpad.net/cirros/trunk/0.3.0/+download/cirros-0.3.0-x86_64-disk.img
|
||||
'''
|
||||
|
||||
import time
|
||||
try:
|
||||
import glanceclient
|
||||
HAS_GLANCECLIENT = True
|
||||
except ImportError:
|
||||
HAS_GLANCECLIENT = False
|
||||
try:
|
||||
from keystoneclient.v2_0 import client as ksclient
|
||||
HAS_KEYSTONECLIENT = True
|
||||
except ImportError:
|
||||
HAS_KEYSTONECLIENT= False
|
||||
|
||||
|
||||
def _get_ksclient(module, kwargs):
|
||||
try:
|
||||
client = ksclient.Client(username=kwargs.get('login_username'),
|
||||
password=kwargs.get('login_password'),
|
||||
tenant_name=kwargs.get('login_tenant_name'),
|
||||
auth_url=kwargs.get('auth_url'))
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Error authenticating to the keystone: %s " % e.message)
|
||||
return client
|
||||
|
||||
|
||||
def _get_endpoint(module, client, endpoint_type):
|
||||
try:
|
||||
endpoint = client.service_catalog.url_for(service_type='image', endpoint_type=endpoint_type)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Error getting endpoint for glance: %s" % e.message)
|
||||
return endpoint
|
||||
|
||||
|
||||
def _get_glance_client(module, kwargs):
|
||||
_ksclient = _get_ksclient(module, kwargs)
|
||||
token = _ksclient.auth_token
|
||||
endpoint =_get_endpoint(module, _ksclient, kwargs.get('endpoint_type'))
|
||||
kwargs = {
|
||||
'token': token,
|
||||
}
|
||||
try:
|
||||
client = glanceclient.Client('1', endpoint, **kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Error in connecting to glance: %s" % e.message)
|
||||
return client
|
||||
|
||||
|
||||
def _glance_image_present(module, params, client):
|
||||
try:
|
||||
for image in client.images.list():
|
||||
if image.name == params['name']:
|
||||
return image.id
|
||||
return None
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Error in fetching image list: %s" % e.message)
|
||||
|
||||
|
||||
def _glance_image_create(module, params, client):
|
||||
kwargs = {
|
||||
'name': params.get('name'),
|
||||
'disk_format': params.get('disk_format'),
|
||||
'container_format': params.get('container_format'),
|
||||
'owner': params.get('owner'),
|
||||
'is_public': params.get('is_public'),
|
||||
'copy_from': params.get('copy_from'),
|
||||
}
|
||||
try:
|
||||
timeout = float(params.get('timeout'))
|
||||
expire = time.time() + timeout
|
||||
image = client.images.create(**kwargs)
|
||||
if not params['copy_from']:
|
||||
image.update(data=open(params['file'], 'rb'))
|
||||
while time.time() < expire:
|
||||
image = client.images.get(image.id)
|
||||
if image.status == 'active':
|
||||
break
|
||||
time.sleep(5)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Error in creating image: %s" % e.message)
|
||||
if image.status == 'active':
|
||||
module.exit_json(changed=True, result=image.status, id=image.id)
|
||||
else:
|
||||
module.fail_json(msg=" The module timed out, please check manually " + image.status)
|
||||
|
||||
|
||||
def _glance_delete_image(module, params, client):
|
||||
try:
|
||||
for image in client.images.list():
|
||||
if image.name == params['name']:
|
||||
client.images.delete(image)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Error in deleting image: %s" % e.message)
|
||||
module.exit_json(changed=True, result="Deleted")
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
argument_spec = openstack_argument_spec()
|
||||
argument_spec.update(dict(
|
||||
name = dict(required=True),
|
||||
disk_format = dict(default='qcow2', choices=['aki', 'vhd', 'vmdk', 'raw', 'qcow2', 'vdi', 'iso']),
|
||||
container_format = dict(default='bare', choices=['aki', 'ari', 'bare', 'ovf']),
|
||||
owner = dict(default=None),
|
||||
min_disk = dict(default=None),
|
||||
min_ram = dict(default=None),
|
||||
is_public = dict(default=True),
|
||||
copy_from = dict(default= None),
|
||||
timeout = dict(default=180),
|
||||
file = dict(default=None),
|
||||
endpoint_type = dict(default='publicURL', choices=['publicURL', 'internalURL']),
|
||||
state = dict(default='present', choices=['absent', 'present'])
|
||||
))
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
mutually_exclusive = [['file','copy_from']],
|
||||
)
|
||||
|
||||
if not HAS_GLANCECLIENT:
|
||||
module.fail_json(msg='python-glanceclient is required for this module')
|
||||
if not HAS_KEYSTONECLIENT:
|
||||
module.fail_json(msg='python-keystoneclient is required for this module')
|
||||
|
||||
if module.params['state'] == 'present':
|
||||
if not module.params['file'] and not module.params['copy_from']:
|
||||
module.fail_json(msg="Either file or copy_from variable should be set to create the image")
|
||||
client = _get_glance_client(module, module.params)
|
||||
id = _glance_image_present(module, module.params, client)
|
||||
if not id:
|
||||
_glance_image_create(module, module.params, client)
|
||||
module.exit_json(changed=False, id=id, result="success")
|
||||
|
||||
if module.params['state'] == 'absent':
|
||||
client = _get_glance_client(module, module.params)
|
||||
id = _glance_image_present(module, module.params, client)
|
||||
if not id:
|
||||
module.exit_json(changed=False, result="Success")
|
||||
else:
|
||||
_glance_delete_image(module, module.params, client)
|
||||
|
||||
# this is magic, see lib/ansible/module_common.py
|
||||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.openstack import *
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,445 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible 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.
|
||||
#
|
||||
# Ansible 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 Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Based on Jimmy Tang's implementation
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.0',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: keystone_user
|
||||
version_added: "1.2"
|
||||
deprecated: Deprecated in 2.0. Use M(os_user) instead.
|
||||
short_description: Manage OpenStack Identity (keystone) users, tenants and roles
|
||||
description:
|
||||
- Manage users,tenants, roles from OpenStack.
|
||||
options:
|
||||
login_user:
|
||||
description:
|
||||
- login username to authenticate to keystone
|
||||
required: false
|
||||
default: admin
|
||||
login_password:
|
||||
description:
|
||||
- Password of login user
|
||||
required: false
|
||||
default: 'yes'
|
||||
login_tenant_name:
|
||||
description:
|
||||
- The tenant login_user belongs to
|
||||
required: false
|
||||
default: None
|
||||
version_added: "1.3"
|
||||
token:
|
||||
description:
|
||||
- The token to be uses in case the password is not specified
|
||||
required: false
|
||||
default: None
|
||||
endpoint:
|
||||
description:
|
||||
- The keystone url for authentication
|
||||
required: false
|
||||
default: http://127.0.0.1:35357/v2.0/
|
||||
user:
|
||||
description:
|
||||
- The name of the user that has to added/removed from OpenStack
|
||||
required: false
|
||||
default: None
|
||||
password:
|
||||
description:
|
||||
- The password to be assigned to the user
|
||||
required: false
|
||||
default: None
|
||||
tenant:
|
||||
description:
|
||||
- The tenant name that has be added/removed
|
||||
required: false
|
||||
default: None
|
||||
tenant_description:
|
||||
description:
|
||||
- A description for the tenant
|
||||
required: false
|
||||
default: None
|
||||
email:
|
||||
description:
|
||||
- An email address for the user
|
||||
required: false
|
||||
default: None
|
||||
role:
|
||||
description:
|
||||
- The name of the role to be assigned or created
|
||||
required: false
|
||||
default: None
|
||||
state:
|
||||
description:
|
||||
- Indicate desired state of the resource
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- python-keystoneclient
|
||||
author: "Ansible Core Team (deprecated)"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Create a tenant
|
||||
keystone_user:
|
||||
tenant: demo
|
||||
tenant_description: "Default Tenant"
|
||||
|
||||
- name: Create a user
|
||||
keystone_user:
|
||||
user: john
|
||||
tenant: demo
|
||||
password: secrete
|
||||
|
||||
- name: Apply the admin role to the john user in the demo tenant
|
||||
keystone_user:
|
||||
role: admin
|
||||
user: john
|
||||
tenant: demo
|
||||
'''
|
||||
|
||||
try:
|
||||
from keystoneclient.v2_0 import client
|
||||
except ImportError:
|
||||
keystoneclient_found = False
|
||||
else:
|
||||
keystoneclient_found = True
|
||||
|
||||
|
||||
def authenticate(endpoint, token, login_user, login_password, login_tenant_name):
|
||||
"""Return a keystone client object"""
|
||||
|
||||
if token:
|
||||
return client.Client(endpoint=endpoint, token=token)
|
||||
else:
|
||||
return client.Client(auth_url=endpoint, username=login_user,
|
||||
password=login_password, tenant_name=login_tenant_name)
|
||||
|
||||
|
||||
def tenant_exists(keystone, tenant):
|
||||
""" Return True if tenant already exists"""
|
||||
return tenant in [x.name for x in keystone.tenants.list()]
|
||||
|
||||
|
||||
def user_exists(keystone, user):
|
||||
"""" Return True if user already exists"""
|
||||
return user in [x.name for x in keystone.users.list()]
|
||||
|
||||
|
||||
def get_tenant(keystone, name):
|
||||
""" Retrieve a tenant by name"""
|
||||
tenants = [x for x in keystone.tenants.list() if x.name == name]
|
||||
count = len(tenants)
|
||||
if count == 0:
|
||||
raise KeyError("No keystone tenants with name %s" % name)
|
||||
elif count > 1:
|
||||
raise ValueError("%d tenants with name %s" % (count, name))
|
||||
else:
|
||||
return tenants[0]
|
||||
|
||||
|
||||
def get_user(keystone, name):
|
||||
""" Retrieve a user by name"""
|
||||
users = [x for x in keystone.users.list() if x.name == name]
|
||||
count = len(users)
|
||||
if count == 0:
|
||||
raise KeyError("No keystone users with name %s" % name)
|
||||
elif count > 1:
|
||||
raise ValueError("%d users with name %s" % (count, name))
|
||||
else:
|
||||
return users[0]
|
||||
|
||||
|
||||
def get_role(keystone, name):
|
||||
""" Retrieve a role by name"""
|
||||
roles = [x for x in keystone.roles.list() if x.name == name]
|
||||
count = len(roles)
|
||||
if count == 0:
|
||||
raise KeyError("No keystone roles with name %s" % name)
|
||||
elif count > 1:
|
||||
raise ValueError("%d roles with name %s" % (count, name))
|
||||
else:
|
||||
return roles[0]
|
||||
|
||||
|
||||
def get_tenant_id(keystone, name):
|
||||
return get_tenant(keystone, name).id
|
||||
|
||||
|
||||
def get_user_id(keystone, name):
|
||||
return get_user(keystone, name).id
|
||||
|
||||
|
||||
def ensure_tenant_exists(keystone, tenant_name, tenant_description,
|
||||
check_mode):
|
||||
""" Ensure that a tenant exists.
|
||||
|
||||
Return (True, id) if a new tenant was created, (False, None) if it
|
||||
already existed.
|
||||
"""
|
||||
|
||||
# Check if tenant already exists
|
||||
try:
|
||||
tenant = get_tenant(keystone, tenant_name)
|
||||
except KeyError:
|
||||
# Tenant doesn't exist yet
|
||||
pass
|
||||
else:
|
||||
if tenant.description == tenant_description:
|
||||
return (False, tenant.id)
|
||||
else:
|
||||
# We need to update the tenant description
|
||||
if check_mode:
|
||||
return (True, tenant.id)
|
||||
else:
|
||||
tenant.update(description=tenant_description)
|
||||
return (True, tenant.id)
|
||||
|
||||
# We now know we will have to create a new tenant
|
||||
if check_mode:
|
||||
return (True, None)
|
||||
|
||||
ks_tenant = keystone.tenants.create(tenant_name=tenant_name,
|
||||
description=tenant_description,
|
||||
enabled=True)
|
||||
return (True, ks_tenant.id)
|
||||
|
||||
|
||||
def ensure_tenant_absent(keystone, tenant, check_mode):
|
||||
""" Ensure that a tenant does not exist
|
||||
|
||||
Return True if the tenant was removed, False if it didn't exist
|
||||
in the first place
|
||||
"""
|
||||
if not tenant_exists(keystone, tenant):
|
||||
return False
|
||||
|
||||
# We now know we will have to delete the tenant
|
||||
if check_mode:
|
||||
return True
|
||||
|
||||
|
||||
def ensure_user_exists(keystone, user_name, password, email, tenant_name,
|
||||
check_mode):
|
||||
""" Check if user exists
|
||||
|
||||
Return (True, id) if a new user was created, (False, id) user already
|
||||
exists
|
||||
"""
|
||||
|
||||
# Check if tenant already exists
|
||||
try:
|
||||
user = get_user(keystone, user_name)
|
||||
except KeyError:
|
||||
# Tenant doesn't exist yet
|
||||
pass
|
||||
else:
|
||||
# User does exist, we're done
|
||||
return (False, user.id)
|
||||
|
||||
# We now know we will have to create a new user
|
||||
if check_mode:
|
||||
return (True, None)
|
||||
|
||||
tenant = get_tenant(keystone, tenant_name)
|
||||
|
||||
user = keystone.users.create(name=user_name, password=password,
|
||||
email=email, tenant_id=tenant.id)
|
||||
return (True, user.id)
|
||||
|
||||
def ensure_role_exists(keystone, role_name):
|
||||
# Get the role if it exists
|
||||
try:
|
||||
role = get_role(keystone, role_name)
|
||||
# Role does exist, we're done
|
||||
return (False, role.id)
|
||||
except KeyError:
|
||||
# Role doesn't exist yet
|
||||
pass
|
||||
|
||||
role = keystone.roles.create(role_name)
|
||||
return (True, role.id)
|
||||
|
||||
def ensure_user_role_exists(keystone, user_name, tenant_name, role_name,
|
||||
check_mode):
|
||||
""" Check if role exists
|
||||
|
||||
Return (True, id) if a new role was created or if the role was newly
|
||||
assigned to the user for the tenant. (False, id) if the role already
|
||||
exists and was already assigned to the user for the tenant.
|
||||
|
||||
"""
|
||||
# Check if the user has the role in the tenant
|
||||
user = get_user(keystone, user_name)
|
||||
tenant = get_tenant(keystone, tenant_name)
|
||||
roles = [x for x in keystone.roles.roles_for_user(user, tenant)
|
||||
if x.name == role_name]
|
||||
count = len(roles)
|
||||
|
||||
if count == 1:
|
||||
# If the role is in there, we are done
|
||||
role = roles[0]
|
||||
return (False, role.id)
|
||||
elif count > 1:
|
||||
# Too many roles with the same name, throw an error
|
||||
raise ValueError("%d roles with name %s" % (count, role_name))
|
||||
|
||||
# At this point, we know we will need to make changes
|
||||
if check_mode:
|
||||
return (True, None)
|
||||
|
||||
# Get the role if it exists
|
||||
try:
|
||||
role = get_role(keystone, role_name)
|
||||
except KeyError:
|
||||
# Role doesn't exist yet
|
||||
role = keystone.roles.create(role_name)
|
||||
|
||||
# Associate the role with the user in the admin
|
||||
keystone.roles.add_user_role(user, role, tenant)
|
||||
return (True, role.id)
|
||||
|
||||
|
||||
def ensure_user_absent(keystone, user, check_mode):
|
||||
raise NotImplementedError("Not yet implemented")
|
||||
|
||||
|
||||
def ensure_user_role_absent(keystone, uesr, tenant, role, check_mode):
|
||||
raise NotImplementedError("Not yet implemented")
|
||||
|
||||
def ensure_role_absent(keystone, role_name):
|
||||
raise NotImplementedError("Not yet implemented")
|
||||
|
||||
def main():
|
||||
|
||||
argument_spec = openstack_argument_spec()
|
||||
argument_spec.update(dict(
|
||||
tenant_description=dict(required=False),
|
||||
email=dict(required=False),
|
||||
user=dict(required=False),
|
||||
tenant=dict(required=False),
|
||||
password=dict(required=False),
|
||||
role=dict(required=False),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
endpoint=dict(required=False,
|
||||
default="http://127.0.0.1:35357/v2.0"),
|
||||
token=dict(required=False),
|
||||
login_user=dict(required=False),
|
||||
login_password=dict(required=False, no_log=True),
|
||||
login_tenant_name=dict(required=False)
|
||||
))
|
||||
# keystone operations themselves take an endpoint, not a keystone auth_url
|
||||
del(argument_spec['auth_url'])
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
mutually_exclusive=[['token', 'login_user'],
|
||||
['token', 'login_password'],
|
||||
['token', 'login_tenant_name']]
|
||||
)
|
||||
|
||||
if not keystoneclient_found:
|
||||
module.fail_json(msg="the python-keystoneclient module is required")
|
||||
|
||||
user = module.params['user']
|
||||
password = module.params['password']
|
||||
tenant = module.params['tenant']
|
||||
tenant_description = module.params['tenant_description']
|
||||
email = module.params['email']
|
||||
role = module.params['role']
|
||||
state = module.params['state']
|
||||
endpoint = module.params['endpoint']
|
||||
token = module.params['token']
|
||||
login_user = module.params['login_user']
|
||||
login_password = module.params['login_password']
|
||||
login_tenant_name = module.params['login_tenant_name']
|
||||
|
||||
keystone = authenticate(endpoint, token, login_user, login_password, login_tenant_name)
|
||||
|
||||
check_mode = module.check_mode
|
||||
|
||||
try:
|
||||
d = dispatch(keystone, user, password, tenant, tenant_description,
|
||||
email, role, state, endpoint, token, login_user,
|
||||
login_password, check_mode)
|
||||
except Exception as e:
|
||||
if check_mode:
|
||||
# If we have a failure in check mode
|
||||
module.exit_json(changed=True,
|
||||
msg="exception: %s" % e)
|
||||
else:
|
||||
module.fail_json(msg="exception: %s" % e)
|
||||
else:
|
||||
module.exit_json(**d)
|
||||
|
||||
|
||||
def dispatch(keystone, user=None, password=None, tenant=None,
|
||||
tenant_description=None, email=None, role=None,
|
||||
state="present", endpoint=None, token=None, login_user=None,
|
||||
login_password=None, check_mode=False):
|
||||
""" Dispatch to the appropriate method.
|
||||
|
||||
Returns a dict that will be passed to exit_json
|
||||
|
||||
tenant user role state
|
||||
------ ---- ---- --------
|
||||
X present ensure_tenant_exists
|
||||
X absent ensure_tenant_absent
|
||||
X X present ensure_user_exists
|
||||
X X absent ensure_user_absent
|
||||
X X X present ensure_user_role_exists
|
||||
X X X absent ensure_user_role_absent
|
||||
X present ensure_role_exists
|
||||
X absent ensure_role_absent
|
||||
"""
|
||||
changed = False
|
||||
id = None
|
||||
if not tenant and not user and role and state == "present":
|
||||
changed, id = ensure_role_exists(keystone, role)
|
||||
elif not tenant and not user and role and state == "absent":
|
||||
changed = ensure_role_absent(keystone, role)
|
||||
elif tenant and not user and not role and state == "present":
|
||||
changed, id = ensure_tenant_exists(keystone, tenant,
|
||||
tenant_description, check_mode)
|
||||
elif tenant and not user and not role and state == "absent":
|
||||
changed = ensure_tenant_absent(keystone, tenant, check_mode)
|
||||
elif tenant and user and not role and state == "present":
|
||||
changed, id = ensure_user_exists(keystone, user, password,
|
||||
email, tenant, check_mode)
|
||||
elif tenant and user and not role and state == "absent":
|
||||
changed = ensure_user_absent(keystone, user, check_mode)
|
||||
elif tenant and user and role and state == "present":
|
||||
changed, id = ensure_user_role_exists(keystone, user, tenant, role,
|
||||
check_mode)
|
||||
elif tenant and user and role and state == "absent":
|
||||
changed = ensure_user_role_absent(keystone, user, tenant, role, check_mode)
|
||||
else:
|
||||
# Should never reach here
|
||||
raise ValueError("Code should never reach here")
|
||||
|
||||
return dict(changed=changed, id=id)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.openstack import *
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,606 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Benno Joy <benno@ansible.com>
|
||||
# (c) 2013, John Dewey <john@dewey.ws>
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.0',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nova_compute
|
||||
version_added: "1.2"
|
||||
author: "Benno Joy (@bennojoy)"
|
||||
deprecated: Deprecated in 2.0. Use M(os_server) instead.
|
||||
short_description: Create/Delete VMs from OpenStack
|
||||
description:
|
||||
- Create or Remove virtual machines from Openstack.
|
||||
options:
|
||||
login_username:
|
||||
description:
|
||||
- login username to authenticate to keystone
|
||||
required: true
|
||||
default: admin
|
||||
login_password:
|
||||
description:
|
||||
- Password of login user
|
||||
required: true
|
||||
default: 'yes'
|
||||
login_tenant_name:
|
||||
description:
|
||||
- The tenant name of the login user
|
||||
required: true
|
||||
default: 'yes'
|
||||
auth_url:
|
||||
description:
|
||||
- The keystone url for authentication
|
||||
required: false
|
||||
default: http://127.0.0.1:35357/v2.0/
|
||||
region_name:
|
||||
description:
|
||||
- Name of the region
|
||||
required: false
|
||||
default: None
|
||||
state:
|
||||
description:
|
||||
- Indicate desired state of the resource
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
name:
|
||||
description:
|
||||
- Name that has to be given to the instance
|
||||
required: true
|
||||
default: None
|
||||
image_id:
|
||||
description:
|
||||
- The id of the base image to boot. Mutually exclusive with image_name
|
||||
required: true
|
||||
default: None
|
||||
image_name:
|
||||
description:
|
||||
- The name of the base image to boot. Mutually exclusive with image_id
|
||||
required: true
|
||||
default: None
|
||||
version_added: "1.8"
|
||||
image_exclude:
|
||||
description:
|
||||
- Text to use to filter image names, for the case, such as HP, where there are multiple image names matching the common identifying
|
||||
portions. image_exclude is a negative match filter - it is text that may not exist in the image name. Defaults to "(deprecated)"
|
||||
version_added: "1.8"
|
||||
flavor_id:
|
||||
description:
|
||||
- The id of the flavor in which the new VM has to be created. Mutually exclusive with flavor_ram
|
||||
required: false
|
||||
default: 1
|
||||
flavor_ram:
|
||||
description:
|
||||
- The minimum amount of ram in MB that the flavor in which the new VM has to be created must have. Mutually exclusive with flavor_id
|
||||
required: false
|
||||
default: 1
|
||||
version_added: "1.8"
|
||||
flavor_include:
|
||||
description:
|
||||
- Text to use to filter flavor names, for the case, such as Rackspace, where there are multiple flavors that have the same ram count.
|
||||
flavor_include is a positive match filter - it must exist in the flavor name.
|
||||
version_added: "1.8"
|
||||
key_name:
|
||||
description:
|
||||
- The key pair name to be used when creating a VM
|
||||
required: false
|
||||
default: None
|
||||
security_groups:
|
||||
description:
|
||||
- The name of the security group to which the VM should be added
|
||||
required: false
|
||||
default: None
|
||||
nics:
|
||||
description:
|
||||
- A list of network id's to which the VM's interface should be attached
|
||||
required: false
|
||||
default: None
|
||||
auto_floating_ip:
|
||||
description:
|
||||
- Should a floating ip be auto created and assigned
|
||||
required: false
|
||||
default: 'no'
|
||||
version_added: "1.8"
|
||||
floating_ips:
|
||||
description:
|
||||
- list of valid floating IPs that pre-exist to assign to this node
|
||||
required: false
|
||||
default: None
|
||||
version_added: "1.8"
|
||||
floating_ip_pools:
|
||||
description:
|
||||
- list of floating IP pools from which to choose a floating IP
|
||||
required: false
|
||||
default: None
|
||||
version_added: "1.8"
|
||||
availability_zone:
|
||||
description:
|
||||
- Name of the availability zone
|
||||
required: false
|
||||
default: None
|
||||
version_added: "1.8"
|
||||
meta:
|
||||
description:
|
||||
- A list of key value pairs that should be provided as a metadata to the new VM
|
||||
required: false
|
||||
default: None
|
||||
wait:
|
||||
description:
|
||||
- If the module should wait for the VM to be created.
|
||||
required: false
|
||||
default: 'yes'
|
||||
wait_for:
|
||||
description:
|
||||
- The amount of time the module should wait for the VM to get into active state
|
||||
required: false
|
||||
default: 180
|
||||
config_drive:
|
||||
description:
|
||||
- Whether to boot the server with config drive enabled
|
||||
required: false
|
||||
default: 'no'
|
||||
version_added: "1.8"
|
||||
user_data:
|
||||
description:
|
||||
- Opaque blob of data which is made available to the instance
|
||||
required: false
|
||||
default: None
|
||||
version_added: "1.6"
|
||||
scheduler_hints:
|
||||
description:
|
||||
- Arbitrary key/value pairs to the scheduler for custom use
|
||||
required: false
|
||||
default: None
|
||||
version_added: "1.9"
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python-novaclient"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Creates a new VM and attaches to a network and passes metadata to the instance
|
||||
- nova_compute:
|
||||
state: present
|
||||
login_username: admin
|
||||
login_password: admin
|
||||
login_tenant_name: admin
|
||||
name: vm1
|
||||
image_id: 4f905f38-e52a-43d2-b6ec-754a13ffb529
|
||||
key_name: ansible_key
|
||||
wait_for: 200
|
||||
flavor_id: 4
|
||||
nics:
|
||||
- net-id: 34605f38-e52a-25d2-b6ec-754a13ffb723
|
||||
meta:
|
||||
hostname: test1
|
||||
group: uge_master
|
||||
|
||||
# Creates a new VM in HP Cloud AE1 region availability zone az2 and automatically assigns a floating IP
|
||||
- name: launch a nova instance
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: launch an instance
|
||||
nova_compute:
|
||||
state: present
|
||||
login_username: username
|
||||
login_password: Equality7-2521
|
||||
login_tenant_name: username-project1
|
||||
name: vm1
|
||||
auth_url: https://region-b.geo-1.identity.hpcloudsvc.com:35357/v2.0/
|
||||
region_name: region-b.geo-1
|
||||
availability_zone: az2
|
||||
image_id: 9302692b-b787-4b52-a3a6-daebb79cb498
|
||||
key_name: test
|
||||
wait_for: 200
|
||||
flavor_id: 101
|
||||
security_groups: default
|
||||
auto_floating_ip: yes
|
||||
|
||||
# Creates a new VM in HP Cloud AE1 region availability zone az2 and assigns a pre-known floating IP
|
||||
- name: launch a nova instance
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: launch an instance
|
||||
nova_compute:
|
||||
state: present
|
||||
login_username: username
|
||||
login_password: Equality7-2521
|
||||
login_tenant_name: username-project1
|
||||
name: vm1
|
||||
auth_url: https://region-b.geo-1.identity.hpcloudsvc.com:35357/v2.0/
|
||||
region_name: region-b.geo-1
|
||||
availability_zone: az2
|
||||
image_id: 9302692b-b787-4b52-a3a6-daebb79cb498
|
||||
key_name: test
|
||||
wait_for: 200
|
||||
flavor_id: 101
|
||||
floating_ips:
|
||||
- 12.34.56.79
|
||||
|
||||
# Creates a new VM with 4G of RAM on Ubuntu Trusty, ignoring deprecated images
|
||||
- name: launch a nova instance
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: launch an instance
|
||||
nova_compute:
|
||||
name: vm1
|
||||
state: present
|
||||
login_username: username
|
||||
login_password: Equality7-2521
|
||||
login_tenant_name: username-project1
|
||||
auth_url: https://region-b.geo-1.identity.hpcloudsvc.com:35357/v2.0/
|
||||
region_name: region-b.geo-1
|
||||
image_name: Ubuntu Server 14.04
|
||||
image_exclude: deprecated
|
||||
flavor_ram: 4096
|
||||
|
||||
# Creates a new VM with 4G of RAM on Ubuntu Trusty on a Rackspace Performance node in DFW
|
||||
- name: launch a nova instance
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- name: launch an instance
|
||||
nova_compute:
|
||||
name: vm1
|
||||
state: present
|
||||
login_username: username
|
||||
login_password: Equality7-2521
|
||||
login_tenant_name: username-project1
|
||||
auth_url: https://identity.api.rackspacecloud.com/v2.0/
|
||||
region_name: DFW
|
||||
image_name: Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)
|
||||
flavor_ram: 4096
|
||||
flavor_include: Performance
|
||||
'''
|
||||
|
||||
import operator
|
||||
import os
|
||||
import time
|
||||
|
||||
try:
|
||||
from novaclient.v1_1 import client as nova_client
|
||||
from novaclient.v1_1 import floating_ips
|
||||
from novaclient import exceptions
|
||||
from novaclient import utils
|
||||
HAS_NOVACLIENT = True
|
||||
except ImportError:
|
||||
HAS_NOVACLIENT = False
|
||||
|
||||
|
||||
def _delete_server(module, nova):
|
||||
name = None
|
||||
server_list = None
|
||||
try:
|
||||
server_list = nova.servers.list(True, {'name': module.params['name']})
|
||||
if server_list:
|
||||
server = [x for x in server_list if x.name == module.params['name']]
|
||||
nova.servers.delete(server.pop())
|
||||
except Exception as e:
|
||||
module.fail_json( msg = "Error in deleting vm: %s" % e.message)
|
||||
if module.params['wait'] == 'no':
|
||||
module.exit_json(changed = True, result = "deleted")
|
||||
expire = time.time() + int(module.params['wait_for'])
|
||||
while time.time() < expire:
|
||||
name = nova.servers.list(True, {'name': module.params['name']})
|
||||
if not name:
|
||||
module.exit_json(changed = True, result = "deleted")
|
||||
time.sleep(5)
|
||||
module.fail_json(msg = "Timed out waiting for server to get deleted, please check manually")
|
||||
|
||||
|
||||
def _add_floating_ip_from_pool(module, nova, server):
|
||||
|
||||
# instantiate FloatingIPManager object
|
||||
floating_ip_obj = floating_ips.FloatingIPManager(nova)
|
||||
|
||||
# empty dict and list
|
||||
usable_floating_ips = {}
|
||||
pools = []
|
||||
|
||||
# user specified
|
||||
pools = module.params['floating_ip_pools']
|
||||
|
||||
# get the list of all floating IPs. Mileage may
|
||||
# vary according to Nova Compute configuration
|
||||
# per cloud provider
|
||||
all_floating_ips = floating_ip_obj.list()
|
||||
|
||||
# iterate through all pools of IP address. Empty
|
||||
# string means all and is the default value
|
||||
for pool in pools:
|
||||
# temporary list per pool
|
||||
pool_ips = []
|
||||
# loop through all floating IPs
|
||||
for f_ip in all_floating_ips:
|
||||
# if not reserved and the correct pool, add
|
||||
if f_ip.fixed_ip is None and (f_ip.pool == pool):
|
||||
pool_ips.append(f_ip.ip)
|
||||
# only need one
|
||||
break
|
||||
|
||||
# if the list is empty, add for this pool
|
||||
if not pool_ips:
|
||||
try:
|
||||
new_ip = nova.floating_ips.create(pool)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Unable to create floating ip: %s" % (e.message))
|
||||
pool_ips.append(new_ip.ip)
|
||||
# Add to the main list
|
||||
usable_floating_ips[pool] = pool_ips
|
||||
|
||||
# finally, add ip(s) to instance for each pool
|
||||
for pool in usable_floating_ips:
|
||||
for ip in usable_floating_ips[pool]:
|
||||
try:
|
||||
server.add_floating_ip(ip)
|
||||
# We only need to assign one ip - but there is an inherent
|
||||
# race condition and some other cloud operation may have
|
||||
# stolen an available floating ip
|
||||
break
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error attaching IP %s to instance %s: %s " % (ip, server.id, e.message))
|
||||
|
||||
|
||||
def _add_floating_ip_list(module, server, ips):
|
||||
# add ip(s) to instance
|
||||
for ip in ips:
|
||||
try:
|
||||
server.add_floating_ip(ip)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error attaching IP %s to instance %s: %s " % (ip, server.id, e.message))
|
||||
|
||||
|
||||
def _add_auto_floating_ip(module, nova, server):
|
||||
|
||||
try:
|
||||
new_ip = nova.floating_ips.create()
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Unable to create floating ip: %s" % (e))
|
||||
|
||||
try:
|
||||
server.add_floating_ip(new_ip)
|
||||
except Exception as e:
|
||||
# Clean up - we auto-created this ip, and it's not attached
|
||||
# to the server, so the cloud will not know what to do with it
|
||||
server.floating_ips.delete(new_ip)
|
||||
module.fail_json(msg = "Error attaching IP %s to instance %s: %s " % (ip, server.id, e.message))
|
||||
|
||||
|
||||
def _add_floating_ip(module, nova, server):
|
||||
|
||||
if module.params['floating_ip_pools']:
|
||||
_add_floating_ip_from_pool(module, nova, server)
|
||||
elif module.params['floating_ips']:
|
||||
_add_floating_ip_list(module, server, module.params['floating_ips'])
|
||||
elif module.params['auto_floating_ip']:
|
||||
_add_auto_floating_ip(module, nova, server)
|
||||
else:
|
||||
return server
|
||||
|
||||
# this may look redundant, but if there is now a
|
||||
# floating IP, then it needs to be obtained from
|
||||
# a recent server object if the above code path exec'd
|
||||
try:
|
||||
server = nova.servers.get(server.id)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error in getting info from instance: %s " % e.message)
|
||||
return server
|
||||
|
||||
|
||||
def _get_image_id(module, nova):
|
||||
if module.params['image_name']:
|
||||
for image in nova.images.list():
|
||||
if (module.params['image_name'] in image.name and (
|
||||
not module.params['image_exclude']
|
||||
or module.params['image_exclude'] not in image.name)):
|
||||
return image.id
|
||||
module.fail_json(msg = "Error finding image id from name(%s)" % module.params['image_name'])
|
||||
return module.params['image_id']
|
||||
|
||||
|
||||
def _get_flavor_id(module, nova):
|
||||
if module.params['flavor_ram']:
|
||||
for flavor in sorted(nova.flavors.list(), key=operator.attrgetter('ram')):
|
||||
if (flavor.ram >= module.params['flavor_ram'] and
|
||||
(not module.params['flavor_include'] or module.params['flavor_include'] in flavor.name)):
|
||||
return flavor.id
|
||||
module.fail_json(msg = "Error finding flavor with %sMB of RAM" % module.params['flavor_ram'])
|
||||
return module.params['flavor_id']
|
||||
|
||||
|
||||
def _create_server(module, nova):
|
||||
image_id = _get_image_id(module, nova)
|
||||
flavor_id = _get_flavor_id(module, nova)
|
||||
bootargs = [module.params['name'], image_id, flavor_id]
|
||||
bootkwargs = {
|
||||
'nics' : module.params['nics'],
|
||||
'meta' : module.params['meta'],
|
||||
'security_groups': module.params['security_groups'].split(','),
|
||||
#userdata is unhyphenated in novaclient, but hyphenated here for consistency with the ec2 module:
|
||||
'userdata': module.params['user_data'],
|
||||
'config_drive': module.params['config_drive'],
|
||||
}
|
||||
|
||||
for optional_param in ('region_name', 'key_name', 'availability_zone', 'scheduler_hints'):
|
||||
if module.params[optional_param]:
|
||||
bootkwargs[optional_param] = module.params[optional_param]
|
||||
try:
|
||||
server = nova.servers.create(*bootargs, **bootkwargs)
|
||||
server = nova.servers.get(server.id)
|
||||
except Exception as e:
|
||||
module.fail_json( msg = "Error in creating instance: %s " % e.message)
|
||||
if module.params['wait'] == 'yes':
|
||||
expire = time.time() + int(module.params['wait_for'])
|
||||
while time.time() < expire:
|
||||
try:
|
||||
server = nova.servers.get(server.id)
|
||||
except Exception as e:
|
||||
module.fail_json( msg = "Error in getting info from instance: %s" % e.message)
|
||||
if server.status == 'ACTIVE':
|
||||
server = _add_floating_ip(module, nova, server)
|
||||
|
||||
private = openstack_find_nova_addresses(getattr(server, 'addresses'), 'fixed', 'private')
|
||||
public = openstack_find_nova_addresses(getattr(server, 'addresses'), 'floating', 'public')
|
||||
|
||||
# now exit with info
|
||||
module.exit_json(changed=True, id=server.id, private_ip=''.join(private), public_ip=''.join(public), status=server.status, info=server._info)
|
||||
|
||||
if server.status == 'ERROR':
|
||||
module.fail_json(msg = "Error in creating the server, please check logs")
|
||||
time.sleep(2)
|
||||
|
||||
module.fail_json(msg = "Timeout waiting for the server to come up.. Please check manually")
|
||||
if server.status == 'ERROR':
|
||||
module.fail_json(msg = "Error in creating the server.. Please check manually")
|
||||
private = openstack_find_nova_addresses(getattr(server, 'addresses'), 'fixed', 'private')
|
||||
public = openstack_find_nova_addresses(getattr(server, 'addresses'), 'floating', 'public')
|
||||
|
||||
module.exit_json(changed = True, id = info['id'], private_ip=''.join(private), public_ip=''.join(public), status = server.status, info = server._info)
|
||||
|
||||
|
||||
def _delete_floating_ip_list(module, nova, server, extra_ips):
|
||||
for ip in extra_ips:
|
||||
nova.servers.remove_floating_ip(server=server.id, address=ip)
|
||||
|
||||
|
||||
def _check_floating_ips(module, nova, server):
|
||||
changed = False
|
||||
if module.params['floating_ip_pools'] or module.params['floating_ips'] or module.params['auto_floating_ip']:
|
||||
ips = openstack_find_nova_addresses(server.addresses, 'floating')
|
||||
if not ips:
|
||||
# If we're configured to have a floating but we don't have one,
|
||||
# let's add one
|
||||
server = _add_floating_ip(module, nova, server)
|
||||
changed = True
|
||||
elif module.params['floating_ips']:
|
||||
# we were configured to have specific ips, let's make sure we have
|
||||
# those
|
||||
missing_ips = []
|
||||
for ip in module.params['floating_ips']:
|
||||
if ip not in ips:
|
||||
missing_ips.append(ip)
|
||||
if missing_ips:
|
||||
server = _add_floating_ip_list(module, server, missing_ips)
|
||||
changed = True
|
||||
extra_ips = []
|
||||
for ip in ips:
|
||||
if ip not in module.params['floating_ips']:
|
||||
extra_ips.append(ip)
|
||||
if extra_ips:
|
||||
_delete_floating_ip_list(module, server, extra_ips)
|
||||
changed = True
|
||||
return (changed, server)
|
||||
|
||||
|
||||
def _get_server_state(module, nova):
|
||||
server = None
|
||||
try:
|
||||
servers = nova.servers.list(True, {'name': module.params['name']})
|
||||
if servers:
|
||||
# the {'name': module.params['name']} will also return servers
|
||||
# with names that partially match the server name, so we have to
|
||||
# strictly filter here
|
||||
servers = [x for x in servers if x.name == module.params['name']]
|
||||
if servers:
|
||||
server = servers[0]
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error in getting the server list: %s" % e.message)
|
||||
if server and module.params['state'] == 'present':
|
||||
if server.status != 'ACTIVE':
|
||||
module.fail_json( msg="The VM is available but not Active. state:" + server.status)
|
||||
(ip_changed, server) = _check_floating_ips(module, nova, server)
|
||||
private = openstack_find_nova_addresses(getattr(server, 'addresses'), 'fixed', 'private')
|
||||
public = openstack_find_nova_addresses(getattr(server, 'addresses'), 'floating', 'public')
|
||||
module.exit_json(changed = ip_changed, id = server.id, public_ip = public, private_ip = private, info = server._info)
|
||||
if server and module.params['state'] == 'absent':
|
||||
return True
|
||||
if module.params['state'] == 'absent':
|
||||
module.exit_json(changed = False, result = "not present")
|
||||
return True
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = openstack_argument_spec()
|
||||
argument_spec.update(dict(
|
||||
name = dict(required=True),
|
||||
image_id = dict(default=None),
|
||||
image_name = dict(default=None),
|
||||
image_exclude = dict(default='(deprecated)'),
|
||||
flavor_id = dict(default=1),
|
||||
flavor_ram = dict(default=None, type='int'),
|
||||
flavor_include = dict(default=None),
|
||||
key_name = dict(default=None),
|
||||
security_groups = dict(default='default'),
|
||||
nics = dict(default=None, type='list'),
|
||||
meta = dict(default=None, type='dict'),
|
||||
wait = dict(default='yes', choices=['yes', 'no']),
|
||||
wait_for = dict(default=180),
|
||||
state = dict(default='present', choices=['absent', 'present']),
|
||||
user_data = dict(default=None),
|
||||
config_drive = dict(default=False, type='bool'),
|
||||
auto_floating_ip = dict(default=False, type='bool'),
|
||||
floating_ips = dict(default=None, type='list'),
|
||||
floating_ip_pools = dict(default=None, type='list'),
|
||||
scheduler_hints = dict(default=None, type='dict'),
|
||||
))
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
mutually_exclusive=[
|
||||
['auto_floating_ip','floating_ips'],
|
||||
['auto_floating_ip','floating_ip_pools'],
|
||||
['floating_ips','floating_ip_pools'],
|
||||
['image_id','image_name'],
|
||||
['flavor_id','flavor_ram'],
|
||||
],
|
||||
)
|
||||
|
||||
if not HAS_NOVACLIENT:
|
||||
module.fail_json(msg='python-novaclient is required for this module')
|
||||
|
||||
nova = nova_client.Client(module.params['login_username'],
|
||||
module.params['login_password'],
|
||||
module.params['login_tenant_name'],
|
||||
module.params['auth_url'],
|
||||
region_name=module.params['region_name'],
|
||||
service_type='compute')
|
||||
try:
|
||||
nova.authenticate()
|
||||
except exceptions.Unauthorized as e:
|
||||
module.fail_json(msg = "Invalid OpenStack Nova credentials.: %s" % e.message)
|
||||
except exceptions.AuthorizationFailure as e:
|
||||
module.fail_json(msg = "Unable to authorize user: %s" % e.message)
|
||||
|
||||
if module.params['state'] == 'present':
|
||||
if not module.params['image_id'] and not module.params['image_name']:
|
||||
module.fail_json( msg = "Parameter 'image_id' or `image_name` is required if state == 'present'")
|
||||
else:
|
||||
_get_server_state(module, nova)
|
||||
_create_server(module, nova)
|
||||
if module.params['state'] == 'absent':
|
||||
_get_server_state(module, nova)
|
||||
_delete_server(module, nova)
|
||||
|
||||
# this is magic, see lib/ansible/module_common.py
|
||||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.openstack import *
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,163 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Benno Joy <benno@ansible.com>
|
||||
# (c) 2013, John Dewey <john@dewey.ws>
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.0',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nova_keypair
|
||||
version_added: "1.2"
|
||||
author:
|
||||
- "Benno Joy (@bennojoy)"
|
||||
- "Michael DeHaan"
|
||||
deprecated: Deprecated in 2.0. Use M(os_keypair) instead.
|
||||
short_description: Add/Delete key pair from nova
|
||||
description:
|
||||
- Add or Remove key pair from nova .
|
||||
options:
|
||||
login_username:
|
||||
description:
|
||||
- login username to authenticate to keystone
|
||||
required: true
|
||||
default: admin
|
||||
login_password:
|
||||
description:
|
||||
- Password of login user
|
||||
required: true
|
||||
default: 'yes'
|
||||
login_tenant_name:
|
||||
description:
|
||||
- The tenant name of the login user
|
||||
required: true
|
||||
default: 'yes'
|
||||
auth_url:
|
||||
description:
|
||||
- The keystone url for authentication
|
||||
required: false
|
||||
default: http://127.0.0.1:35357/v2.0/
|
||||
region_name:
|
||||
description:
|
||||
- Name of the region
|
||||
required: false
|
||||
default: None
|
||||
state:
|
||||
description:
|
||||
- Indicate desired state of the resource
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
name:
|
||||
description:
|
||||
- Name that has to be given to the key pair
|
||||
required: true
|
||||
default: None
|
||||
public_key:
|
||||
description:
|
||||
- The public key that would be uploaded to nova and injected to vm's upon creation
|
||||
required: false
|
||||
default: None
|
||||
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python-novaclient"
|
||||
'''
|
||||
EXAMPLES = '''
|
||||
- name: Create a key pair with the running users public key
|
||||
nova_keypair:
|
||||
state: present
|
||||
login_username: admin
|
||||
login_password: admin
|
||||
login_tenant_name: admin
|
||||
name: ansible_key
|
||||
public_key: "{{ lookup('file','~/.ssh/id_rsa.pub') }}"
|
||||
|
||||
- name: Create a new key pair and the private key returned after the run.
|
||||
nova_keypair:
|
||||
state: present
|
||||
login_username: admin
|
||||
login_password: admin
|
||||
login_tenant_name: admin
|
||||
name: ansible_key
|
||||
'''
|
||||
|
||||
import time
|
||||
try:
|
||||
from novaclient.v1_1 import client as nova_client
|
||||
from novaclient import exceptions as exc
|
||||
HAS_NOVACLIENT = True
|
||||
except ImportError:
|
||||
HAS_NOVACLIENT = False
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = openstack_argument_spec()
|
||||
argument_spec.update(dict(
|
||||
name = dict(required=True),
|
||||
public_key = dict(default=None),
|
||||
state = dict(default='present', choices=['absent', 'present'])
|
||||
))
|
||||
module = AnsibleModule(argument_spec=argument_spec)
|
||||
if not HAS_NOVACLIENT:
|
||||
module.fail_json(msg='python-novaclient is required for this module to work')
|
||||
|
||||
nova = nova_client.Client(module.params['login_username'],
|
||||
module.params['login_password'],
|
||||
module.params['login_tenant_name'],
|
||||
module.params['auth_url'],
|
||||
region_name=module.params['region_name'],
|
||||
service_type='compute')
|
||||
try:
|
||||
nova.authenticate()
|
||||
except exc.Unauthorized as e:
|
||||
module.fail_json(msg = "Invalid OpenStack Nova credentials.: %s" % e.message)
|
||||
except exc.AuthorizationFailure as e:
|
||||
module.fail_json(msg = "Unable to authorize user: %s" % e.message)
|
||||
|
||||
if module.params['state'] == 'present':
|
||||
for key in nova.keypairs.list():
|
||||
if key.name == module.params['name']:
|
||||
if module.params['public_key'] and (module.params['public_key'] != key.public_key ):
|
||||
module.fail_json(msg = "name {} present but key hash not the same as offered. Delete key first.".format(key['name']))
|
||||
else:
|
||||
module.exit_json(changed = False, result = "Key present")
|
||||
try:
|
||||
key = nova.keypairs.create(module.params['name'], module.params['public_key'])
|
||||
except Exception as e:
|
||||
module.exit_json(msg = "Error in creating the keypair: %s" % e.message)
|
||||
if not module.params['public_key']:
|
||||
module.exit_json(changed = True, key = key.private_key)
|
||||
module.exit_json(changed = True, key = None)
|
||||
if module.params['state'] == 'absent':
|
||||
for key in nova.keypairs.list():
|
||||
if key.name == module.params['name']:
|
||||
try:
|
||||
nova.keypairs.delete(module.params['name'])
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "The keypair deletion has failed: %s" % e.message)
|
||||
module.exit_json( changed = True, result = "deleted")
|
||||
module.exit_json(changed = False, result = "not present")
|
||||
|
||||
# this is magic, see lib/ansible/module.params['common.py
|
||||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.openstack import *
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
@ -1,301 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Benno Joy <benno@ansible.com>
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.0',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: quantum_floating_ip
|
||||
version_added: "1.2"
|
||||
author:
|
||||
- "Benno Joy (@bennojoy)"
|
||||
- "Brad P. Crochet (@bcrochet)"
|
||||
deprecated: Deprecated in 2.0. Use M(os_floating_ip) instead.
|
||||
short_description: Add/Remove floating IP from an instance
|
||||
description:
|
||||
- Add or Remove a floating IP to an instance
|
||||
options:
|
||||
login_username:
|
||||
description:
|
||||
- login username to authenticate to keystone
|
||||
required: true
|
||||
default: admin
|
||||
login_password:
|
||||
description:
|
||||
- Password of login user
|
||||
required: true
|
||||
default: 'yes'
|
||||
login_tenant_name:
|
||||
description:
|
||||
- The tenant name of the login user
|
||||
required: true
|
||||
default: 'yes'
|
||||
auth_url:
|
||||
description:
|
||||
- The keystone url for authentication
|
||||
required: false
|
||||
default: http://127.0.0.1:35357/v2.0/
|
||||
region_name:
|
||||
description:
|
||||
- Name of the region
|
||||
required: false
|
||||
default: None
|
||||
state:
|
||||
description:
|
||||
- Indicate desired state of the resource
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
network_name:
|
||||
description:
|
||||
- Name of the network from which IP has to be assigned to VM. Please make sure the network is an external network
|
||||
required: true
|
||||
default: None
|
||||
instance_name:
|
||||
description:
|
||||
- The name of the instance to which the IP address should be assigned
|
||||
required: true
|
||||
default: None
|
||||
internal_network_name:
|
||||
description:
|
||||
- The name of the network of the port to associate with the floating ip. Necessary when VM multiple networks.
|
||||
required: false
|
||||
default: None
|
||||
version_added: "1.5"
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python-novaclient"
|
||||
- "python-neutronclient or python-quantumclient"
|
||||
- "python-keystoneclient"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Assign a floating ip to the instance from an external network
|
||||
quantum_floating_ip:
|
||||
state: present
|
||||
login_username: admin
|
||||
login_password: admin
|
||||
login_tenant_name: admin
|
||||
network_name: external_network
|
||||
instance_name: vm1
|
||||
internal_network_name: internal_network
|
||||
'''
|
||||
|
||||
import time
|
||||
|
||||
try:
|
||||
from novaclient.v1_1 import client as nova_client
|
||||
try:
|
||||
from neutronclient.neutron import client
|
||||
except ImportError:
|
||||
from quantumclient.quantum import client
|
||||
from keystoneclient.v2_0 import client as ksclient
|
||||
HAVE_DEPS = True
|
||||
except ImportError:
|
||||
HAVE_DEPS = False
|
||||
|
||||
|
||||
def _get_ksclient(module, kwargs):
|
||||
try:
|
||||
kclient = ksclient.Client(username=kwargs.get('login_username'),
|
||||
password=kwargs.get('login_password'),
|
||||
tenant_name=kwargs.get('login_tenant_name'),
|
||||
auth_url=kwargs.get('auth_url'),
|
||||
region_name=kwargs.get('region_name'))
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error authenticating to the keystone: %s " % e.message)
|
||||
global _os_keystone
|
||||
_os_keystone = kclient
|
||||
return kclient
|
||||
|
||||
|
||||
def _get_endpoint(module, ksclient):
|
||||
try:
|
||||
endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error getting network endpoint: %s" % e.message)
|
||||
return endpoint
|
||||
|
||||
def _get_neutron_client(module, kwargs):
|
||||
_ksclient = _get_ksclient(module, kwargs)
|
||||
token = _ksclient.auth_token
|
||||
endpoint = _get_endpoint(module, _ksclient)
|
||||
kwargs = {
|
||||
'token': token,
|
||||
'endpoint_url': endpoint
|
||||
}
|
||||
try:
|
||||
neutron = client.Client('2.0', **kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error in connecting to neutron: %s " % e.message)
|
||||
return neutron
|
||||
|
||||
def _get_server_state(module, nova):
|
||||
server_info = None
|
||||
server = None
|
||||
try:
|
||||
for server in nova.servers.list():
|
||||
if server:
|
||||
info = server._info
|
||||
if info['name'] == module.params['instance_name']:
|
||||
if info['status'] != 'ACTIVE' and module.params['state'] == 'present':
|
||||
module.fail_json( msg="The VM is available but not Active. state:" + info['status'])
|
||||
server_info = info
|
||||
break
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error in getting the server list: %s" % e.message)
|
||||
return server_info, server
|
||||
|
||||
def _get_port_info(neutron, module, instance_id, internal_network_name=None):
|
||||
subnet_id = None
|
||||
if internal_network_name:
|
||||
kwargs = {'name': internal_network_name}
|
||||
networks = neutron.list_networks(**kwargs)
|
||||
network_id = networks['networks'][0]['id']
|
||||
kwargs = {
|
||||
'network_id': network_id,
|
||||
'ip_version': 4
|
||||
}
|
||||
subnets = neutron.list_subnets(**kwargs)
|
||||
subnet_id = subnets['subnets'][0]['id']
|
||||
kwargs = {
|
||||
'device_id': instance_id,
|
||||
}
|
||||
try:
|
||||
ports = neutron.list_ports(**kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json( msg = "Error in listing ports: %s" % e.message)
|
||||
if subnet_id:
|
||||
port = next(port for port in ports['ports'] if port['fixed_ips'][0]['subnet_id'] == subnet_id)
|
||||
port_id = port['id']
|
||||
fixed_ip_address = port['fixed_ips'][0]['ip_address']
|
||||
else:
|
||||
port_id = ports['ports'][0]['id']
|
||||
fixed_ip_address = ports['ports'][0]['fixed_ips'][0]['ip_address']
|
||||
if not ports['ports']:
|
||||
return None, None
|
||||
return fixed_ip_address, port_id
|
||||
|
||||
def _get_floating_ip(module, neutron, fixed_ip_address, network_name):
|
||||
kwargs = {
|
||||
'fixed_ip_address': fixed_ip_address
|
||||
}
|
||||
try:
|
||||
ips = neutron.list_floatingips(**kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "error in fetching the floatingips's %s" % e.message)
|
||||
if not ips['floatingips']:
|
||||
return None, None
|
||||
for address in ips['floatingips']:
|
||||
if _check_ips_network(neutron, address['floating_network_id'], network_name):
|
||||
return address['id'], address['floating_ip_address']
|
||||
return None, None
|
||||
|
||||
def _check_ips_network(neutron, net_id, network_name):
|
||||
if neutron.show_network(net_id)['network']['name'] == network_name:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def _create_floating_ip(neutron, module, port_id, net_id, fixed_ip):
|
||||
kwargs = {
|
||||
'port_id': port_id,
|
||||
'floating_network_id': net_id,
|
||||
'fixed_ip_address': fixed_ip
|
||||
}
|
||||
try:
|
||||
result = neutron.create_floatingip({'floatingip': kwargs})
|
||||
except Exception as e:
|
||||
module.fail_json(msg="There was an error in updating the floating ip address: %s" % e.message)
|
||||
module.exit_json(changed=True, result=result, public_ip=result['floatingip']['floating_ip_address'])
|
||||
|
||||
def _get_net_id(neutron, module):
|
||||
kwargs = {
|
||||
'name': module.params['network_name'],
|
||||
}
|
||||
try:
|
||||
networks = neutron.list_networks(**kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Error in listing neutron networks: %s" % e.message)
|
||||
if not networks['networks']:
|
||||
return None
|
||||
return networks['networks'][0]['id']
|
||||
|
||||
def _update_floating_ip(neutron, module, port_id, floating_ip_id):
|
||||
kwargs = {
|
||||
'port_id': port_id
|
||||
}
|
||||
try:
|
||||
result = neutron.update_floatingip(floating_ip_id, {'floatingip': kwargs})
|
||||
except Exception as e:
|
||||
module.fail_json(msg="There was an error in updating the floating ip address: %s" % e.message)
|
||||
module.exit_json(changed=True, result=result)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
argument_spec = openstack_argument_spec()
|
||||
argument_spec.update(dict(
|
||||
network_name = dict(required=True),
|
||||
instance_name = dict(required=True),
|
||||
state = dict(default='present', choices=['absent', 'present']),
|
||||
internal_network_name = dict(default=None),
|
||||
))
|
||||
module = AnsibleModule(argument_spec=argument_spec)
|
||||
|
||||
if not HAVE_DEPS:
|
||||
module.fail_json(msg='python-novaclient, python-keystoneclient, and either python-neutronclient or python-quantumclient are required')
|
||||
|
||||
try:
|
||||
nova = nova_client.Client(module.params['login_username'], module.params['login_password'],
|
||||
module.params['login_tenant_name'], module.params['auth_url'], region_name=module.params['region_name'], service_type='compute')
|
||||
neutron = _get_neutron_client(module, module.params)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Error in authenticating to nova: %s" % e.message)
|
||||
|
||||
server_info, server_obj = _get_server_state(module, nova)
|
||||
if not server_info:
|
||||
module.fail_json(msg="The instance name provided cannot be found")
|
||||
|
||||
fixed_ip, port_id = _get_port_info(neutron, module, server_info['id'], module.params['internal_network_name'])
|
||||
if not port_id:
|
||||
module.fail_json(msg="Cannot find a port for this instance, maybe fixed ip is not assigned")
|
||||
|
||||
floating_id, floating_ip = _get_floating_ip(module, neutron, fixed_ip, module.params['network_name'])
|
||||
|
||||
if module.params['state'] == 'present':
|
||||
if floating_ip:
|
||||
module.exit_json(changed = False, public_ip=floating_ip)
|
||||
net_id = _get_net_id(neutron, module)
|
||||
if not net_id:
|
||||
module.fail_json(msg = "cannot find the network specified, please check")
|
||||
_create_floating_ip(neutron, module, port_id, net_id, fixed_ip)
|
||||
|
||||
if module.params['state'] == 'absent':
|
||||
if floating_ip:
|
||||
_update_floating_ip(neutron, module, None, floating_id)
|
||||
module.exit_json(changed=False)
|
||||
|
||||
# this is magic, see lib/ansible/module.params['common.py
|
||||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.openstack import *
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
@ -1,235 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Benno Joy <benno@ansible.com>
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.0',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: quantum_floating_ip_associate
|
||||
version_added: "1.2"
|
||||
author: "Benno Joy (@bennojoy)"
|
||||
deprecated: Deprecated in 2.0. Use M(os_floating_ip) instead.
|
||||
short_description: Associate or disassociate a particular floating IP with an instance
|
||||
description:
|
||||
- Associates or disassociates a specific floating IP with a particular instance
|
||||
options:
|
||||
login_username:
|
||||
description:
|
||||
- login username to authenticate to keystone
|
||||
required: true
|
||||
default: admin
|
||||
login_password:
|
||||
description:
|
||||
- password of login user
|
||||
required: true
|
||||
default: 'yes'
|
||||
login_tenant_name:
|
||||
description:
|
||||
- the tenant name of the login user
|
||||
required: true
|
||||
default: true
|
||||
auth_url:
|
||||
description:
|
||||
- the keystone url for authentication
|
||||
required: false
|
||||
default: http://127.0.0.1:35357/v2.0/
|
||||
region_name:
|
||||
description:
|
||||
- name of the region
|
||||
required: false
|
||||
default: None
|
||||
state:
|
||||
description:
|
||||
- indicates the desired state of the resource
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
instance_name:
|
||||
description:
|
||||
- name of the instance to which the public IP should be assigned
|
||||
required: true
|
||||
default: None
|
||||
ip_address:
|
||||
description:
|
||||
- floating ip that should be assigned to the instance
|
||||
required: true
|
||||
default: None
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python-novaclient"
|
||||
- "python-neutronclient or python-quantumclient"
|
||||
- "python-keystoneclient"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Associate a specific floating IP with an Instance
|
||||
quantum_floating_ip_associate:
|
||||
state: present
|
||||
login_username: admin
|
||||
login_password: admin
|
||||
login_tenant_name: admin
|
||||
ip_address: 1.1.1.1
|
||||
instance_name: vm1
|
||||
'''
|
||||
|
||||
import time
|
||||
try:
|
||||
from novaclient.v1_1 import client as nova_client
|
||||
try:
|
||||
from neutronclient.neutron import client
|
||||
except ImportError:
|
||||
from quantumclient.quantum import client
|
||||
from keystoneclient.v2_0 import client as ksclient
|
||||
HAVE_DEPS = True
|
||||
except ImportError:
|
||||
HAVE_DEPS = False
|
||||
|
||||
|
||||
def _get_ksclient(module, kwargs):
|
||||
try:
|
||||
kclient = ksclient.Client(username=kwargs.get('login_username'),
|
||||
password=kwargs.get('login_password'),
|
||||
tenant_name=kwargs.get('login_tenant_name'),
|
||||
auth_url=kwargs.get('auth_url'))
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error authenticating to the keystone: %s " % e.message)
|
||||
global _os_keystone
|
||||
_os_keystone = kclient
|
||||
return kclient
|
||||
|
||||
|
||||
def _get_endpoint(module, ksclient):
|
||||
try:
|
||||
endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error getting network endpoint: %s" % e.message)
|
||||
return endpoint
|
||||
|
||||
def _get_neutron_client(module, kwargs):
|
||||
_ksclient = _get_ksclient(module, kwargs)
|
||||
token = _ksclient.auth_token
|
||||
endpoint = _get_endpoint(module, _ksclient)
|
||||
kwargs = {
|
||||
'token': token,
|
||||
'endpoint_url': endpoint
|
||||
}
|
||||
try:
|
||||
neutron = client.Client('2.0', **kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error in connecting to neutron: %s " % e.message)
|
||||
return neutron
|
||||
|
||||
def _get_server_state(module, nova):
|
||||
server_info = None
|
||||
server = None
|
||||
try:
|
||||
for server in nova.servers.list():
|
||||
if server:
|
||||
info = server._info
|
||||
if info['name'] == module.params['instance_name']:
|
||||
if info['status'] != 'ACTIVE' and module.params['state'] == 'present':
|
||||
module.fail_json(msg="The VM is available but not Active. state:" + info['status'])
|
||||
server_info = info
|
||||
break
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error in getting the server list: %s" % e.message)
|
||||
return server_info, server
|
||||
|
||||
def _get_port_id(neutron, module, instance_id):
|
||||
kwargs = dict(device_id = instance_id)
|
||||
try:
|
||||
ports = neutron.list_ports(**kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json( msg = "Error in listing ports: %s" % e.message)
|
||||
if not ports['ports']:
|
||||
return None
|
||||
return ports['ports'][0]['id']
|
||||
|
||||
def _get_floating_ip_id(module, neutron):
|
||||
kwargs = {
|
||||
'floating_ip_address': module.params['ip_address']
|
||||
}
|
||||
try:
|
||||
ips = neutron.list_floatingips(**kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "error in fetching the floatingips's %s" % e.message)
|
||||
if not ips['floatingips']:
|
||||
module.fail_json(msg = "Could find the ip specified in parameter, Please check")
|
||||
ip = ips['floatingips'][0]['id']
|
||||
if not ips['floatingips'][0]['port_id']:
|
||||
state = "detached"
|
||||
else:
|
||||
state = "attached"
|
||||
return state, ip
|
||||
|
||||
def _update_floating_ip(neutron, module, port_id, floating_ip_id):
|
||||
kwargs = {
|
||||
'port_id': port_id
|
||||
}
|
||||
try:
|
||||
result = neutron.update_floatingip(floating_ip_id, {'floatingip': kwargs})
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "There was an error in updating the floating ip address: %s" % e.message)
|
||||
module.exit_json(changed = True, result = result, public_ip=module.params['ip_address'])
|
||||
|
||||
def main():
|
||||
|
||||
argument_spec = openstack_argument_spec()
|
||||
argument_spec.update(dict(
|
||||
ip_address = dict(required=True),
|
||||
instance_name = dict(required=True),
|
||||
state = dict(default='present', choices=['absent', 'present'])
|
||||
))
|
||||
module = AnsibleModule(argument_spec=argument_spec)
|
||||
|
||||
if not HAVE_DEPS:
|
||||
module.fail_json(msg='python-novaclient, python-keystoneclient, and either python-neutronclient or python-quantumclient are required')
|
||||
|
||||
try:
|
||||
nova = nova_client.Client(module.params['login_username'], module.params['login_password'],
|
||||
module.params['login_tenant_name'], module.params['auth_url'], service_type='compute')
|
||||
except Exception as e:
|
||||
module.fail_json( msg = " Error in authenticating to nova: %s" % e.message)
|
||||
neutron = _get_neutron_client(module, module.params)
|
||||
state, floating_ip_id = _get_floating_ip_id(module, neutron)
|
||||
if module.params['state'] == 'present':
|
||||
if state == 'attached':
|
||||
module.exit_json(changed = False, result = 'attached', public_ip=module.params['ip_address'])
|
||||
server_info, server_obj = _get_server_state(module, nova)
|
||||
if not server_info:
|
||||
module.fail_json(msg = " The instance name provided cannot be found")
|
||||
port_id = _get_port_id(neutron, module, server_info['id'])
|
||||
if not port_id:
|
||||
module.fail_json(msg = "Cannot find a port for this instance, maybe fixed ip is not assigned")
|
||||
_update_floating_ip(neutron, module, port_id, floating_ip_id)
|
||||
|
||||
if module.params['state'] == 'absent':
|
||||
if state == 'detached':
|
||||
module.exit_json(changed = False, result = 'detached')
|
||||
if state == 'attached':
|
||||
_update_floating_ip(neutron, module, None, floating_ip_id)
|
||||
module.exit_json(changed = True, result = "detached")
|
||||
|
||||
# this is magic, see lib/ansible/module.params['common.py
|
||||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.openstack import *
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
@ -1,305 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Benno Joy <benno@ansible.com>
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.0',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: quantum_network
|
||||
version_added: "1.4"
|
||||
author: "Benno Joy (@bennojoy)"
|
||||
deprecated: Deprecated in 2.0. Use M(os_network) instead.
|
||||
short_description: Creates/Removes networks from OpenStack
|
||||
description:
|
||||
- Add or Remove network from OpenStack.
|
||||
options:
|
||||
login_username:
|
||||
description:
|
||||
- login username to authenticate to keystone
|
||||
required: true
|
||||
default: admin
|
||||
login_password:
|
||||
description:
|
||||
- Password of login user
|
||||
required: true
|
||||
default: 'yes'
|
||||
login_tenant_name:
|
||||
description:
|
||||
- The tenant name of the login user
|
||||
required: true
|
||||
default: 'yes'
|
||||
tenant_name:
|
||||
description:
|
||||
- The name of the tenant for whom the network is created
|
||||
required: false
|
||||
default: None
|
||||
auth_url:
|
||||
description:
|
||||
- The keystone url for authentication
|
||||
required: false
|
||||
default: http://127.0.0.1:35357/v2.0/
|
||||
region_name:
|
||||
description:
|
||||
- Name of the region
|
||||
required: false
|
||||
default: None
|
||||
state:
|
||||
description:
|
||||
- Indicate desired state of the resource
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
name:
|
||||
description:
|
||||
- Name to be assigned to the network
|
||||
required: true
|
||||
default: None
|
||||
provider_network_type:
|
||||
description:
|
||||
- The type of the network to be created, gre, vlan, local. Available types depend on the plugin. The Quantum service decides if not specified.
|
||||
required: false
|
||||
default: None
|
||||
provider_physical_network:
|
||||
description:
|
||||
- The physical network which would realize the virtual network for flat and vlan networks.
|
||||
required: false
|
||||
default: None
|
||||
provider_segmentation_id:
|
||||
description:
|
||||
- The id that has to be assigned to the network, in case of vlan networks that would be vlan id and for gre the tunnel id
|
||||
required: false
|
||||
default: None
|
||||
router_external:
|
||||
description:
|
||||
- If 'yes', specifies that the virtual network is a external network (public).
|
||||
required: false
|
||||
default: false
|
||||
shared:
|
||||
description:
|
||||
- Whether this network is shared or not
|
||||
required: false
|
||||
default: false
|
||||
admin_state_up:
|
||||
description:
|
||||
- Whether the state should be marked as up or down
|
||||
required: false
|
||||
default: true
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python-neutronclient or python-quantumclient"
|
||||
- "python-keystoneclient"
|
||||
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Create a GRE backed Quantum network with tunnel id 1 for tenant1
|
||||
quantum_network:
|
||||
name: t1network
|
||||
tenant_name: tenant1
|
||||
state: present
|
||||
provider_network_type: gre
|
||||
provider_segmentation_id: 1
|
||||
login_username: admin
|
||||
login_password: admin
|
||||
login_tenant_name: admin
|
||||
|
||||
- name: Create an external network
|
||||
quantum_network:
|
||||
name: external_network
|
||||
state: present
|
||||
provider_network_type: local
|
||||
router_external: yes
|
||||
login_username: admin
|
||||
login_password: admin
|
||||
login_tenant_name: admin
|
||||
'''
|
||||
|
||||
try:
|
||||
try:
|
||||
from neutronclient.neutron import client
|
||||
except ImportError:
|
||||
from quantumclient.quantum import client
|
||||
from keystoneclient.v2_0 import client as ksclient
|
||||
HAVE_DEPS = True
|
||||
except ImportError:
|
||||
HAVE_DEPS = False
|
||||
|
||||
_os_keystone = None
|
||||
_os_tenant_id = None
|
||||
|
||||
|
||||
def _get_ksclient(module, kwargs):
|
||||
try:
|
||||
kclient = ksclient.Client(username=kwargs.get('login_username'),
|
||||
password=kwargs.get('login_password'),
|
||||
tenant_name=kwargs.get('login_tenant_name'),
|
||||
auth_url=kwargs.get('auth_url'))
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error authenticating to the keystone: %s" %e.message)
|
||||
global _os_keystone
|
||||
_os_keystone = kclient
|
||||
return kclient
|
||||
|
||||
|
||||
def _get_endpoint(module, ksclient):
|
||||
try:
|
||||
endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error getting network endpoint: %s " %e.message)
|
||||
return endpoint
|
||||
|
||||
def _get_neutron_client(module, kwargs):
|
||||
_ksclient = _get_ksclient(module, kwargs)
|
||||
token = _ksclient.auth_token
|
||||
endpoint = _get_endpoint(module, _ksclient)
|
||||
kwargs = {
|
||||
'token': token,
|
||||
'endpoint_url': endpoint
|
||||
}
|
||||
try:
|
||||
neutron = client.Client('2.0', **kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = " Error in connecting to neutron: %s " %e.message)
|
||||
return neutron
|
||||
|
||||
def _set_tenant_id(module):
|
||||
global _os_tenant_id
|
||||
if not module.params['tenant_name']:
|
||||
_os_tenant_id = _os_keystone.tenant_id
|
||||
else:
|
||||
tenant_name = module.params['tenant_name']
|
||||
|
||||
for tenant in _os_keystone.tenants.list():
|
||||
if tenant.name == tenant_name:
|
||||
_os_tenant_id = tenant.id
|
||||
break
|
||||
if not _os_tenant_id:
|
||||
module.fail_json(msg = "The tenant id cannot be found, please check the parameters")
|
||||
|
||||
def _get_net_id(neutron, module):
|
||||
kwargs = {
|
||||
'tenant_id': _os_tenant_id,
|
||||
'name': module.params['name'],
|
||||
}
|
||||
try:
|
||||
networks = neutron.list_networks(**kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error in listing neutron networks: %s" % e.message)
|
||||
if not networks['networks']:
|
||||
return None
|
||||
return networks['networks'][0]['id']
|
||||
|
||||
def _create_network(module, neutron):
|
||||
|
||||
neutron.format = 'json'
|
||||
|
||||
network = {
|
||||
'name': module.params.get('name'),
|
||||
'tenant_id': _os_tenant_id,
|
||||
'provider:network_type': module.params.get('provider_network_type'),
|
||||
'provider:physical_network': module.params.get('provider_physical_network'),
|
||||
'provider:segmentation_id': module.params.get('provider_segmentation_id'),
|
||||
'router:external': module.params.get('router_external'),
|
||||
'shared': module.params.get('shared'),
|
||||
'admin_state_up': module.params.get('admin_state_up'),
|
||||
}
|
||||
|
||||
if module.params['provider_network_type'] == 'local':
|
||||
network.pop('provider:physical_network', None)
|
||||
network.pop('provider:segmentation_id', None)
|
||||
|
||||
if module.params['provider_network_type'] == 'flat':
|
||||
network.pop('provider:segmentation_id', None)
|
||||
|
||||
if module.params['provider_network_type'] == 'gre':
|
||||
network.pop('provider:physical_network', None)
|
||||
|
||||
if module.params['provider_network_type'] is None:
|
||||
network.pop('provider:network_type', None)
|
||||
network.pop('provider:physical_network', None)
|
||||
network.pop('provider:segmentation_id', None)
|
||||
|
||||
try:
|
||||
net = neutron.create_network({'network':network})
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error in creating network: %s" % e.message)
|
||||
return net['network']['id']
|
||||
|
||||
def _delete_network(module, net_id, neutron):
|
||||
|
||||
try:
|
||||
id = neutron.delete_network(net_id)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error in deleting the network: %s" % e.message)
|
||||
return True
|
||||
|
||||
def main():
|
||||
|
||||
argument_spec = openstack_argument_spec()
|
||||
argument_spec.update(dict(
|
||||
name = dict(required=True),
|
||||
tenant_name = dict(default=None),
|
||||
provider_network_type = dict(default=None, choices=['local', 'vlan', 'flat', 'gre']),
|
||||
provider_physical_network = dict(default=None),
|
||||
provider_segmentation_id = dict(default=None),
|
||||
router_external = dict(default=False, type='bool'),
|
||||
shared = dict(default=False, type='bool'),
|
||||
admin_state_up = dict(default=True, type='bool'),
|
||||
state = dict(default='present', choices=['absent', 'present'])
|
||||
))
|
||||
module = AnsibleModule(argument_spec=argument_spec)
|
||||
|
||||
if not HAVE_DEPS:
|
||||
module.fail_json(msg='python-keystoneclient and either python-neutronclient or python-quantumclient are required')
|
||||
|
||||
if module.params['provider_network_type'] in ['vlan' , 'flat']:
|
||||
if not module.params['provider_physical_network']:
|
||||
module.fail_json(msg = " for vlan and flat networks, variable provider_physical_network should be set.")
|
||||
|
||||
if module.params['provider_network_type'] in ['vlan', 'gre']:
|
||||
if not module.params['provider_segmentation_id']:
|
||||
module.fail_json(msg = " for vlan & gre networks, variable provider_segmentation_id should be set.")
|
||||
|
||||
neutron = _get_neutron_client(module, module.params)
|
||||
|
||||
_set_tenant_id(module)
|
||||
|
||||
if module.params['state'] == 'present':
|
||||
network_id = _get_net_id(neutron, module)
|
||||
if not network_id:
|
||||
network_id = _create_network(module, neutron)
|
||||
module.exit_json(changed = True, result = "Created", id = network_id)
|
||||
else:
|
||||
module.exit_json(changed = False, result = "Success", id = network_id)
|
||||
|
||||
if module.params['state'] == 'absent':
|
||||
network_id = _get_net_id(neutron, module)
|
||||
if not network_id:
|
||||
module.exit_json(changed = False, result = "Success")
|
||||
else:
|
||||
_delete_network(module, network_id, neutron)
|
||||
module.exit_json(changed = True, result = "Deleted")
|
||||
|
||||
# this is magic, see lib/ansible/module.params['common.py
|
||||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.openstack import *
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
@ -1,225 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Benno Joy <benno@ansible.com>
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.0',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: quantum_router
|
||||
version_added: "1.2"
|
||||
author: "Benno Joy (@bennojoy)"
|
||||
deprecated: Deprecated in 2.0. Use M(os_router) instead.
|
||||
short_description: Create or Remove router from openstack
|
||||
description:
|
||||
- Create or Delete routers from OpenStack
|
||||
options:
|
||||
login_username:
|
||||
description:
|
||||
- login username to authenticate to keystone
|
||||
required: true
|
||||
default: admin
|
||||
login_password:
|
||||
description:
|
||||
- Password of login user
|
||||
required: true
|
||||
default: 'yes'
|
||||
login_tenant_name:
|
||||
description:
|
||||
- The tenant name of the login user
|
||||
required: true
|
||||
default: 'yes'
|
||||
auth_url:
|
||||
description:
|
||||
- The keystone url for authentication
|
||||
required: false
|
||||
default: http://127.0.0.1:35357/v2.0/
|
||||
region_name:
|
||||
description:
|
||||
- Name of the region
|
||||
required: false
|
||||
default: None
|
||||
state:
|
||||
description:
|
||||
- Indicate desired state of the resource
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
name:
|
||||
description:
|
||||
- Name to be give to the router
|
||||
required: true
|
||||
default: None
|
||||
tenant_name:
|
||||
description:
|
||||
- Name of the tenant for which the router has to be created, if none router would be created for the login tenant.
|
||||
required: false
|
||||
default: None
|
||||
admin_state_up:
|
||||
description:
|
||||
- desired admin state of the created router .
|
||||
required: false
|
||||
default: true
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python-neutronclient or python-quantumclient"
|
||||
- "python-keystoneclient"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Create a router for tenant admin
|
||||
quantum_router:
|
||||
state: present
|
||||
login_username: admin
|
||||
login_password: admin
|
||||
login_tenant_name: admin
|
||||
name: router1
|
||||
'''
|
||||
|
||||
try:
|
||||
try:
|
||||
from neutronclient.neutron import client
|
||||
except ImportError:
|
||||
from quantumclient.quantum import client
|
||||
from keystoneclient.v2_0 import client as ksclient
|
||||
HAVE_DEPS = True
|
||||
except ImportError:
|
||||
HAVE_DEPS = False
|
||||
|
||||
_os_keystone = None
|
||||
_os_tenant_id = None
|
||||
|
||||
|
||||
def _get_ksclient(module, kwargs):
|
||||
try:
|
||||
kclient = ksclient.Client(username=kwargs.get('login_username'),
|
||||
password=kwargs.get('login_password'),
|
||||
tenant_name=kwargs.get('login_tenant_name'),
|
||||
auth_url=kwargs.get('auth_url'))
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error authenticating to the keystone: %s " % e.message)
|
||||
global _os_keystone
|
||||
_os_keystone = kclient
|
||||
return kclient
|
||||
|
||||
|
||||
def _get_endpoint(module, ksclient):
|
||||
try:
|
||||
endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error getting network endpoint: %s" % e.message)
|
||||
return endpoint
|
||||
|
||||
def _get_neutron_client(module, kwargs):
|
||||
_ksclient = _get_ksclient(module, kwargs)
|
||||
token = _ksclient.auth_token
|
||||
endpoint = _get_endpoint(module, _ksclient)
|
||||
kwargs = {
|
||||
'token': token,
|
||||
'endpoint_url': endpoint
|
||||
}
|
||||
try:
|
||||
neutron = client.Client('2.0', **kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error in connecting to neutron: %s " % e.message)
|
||||
return neutron
|
||||
|
||||
def _set_tenant_id(module):
|
||||
global _os_tenant_id
|
||||
if not module.params['tenant_name']:
|
||||
_os_tenant_id = _os_keystone.tenant_id
|
||||
else:
|
||||
tenant_name = module.params['tenant_name']
|
||||
|
||||
for tenant in _os_keystone.tenants.list():
|
||||
if tenant.name == tenant_name:
|
||||
_os_tenant_id = tenant.id
|
||||
break
|
||||
if not _os_tenant_id:
|
||||
module.fail_json(msg = "The tenant id cannot be found, please check the parameters")
|
||||
|
||||
def _get_router_id(module, neutron):
|
||||
kwargs = {
|
||||
'name': module.params['name'],
|
||||
'tenant_id': _os_tenant_id,
|
||||
}
|
||||
try:
|
||||
routers = neutron.list_routers(**kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error in getting the router list: %s " % e.message)
|
||||
if not routers['routers']:
|
||||
return None
|
||||
return routers['routers'][0]['id']
|
||||
|
||||
def _create_router(module, neutron):
|
||||
router = {
|
||||
'name': module.params['name'],
|
||||
'tenant_id': _os_tenant_id,
|
||||
'admin_state_up': module.params['admin_state_up'],
|
||||
}
|
||||
try:
|
||||
new_router = neutron.create_router(dict(router=router))
|
||||
except Exception as e:
|
||||
module.fail_json( msg = "Error in creating router: %s" % e.message)
|
||||
return new_router['router']['id']
|
||||
|
||||
def _delete_router(module, neutron, router_id):
|
||||
try:
|
||||
neutron.delete_router(router_id)
|
||||
except:
|
||||
module.fail_json(msg="Error in deleting the router")
|
||||
return True
|
||||
|
||||
def main():
|
||||
argument_spec = openstack_argument_spec()
|
||||
argument_spec.update(dict(
|
||||
name = dict(required=True),
|
||||
tenant_name = dict(default=None),
|
||||
state = dict(default='present', choices=['absent', 'present']),
|
||||
admin_state_up = dict(type='bool', default=True),
|
||||
))
|
||||
module = AnsibleModule(argument_spec=argument_spec)
|
||||
if not HAVE_DEPS:
|
||||
module.fail_json(msg='python-keystoneclient and either python-neutronclient or python-quantumclient are required')
|
||||
|
||||
neutron = _get_neutron_client(module, module.params)
|
||||
_set_tenant_id(module)
|
||||
|
||||
if module.params['state'] == 'present':
|
||||
router_id = _get_router_id(module, neutron)
|
||||
if not router_id:
|
||||
router_id = _create_router(module, neutron)
|
||||
module.exit_json(changed=True, result="Created", id=router_id)
|
||||
else:
|
||||
module.exit_json(changed=False, result="success" , id=router_id)
|
||||
|
||||
else:
|
||||
router_id = _get_router_id(module, neutron)
|
||||
if not router_id:
|
||||
module.exit_json(changed=False, result="success")
|
||||
else:
|
||||
_delete_router(module, neutron, router_id)
|
||||
module.exit_json(changed=True, result="deleted")
|
||||
|
||||
# this is magic, see lib/ansible/module.params['common.py
|
||||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.openstack import *
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
@ -1,234 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Benno Joy <benno@ansible.com>
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.0',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: quantum_router_gateway
|
||||
version_added: "1.2"
|
||||
author: "Benno Joy (@bennojoy)"
|
||||
deprecated: Deprecated in 2.0. Use M(os_router) instead.
|
||||
short_description: set/unset a gateway interface for the router with the specified external network
|
||||
description:
|
||||
- Creates/Removes a gateway interface from the router, used to associate a external network with a router to route external traffic.
|
||||
options:
|
||||
login_username:
|
||||
description:
|
||||
- login username to authenticate to keystone
|
||||
required: true
|
||||
default: admin
|
||||
login_password:
|
||||
description:
|
||||
- Password of login user
|
||||
required: true
|
||||
default: 'yes'
|
||||
login_tenant_name:
|
||||
description:
|
||||
- The tenant name of the login user
|
||||
required: true
|
||||
default: 'yes'
|
||||
auth_url:
|
||||
description:
|
||||
- The keystone URL for authentication
|
||||
required: false
|
||||
default: http://127.0.0.1:35357/v2.0/
|
||||
region_name:
|
||||
description:
|
||||
- Name of the region
|
||||
required: false
|
||||
default: None
|
||||
state:
|
||||
description:
|
||||
- Indicate desired state of the resource
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
router_name:
|
||||
description:
|
||||
- Name of the router to which the gateway should be attached.
|
||||
required: true
|
||||
default: None
|
||||
network_name:
|
||||
description:
|
||||
- Name of the external network which should be attached to the router.
|
||||
required: true
|
||||
default: None
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python-neutronclient or python-quantumclient"
|
||||
- "python-keystoneclient"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Attach an external network with a router to allow flow of external traffic
|
||||
quantum_router_gateway:
|
||||
state: present
|
||||
login_username: admin
|
||||
login_password: admin
|
||||
login_tenant_name: admin
|
||||
router_name: external_router
|
||||
network_name: external_network
|
||||
'''
|
||||
|
||||
try:
|
||||
try:
|
||||
from neutronclient.neutron import client
|
||||
except ImportError:
|
||||
from quantumclient.quantum import client
|
||||
from keystoneclient.v2_0 import client as ksclient
|
||||
HAVE_DEPS = True
|
||||
except ImportError:
|
||||
HAVE_DEPS = False
|
||||
|
||||
_os_keystone = None
|
||||
|
||||
|
||||
def _get_ksclient(module, kwargs):
|
||||
try:
|
||||
kclient = ksclient.Client(username=kwargs.get('login_username'),
|
||||
password=kwargs.get('login_password'),
|
||||
tenant_name=kwargs.get('login_tenant_name'),
|
||||
auth_url=kwargs.get('auth_url'))
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error authenticating to the keystone: %s " % e.message)
|
||||
global _os_keystone
|
||||
_os_keystone = kclient
|
||||
return kclient
|
||||
|
||||
|
||||
def _get_endpoint(module, ksclient):
|
||||
try:
|
||||
endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error getting network endpoint: %s" % e.message)
|
||||
return endpoint
|
||||
|
||||
def _get_neutron_client(module, kwargs):
|
||||
_ksclient = _get_ksclient(module, kwargs)
|
||||
token = _ksclient.auth_token
|
||||
endpoint = _get_endpoint(module, _ksclient)
|
||||
kwargs = {
|
||||
'token': token,
|
||||
'endpoint_url': endpoint
|
||||
}
|
||||
try:
|
||||
neutron = client.Client('2.0', **kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error in connecting to neutron: %s " % e.message)
|
||||
return neutron
|
||||
|
||||
def _get_router_id(module, neutron):
|
||||
kwargs = {
|
||||
'name': module.params['router_name'],
|
||||
}
|
||||
try:
|
||||
routers = neutron.list_routers(**kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error in getting the router list: %s " % e.message)
|
||||
if not routers['routers']:
|
||||
return None
|
||||
return routers['routers'][0]['id']
|
||||
|
||||
def _get_net_id(neutron, module):
|
||||
kwargs = {
|
||||
'name': module.params['network_name'],
|
||||
'router:external': True
|
||||
}
|
||||
try:
|
||||
networks = neutron.list_networks(**kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Error in listing neutron networks: %s" % e.message)
|
||||
if not networks['networks']:
|
||||
return None
|
||||
return networks['networks'][0]['id']
|
||||
|
||||
def _get_port_id(neutron, module, router_id, network_id):
|
||||
kwargs = {
|
||||
'device_id': router_id,
|
||||
'network_id': network_id,
|
||||
}
|
||||
try:
|
||||
ports = neutron.list_ports(**kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json( msg = "Error in listing ports: %s" % e.message)
|
||||
if not ports['ports']:
|
||||
return None
|
||||
return ports['ports'][0]['id']
|
||||
|
||||
def _add_gateway_router(neutron, module, router_id, network_id):
|
||||
kwargs = {
|
||||
'network_id': network_id
|
||||
}
|
||||
try:
|
||||
neutron.add_gateway_router(router_id, kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error in adding gateway to router: %s" % e.message)
|
||||
return True
|
||||
|
||||
def _remove_gateway_router(neutron, module, router_id):
|
||||
try:
|
||||
neutron.remove_gateway_router(router_id)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error in removing gateway to router: %s" % e.message)
|
||||
return True
|
||||
|
||||
def main():
|
||||
|
||||
argument_spec = openstack_argument_spec()
|
||||
argument_spec.update(dict(
|
||||
router_name = dict(required=True),
|
||||
network_name = dict(required=True),
|
||||
state = dict(default='present', choices=['absent', 'present']),
|
||||
))
|
||||
module = AnsibleModule(argument_spec=argument_spec)
|
||||
if not HAVE_DEPS:
|
||||
module.fail_json(msg='python-keystoneclient and either python-neutronclient or python-quantumclient are required')
|
||||
|
||||
neutron = _get_neutron_client(module, module.params)
|
||||
router_id = _get_router_id(module, neutron)
|
||||
|
||||
if not router_id:
|
||||
module.fail_json(msg="failed to get the router id, please check the router name")
|
||||
|
||||
network_id = _get_net_id(neutron, module)
|
||||
if not network_id:
|
||||
module.fail_json(msg="failed to get the network id, please check the network name and make sure it is external")
|
||||
|
||||
if module.params['state'] == 'present':
|
||||
port_id = _get_port_id(neutron, module, router_id, network_id)
|
||||
if not port_id:
|
||||
_add_gateway_router(neutron, module, router_id, network_id)
|
||||
module.exit_json(changed=True, result="created")
|
||||
module.exit_json(changed=False, result="success")
|
||||
|
||||
if module.params['state'] == 'absent':
|
||||
port_id = _get_port_id(neutron, module, router_id, network_id)
|
||||
if not port_id:
|
||||
module.exit_json(changed=False, result="Success")
|
||||
_remove_gateway_router(neutron, module, router_id)
|
||||
module.exit_json(changed=True, result="Deleted")
|
||||
|
||||
# this is magic, see lib/ansible/module.params['common.py
|
||||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.openstack import *
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
@ -1,265 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Benno Joy <benno@ansible.com>
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.0',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: quantum_router_interface
|
||||
version_added: "1.2"
|
||||
author: "Benno Joy (@bennojoy)"
|
||||
deprecated: Deprecated in 2.0. Use M(os_router) instead.
|
||||
short_description: Attach/Detach a subnet's interface to a router
|
||||
description:
|
||||
- Attach/Detach a subnet interface to a router, to provide a gateway for the subnet.
|
||||
options:
|
||||
login_username:
|
||||
description:
|
||||
- login username to authenticate to keystone
|
||||
required: true
|
||||
default: admin
|
||||
login_password:
|
||||
description:
|
||||
- Password of login user
|
||||
required: true
|
||||
default: 'yes'
|
||||
login_tenant_name:
|
||||
description:
|
||||
- The tenant name of the login user
|
||||
required: true
|
||||
default: 'yes'
|
||||
auth_url:
|
||||
description:
|
||||
- The keystone URL for authentication
|
||||
required: false
|
||||
default: 'http://127.0.0.1:35357/v2.0/'
|
||||
region_name:
|
||||
description:
|
||||
- Name of the region
|
||||
required: false
|
||||
default: None
|
||||
state:
|
||||
description:
|
||||
- Indicate desired state of the resource
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
router_name:
|
||||
description:
|
||||
- Name of the router to which the subnet's interface should be attached.
|
||||
required: true
|
||||
default: None
|
||||
subnet_name:
|
||||
description:
|
||||
- Name of the subnet to whose interface should be attached to the router.
|
||||
required: true
|
||||
default: None
|
||||
tenant_name:
|
||||
description:
|
||||
- Name of the tenant whose subnet has to be attached.
|
||||
required: false
|
||||
default: None
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python-neutronclient or python-quantumclient"
|
||||
- "python-keystoneclient"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: "Attach tenant1's subnet to the external router"
|
||||
quantum_router_interface:
|
||||
state: present
|
||||
login_username: admin
|
||||
login_password: admin
|
||||
login_tenant_name: admin
|
||||
tenant_name: tenant1
|
||||
router_name: external_route
|
||||
subnet_name: t1subnet
|
||||
'''
|
||||
|
||||
try:
|
||||
try:
|
||||
from neutronclient.neutron import client
|
||||
except ImportError:
|
||||
from quantumclient.quantum import client
|
||||
from keystoneclient.v2_0 import client as ksclient
|
||||
HAVE_DEPS = True
|
||||
except ImportError:
|
||||
HAVE_DEPS = False
|
||||
|
||||
_os_keystone = None
|
||||
_os_tenant_id = None
|
||||
|
||||
|
||||
def _get_ksclient(module, kwargs):
|
||||
try:
|
||||
kclient = ksclient.Client(username=kwargs.get('login_username'),
|
||||
password=kwargs.get('login_password'),
|
||||
tenant_name=kwargs.get('login_tenant_name'),
|
||||
auth_url=kwargs.get('auth_url'))
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error authenticating to the keystone: %s " % e.message)
|
||||
global _os_keystone
|
||||
_os_keystone = kclient
|
||||
return kclient
|
||||
|
||||
|
||||
def _get_endpoint(module, ksclient):
|
||||
try:
|
||||
endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error getting network endpoint: %s" % e.message)
|
||||
return endpoint
|
||||
|
||||
def _get_neutron_client(module, kwargs):
|
||||
_ksclient = _get_ksclient(module, kwargs)
|
||||
token = _ksclient.auth_token
|
||||
endpoint = _get_endpoint(module, _ksclient)
|
||||
kwargs = {
|
||||
'token': token,
|
||||
'endpoint_url': endpoint
|
||||
}
|
||||
try:
|
||||
neutron = client.Client('2.0', **kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error in connecting to neutron: %s " % e.message)
|
||||
return neutron
|
||||
|
||||
def _set_tenant_id(module):
|
||||
global _os_tenant_id
|
||||
if not module.params['tenant_name']:
|
||||
_os_tenant_id = _os_keystone.tenant_id
|
||||
else:
|
||||
tenant_name = module.params['tenant_name']
|
||||
|
||||
for tenant in _os_keystone.tenants.list():
|
||||
if tenant.name == tenant_name:
|
||||
_os_tenant_id = tenant.id
|
||||
break
|
||||
if not _os_tenant_id:
|
||||
module.fail_json(msg = "The tenant id cannot be found, please check the parameters")
|
||||
|
||||
def _get_router_id(module, neutron):
|
||||
kwargs = {
|
||||
'name': module.params['router_name'],
|
||||
}
|
||||
try:
|
||||
routers = neutron.list_routers(**kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error in getting the router list: %s " % e.message)
|
||||
if not routers['routers']:
|
||||
return None
|
||||
return routers['routers'][0]['id']
|
||||
|
||||
|
||||
def _get_subnet_id(module, neutron):
|
||||
subnet_id = None
|
||||
kwargs = {
|
||||
'tenant_id': _os_tenant_id,
|
||||
'name': module.params['subnet_name'],
|
||||
}
|
||||
try:
|
||||
subnets = neutron.list_subnets(**kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json( msg = " Error in getting the subnet list:%s " % e.message)
|
||||
if not subnets['subnets']:
|
||||
return None
|
||||
return subnets['subnets'][0]['id']
|
||||
|
||||
def _get_port_id(neutron, module, router_id, subnet_id):
|
||||
kwargs = {
|
||||
'tenant_id': _os_tenant_id,
|
||||
'device_id': router_id,
|
||||
}
|
||||
try:
|
||||
ports = neutron.list_ports(**kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json( msg = "Error in listing ports: %s" % e.message)
|
||||
if not ports['ports']:
|
||||
return None
|
||||
for port in ports['ports']:
|
||||
for subnet in port['fixed_ips']:
|
||||
if subnet['subnet_id'] == subnet_id:
|
||||
return port['id']
|
||||
return None
|
||||
|
||||
def _add_interface_router(neutron, module, router_id, subnet_id):
|
||||
kwargs = {
|
||||
'subnet_id': subnet_id
|
||||
}
|
||||
try:
|
||||
neutron.add_interface_router(router_id, kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error in adding interface to router: %s" % e.message)
|
||||
return True
|
||||
|
||||
def _remove_interface_router(neutron, module, router_id, subnet_id):
|
||||
kwargs = {
|
||||
'subnet_id': subnet_id
|
||||
}
|
||||
try:
|
||||
neutron.remove_interface_router(router_id, kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Error in removing interface from router: %s" % e.message)
|
||||
return True
|
||||
|
||||
def main():
|
||||
argument_spec = openstack_argument_spec()
|
||||
argument_spec.update(dict(
|
||||
router_name = dict(required=True),
|
||||
subnet_name = dict(required=True),
|
||||
tenant_name = dict(default=None),
|
||||
state = dict(default='present', choices=['absent', 'present']),
|
||||
))
|
||||
module = AnsibleModule(argument_spec=argument_spec)
|
||||
if not HAVE_DEPS:
|
||||
module.fail_json(msg='python-keystoneclient and either python-neutronclient or python-quantumclient are required')
|
||||
|
||||
neutron = _get_neutron_client(module, module.params)
|
||||
_set_tenant_id(module)
|
||||
|
||||
router_id = _get_router_id(module, neutron)
|
||||
if not router_id:
|
||||
module.fail_json(msg="failed to get the router id, please check the router name")
|
||||
|
||||
subnet_id = _get_subnet_id(module, neutron)
|
||||
if not subnet_id:
|
||||
module.fail_json(msg="failed to get the subnet id, please check the subnet name")
|
||||
|
||||
if module.params['state'] == 'present':
|
||||
port_id = _get_port_id(neutron, module, router_id, subnet_id)
|
||||
if not port_id:
|
||||
_add_interface_router(neutron, module, router_id, subnet_id)
|
||||
module.exit_json(changed=True, result="created", id=port_id)
|
||||
module.exit_json(changed=False, result="success", id=port_id)
|
||||
|
||||
if module.params['state'] == 'absent':
|
||||
port_id = _get_port_id(neutron, module, router_id, subnet_id)
|
||||
if not port_id:
|
||||
module.exit_json(changed = False, result = "Success")
|
||||
_remove_interface_router(neutron, module, router_id, subnet_id)
|
||||
module.exit_json(changed=True, result="Deleted")
|
||||
|
||||
# this is magic, see lib/ansible/module.params['common.py
|
||||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.openstack import *
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
@ -1,313 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Benno Joy <benno@ansible.com>
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.0',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: quantum_subnet
|
||||
author: "Benno Joy (@bennojoy)"
|
||||
deprecated: Deprecated in 2.0. Use M(os_subnet) instead.
|
||||
version_added: "1.2"
|
||||
short_description: Add/remove subnet from a network
|
||||
description:
|
||||
- Add/remove subnet from a network
|
||||
options:
|
||||
login_username:
|
||||
description:
|
||||
- login username to authenticate to keystone
|
||||
required: true
|
||||
default: admin
|
||||
login_password:
|
||||
description:
|
||||
- Password of login user
|
||||
required: true
|
||||
default: True
|
||||
login_tenant_name:
|
||||
description:
|
||||
- The tenant name of the login user
|
||||
required: true
|
||||
default: True
|
||||
auth_url:
|
||||
description:
|
||||
- The keystone URL for authentication
|
||||
required: false
|
||||
default: http://127.0.0.1:35357/v2.0/
|
||||
region_name:
|
||||
description:
|
||||
- Name of the region
|
||||
required: false
|
||||
default: None
|
||||
state:
|
||||
description:
|
||||
- Indicate desired state of the resource
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
network_name:
|
||||
description:
|
||||
- Name of the network to which the subnet should be attached
|
||||
required: true
|
||||
default: None
|
||||
name:
|
||||
description:
|
||||
- The name of the subnet that should be created
|
||||
required: true
|
||||
default: None
|
||||
cidr:
|
||||
description:
|
||||
- The CIDR representation of the subnet that should be assigned to the subnet
|
||||
required: true
|
||||
default: None
|
||||
tenant_name:
|
||||
description:
|
||||
- The name of the tenant for whom the subnet should be created
|
||||
required: false
|
||||
default: None
|
||||
ip_version:
|
||||
description:
|
||||
- The IP version of the subnet 4 or 6
|
||||
required: false
|
||||
default: 4
|
||||
enable_dhcp:
|
||||
description:
|
||||
- Whether DHCP should be enabled for this subnet.
|
||||
required: false
|
||||
default: true
|
||||
gateway_ip:
|
||||
description:
|
||||
- The ip that would be assigned to the gateway for this subnet
|
||||
required: false
|
||||
default: None
|
||||
dns_nameservers:
|
||||
description:
|
||||
- DNS nameservers for this subnet, comma-separated
|
||||
required: false
|
||||
default: None
|
||||
version_added: "1.4"
|
||||
allocation_pool_start:
|
||||
description:
|
||||
- From the subnet pool the starting address from which the IP should be allocated
|
||||
required: false
|
||||
default: None
|
||||
allocation_pool_end:
|
||||
description:
|
||||
- From the subnet pool the last IP that should be assigned to the virtual machines
|
||||
required: false
|
||||
default: None
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python-neutronclient or python-quantumclient"
|
||||
- "python-keystoneclient"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Create a subnet for a tenant with the specified subnet
|
||||
quantum_subnet:
|
||||
state: present
|
||||
login_username: admin
|
||||
login_password: admin
|
||||
login_tenant_name: admin
|
||||
tenant_name: tenant1
|
||||
network_name: network1
|
||||
name: net1subnet
|
||||
cidr: 192.168.0.0/24
|
||||
'''
|
||||
|
||||
try:
|
||||
try:
|
||||
from neutronclient.neutron import client
|
||||
except ImportError:
|
||||
from quantumclient.quantum import client
|
||||
from keystoneclient.v2_0 import client as ksclient
|
||||
HAVE_DEPS = True
|
||||
except ImportError:
|
||||
HAVE_DEPS = False
|
||||
|
||||
_os_keystone = None
|
||||
_os_tenant_id = None
|
||||
_os_network_id = None
|
||||
|
||||
|
||||
def _get_ksclient(module, kwargs):
|
||||
try:
|
||||
kclient = ksclient.Client(username=kwargs.get('login_username'),
|
||||
password=kwargs.get('login_password'),
|
||||
tenant_name=kwargs.get('login_tenant_name'),
|
||||
auth_url=kwargs.get('auth_url'))
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error authenticating to the keystone: %s" %e.message)
|
||||
global _os_keystone
|
||||
_os_keystone = kclient
|
||||
return kclient
|
||||
|
||||
|
||||
def _get_endpoint(module, ksclient):
|
||||
try:
|
||||
endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Error getting network endpoint: %s" % e.message)
|
||||
return endpoint
|
||||
|
||||
def _get_neutron_client(module, kwargs):
|
||||
_ksclient = _get_ksclient(module, kwargs)
|
||||
token = _ksclient.auth_token
|
||||
endpoint = _get_endpoint(module, _ksclient)
|
||||
kwargs = {
|
||||
'token': token,
|
||||
'endpoint_url': endpoint
|
||||
}
|
||||
try:
|
||||
neutron = client.Client('2.0', **kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg = " Error in connecting to neutron: %s" % e.message)
|
||||
return neutron
|
||||
|
||||
def _set_tenant_id(module):
|
||||
global _os_tenant_id
|
||||
if not module.params['tenant_name']:
|
||||
_os_tenant_id = _os_keystone.tenant_id
|
||||
else:
|
||||
tenant_name = module.params['tenant_name']
|
||||
|
||||
for tenant in _os_keystone.tenants.list():
|
||||
if tenant.name == tenant_name:
|
||||
_os_tenant_id = tenant.id
|
||||
break
|
||||
if not _os_tenant_id:
|
||||
module.fail_json(msg = "The tenant id cannot be found, please check the parameters")
|
||||
|
||||
def _get_net_id(neutron, module):
|
||||
kwargs = {
|
||||
'tenant_id': _os_tenant_id,
|
||||
'name': module.params['network_name'],
|
||||
}
|
||||
try:
|
||||
networks = neutron.list_networks(**kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Error in listing neutron networks: %s" % e.message)
|
||||
if not networks['networks']:
|
||||
return None
|
||||
return networks['networks'][0]['id']
|
||||
|
||||
|
||||
def _get_subnet_id(module, neutron):
|
||||
global _os_network_id
|
||||
subnet_id = None
|
||||
_os_network_id = _get_net_id(neutron, module)
|
||||
if not _os_network_id:
|
||||
module.fail_json(msg = "network id of network not found.")
|
||||
else:
|
||||
kwargs = {
|
||||
'tenant_id': _os_tenant_id,
|
||||
'name': module.params['name'],
|
||||
}
|
||||
try:
|
||||
subnets = neutron.list_subnets(**kwargs)
|
||||
except Exception as e:
|
||||
module.fail_json( msg = " Error in getting the subnet list:%s " % e.message)
|
||||
if not subnets['subnets']:
|
||||
return None
|
||||
return subnets['subnets'][0]['id']
|
||||
|
||||
def _create_subnet(module, neutron):
|
||||
neutron.format = 'json'
|
||||
subnet = {
|
||||
'name': module.params['name'],
|
||||
'ip_version': module.params['ip_version'],
|
||||
'enable_dhcp': module.params['enable_dhcp'],
|
||||
'tenant_id': _os_tenant_id,
|
||||
'gateway_ip': module.params['gateway_ip'],
|
||||
'dns_nameservers': module.params['dns_nameservers'],
|
||||
'network_id': _os_network_id,
|
||||
'cidr': module.params['cidr'],
|
||||
}
|
||||
if module.params['allocation_pool_start'] and module.params['allocation_pool_end']:
|
||||
allocation_pools = [
|
||||
{
|
||||
'start' : module.params['allocation_pool_start'],
|
||||
'end' : module.params['allocation_pool_end']
|
||||
}
|
||||
]
|
||||
subnet.update({'allocation_pools': allocation_pools})
|
||||
if not module.params['gateway_ip']:
|
||||
subnet.pop('gateway_ip')
|
||||
if module.params['dns_nameservers']:
|
||||
subnet['dns_nameservers'] = module.params['dns_nameservers'].split(',')
|
||||
else:
|
||||
subnet.pop('dns_nameservers')
|
||||
try:
|
||||
new_subnet = neutron.create_subnet(dict(subnet=subnet))
|
||||
except Exception as e:
|
||||
module.fail_json(msg = "Failure in creating subnet: %s" % e.message)
|
||||
return new_subnet['subnet']['id']
|
||||
|
||||
|
||||
def _delete_subnet(module, neutron, subnet_id):
|
||||
try:
|
||||
neutron.delete_subnet(subnet_id)
|
||||
except Exception as e:
|
||||
module.fail_json( msg = "Error in deleting subnet: %s" % e.message)
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
argument_spec = openstack_argument_spec()
|
||||
argument_spec.update(dict(
|
||||
name = dict(required=True),
|
||||
network_name = dict(required=True),
|
||||
cidr = dict(required=True),
|
||||
tenant_name = dict(default=None),
|
||||
state = dict(default='present', choices=['absent', 'present']),
|
||||
ip_version = dict(default='4', choices=['4', '6']),
|
||||
enable_dhcp = dict(default='true', type='bool'),
|
||||
gateway_ip = dict(default=None),
|
||||
dns_nameservers = dict(default=None),
|
||||
allocation_pool_start = dict(default=None),
|
||||
allocation_pool_end = dict(default=None),
|
||||
))
|
||||
module = AnsibleModule(argument_spec=argument_spec)
|
||||
if not HAVE_DEPS:
|
||||
module.fail_json(msg='python-keystoneclient and either python-neutronclient or python-quantumclient are required')
|
||||
|
||||
neutron = _get_neutron_client(module, module.params)
|
||||
_set_tenant_id(module)
|
||||
if module.params['state'] == 'present':
|
||||
subnet_id = _get_subnet_id(module, neutron)
|
||||
if not subnet_id:
|
||||
subnet_id = _create_subnet(module, neutron)
|
||||
module.exit_json(changed = True, result = "Created" , id = subnet_id)
|
||||
else:
|
||||
module.exit_json(changed = False, result = "success" , id = subnet_id)
|
||||
else:
|
||||
subnet_id = _get_subnet_id(module, neutron)
|
||||
if not subnet_id:
|
||||
module.exit_json(changed = False, result = "success")
|
||||
else:
|
||||
_delete_subnet(module, neutron, subnet_id)
|
||||
module.exit_json(changed = True, result = "deleted")
|
||||
|
||||
# this is magic, see lib/ansible/module.params['common.py
|
||||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.openstack import *
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
Loading…
Reference in New Issue