4341b79b3a
This patch introduces an insecure flag for the Keystone internal and admin endpoints: * keystone_service_adminuri_insecure * keystone_service_internaluri_insecure Both values default to false. If you have setup SSL endpoints for Keystone using an untrusted certificate then you should set the appropriate flag to true in your user_variables. This patch is used to enable testing and development with Keystone SSL endpoints without having to make use of SSL certificates signed by a trusted, public CA. The patch introduces a new optional argument (insecure) to the keystone, glance and neutron Ansible libraries. This is a boolean value which, when true, enables these libraries to access Keystone endpoints 'insecurely'. When these libraries are used in plays, the appropriate value is set automatically as per the above conditions. Implements: blueprint keystone-federation Change-Id: Ia07e7e201f901042dd06a86efe5c6f6725e9ce13
420 lines
14 KiB
Python
420 lines
14 KiB
Python
#!/usr/bin/env python
|
|
# Copyright 2014, Rackspace US, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
DOCUMENTATION = """
|
|
---
|
|
module: neutron
|
|
short_description:
|
|
- Basic module for interacting with openstack neutron
|
|
description:
|
|
- Basic module for interacting with openstack neutron
|
|
options:
|
|
command:
|
|
description:
|
|
- Operation for the module to perform. Currently available
|
|
choices:
|
|
- create_network
|
|
- create_subnet
|
|
- create_router
|
|
- add_router_interface
|
|
required: True
|
|
openrc_path:
|
|
decription:
|
|
- Path to openrc file from which credentials and keystone endpoint
|
|
will be extracted
|
|
net_name:
|
|
description:
|
|
- Name of network
|
|
subnet_name:
|
|
description:
|
|
- Name of subnet
|
|
router_name:
|
|
description:
|
|
- Name of router
|
|
cidr:
|
|
description:
|
|
- Specify CIDR to use when creating subnet
|
|
provider_physical_network:
|
|
description:
|
|
- Specify provider:physical_network when creating network
|
|
provider_network_type:
|
|
description:
|
|
- Specify provider:network_type when creating network
|
|
provider_segmentation_id:
|
|
description:
|
|
- Specify provider:segmentation_id when creating network
|
|
router_external:
|
|
description:
|
|
- Specify router:external' when creating network
|
|
external_gateway_info:
|
|
description:
|
|
- Specify external_gateway_info when creating router
|
|
insecure:
|
|
description:
|
|
- Explicitly allow client to perform "insecure" TLS
|
|
choices:
|
|
- false
|
|
- true
|
|
default: false
|
|
author: Hugh Saunders
|
|
"""
|
|
|
|
EXAMPLES = """
|
|
- name: Create private network
|
|
neutron:
|
|
command: create_network
|
|
openrc_path: /root/openrc
|
|
net_name: private
|
|
- name: Create public network
|
|
neutron:
|
|
command: create_network
|
|
openrc_path: /root/openrc
|
|
net_name: public
|
|
provider_network_type: flat
|
|
provider_physical_network: vlan
|
|
router_external: true
|
|
- name: Create private subnet
|
|
neutron:
|
|
command: create_subnet
|
|
openrc_path: /root/openrc
|
|
net_name: private
|
|
subnet_name: private-subnet
|
|
cidr: "192.168.74.0/24"
|
|
- name: Create public subnet
|
|
neutron:
|
|
command: create_subnet
|
|
openrc_path: /root/openrc
|
|
net_name: public
|
|
subnet_name: public-subnet
|
|
cidr: "10.1.13.0/24"
|
|
- name: Create router
|
|
neutron:
|
|
command: create_router
|
|
openrc_path: /root/openrc
|
|
router_name: router
|
|
external_gateway_info: public
|
|
- name: Add private subnet to router
|
|
neutron:
|
|
command: add_router_interface
|
|
openrc_path: /root/openrc
|
|
router_name: router
|
|
subnet_name: private-subnet
|
|
"""
|
|
|
|
|
|
import keystoneclient.v2_0.client as ksclient
|
|
from neutronclient.neutron import client as nclient
|
|
|
|
|
|
COMMAND_MAP = {
|
|
'create_network': {
|
|
'variables': [
|
|
'net_name',
|
|
'provider_physical_network',
|
|
'provider_network_type',
|
|
'provider_segmentation_id',
|
|
'router_external',
|
|
'tenant_id'
|
|
]
|
|
},
|
|
'create_subnet': {
|
|
'variables': [
|
|
'net_name',
|
|
'subnet_name',
|
|
'cidr',
|
|
'tenant_id'
|
|
]
|
|
},
|
|
'create_router': {
|
|
'variables': [
|
|
'router_name',
|
|
'external_gateway_info',
|
|
'tenant_id'
|
|
]
|
|
},
|
|
'add_router_interface': {
|
|
'variables': [
|
|
'router_name',
|
|
'subnet_name'
|
|
]
|
|
}
|
|
}
|
|
|
|
|
|
class ManageNeutron(object):
|
|
def __init__(self, module):
|
|
self.state_change = False
|
|
self.neutron = None
|
|
self.keystone = None
|
|
self.module = module
|
|
|
|
def command_router(self):
|
|
"""Run the command as its provided to the module."""
|
|
command_name = self.module.params['command']
|
|
if command_name not in COMMAND_MAP:
|
|
self.failure(
|
|
error='No Command Found',
|
|
rc=2,
|
|
msg='Command [ %s ] was not found.' % command_name
|
|
)
|
|
|
|
action_command = COMMAND_MAP[command_name]
|
|
if hasattr(self, '_%s' % command_name):
|
|
action = getattr(self, '_%s' % command_name)
|
|
try:
|
|
self._keystone_authenticate()
|
|
self._init_neutron()
|
|
except Exception as e:
|
|
self.module.fail_json(
|
|
err="Initialisation Error: %s" % e,
|
|
rc=2, msg=str(e))
|
|
facts = action(variables=action_command['variables'])
|
|
if facts is None:
|
|
self.module.exit_json(changed=self.state_change)
|
|
else:
|
|
self.module.exit_json(
|
|
changed=self.state_change,
|
|
ansible_facts=facts
|
|
)
|
|
else:
|
|
self.failure(
|
|
error='Command not in ManageNeutron class',
|
|
rc=2,
|
|
msg='Method [ %s ] was not found.' % command_name
|
|
)
|
|
|
|
@staticmethod
|
|
def _facts(resource_type, resource_data):
|
|
"""Return a dict for our Ansible facts."""
|
|
key = 'neutron_%s' % resource_type
|
|
facts = {key: {}}
|
|
for f in resource_data[resource_type]:
|
|
res_name = f['name']
|
|
del f['name']
|
|
facts[key][res_name] = f
|
|
|
|
return facts
|
|
|
|
def _get_vars(self, variables, required=None):
|
|
"""Return a dict of all variables as found within the module.
|
|
|
|
:param variables: ``list`` List of all variables that are available to
|
|
use within the Neutron Command.
|
|
:param required: ``list`` Name of variables that are required.
|
|
"""
|
|
return_dict = {}
|
|
for variable in variables:
|
|
return_dict[variable] = self.module.params.get(variable)
|
|
else:
|
|
if isinstance(required, list):
|
|
for var_name in required:
|
|
check = return_dict.get(var_name)
|
|
if check is None:
|
|
self.failure(
|
|
error='Missing [ %s ] from Task or found a None'
|
|
' value' % var_name,
|
|
rc=000,
|
|
msg='variables %s - available params [ %s ]'
|
|
% (variables, self.module.params)
|
|
)
|
|
return return_dict
|
|
|
|
def failure(self, error, rc, msg):
|
|
"""Return a Failure when running an Ansible command.
|
|
|
|
:param error: ``str`` Error that occurred.
|
|
:param rc: ``int`` Return code while executing an Ansible command.
|
|
:param msg: ``str`` Message to report.
|
|
"""
|
|
self.module.fail_json(msg=msg, rc=rc, err=error)
|
|
|
|
def _parse_openrc(self):
|
|
"""Get credentials from an openrc file."""
|
|
openrc_path = self.module.params['openrc_path']
|
|
line_re = re.compile('^export (?P<key>OS_\w*)=(?P<value>[^\n]*)')
|
|
with open(openrc_path) as openrc:
|
|
matches = [line_re.match(l) for l in openrc]
|
|
return dict(
|
|
(g.groupdict()['key'], g.groupdict()['value'])
|
|
for g in matches if g
|
|
)
|
|
|
|
def _keystone_authenticate(self):
|
|
"""Authenticate with Keystone."""
|
|
openrc = self._parse_openrc()
|
|
insecure = self.module.params['insecure']
|
|
self.keystone = ksclient.Client(insecure=insecure,
|
|
username=openrc['OS_USERNAME'],
|
|
password=openrc['OS_PASSWORD'],
|
|
tenant_name=openrc['OS_TENANT_NAME'],
|
|
auth_url=openrc['OS_AUTH_URL'])
|
|
|
|
def _init_neutron(self):
|
|
"""Create neutron client object using token and url from keystone."""
|
|
openrc = self._parse_openrc()
|
|
self.neutron = nclient.Client(
|
|
'2.0',
|
|
endpoint_url=self.keystone.service_catalog.url_for(
|
|
service_type='network',
|
|
endpoint_type=openrc['OS_ENDPOINT_TYPE']),
|
|
token=self.keystone.get_token(self.keystone.session))
|
|
|
|
def _get_resource_by_name(self, resource_type, resource_name):
|
|
action = getattr(self.neutron, 'list_%s' % resource_type)
|
|
resource = action(name=resource_name)[resource_type]
|
|
|
|
if resource:
|
|
return resource[0]['id']
|
|
else:
|
|
return None
|
|
|
|
def _create_network(self, variables):
|
|
required_vars = ['net_name']
|
|
variables_dict = self._get_vars(variables, required=required_vars)
|
|
net_name = variables_dict.pop('net_name')
|
|
provider_physical_network = variables_dict.pop(
|
|
'provider_physical_network'
|
|
)
|
|
provider_network_type = variables_dict.pop('provider_network_type')
|
|
provider_segmentation_id = variables_dict.pop(
|
|
'provider_segmentation_id'
|
|
)
|
|
router_external = variables_dict.pop('router_external')
|
|
tenant_id = variables_dict.pop('tenant_id')
|
|
|
|
if not self._get_resource_by_name('networks', net_name):
|
|
n = {"name": net_name, "admin_state_up": True}
|
|
if provider_physical_network:
|
|
n['provider:physical_network'] = provider_physical_network
|
|
if provider_network_type:
|
|
n['provider:network_type'] = provider_network_type
|
|
if provider_segmentation_id:
|
|
n['provider:segmentation_id'] = str(provider_segmentation_id)
|
|
if router_external:
|
|
n['router:external'] = router_external
|
|
if tenant_id:
|
|
n['tenant_id'] = tenant_id
|
|
|
|
self.state_change = True
|
|
self.neutron.create_network({"network": n})
|
|
|
|
return self._facts('networks', self.neutron.list_networks())
|
|
|
|
def _create_subnet(self, variables):
|
|
required_vars = ['net_name', 'subnet_name', 'cidr']
|
|
variables_dict = self._get_vars(variables, required=required_vars)
|
|
net_name = variables_dict.pop('net_name')
|
|
subnet_name = variables_dict.pop('subnet_name')
|
|
cidr = variables_dict.pop('cidr')
|
|
network_id = self._get_resource_by_name('networks', net_name)
|
|
tenant_id = variables_dict.pop('tenant_id')
|
|
|
|
if not network_id:
|
|
self.failure(
|
|
error='Network not found',
|
|
rc=1,
|
|
msg='The specified network could not be found'
|
|
)
|
|
if not self.neutron.list_subnets(cidr=cidr,
|
|
network_id=network_id)['subnets']:
|
|
self.state_change = True
|
|
s = {"name": subnet_name, "cidr": cidr, "ip_version": 4,
|
|
"network_id": network_id}
|
|
if tenant_id:
|
|
s["tenant_id"] = tenant_id
|
|
self.neutron.create_subnet({"subnet": s})
|
|
return self._facts('subnets', self.neutron.list_subnets())
|
|
|
|
def _create_router(self, variables):
|
|
required_vars = ['router_name', 'external_gateway_info']
|
|
variables_dict = self._get_vars(variables, required=required_vars)
|
|
router_name = variables_dict.pop('router_name')
|
|
external_gateway_info = variables_dict.pop('external_gateway_info')
|
|
tenant_id = variables_dict.pop('tenant_id')
|
|
|
|
if not self._get_resource_by_name('routers', router_name):
|
|
self.state_change = True
|
|
r = {'name': router_name}
|
|
if external_gateway_info:
|
|
network_id = self._get_resource_by_name('networks',
|
|
external_gateway_info)
|
|
r['external_gateway_info'] = {'network_id': network_id}
|
|
if tenant_id:
|
|
r['tenant_id'] = tenant_id
|
|
self.neutron.create_router({'router': r})
|
|
|
|
return self._facts('routers', self.neutron.list_routers())
|
|
|
|
def _add_router_interface(self, variables):
|
|
required_vars = ['router_name', 'subnet_name']
|
|
variables_dict = self._get_vars(variables, required=required_vars)
|
|
router_name = variables_dict.pop('router_name')
|
|
subnet_name = variables_dict.pop('subnet_name')
|
|
router_id = self._get_resource_by_name('routers', router_name)
|
|
subnet_id = self._get_resource_by_name('subnets', subnet_name)
|
|
|
|
if not router_id:
|
|
self.failure(
|
|
error='Router not found',
|
|
rc=1,
|
|
msg='The specified router could not be found'
|
|
)
|
|
|
|
if not subnet_id:
|
|
self.failure(
|
|
error='Subnet not found',
|
|
rc=1,
|
|
msg='The specified subnet could not be found'
|
|
)
|
|
|
|
found = False
|
|
|
|
for port in self.neutron.list_ports(device_id=router_id)['ports']:
|
|
for fixed_ips in port['fixed_ips']:
|
|
if fixed_ips['subnet_id'] == subnet_id:
|
|
found = True
|
|
if not found:
|
|
self.state_change = True
|
|
self.neutron.add_interface_router(router_id,
|
|
{'subnet_id': subnet_id})
|
|
|
|
|
|
def main():
|
|
module = AnsibleModule(
|
|
argument_spec=dict(
|
|
command=dict(required=True, choices=COMMAND_MAP.keys()),
|
|
openrc_path=dict(required=True),
|
|
net_name=dict(required=False),
|
|
subnet_name=dict(required=False),
|
|
cidr=dict(required=False),
|
|
provider_physical_network=dict(required=False),
|
|
provider_network_type=dict(required=False),
|
|
provider_segmentation_id=dict(required=False),
|
|
router_external=dict(required=False),
|
|
router_name=dict(required=False),
|
|
external_gateway_info=dict(required=False),
|
|
tenant_id=dict(required=False),
|
|
insecure=dict(default=False, required=False,
|
|
choices=BOOLEANS + ['True', 'False'])
|
|
),
|
|
supports_check_mode=False
|
|
)
|
|
mn = ManageNeutron(module)
|
|
mn.command_router()
|
|
|
|
from ansible.module_utils.basic import *
|
|
if __name__ == '__main__':
|
|
main()
|