Baseline commit

This commit is contained in:
James Page 2016-11-10 09:18:28 +00:00
parent 2ac57d5978
commit 0939c64c8e
8 changed files with 271 additions and 5 deletions

View File

@ -4,9 +4,9 @@ snap.openstack
Helpers for writing Snaps for OpenStack
Please fill here a long description which must be at least 3 lines wrapped on
80 cols, so that distribution package maintainers can use it in their packages.
Note that this is a hard requirement.
This project provides a wrapper for automatically wrapping openstack
commands in snaps, building out appropriate Oslo configuration and
logging options on the command line.
* Free software: Apache license
* Documentation: http://docs.openstack.org/developer/snap.openstack
@ -16,4 +16,4 @@ Note that this is a hard requirement.
Features
--------
* TODO
* Support for classic mode snap use

View File

@ -0,0 +1,45 @@
# Sample snap-openstack configuration file
#
# snap-openstack will automatically substitute the following
# in both paths for files and directories and in templates:
#
# SNAP_COMMON -> snap_common
# SNAP -> snap
# SNAP -> snap_shared
#
# Setup is executed for all entry points prior to execution
# snap-openstack will assure that templated files are in place
# and that any directory structure in $SNAP_COMMON is created
setup:
dirs:
- "{snap_common}/etc/nova.conf.d"
- "{snap_common}/etc/nova"
- "{snap_common}/logs"
templates:
"nova-snap.conf.j2": "[snap_common}/etc/nova.conf.d/nova-snap.conf"
# Entry points are used to execute commands from with the snap
# with a sane set of defaults in terms of configuration files
# and directories
entry_points:
# Executes the following:
#
# nova-manage --config-file=$SNAP/etc/nova/nova,conf \
# --config-file=$SNAP_COMMON/etc/nova/nova.conf \
# --config-dir=$SNAP_COMMON/etc/nova.conf.d \
# --log-file=$SNAP_COMMON/logs/nova-manage.log
#
# this is designed to be executed from the snapcraft.yaml apps section
# using:
#
# command: snap-openstack nova-manage
#
# any additional arguments will be passed to the underlying binary
nova-manage:
binary: nova-manage
config-files:
- "{snap}/etc/nova/nova.conf"
- "[snap_common}/etc/nova/nova.conf"
config-dirs:
- "{snap_common}/etc/nova.conf.d"
log-file:
- "{snap_common}/logs/nova-manage.log"

View File

@ -3,3 +3,6 @@
# process, which may cause wedges in the gate later.
pbr>=1.6 # Apache-2.0
# Left unversioned as designed to align with OpenStack component being snapped
jinja2

View File

@ -49,3 +49,7 @@ output_file = snap_openstack/locale/snap_openstack.pot
all_files = 1
build-dir = releasenotes/build
source-dir = releasenotes/source
[entry_points]
console_scripts =
snap-openstack = snap_openstack.cmd.run:main

125
snap_openstack/base.py Normal file
View File

