209b11a63b
Currently, neutron_migrations_facts ensures the migrations dir exists, but doesn't check if the branch dir for the given release exists. This commit updates neutron_migrations_facts to handle cases where a branch dir may not exist (as is the case on mitaka). Change-Id: I4b0d15ae6a0c2a840e66f81994f0956382130abc Closes-Bug: #1527582
277 lines
8.7 KiB
Python
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: mitaka
|
|
"""
|
|
|
|
|
|
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 = ['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()
|