diff --git a/ansible/library/docker_compose b/ansible/library/docker_compose deleted file mode 100644 index 0f947e1202..0000000000 --- a/ansible/library/docker_compose +++ /dev/null @@ -1,301 +0,0 @@ -#!/usr/bin/python - -DOCUMENTATION = ''' ---- -module: docker_compose -version_added: 1.8.4 -short_description: Manage docker containers with docker-compose -description: - - Manage the lifecycle of groups of docker containers with docker-compose -options: - command: - description: - - Select the compose action to perform - required: true - choices: ['build', 'kill', 'pull', 'rm', 'scale', 'start', 'stop', 'restart', 'up'] - compose_file: - description: - - Specify the compose file to build from - required: true - type: bool - insecure_registry: - description: - - Allow access to insecure registry (HTTP or TLS with a CA not known by the Docker daemon) - type: bool - kill_signal: - description: - - The SIG to send to the docker container process when killing it - default: SIGKILL - no_build: - description: - - Do not build an image, even if it's missing - type: bool - no_deps: - description: - - Don't start linked services - type: bool - no_recreate: - description: - - If containers already exist, don't recreate them - type: bool - project_name: - description: - - Specify project name (defaults to directory name of the compose file) - type: str - service_names: - description: - - Only modify services in this list (can be used in conjunction with no_deps) - type: list - stop_timeout: - description: - - The amount of time in seconds to wait for an instance to cleanly terminate before killing it (can be used in conjunction with stop_timeout) - default: 10 - type: int - -author: Sam Yaple -requirements: [ "docker-compose", "docker >= 1.3" ] -''' - -EXAMPLES = ''' -Compose web application: - -- hosts: web - tasks: - - name: compose super important weblog - docker_compose: command="up" compose_file="/opt/compose/weblog.yml" - -Compose only mysql server from previous example: - -- hosts: web - tasks: - - name: compose mysql - docker_compose: command="up" compose_file="/opt/compose/weblog.yml" service_names=['mysql'] - -Compose project with specified prefix: - -- hosts: web - tasks: - - name: compose weblog named "myproject_weblog_1" - docker_compose: command="up" compose_file="/opt/compose/weblog.yml" project_name="myproject" - -Compose project only if already built (or no build instructions). Explicitly refuse to build the container image(s): - -- hosts: web - tasks: - - name: compose weblog - docker_compose: command="up" compose_file="/opt/compose/weblog.yml" no_build=True - -Allow the container image to be pulled from an insecure registry (this requires the docker daemon to allow the insecure registry as well): - -- hosts: web - tasks: - - name: compose weblog from local registry - docker_compose: command="up" compose_file="/opt/compose/weblog.yml" insecure_registry=True - -Start the containers in the compose project, but do not create any: - -- hosts: web - tasks: - - name: compose weblog - docker_compose: command="start" compose_file="/opt/compose/weblog.yml" - -Removed all the containers associated with a project; only wait 5 seconds for the container to respond to a SIGTERM: - -- hosts: web - tasks: - - name: Destroy ALL containers for project "devweblog" - docker_compose: command="rm" stop_timeout=5 compose_file="/opt/compose/weblog.yml" project_name="devweblog" -''' - -HAS_DOCKER_COMPOSE = True - -import re - -try: - from compose import config - from compose.cli.command import Command as compose_command - from compose.cli.docker_client import docker_client -except ImportError, e: - HAS_DOCKER_COMPOSE = False - -TIMEMAP = { - 'second': 0, - 'seconds': 0, - 'minute': 1, - 'minutes': 1, - 'hour': 2, - 'hours': 2, - 'weeks': 3, - 'months': 4, - 'years': 5, -} - -class DockerComposer: - def __init__(self, module): - self.module = module - - self.compose_file = self.module.params.get('compose_file') - self.insecure_registry = self.module.params.get('insecure_registry') - self.no_build = self.module.params.get('no_build') - self.no_cache = self.module.params.get('no_cache') - self.no_deps = self.module.params.get('no_deps') - self.no_recreate = self.module.params.get('no_recreate') - self.project_name = self.module.params.get('project_name') - self.stop_timeout = self.module.params.get('stop_timeout') - self.scale = self.module.params.get('scale') - self.service_names = self.module.params.get('service_names') - - - self.project = compose_command.get_project(compose_command(), - self.compose_file, - self.project_name, - ) - self.containers = self.project.client.containers(all=True) - - - def build(self): - self.project.build(no_cache = self.no_cache, - service_names = self.service_names, - ) - - def kill(self): - self.project.kill(signal = self.kill_signal, - service_names = self.service_names, - ) - - def pull(self): - self.project.pull(insecure_registry = self.insecure_registry, - service_names = self.service_names, - ) - - def rm(self): - self.stop() - self.project.remove_stopped(service_names = self.service_names) - - def scale(self): - for s in self.service_names: - try: - num = int(self.scale[s]) - except ValueError: - msg = ('Value for scaling service "%s" should be an int, but ' - 'value "%s" was recieved' % (s, self.scale[s])) - module.fail_json(msg=msg, failed=True) - - try: - project.get_service(s).scale(num) - except CannotBeScaledError: - msg = ('Service "%s" cannot be scaled because it specifies a ' - 'port on the host.' % s) - module.fail_json(msg=msg, failed=True) - - def start(self): - self.project.start(service_names = self.service_names) - - def stop(self): - self.project.stop(service_names = self.service_names, - timeout = self.stop_timeout, - ) - - def restart(self): - self.project.restart(service_names = self.service_names) - - def up(self): - self.project_contains = self.project.up( - do_build = not self.no_build, - insecure_registry = self.insecure_registry, - allow_recreate = not self.no_recreate, - service_names = self.service_names, - start_deps = not self.no_deps, - ) - - def check_if_changed(self): - new_containers = self.project.client.containers(all=True) - - for pc in self.project_contains: - old_container = None - new_container = None - name = '/' + re.split(' |>',str(pc))[1] - - for c in self.containers: - if c['Names'][0] == name: - old_container = c - - for c in new_containers: - if c['Names'][0] == name: - new_container = c - - if not old_container or not new_container: - return True - - if old_container['Created'] != new_container['Created']: - return True - - old_status = re.split(' ', old_container['Status']) - new_status = re.split(' ', new_container['Status']) - - if old_status[0] != new_status[0]: - return True - - if old_status[0] == 'Up': - if TIMEMAP[old_status[-1]] > TIMEMAP[new_status[-1]]: - return True - else: - if TIMEMAP[old_status[-2]] < TIMEMAP[new_status[-2]]: - return True - - return False - -def check_dependencies(module): - if not HAS_DOCKER_COMPOSE: - msg = ('`docker-compose` does not seem to be installed, but is required ' - 'by the Ansible docker-compose module.') - module.fail_json(failed=True, msg=msg) - -def main(): - module = AnsibleModule( - argument_spec = dict( - command = dict(required=True, - choices=['build', 'kill', 'pull', 'rm', - 'scale', 'start', 'stop', - 'restart', 'up'] - ), - compose_file = dict(required=True, type='str'), - insecure_registry = dict(type='bool'), - kill_signal = dict(default='SIGKILL'), - no_build = dict(type='bool'), - no_cache = dict(type='bool'), - no_deps = dict(type='bool'), - no_recreate = dict(type='bool'), - project_name = dict(type='str'), - scale = dict(type='dict'), - service_names = dict(type='list'), - stop_timeout = dict(default=10, type='int') - ) - ) - - check_dependencies(module) - - - try: - composer = DockerComposer(module) - command = getattr(composer, module.params.get('command')) - - command() - - changed = composer.check_if_changed() - - module.exit_json(changed=changed) - except Exception, e: - try: - changed = composer.check_if_changed() - except Exception: - changed = True - - module.exit_json(failed=True, changed=changed, msg=repr(e)) - - -# import module snippets -from ansible.module_utils.basic import * - -if __name__ == '__main__': - main() diff --git a/ansible/library/kolla_keystone_service.py b/ansible/library/kolla_keystone_service.py new file mode 100644 index 0000000000..455aaf578e --- /dev/null +++ b/ansible/library/kolla_keystone_service.py @@ -0,0 +1,84 @@ +#!/usr/bin/python + +# Copyright 2015 Sam Yaple +# +# 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. + +# This file is a barebones file needed to file a gap until Ansible 2.0. No error +# checking, no deletions, no updates. Idempotent creation only. + +# If you look closely, you will see we arent _really_ using the shade module +# we just use it to slightly abstract the authentication model. As patches land +# in upstream shade we will be able to use more of the shade module. Until then +# if we want to be 'stable' we really need to be using it as a passthrough + +import shade + +def main(): + module = AnsibleModule( + argument_spec = openstack_full_argument_spec( + description = dict(required=True, type='str'), + service_name = dict(required=True, type='str'), + service_type = dict(required=True, type='str'), + admin_url = dict(required=True, type='str'), + internal_url = dict(required=True, type='str'), + public_url = dict(required=True, type='str'), + endpoint_region = dict(required=True, type='str') + ) + ) + + try: + description = module.params.pop('description') + service_name = module.params.pop('service_name') + service_type = module.params.pop('service_type') + admin_url = module.params.pop('admin_url') + internal_url = module.params.pop('internal_url') + public_url = module.params.pop('public_url') + endpoint_region = module.params.pop('endpoint_region') + + changed = False + service = None + endpoint = None + + cloud = shade.operator_cloud(**module.params) + + for _service in cloud.keystone_client.services.list(): + if _service.type == service_type: + service = _service + + if service is not None: + for _endpoint in cloud.keystone_client.endpoints.list(): + if _endpoint.service_id == service.id: + endpoint = _endpoint + else: + service = cloud.keystone_client.services.create(name=service_name, + service_type=service_type, + description=description) + + if endpoint is None: + changed = True + cloud.keystone_client.endpoints.create(service_id=service.id, + adminurl=admin_url, + internalurl=public_url, + publicurl=public_url, + region=endpoint_region) + + module.exit_json(changed=changed) + except Exception as e: + module.exit_json(failed=True, changed=True, msg=e) + +# import module snippets +from ansible.module_utils.basic import * +from ansible.module_utils.openstack import * +if __name__ == '__main__': + main() diff --git a/ansible/library/kolla_keystone_user.py b/ansible/library/kolla_keystone_user.py new file mode 100644 index 0000000000..5d295c1e27 --- /dev/null +++ b/ansible/library/kolla_keystone_user.py @@ -0,0 +1,83 @@ +#!/usr/bin/python + +# Copyright 2015 Sam Yaple +# +# 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. + +# This file is a barebones file needed to file a gap until Ansible 2.0. No error +# checking, no deletions, no updates. Idempotent creation only. + +# If you look closely, you will see we arent _really_ using the shade module +# we just use it to slightly abstract the authentication model. As patches land +# in upstream shade we will be able to use more of the shade module. Until then +# if we want to be 'stable' we really need to be using it as a passthrough + +import shade + +def main(): + module = AnsibleModule( + argument_spec = openstack_full_argument_spec( + password = dict(required=True, type='str'), + project = dict(required=True, type='str'), + role = dict(required=True, type='str'), + user = dict(required=True, type='str') + ) + ) + + try: + password = module.params.pop('password') + project_name = module.params.pop('project') + role_name = module.params.pop('role') + user_name = module.params.pop('user') + + changed = False + project = None + role = None + user = None + + cloud = shade.operator_cloud(**module.params) + + for _project in cloud.keystone_client.tenants.list(): + if _project.name == project_name: + project = _project + + for _role in cloud.keystone_client.roles.list(): + if _role.name == role_name: + role = _role + + for _user in cloud.keystone_client.users.list(): + if _user.name == user_name: + user = _user + + if not project: + changed = True + project = cloud.keystone_client.tenants.create(tenant_name=project_name) + + if not role: + changed = True + role = cloud.keystone_client.roles.create(name=role_name) + + if not user: + changed = True + user = cloud.keystone_client.users.create(name=user_name, password=password, tenant_id=project.id) + cloud.keystone_client.roles.add_user_role(role=role.id, user=user.id, tenant=project.id) + + module.exit_json(changed=changed) + except Exception as e: + module.exit_json(failed=True, changed=True, msg=e) + +# import module snippets +from ansible.module_utils.basic import * +from ansible.module_utils.openstack import * +if __name__ == '__main__': + main() diff --git a/ansible/library/merge_configs b/ansible/library/merge_configs.py similarity index 100% rename from ansible/library/merge_configs rename to ansible/library/merge_configs.py diff --git a/ansible/roles/glance/tasks/main.yml b/ansible/roles/glance/tasks/main.yml index 2821641c4b..5c48120b7c 100644 --- a/ansible/roles/glance/tasks/main.yml +++ b/ansible/roles/glance/tasks/main.yml @@ -1,8 +1,8 @@ --- +- include: register.yml + - include: config.yml - include: bootstrap.yml - include: start.yml - -#- include: register.yml diff --git a/ansible/roles/glance/tasks/register.yml b/ansible/roles/glance/tasks/register.yml index 8ee20320bb..711ea5ff97 100644 --- a/ansible/roles/glance/tasks/register.yml +++ b/ansible/roles/glance/tasks/register.yml @@ -1,12 +1,21 @@ --- -# NB: Not an Attorney -# -# Upstream ansible will have all of the new modules we need based on -# the shade library. They are written, but the keystone modules haven't3 -# been merged yet. None of the modules will land before Ansible 2.0. -# -# These new modules will be relicensed using ASL2.0 as the result of a -# gentlemen's agreement that the Kolla authors will not alter the Shade code. -# This does not place additional restrictions on the license of this work. The -# relicense agreement is based upon trust, not something legally binding and -# has no binding impact on the license of Kolla.. +- name: Creating the Glance service and endpoint + kolla_keystone_service: + service_name: "glance" + service_type: "image" + description: "Openstack Image" + endpoint_region: "{{ openstack_region_name }}" + admin_url: "http://{{ kolla_internal_address }}:{{ glance_api_port }}" + internal_url: "http://{{ kolla_internal_address }}:{{ glance_api_port }}" + public_url: "http://{{ kolla_external_address }}:{{ glance_api_port }}" + auth: "{{ openstack_auth_v2 }}" + region_name: "{{ openstack_region_name }}" + +- name: Creating the Glance project, user, and role + kolla_keystone_user: + project: "service" + user: "glance" + password: "{{ glance_keystone_password }}" + role: "admin" + auth: "{{ openstack_auth_v2 }}" + region_name: "{{ openstack_region_name }}" diff --git a/etc/kolla/globals.yml b/etc/kolla/globals.yml index 3c0ae3b621..e90380990d 100644 --- a/etc/kolla/globals.yml +++ b/etc/kolla/globals.yml @@ -72,6 +72,20 @@ openstack_region_name: "RegionOne" keystone_public_port: "5000" keystone_admin_port: "35357" +openstack_auth: + auth_url: "http://{{ kolla_internal_address }}:{{ keystone_admin_port }}" + username: "admin" + password: "{{ keystone_admin_password }}" + project_name: "admin" + +# This shouldn't be needed for long. It is only temporary until we get the +# ansible modules sorted out +openstack_auth_v2: + auth_url: "http://{{ kolla_internal_address }}:{{ keystone_admin_port }}/v2.0" + username: "admin" + password: "{{ keystone_admin_password }}" + project_name: "admin" + #################### # RabbitMQ options