@ -0,0 +1,125 @@
#!/usr/bin/env python
# Copyright 2016 Canonical UK Limited
#
# 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 subprocess
import yaml
import logging
from snap_openstack.renderer import SnapFileRenderer
LOG = logging.getLogger(__name__)
SNAP_ENV = ['SNAP_NAME',
'SNAP_VERSION',
'SNAP_REVISION',
'SNAP_ARCH',
'SNAP_LIBRARY_PATH',
'SNAP',
'SNAP_DATA',
'SNAP_COMMON',
'SNAP_USER_DATA',
'SNAP_USER_COMMON',
'TMPDIR']
def snap_env():
'''Grab SNAP* environment variables
@return dict of all SNAP* environment variables indexed in lower case
'''
_env = {}
for key in SNAP_ENV:
_env[key.lower()] = os.environ.get(key)
return _env
def ensure_dir(filepath):
'''Ensure that the directory structure to support a give file path exists'''
dir_name = os.path.dirname(filepath)
if not os.path.exists(dir_name):
LOG.info('Creating directory {}'.format(dir_name))
os.makedirs(dir_name, 0o750)
class OpenStackSnap():
'''Main executor class for snap-openstack'''
def __init__(self, config_file):
with open(config_file, 'r') as config:
self.configuration = yaml.load(config)
self.snap_env = snap_env()
def setup(self):
'''Perform any pre-execution snap setup
Run this method prior to use of the execute metho
'''
setup = self.configuration['setup']
renderer = SnapFileRenderer()
for dirs in setup['dirs']:
dir_name = dirs.format(self.snap_env)
ensure_dir(dir_name)
for template in setup['templates']:
target = setup['templates'][template]
target_file = target.format(self.snap_env)
ensure_dir(target_file)
LOG.info('Rendering {} to {}'.format(template,
target_file))
with open(target_file, 'w') as tf:
os.fchmod(tf.fileno(), 0o550)
tf.write(renderer.render(template,
self.snap_env))
def execute(self, argv):
'''Execute snap command building out configuration and log options'''
entry_point = self.configuration['entry_points'].get(argv[1])
if not entry_point:
_msg = 'Enable to find entry point for {}'.format(argv[1])
LOG.error(_msg)
raise ValueError(_msg)
other_args = argv[:2]
# Build out command to run
cmd = [entry_point['binary']]
for cfile in entry_point.get('config-files', []):
cfile = cfile.format(self.snap_env)
if os.path.exists(cfile):
cmd.append('--config-file={}'.format(cfile))
else:
LOG.warning('Configuration file {} not found'
', skipping'.format(cfile))
for cdir in entry_point.get('config-dirs', []):
cdir = cdir.format(self.snap_env)
if os.path.exists(cdir):
cmd.append('--config-dir={}'.format(cdir))
else:
LOG.warning('Configuration directory {} not found'
', skipping'.format(cdir))
log_file = entry_point.get('log-file')
if log_file:
log_file = log_file.format(self.snap_env)
cmd.append('--log-file={}'.format(log_file))
# Ensure any arguments passed to wrapper are propagated
cmd.extend(other_args)
subprocess.check_call(cmd)

View File

43
snap_openstack/cmd/run.py Normal file
View File

@ -0,0 +1,43 @@
#!/usr/bin/env python
# Copyright 2016 Canonical UK Limited
#
# 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 sys
import logging
from snap_openstack.base import OpenStackSnap
LOG = logging.getLogger(__name__)
CONFIG_FILE = 'snap-openstack.yaml'
def main():
logging.basicConfig(level=logging.INFO)
snap = os.environ.get('SNAP')
if not snap:
LOG.error('Not executing in snap environment, exiting')
sys.exit(1)
config_path = os.path.join(snap,
CONFIG_FILE)
if os.path.exists(config_path):
LOG.info('Using snap wrapper: {}'.format(config_path))
s_openstack = OpenStackSnap(config_path)
s_openstack.setup()
s_openstack.execute(sys.argv)
else:
LOG.error('Unable to find snap-openstack.yaml configuration file')
sys.exit(1)

View File

@ -0,0 +1,46 @@
#!/usr/bin/env python
# Copyright 2016 Canonical UK Limited
#
# 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 logging
from jinja2 import FileSystemLoader, Environment, exceptions
LOG = logging.getLogger(__name__)
class SnapFileRenderer():
'''Helper class for rendering snap templates for runtime use'''
def __init__(self):
self._loaders = [
FileSystemLoader(os.path.join(os.environ.get('SNAP'),
'templates'))
]
self._tmpl_env = Environment(loader=self._loaders)
def render(self, template_name, env):
'''Render j2 template using SNAP environment context
@param template_name: name of the template to use for rendering
@return: string of rendered context, ready to write back to a file
'''
try:
template = self._tmpl_env.get_template(template_name)
except exceptions.TemplateNotFound as te:
LOG.error('Unable to locate template: {}'.format(template_name))
raise te
return template.render(env)