diff options
Diffstat (limited to 'tools')
-rwxr-xr-x | tools/bddeb | 33 | ||||
-rwxr-xr-x | tools/make-dist-tarball | 25 | ||||
-rwxr-xr-x | tools/mock-meta.py | 334 | ||||
-rwxr-xr-x | tools/read-dependencies | 45 | ||||
-rwxr-xr-x | tools/read-version | 70 | ||||
-rwxr-xr-x | tools/run-pep8 | 28 | ||||
-rwxr-xr-x | tools/run-pylint | 13 |
7 files changed, 478 insertions, 70 deletions
diff --git a/tools/bddeb b/tools/bddeb deleted file mode 100755 index 598f71bb..00000000 --- a/tools/bddeb +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh - -TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXXX") -#TEMP_D=/tmp/my.d -start=${PWD} -rm -Rf "${TEMP_D}"; mkdir "${TEMP_D}" -set -e -trap "rm -Rf '${TEMP_D}'" exit -files=$(bzr ls --versioned) -revno=$(bzr revno) -version=$(awk \ - -F= '$1 ~ /version$/ { gsub("[^0-9.]","",$2); print $2; }' setup.py) -mkdir "${TEMP_D}/cloud-init" -otar="$TEMP_D/cloud-init_$version~bzr${revno}.orig.tar.gz" -tar -czf - ${files} > "$otar" -tar -C "${TEMP_D}/cloud-init" -xzf - <"$otar" - -if [ ! -d "${TEMP_D}/cloud-init/debian" ]; then - rsync -a debian.trunk/ "${TEMP_D}/cloud-init/debian" -fi -sed -i -e "s,VERSION,$version," -e "s,REVNO,bzr$revno," \ - "$TEMP_D/cloud-init/debian/changelog" -cd "${TEMP_D}/cloud-init" -debuild "$@" -#for x in ../*.deb; do -# echo wrote ${x##*/} -#done -debname="cloud-init_${version}~bzr${revno}-0_all.deb" -mv "../$debname" "$start" -link="$start/cloud-init_all.deb" -echo "wrote $debname" -[ ! -e "$link" -o -L "$link" ] - { ln -sf "$debname" "$link" && echo "linked ${link##*/}"; } diff --git a/tools/make-dist-tarball b/tools/make-dist-tarball deleted file mode 100755 index d6d53aa7..00000000 --- a/tools/make-dist-tarball +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh - -Usage() { - cat <<EOF -Usage: ${0##*/} version - make a tarball of 'version' - must be in a bzr directory, and 'version' must be a tag - -EOF -} - -topdir=$PWD -tag=${1} - -[ -n "$tag" ] || { Usage 1>&2 ; exit 1; } - -tmpd=$(mktemp -d ); -trap "rm -Rf '${tmpd}'" 0 - -out=${topdir}/cloud-init-${tag}.tar.gz - -cd ${tmpd} && - bzr branch -r "tag:${tag}" "${topdir}" ./cloud-init-${tag} && - tar czf "${out}" cloud-init-${tag}/ --exclude cloud-init-${tag}/.bzr && - echo "wrote ${out}" diff --git a/tools/mock-meta.py b/tools/mock-meta.py new file mode 100755 index 00000000..0f13acd6 --- /dev/null +++ b/tools/mock-meta.py @@ -0,0 +1,334 @@ +#!/usr/bin/python + +# Provides a somewhat random, somewhat compat, somewhat useful mock version of +# +# http://docs.amazonwebservices.com/AWSEC2/2007-08-29/DeveloperGuide/AESDG-chapter-instancedata.html + +import functools +import httplib +import json +import logging +import random +import string +import sys +import yaml + +from optparse import OptionParser + +from BaseHTTPServer import (HTTPServer, BaseHTTPRequestHandler) + +log = logging.getLogger('meta-server') + +# Constants +EC2_VERSIONS = [ + '1.0', + '2007-01-19', + '2007-03-01', + '2007-08-29', + '2007-10-10', + '2007-12-15', + '2008-02-01', + '2008-09-01', + '2009-04-04', + 'latest', +] + +BLOCK_DEVS = [ + 'ami', + 'root', + 'ephemeral0', +] + +DEV_PREFIX = 'v' +DEV_MAPPINGS = { + 'ephemeral0': '%sda2' % (DEV_PREFIX), + 'root': '/dev/%sda1' % (DEV_PREFIX), + 'ami': '%sda1' % (DEV_PREFIX), + 'swap': '%sda3' % (DEV_PREFIX), +} + +META_CAPABILITIES = [ + 'aki-id', + 'ami-id', + 'ami-launch-index', + 'ami-manifest-path', + 'ari-id', + 'block-device-mapping/', + 'hostname', + 'instance-action', + 'instance-id', + 'instance-type', + 'local-hostname', + 'local-ipv4', + 'placement/', + 'product-codes', + 'public-hostname', + 'public-ipv4', + 'reservation-id', + 'security-groups' +] + +INSTANCE_TYPES = [ + 'm1.small', + 'm1.medium', + 'm1.large', + 'm1.xlarge', +] + +AVAILABILITY_ZONES = [ + "us-east-1a", + "us-east-1b", + "us-east-1c", + 'us-west-1', + "us-east-1d", + 'eu-west-1a', + 'eu-west-1b', +] + +PLACEMENT_CAPABILITIES = { + 'availability-zone': AVAILABILITY_ZONES, +} + +NOT_IMPL_RESPONSE = json.dumps({}) + + +class WebException(Exception): + def __init__(self, code, msg): + Exception.__init__(self, msg) + self.code = code + + +def yamlify(data): + formatted = yaml.dump(data, + line_break="\n", + indent=4, + explicit_start=True, + explicit_end=True, + default_flow_style=False) + return formatted + + +def format_text(text): + if not len(text): + return "<<" + lines = text.splitlines() + nlines = [] + for line in lines: + nlines.append("<< %s" % line) + return "\n".join(nlines) + + +ID_CHARS = [c for c in (string.ascii_uppercase + string.digits)] +def id_generator(size=6, lower=False): + txt = ''.join(random.choice(ID_CHARS) for x in range(size)) + if lower: + return txt.lower() + else: + return txt + + +class MetaDataHandler(object): + + def __init__(self, opts): + self.opts = opts + self.instances = {} + + def get_data(self, params, who, **kwargs): + if not params: + caps = sorted(META_CAPABILITIES) + return "\n".join(caps) + action = params[0] + action = action.lower() + if action == 'instance-id': + return 'i-%s' % (id_generator(lower=True)) + elif action == 'ami-launch-index': + return "%s" % random.choice([0,1,2,3]) + elif action == 'aki-id': + return 'aki-%s' % (id_generator(lower=True)) + elif action == 'ami-id': + return 'ami-%s' % (id_generator(lower=True)) + elif action == 'ari-id': + return 'ari-%s' % (id_generator(lower=True)) + elif action == 'block-device-mapping': + nparams = params[1:] + if not nparams: + devs = sorted(BLOCK_DEVS) + return "\n".join(devs) + else: + return "%s" % (DEV_MAPPINGS.get(nparams[0].strip(), '')) + elif action in ['hostname', 'local-hostname', 'public-hostname']: + return "%s" % (who) + elif action == 'instance-type': + return random.choice(INSTANCE_TYPES) + elif action == 'ami-manifest-path': + return 'my-amis/spamd-image.manifest.xml' + elif action == 'security-groups': + return 'default' + elif action in ['local-ipv4', 'public-ipv4']: + there_ip = kwargs.get('client_ip', '10.0.0.1') + return "%s" % (there_ip) + elif action == 'reservation-id': + return "r-%s" % (id_generator(lower=True)) + elif action == 'product-codes': + return "%s" % (id_generator(size=8)) + elif action == 'placement': + nparams = params[1:] + if not nparams: + pcaps = sorted(PLACEMENT_CAPABILITIES.keys()) + return "\n".join(pcaps) + else: + pentry = nparams[0].strip().lower() + if pentry == 'availability-zone': + zones = PLACEMENT_CAPABILITIES[pentry] + return "%s" % random.choice(zones) + else: + return "%s" % (PLACEMENT_CAPABILITIES.get(pentry, '')) + else: + log.warn(("Did not implement action %s, " + "returning empty response: %r"), + action, NOT_IMPL_RESPONSE) + return NOT_IMPL_RESPONSE + + +class UserDataHandler(object): + + def __init__(self, opts): + self.opts = opts + + def _get_user_blob(self, **kwargs): + blob_mp = {} + blob_mp['hostname'] = kwargs.get('who', '') + lines = [] + lines.append("#cloud-config") + lines.append(yamlify(blob_mp)) + blob = "\n".join(lines) + return blob.strip() + + def get_data(self, params, who, **kwargs): + if not params: + return self._get_user_blob(who=who) + return '' + + +# Seem to need to use globals since can't pass +# data into the request handlers instances... +# Puke! +meta_fetcher = None +user_fetcher = None + + +class Ec2Handler(BaseHTTPRequestHandler): + + def _get_versions(self): + versions = [] + for v in EC2_VERSIONS: + if v == 'latest': + continue + else: + versions.append(v) + versions = sorted(versions) + return "\n".join(versions) + + def log_message(self, format, *args): + msg = "%s - %s" % (self.address_string(), format % (args)) + log.info(msg) + + def _find_method(self, path): + # Puke! (globals) + global meta_fetcher + global user_fetcher + func_mapping = { + 'user-data': user_fetcher.get_data, + 'meta-data': meta_fetcher.get_data, + } + segments = [piece for piece in path.split('/') if len(piece)] + if not segments: + return self._get_versions + date = segments[0].strip().lower() + if date not in EC2_VERSIONS: + raise WebException(httplib.BAD_REQUEST, "Unknown date format %r" % date) + if len(segments) < 2: + raise WebException(httplib.BAD_REQUEST, "No action provided") + look_name = segments[1].lower() + if look_name not in func_mapping: + raise WebException(httplib.BAD_REQUEST, "Unknown requested data %r" % look_name) + base_func = func_mapping[look_name] + who = self.address_string() + kwargs = { + 'params': list(segments[2:]), + 'who': self.address_string(), + 'client_ip': self.client_address[0], + } + return functools.partial(base_func, **kwargs) + + def _do_response(self): + who = self.client_address + log.info("Got a call from %s for path %s", who, self.path) + try: + func = self._find_method(self.path) + log.info("Calling into func %s to get your data.", func) + data = func() + if not data: + data = '' + self.send_response(httplib.OK) + self.send_header("Content-Type", "binary/octet-stream") + self.send_header("Content-Length", len(data)) + log.info("Sending data (len=%s):\n%s", len(data), format_text(data)) + self.end_headers() + self.wfile.write(data) + except RuntimeError as e: + log.exception("Error somewhere in the server.") + self.send_error(httplib.INTERNAL_SERVER_ERROR, message=str(e)) + except WebException as e: + code = e.code + log.exception(str(e)) + self.send_error(code, message=str(e)) + + def do_GET(self): + self._do_response() + + def do_POST(self): + self._do_response() + + +def setup_logging(log_level, format='%(levelname)s: @%(name)s : %(message)s'): + root_logger = logging.getLogger() + console_logger = logging.StreamHandler(sys.stdout) + console_logger.setFormatter(logging.Formatter(format)) + root_logger.addHandler(console_logger) + root_logger.setLevel(log_level) + + +def extract_opts(): + parser = OptionParser() + parser.add_option("-p", "--port", dest="port", action="store", type=int, default=80, + help="port from which to serve traffic (default: %default)", metavar="PORT") + (options, args) = parser.parse_args() + out = dict() + out['extra'] = args + out['port'] = options.port + return out + + +def setup_fetchers(opts): + global meta_fetcher + global user_fetcher + meta_fetcher = MetaDataHandler(opts) + user_fetcher = UserDataHandler(opts) + + +def run_server(): + # Using global here since it doesn't seem like we + # can pass opts into a request handler constructor... + opts = extract_opts() + setup_logging(logging.DEBUG) + setup_fetchers(opts) + log.info("CLI opts: %s", opts) + server = HTTPServer(('0.0.0.0', opts['port']), Ec2Handler) + sa = server.socket.getsockname() + log.info("Serving server on %s using port %s ...", sa[0], sa[1]) + server.serve_forever() + + +if __name__ == '__main__': + run_server() diff --git a/tools/read-dependencies b/tools/read-dependencies new file mode 100755 index 00000000..72e1e095 --- /dev/null +++ b/tools/read-dependencies @@ -0,0 +1,45 @@ +#!/usr/bin/python +# vi: ts=4 expandtab + +import os +import sys +import re + + +def parse_requires(fn): + requires = [] + with open(fn, 'r') as fh: + lines = fh.read().splitlines() + for line in lines: + line = line.strip() + if not line or line[0] == '#': + continue + else: + requires.append(line) + return requires + + +def find_requires(args): + p_files = [] + if args: + p_files.append(args[0]) + p_files.append(os.path.join(os.pardir, "Requires")) + p_files.append(os.path.join(os.getcwd(), 'Requires')) + found = None + for fn in p_files: + if os.path.isfile(fn): + found = fn + break + return found + + +if __name__ == '__main__': + run_args = sys.argv[1:] + fn = find_requires(run_args) + if not fn: + sys.stderr.write("'Requires' file not found!\n") + sys.exit(1) + else: + deps = parse_requires(fn) + for entry in deps: + print entry diff --git a/tools/read-version b/tools/read-version new file mode 100755 index 00000000..e6167a2c --- /dev/null +++ b/tools/read-version @@ -0,0 +1,70 @@ +#!/usr/bin/python +# vi: ts=4 expandtab + +import os +import sys +import re + +from distutils import version as ver + +possible_topdir = os.path.normpath(os.path.join(os.path.abspath( + sys.argv[0]), os.pardir, os.pardir)) +if os.path.exists(os.path.join(possible_topdir, "cloudinit", "__init__.py")): + sys.path.insert(0, possible_topdir) + +from cloudinit import version as cver + +def parse_versions(fn): + with open(fn, 'r') as fh: + lines = fh.read().splitlines() + versions = [] + for line in lines: + line = line.strip() + if line.startswith("-") or not line: + continue + if not re.match(r"[\d]", line): + continue + line = line.strip(":") + if (re.match(r"^[\d+]\.[\d+]\.[\d+]$", line) or + re.match(r"^[\d+]\.[\d+]$", line)): + versions.append(line) + return versions + +def find_changelog(args): + p_files = [] + if args: + p_files.append(args[0]) + p_files.append(os.path.join(os.pardir, "ChangeLog")) + p_files.append(os.path.join(os.getcwd(), 'ChangeLog')) + found = None + for fn in p_files: + if os.path.isfile(fn): + found = fn + break + return found + + +if __name__ == '__main__': + run_args = sys.argv[1:] + fn = find_changelog(run_args) + if not fn: + sys.stderr.write("'ChangeLog' file not found!\n") + sys.exit(1) + else: + versions = parse_versions(fn) + if not versions: + sys.stderr.write("No versions found in %s!\n" % (fn)) + sys.exit(1) + else: + # Check that the code version is the same + # as the version we found! + ch_ver = versions[0].strip() + code_ver = cver.version() + ch_ver_obj = ver.StrictVersion(ch_ver) + if ch_ver_obj != code_ver: + sys.stderr.write(("Code version %s does not match" + " changelog version %s\n") % + (code_ver, ch_ver_obj)) + sys.exit(1) + sys.stdout.write(ch_ver) + sys.exit(0) diff --git a/tools/run-pep8 b/tools/run-pep8 new file mode 100755 index 00000000..e7707985 --- /dev/null +++ b/tools/run-pep8 @@ -0,0 +1,28 @@ +#!/bin/bash + +ci_files='cloud*.py cloudinit/*.py cloudinit/config/*.py' +test_files=$(find tests -name "*.py") +def_files="$ci_files $test_files" + +if [ $# -eq 0 ]; then + files=( ) + for f in $def_files; do + [ -f "$f" ] || { echo "failed, $f not a file" 1>&2; exit 1; } + files[${#files[@]}]=${f} + done +else + files=( "$@" ); +fi + +cmd=( + pep8 + + --ignore=E501 # Line too long (these are caught by pylint) + + "${files[@]}" +) + +echo -e "\nRunning pep8:" +echo "${cmd[@]}" +"${cmd[@]}" + diff --git a/tools/run-pylint b/tools/run-pylint index 46748ffb..dd6369aa 100755 --- a/tools/run-pylint +++ b/tools/run-pylint @@ -1,6 +1,6 @@ #!/bin/bash -ci_files='cloud*.py cloudinit/*.py cloudinit/CloudConfig/*.py' +ci_files='cloud*.py cloudinit/*.py cloudinit/config/*.py' test_files=$(find tests -name "*.py") def_files="$ci_files $test_files" @@ -38,14 +38,3 @@ echo -e "\nRunning pylint:" echo "${cmd[@]}" "${cmd[@]}" -cmd=( - pep8 - - --ignore=E501 # Line too long (these are caught by pylint above) - - "${files[@]}" -) - -echo -e "\nRunning pep8:" -echo "${cmd[@]}" -"${cmd[@]}" |