openstack-ansible/playbooks/library/neutron
Jesse Pretorius 4341b79b3a Enable all services to use Keystone 'insecurely'
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
2015-07-10 14:06:25 +01:00

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()