openstack-ansible-os_neutron/library/neutron_migrations_facts
Matt Thompson a277ebd2cf Update os_neutron to handle newton migrations
Performing initial neutron migrations currently works, but applying
subsequent migrations will fail due to the role attempting to apply
mitaka migrations.  This update moves tasks/neutron_db_setup.yml
over to newton and updates library/neutron_migrations_facts to be
aware of newton migrations.

Change-Id: I56416f21144f0cdb9e096576922fd441bc7f9cb3
2016-04-20 21:17:39 +01:00

277 lines
8.7 KiB
Python

#!/usr/bin/env python
# Copyright 2015, 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.
import os
import re
import subprocess
from ansible.module_utils.basic import *
DOCUMENTATION = """
---
module: neutron_migrations_facts
short_description:
- A module for gathering neutron migrations facts.
description:
- This module creates a fact called 'neutron_migrations', which is a dict
containing keys that represent each alembic migration branch. The value
for each key is another dict, containing a key value for the current
neutron revision for that branch (revision), and whether or not the
branch is currently at the latest revision (head). The
'neutron_migrations' fact can then be used to determine if migrations
for either branch are required, and allows us to only apply migrations
if necessary.
options:
release:
description:
- This is the OpenStack release you're running, used when
searching for migration revisions in the neutron code.
default: liberty
library_path:
description:
- Local path to the location where the neutron python package
is installed.
default: /usr/local/lib/python2.7/dist-packages/neutron
bin_path:
description:
- Local path to the where the neutron binaries are.
default: /usr/local/bin
author: Rcbops
"""
EXAMPLES = """
- name: Gather neutron migration facts
neutron_migrations_facts:
release: newton
"""
MIGRATIONS = {
'projects': {
'neutron': {
'branches': {
'expand': {
'revision': None,
'head': None
},
'contract': {
'revision': None,
'head': None
}
},
'installed': False
},
'neutron-fwaas': {
'branches': {
'expand': {
'revision': None,
'head': None
},
'contract': {
'revision': None,
'head': None
}
},
'installed': False
},
'neutron-lbaas': {
'branches': {
'expand': {
'revision': None,
'head': None
},
'contract': {
'revision': None,
'head': None
}
},
'installed': False
},
'neutron-vpnaas': {
'branches': {
'expand': {
'revision': None,
'head': None
},
'contract': {
'revision': None,
'head': None
}
},
'installed': False
}
},
'run_contract': True,
'run_expand': True
}
RELEASES = ['newton', 'mitaka', 'liberty']
def get_branch(release, revision, library_path, project):
# Here we drop any releases that are newer than the specified release.
# We have to check previous releases for migrations as at time of writing,
# neutron-lbaas doesn't have mitaka migrations and therefore the plugin
# will fail unless we also check the previous release's migrations.
for release in RELEASES[RELEASES.index(release):]:
migrations_dir = (
'%s/%s/db/migration/alembic_migrations/versions/%s/' % (
library_path,
project.replace('-', '_'),
release,
)
)
if os.path.isdir(migrations_dir):
for branch in MIGRATIONS['projects'][project]['branches'].keys():
migration_dir = os.path.join(
get_abs_path(migrations_dir), branch
)
# If a release has no migrations for a given branch, the branch
# directory will not exist.
if os.path.isdir(migration_dir):
for file in os.listdir(migration_dir):
if (file.endswith('.py') and
file.split('_')[0] == revision):
return branch
def get_abs_path(path):
return os.path.abspath(
os.path.expanduser(
path
)
)
def update_run_keys():
projects = MIGRATIONS['projects']
# If a probject or subproject is marked as installed but has no revision,
# we can assume the project has just been installed. In this case we
# just return to main, leaving run_contract/run_expand as True so that
# migrations will run.
# TODO(mattt): We will want to try to figure out if migrations in this
# situation are expand, contract, or both, so that we avoid
# unnecessary neutron-server downtime.
for p in projects:
if (projects[p]['installed'] is True and
projects[p]['branches']['expand']['revision'] is None and
projects[p]['branches']['contract']['revision'] is None):
return
# Here we will determine if migrations should be skipped -- this will
# happen when all the projects or subprojects that have a revision
# are also at head.
for b in ('contract', 'expand'):
migrations_with_a_rev = [
v
for v in projects.values()
if v['branches'][b]['revision'] is not None
]
if (len(migrations_with_a_rev) > 0 and
all(m['branches'][b]['head'] is True
for m in migrations_with_a_rev)):
MIGRATIONS['run_%s' % b] = False
def main():
module = AnsibleModule(
argument_spec=dict(
release=dict(
type='str',
default='liberty'
),
library_path=dict(
type='str',
default='/usr/local/lib/python2.7/dist-packages/neutron'
),
bin_path=dict(
type='str',
default='/usr/local/bin'
)
),
supports_check_mode=False
)
if module.params['release'] not in RELEASES:
module.fail_json(
msg='neutron fact collection failed: release %s not found in'
' %s' % (module.params['release'], RELEASES)
)
state_change = False
project = None
command = [
'%s/neutron-db-manage' % get_abs_path(module.params['bin_path']),
'current'
]
try:
current = subprocess.check_output(command)
except subprocess.CalledProcessError as e:
module.fail_json(msg='neutron fact collection failed: "%s".' % e)
for line in current.splitlines():
head = False
project_match = re.search(
"^\s*Running current for (neutron(-[a-z]+)?) ...$",
line
)
migration_match = re.search("^([0-9a-z]{4,12})(\s\(head\))?$", line)
if project_match:
project = project_match.group(1)
MIGRATIONS['projects'][project]['installed'] = True
if migration_match:
revision = migration_match.group(1)
if project is None:
module.fail_json(
msg='neutron fact collection failed: unable to detect'
' project for revision %s' % revision
)
if migration_match.group(2):
head = True
branch = get_branch(
release=module.params['release'],
revision=revision,
library_path=get_abs_path(module.params['library_path']),
project=project
)
if branch is None:
module.fail_json(
msg='neutron fact collection failed: unable to find'
' migration with revision %s' % revision
)
proj_migrations = MIGRATIONS['projects'][project]['branches']
proj_migrations[branch]['revision'] = revision
proj_migrations[branch]['head'] = head
update_run_keys()
module.exit_json(
changed=state_change,
ansible_facts={'neutron_migrations': MIGRATIONS}
)
if __name__ == '__main__':
main()