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 Helpers for writing Snaps for OpenStack
Please fill here a long description which must be at least 3 lines wrapped on This project provides a wrapper for automatically wrapping openstack
80 cols, so that distribution package maintainers can use it in their packages. commands in snaps, building out appropriate Oslo configuration and
Note that this is a hard requirement. logging options on the command line.
* Free software: Apache license * Free software: Apache license
* Documentation: http://docs.openstack.org/developer/snap.openstack * Documentation: http://docs.openstack.org/developer/snap.openstack
@ -16,4 +16,4 @@ Note that this is a hard requirement.
Features 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. # process, which may cause wedges in the gate later.
pbr>=1.6 # Apache-2.0 pbr>=1.6 # Apache-2.0
# Left unversioned as designed to align with OpenStack component being snapped
jinja2

View File

@ -48,4 +48,8 @@ output_file = snap_openstack/locale/snap_openstack.pot
[build_releasenotes] [build_releasenotes]
all_files = 1 all_files = 1
build-dir = releasenotes/build build-dir = releasenotes/build
source-dir = releasenotes/source 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)