Remove cfntools and jeos

These files are now available in the heat-jeos repository.

Change-Id: I392e7443348a31e8454ae14d957b0b54560c2ec3
Signed-off-by: Steven Dake <sdake@redhat.com>
This commit is contained in:
Steven Dake 2012-06-07 07:47:47 -07:00
parent 4b5c3fee7b
commit 4c359db1b0
21 changed files with 15 additions and 2090 deletions

View File

@ -7,20 +7,6 @@ include MANIFEST.in pylintrc
include openstack-common.conf include openstack-common.conf
include babel.cfg include babel.cfg
graft templates graft templates
include heat/jeos/F16-i386-gold-jeos.tdl
include heat/jeos/F17-i386-gold-jeos.tdl
include heat/jeos/F16-i386-cfntools-jeos.tdl
include heat/jeos/F17-i386-cfntools-jeos.tdl
include heat/jeos/F16-x86_64-gold-jeos.tdl
include heat/jeos/F17-x86_64-gold-jeos.tdl
include heat/jeos/F16-x86_64-cfntools-jeos.tdl
include heat/jeos/F17-x86_64-cfntools-jeos.tdl
include heat/jeos/U10-amd64-cfntools-jeos.tdl
include heat/cfntools/cfn-init
include heat/cfntools/cfn-hup
include heat/cfntools/cfn-signal
include heat/cfntools/cfn-get-metadata
include heat/cfntools/cfn-push-stats
include heat/cloudinit/config include heat/cloudinit/config
include heat/cloudinit/part-handler.py include heat/cloudinit/part-handler.py
include heat/db/sqlalchemy/migrate_repo/migrate.cfg include heat/db/sqlalchemy/migrate_repo/migrate.cfg

View File

@ -270,25 +270,6 @@ def stack_list(options, arguments):
print json.dumps(result, indent=2) print json.dumps(result, indent=2)
@utils.catch_error('jeos-create')
def jeos_create(options, arguments):
'''
Create a new JEOS (Just Enough Operating System) image.
Usage: heat jeos-create <distribution> <architecture> <image type>
Distribution: Distribution such as 'F16', 'F17', 'U10', 'D6'.
Architecture: Architecture such as 'i386' 'i686' or 'x86_64'.
Image Type: Image type such as 'gold' or 'cfntools'.
'gold' is a basic gold JEOS.
'cfntools' contains the cfntools helper scripts.
The command must be run as root in order for libvirt to have permissions
to create virtual machines and read the raw DVDs.
'''
utils.jeos_create(options, arguments, jeos_path, cfntools_path)
def get_client(options): def get_client(options):
""" """
Returns a new client object to a heat server Returns a new client object to a heat server
@ -466,9 +447,7 @@ def lookup_command(parser, command_name):
'event-list': stack_events_list, 'event-list': stack_events_list,
'validate': template_validate, 'validate': template_validate,
'gettemplate': get_template, 'gettemplate': get_template,
'describe': stack_describe, 'describe': stack_describe}
'jeos_create': jeos_create, # DEPRECATED
'jeos-create': jeos_create}
commands = {} commands = {}
for command_set in (base_commands, stack_commands): for command_set in (base_commands, stack_commands):
@ -507,8 +486,6 @@ Commands:
validate Validate a template validate Validate a template
jeos-create Create a JEOS image
event-list List events for a stack event-list List events for a stack
""" """

View File

@ -121,29 +121,27 @@ This is for Heat to associate with the virtual machines.
nova keypair-add --pub_key ~/.ssh/id_rsa.pub ${USER}_key nova keypair-add --pub_key ~/.ssh/id_rsa.pub ${USER}_key
Install Oz
----------
Verify that Oz_ is installed :: Download and install heat_jeos via git
--------------------------------------
sudo yum -y install oz Download heat_jeos via git
Oz is used below to create the JEOS.
.. _Oz: http://aeolusproject.org/oz.html
Create a JEOS
-------------
:: ::
git clone git://github.com/heat-api/heat-jeos.git
cd heat-jeos
setup.py install
sudo -E heat -y jeos-create F16 x86_64 cfntools Create a JEOS with heat_jeos tools
----------------------------------
::
Note: The ``-E`` option to ``sudo`` preserves the environment, specifically the keystone credentials, when ``jeos-create`` is run as root. sudo -E heat-jeos -y create F16 x86_64 cfntools
Note: ``jeos-create`` must be run as root in order to create the cfntools disk image. Note: The ``-E`` option to ``sudo`` preserves the environment, specifically the keystone credentials, when ``heat-jeos`` is run as root.
Note: If you want to enable debugging output from Oz, add '``-d``' (debugging) to the ``jeos-create`` command. Note: ``heat-jeos`` must be run as root in order to create the cfntools disk image.
Note: If you want to enable debugging output from Oz, add '``-d``' (debugging) to the ``heat-jeos`` command.
Verify JEOS registration Verify JEOS registration
~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -1,13 +0,0 @@
There are several bootstrap methods for cloudformations:
1. Create image with application ready to go
2. Use cloud-init to run a startup script passed as userdata to the nova
server create
3. Use the CloudFormation instance helper scripts
This directory contains files required for choice #3.
cfn-init - Reads the AWS::CloudFormation::Init for the instance resource,
installs packages, and starts services
cfn-signal - Waits for an application to be ready before continuing, ie:
supporting the WaitCondition feature
cfn-hup - Handle updates from the UpdateStack CloudFormation API call

View File

@ -1,91 +0,0 @@
#!/usr/bin/env python
#
# 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.
"""
Implements cfn-get-metadata CloudFormation functionality
"""
import argparse
import io
import logging
import os
import os.path
import sys
if os.path.exists('/opt/aws/bin'):
sys.path.insert(0, '/opt/aws/bin')
from cfn_helper import *
else:
from heat.cfntools.cfn_helper import *
description = " "
parser = argparse.ArgumentParser(description=description)
parser.add_argument('-s', '--stack',
dest="stack_name",
help="A Heat stack name",
required=True)
parser.add_argument('-r', '--resource',
dest="logical_resource_id",
help="A Heat logical resource ID",
required=True)
parser.add_argument('--access-key',
dest="access_key",
help="A Keystone access key",
required=False)
parser.add_argument('--secret-key',
dest="secret_key",
help="A Keystone secret key",
required=False)
parser.add_argument('--region',
dest="region",
help="Openstack region",
required=False)
parser.add_argument('--credential-file',
dest="credential_file",
help="credential-file",
required=False)
parser.add_argument('-u', '--url',
dest="url",
help="service url",
required=False)
parser.add_argument('-k', '--key',
dest="key",
help="key",
required=False)
args = parser.parse_args()
if not args.stack_name:
print 'The Stack name must not be empty.'
exit(1)
if not args.logical_resource_id:
print 'The Resource ID must not be empty'
exit(1)
log_format = '%(levelname)s [%(asctime)s] %(message)s'
logging.basicConfig(format=log_format, level=logging.DEBUG)
logger = logging.getLogger('cfn-get-metadata')
log_file_name = "/var/log/cfn-get-metadata.log"
file_handler = logging.FileHandler(log_file_name)
file_handler.setFormatter(logging.Formatter(log_format))
logger.addHandler(file_handler)
metadata = Metadata(args.stack_name,
args.logical_resource_id,
access_key=args.access_key,
secret_key=args.secret_key,
region=args.region)
metadata.retrieve()
print str(metadata)

View File

@ -1,114 +0,0 @@
#!/usr/bin/env python
#
# 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.
"""
Implements cfn-hup CloudFormation functionality
"""
import argparse
import io
import logging
import os
import os.path
import sys
if os.path.exists('/opt/aws/bin'):
sys.path.insert(0, '/opt/aws/bin')
from cfn_helper import *
else:
from heat.cfntools.cfn_helper import *
description = " "
parser = argparse.ArgumentParser(description=description)
parser.add_argument('-c', '--config',
dest="config_dir",
help="Hook Config Directory",
required=False,
default='/etc/cfn/hooks.d')
parser.add_argument('-f', '--no-daemon',
dest="no_deamon",
action="store_true",
help="Do not run as a deamon",
required=False)
parser.add_argument('-v', '--verbose',
action="store_true",
dest="verbose",
help="Verbose logging",
required=False)
args = parser.parse_args()
log_format = '%(levelname)s [%(asctime)s] %(message)s'
if args.verbose:
logging.basicConfig(format=log_format, level=logging.DEBUG)
else:
logging.basicConfig(format=log_format, level=logging.INFO)
logger = logging.getLogger('cfntools')
log_file_name = "/var/log/cfn-hup.log"
file_handler = logging.FileHandler(log_file_name)
file_handler.setFormatter(logging.Formatter(log_format))
logger.addHandler(file_handler)
main_conf_path = '/etc/cfn/cfn-hup.conf'
try:
main_config_file = open(main_conf_path)
except IOError as exc:
logger.error('Could not open main configuration at %s' % main_conf_path)
exit(1)
config_files = []
hooks_conf_path = '/etc/cfn/hooks.conf'
if os.path.exists(hooks_conf_path):
try:
config_files.append(open(hooks_conf_path))
except IOError as exc:
logger.exception(exc)
if args.config_dir and os.path.exists(args.config_dir):
try:
for f in os.listdir(args.config_dir):
config_files.append(open(os.path.join(args.config_dir, f)))
except OSError as exc:
logger.exception(exc)
if not config_files:
logger.error('No hook files found at %s or %s' % (hooks_conf_path,
args.config_dir))
exit(1)
try:
mainconfig = HupConfig([main_config_file] + config_files)
except Exception as ex:
logger.error('Cannot load configuration: %s' % str(ex))
exit(1)
if not mainconfig.unique_resources_get():
logger.error('No hooks were found. Add some to %s or %s' % (hooks_conf_path,
args.config_dir))
exit(1)
for r in mainconfig.unique_resources_get():
print r
metadata = Metadata(mainconfig.stack,
r,
credentials_file=mainconfig.credential_file,
region=mainconfig.region)
metadata.retrieve()
try:
metadata.cfn_hup(mainconfig.hooks)
except Exception as e:
logger.exception("Error processing metadata")
exit(1)

View File

@ -1,74 +0,0 @@
#!/usr/bin/python
#
# 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.
"""
Implements cfn-init CloudFormation functionality
"""
import argparse
import logging
import os
import sys
if os.path.exists('/opt/aws/bin'):
sys.path.insert(0, '/opt/aws/bin')
from cfn_helper import *
else:
from heat.cfntools.cfn_helper import *
description = " "
parser = argparse.ArgumentParser(description=description)
parser.add_argument('-s', '--stack',
dest="stack_name",
help="A Heat stack name",
required=False)
parser.add_argument('-r', '--resource',
dest="logical_resource_id",
help="A Heat logical resource ID",
required=False)
parser.add_argument('--access-key',
dest="access_key",
help="A Keystone access key",
required=False)
parser.add_argument('--secret-key',
dest="secret_key",
help="A Keystone secret key",
required=False)
parser.add_argument('--region',
dest="region",
help="Openstack region",
required=False)
args = parser.parse_args()
log_format = '%(levelname)s [%(asctime)s] %(message)s'
logging.basicConfig(format=log_format, level=logging.DEBUG)
logger = logging.getLogger('cfn-init')
log_file_name = "/var/log/cfn-init.log"
file_handler = logging.FileHandler(log_file_name)
file_handler.setFormatter(logging.Formatter(log_format))
logger.addHandler(file_handler)
metadata = Metadata(args.stack_name,
args.logical_resource_id,
access_key=args.access_key,
secret_key=args.secret_key,
region=args.region)
metadata.retrieve()
try:
metadata.cfn_init()
except Exception as e:
logger.exception("Error processing metadata")
exit(1)

View File

@ -1,169 +0,0 @@
#!/usr/bin/env python
#
# 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.
"""
Implements cfn-signal CloudFormation functionality
"""
import argparse
import logging
import os
import random
import sys
log_format = '%(levelname)s [%(asctime)s] %(message)s'
logging.basicConfig(format=log_format, level=logging.DEBUG)
logger = logging.getLogger('cfn-push-stats')
try:
import psutil
except ImportError:
logger.warn("psutil not available. If you want process and memory "
"statistics, you need to install it.")
#
# --aws-credential-file=PATH Specifies the location of the file with AWS
# credentials.
# --aws-access-key-id=VALUE Specifies the AWS access key ID to use to
# identify the caller.
# --aws-secret-key=VALUE Specifies the AWS secret key to use to sign
# the request.
# --from-cron Specifies that this script is running from cron.
#
# Examples
#
# To perform a simple test run without posting data to Amazon CloudWatch
#
# ./mon-put-instance-data.pl --mem-util --verify --verbose
#
# To set a five-minute cron schedule to report memory and disk space utilization to CloudWatch
#
# */5 * * * * ~/aws-scripts-mon/mon-put-instance-data.pl --mem-util --disk-space-util --disk-path=/ --from-cron
#
if os.path.exists('/opt/aws/bin'):
sys.path.insert(0, '/opt/aws/bin')
from cfn_helper import *
else:
from heat.cfntools.cfn_helper import *
KILO = 1024
MEGA = 1048576
GIGA = 1073741824
unit_map = {'bytes': 1,
'kilobytes': KILO,
'megabytes': MEGA,
'gigabytes': GIGA}
description = " "
parser = argparse.ArgumentParser(description=description)
parser.add_argument('-v', '--verbose', action="store_true",
help="Verbose logging", required=False)
parser.add_argument('--service-failure', required=False, action="store_true",
help='Reports a service falure.')
parser.add_argument('--mem-util', required=False, action="store_true",
help='Reports memory utilization in percentages.')
parser.add_argument('--mem-used', required=False, action="store_true",
help='Reports memory used (excluding cache and buffers) in megabytes.')
parser.add_argument('--mem-avail', required=False, action="store_true",
help='Reports available memory (including cache and buffers) in megabytes.')
parser.add_argument('--swap-util', required=False, action="store_true",
help='Reports swap utilization in percentages.')
parser.add_argument('--swap-used', required=False, action="store_true",
help='Reports allocated swap space in megabytes.')
parser.add_argument('--disk-space-util', required=False, action="store_true",
help='Reports disk space utilization in percentages.')
parser.add_argument('--disk-space-used', required=False, action="store_true",
help='Reports allocated disk space in gigabytes.')
parser.add_argument('--disk-space-avail',required=False, action="store_true",
help='Reports available disk space in gigabytes.')
parser.add_argument('--memory-units', required=False, default='megabytes',
help='Specifies units for memory metrics.')
parser.add_argument('--disk-units', required=False, default='megabytes',
help='Specifies units for disk metrics.')
parser.add_argument('--disk-path', required=False, default='/',
help='Selects the disk by the path on which to report.')
parser.add_argument('--watch', required=True,
help='the name of the watch to post to.')
args = parser.parse_args()
data = {'Namespace': 'system/linux'}
# service failure
# ===============
if args.service_failure:
data['ServiceFailure'] = {
'Value': 1,
'Units': 'Counter'}
# memory space
# ==========
if args.mem_util or args.mem_used or args.mem_avail:
mem = psutil.phymem_usage()
if args.mem_util:
data['MemoryUtilization'] = {
'Value': mem.percent,
'Units': 'Percent'}
if args.mem_used:
data['MemoryUsed'] = {
'Value': mem.used / unit_map[args.memory_units],
'Units': args.memory_units}
if args.mem_avail:
data['MemoryAvailable'] = {
'Value': mem.free / unit_map[args.memory_units],
'Units': args.memory_units}
# swap space
# ==========
if args.swap_util or args.swap_used:
swap = psutil.virtmem_usage()
if args.swap_util:
data['SwapUtilization'] = {
'Value': swap.percent,
'Units': 'Percent'}
if args.swap_used:
data['SwapUsed'] = {
'Value': swap.used / unit_map[args.memory_units],
'Units': args.memory_units}
# disk space
# ==========
if args.disk_space_util or args.disk_space_used or args.disk_space_avail:
disk = psutil.disk_usage(args.disk_path)
if args.disk_space_util:
data['DiskSpaceUtilization'] = {
'Value': disk.percent,
'Units': 'Percent'}
if args.disk_space_used:
data['DiskSpaceUsed'] = {
'Value': disk.used / unit_map[args.disk_units],
'Units': args.disk_units}
if args.disk_space_avail:
data['DiskSpaceAvailable'] = {
'Value': disk.free / unit_map[args.disk_units],
'Units': args.disk_units}
logger.info(str(data))
server_url = metadata_server_url()
if not server_url:
logger.error('can not get the metadata_server_url')
exit(1)
url = '%sstats/%s/data/' % (server_url, args.watch)
cmd_str = "curl -X POST -H \'Content-Type:\' --data-binary \'%s\' %s" % \
(json.dumps(data), url)
cmd = CommandRunner(cmd_str)
cmd.run()
logger.info(cmd.stdout)

View File

@ -1,91 +0,0 @@
#!/usr/bin/env python
#
# 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.
"""
Implements cfn-signal CloudFormation functionality
"""
import argparse
import logging
import os
import sys
if os.path.exists('/opt/aws/bin'):
sys.path.insert(0, '/opt/aws/bin')
from cfn_helper import *
else:
from heat.cfntools.cfn_helper import *
description = " "
parser = argparse.ArgumentParser(description=description)
parser.add_argument('-s', '--success',
dest="success",
help="signal status to report",
default='true',
required=False)
parser.add_argument('-r', '--reason',
dest="reason",
help="The reason for the failure",
default="Configuration Complete",
required=False)
parser.add_argument('--data',
dest="data",
default="Application has completed configuration.",
help="The data to send",
required=False)
parser.add_argument('-i', '--id',
dest="unique_id",
help="the unique id to send back to the WaitCondition",
default='00000',
required=False)
parser.add_argument('-e', '--exit',
dest="exit_code",
help="The exit code from a procecc to interpret",
default=None,
required=False)
parser.add_argument('url',
help='the url to post to')
args = parser.parse_args()
log_format = '%(levelname)s [%(asctime)s] %(message)s'
logging.basicConfig(format=log_format, level=logging.DEBUG)
logger = logging.getLogger('cfn-init')
log_file_name = "/var/log/cfn-signal.log"
file_handler = logging.FileHandler(log_file_name)
file_handler.setFormatter(logging.Formatter(log_format))
logger.addHandler(file_handler)
logger.debug('cfn-signal called %s ' % (str(args)))
status = 'FAILURE'
if args.exit_code:
# "exit_code" takes presedence over "success".
if args.exit_code == '0':
status = 'SUCCESS'
else:
if args.success == 'true':
status = 'SUCCESS'
body = {
"Status" : status,
"Reason" : args.reason,
"UniqueId" : args.unique_id,
"Data" : args.data
}
cmd_str = "curl -X PUT -H \'Content-Type:\' --data-binary \'%s\' %s" % \
(json.dumps(body), args.url)
CommandRunner(cmd_str).run()

View File

@ -1,893 +0,0 @@
#
# 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.
"""
Implements cfn metadata handling
Resource metadata currently implemented:
* config/packages
* config/services
Not implemented yet:
* config sets
* config/sources
* config/commands
* config/files
* config/users
* config/groups
* command line args
- placeholders are ignored
"""
import ConfigParser
import errno
import grp
import json
import logging
import os
import os.path
import pwd
try:
import rpmUtils.updates as rpmupdates
import rpmUtils.miscutils as rpmutils
rpmutils_present = True
except:
rpmutils_present = False
import subprocess
import sys
from urllib2 import urlopen, Request
from urlparse import urlparse, urlunparse
logger = logging.getLogger('cfntools')
def to_boolean(b):
val = b.lower().strip() if isinstance(b, basestring) else b
return val in [True, 'true', 'yes', '1', 1]
class HupConfig(object):
def __init__(self, fp_list):
self.config = ConfigParser.SafeConfigParser()
for fp in fp_list:
self.config.readfp(fp)
self.load_main_section()
self.hooks = {}
for s in self.config.sections():
if s != 'main':
self.hooks[s] = Hook(s,
self.config.get(s, 'triggers'),
self.config.get(s, 'path'),
self.config.get(s, 'runas'),
self.config.get(s, 'action'))
def load_main_section(self):
# required values
self.stack = self.config.get('main', 'stack')
self.credential_file = self.config.get('main', 'credential-file')
try:
with open(self.credential_file) as f:
self.credentials = f.read()
except:
raise Exception("invalid credentials file %s" %
self.credential_file)
# optional values
try:
self.region = self.config.get('main', 'region')
except ConfigParser.NoOptionError:
self.region = 'nova'
try:
self.interval = self.config.getint('main', 'interval')
except ConfigParser.NoOptionError:
self.interval = 10
def __str__(self):
return '{stack: %s, credential_file: %s, region: %s, interval:%d}' % \
(self.stack, self.credential_file, self.region, self.interval)
def unique_resources_get(self):
resources = []
for h in self.hooks:
r = self.hooks[h].resource_name_get()
if not r in resources:
resources.append(self.hooks[h].resource_name_get())
return resources
class Hook(object):
def __init__(self, name, triggers, path, runas, action):
self.name = name
self.triggers = triggers
self.path = path
self.runas = runas
self.action = action
def resource_name_get(self):
sp = self.path.split('.')
return sp[1]
def event(self, ev_name, ev_object, ev_resource):
if self.resource_name_get() == ev_resource and \
ev_name in self.triggers:
CommandRunner(self.action).run(user=self.runas)
else:
logger.debug('event: {%s, %s, %s} did not match %s' %
(ev_name, ev_object, ev_resource, self.__str__()))
def __str__(self):
return '{%s, %s, %s, %s, %s}' % \
(self.name,
self.triggers,
self.path,
self.runas,
self.action)
class CommandRunner(object):
"""
Helper class to run a command and store the output.
"""
def __init__(self, command, nextcommand=None):
self._command = command
self._next = nextcommand
self._stdout = None
self._stderr = None
self._status = None
def __str__(self):
s = "CommandRunner:"
s += "\n\tcommand: %s" % self._command
if self._status:
s += "\n\tstatus: %s" % self._status
if self._stdout:
s += "\n\tstdout: %s" % self._stdout
if self._stderr:
s += "\n\tstderr: %s" % self._stderr
return s
def run(self, user='root'):
"""
Run the Command and return the output.
Returns:
self
"""
logger.debug("Running command: %s" % self._command)
cmd = ['su', user, '-c', self._command]
subproc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
output = subproc.communicate()
self._status = subproc.returncode
self._stdout = output[0]
self._stderr = output[1]
if self._next:
self._next.run()
return self
@property
def stdout(self):
return self._stdout
@property
def stderr(self):
return self._stderr
@property
def status(self):
return self._status
class RpmHelper(object):
if rpmutils_present:
_rpm_util = rpmupdates.Updates([], [])
@classmethod
def prepcache(cls):
"""
Prepare the yum cache
"""
CommandRunner("yum -y makecache").run()
@classmethod
def compare_rpm_versions(cls, v1, v2):
"""
Compare two RPM version strings.
Arguments:
v1 -- a version string
v2 -- a version string
Returns:
0 -- the versions are equal
1 -- v1 is greater
-1 -- v2 is greater
"""
if v1 and v2:
return rpmutils.compareVerOnly(v1, v2)
elif v1:
return 1
elif v2:
return -1
else:
return 0
@classmethod
def newest_rpm_version(cls, versions):
"""
Returns the highest (newest) version from a list of versions.
Arguments:
versions -- A list of version strings
e.g., ['2.0', '2.2', '2.2-1.fc16', '2.2.22-1.fc16']
"""
if versions:
if isinstance(versions, basestring):
return versions
versions = sorted(versions, rpmutils.compareVerOnly,
reverse=True)
return versions[0]
else:
return None
@classmethod
def rpm_package_version(cls, pkg):
"""
Returns the version of an installed RPM.
Arguments:
pkg -- A package name
"""
cmd = "rpm -q --queryformat '%{VERSION}-%{RELEASE}' %s" % pkg
command = CommandRunner(cmd).run()
return command.stdout
@classmethod
def rpm_package_installed(cls, pkg):
"""
Indicates whether pkg is in rpm database.
Arguments:
pkg -- A package name (with optional version and release spec).
e.g., httpd
e.g., httpd-2.2.22
e.g., httpd-2.2.22-1.fc16
"""
command = CommandRunner("rpm -q %s" % pkg).run()
return command.status == 0
@classmethod
def yum_package_available(cls, pkg):
"""
Indicates whether pkg is available via yum
Arguments:
pkg -- A package name (with optional version and release spec).
e.g., httpd
e.g., httpd-2.2.22
e.g., httpd-2.2.22-1.fc16
"""
cmd_str = "yum -C -y --showduplicates list available %s" % pkg
command = CommandRunner(cmd_str).run()
return command.status == 0
@classmethod
def install(cls, packages, rpms=True):
"""
Installs (or upgrades) a set of packages via RPM or via Yum.
Arguments:
packages -- a list of packages to install
rpms -- if True:
* use RPM to install the packages
* packages must be a list of URLs to retrieve RPMs
if False:
* use Yum to install packages
* packages is a list of:
- pkg name (httpd), or
- pkg name with version spec (httpd-2.2.22), or
- pkg name with version-release spec
(httpd-2.2.22-1.fc16)
"""
if rpms:
cmd = "rpm -U --force --nosignature "
cmd += " ".join(packages)
logger.info("Installing packages: %s" % cmd)
else:
cmd = "yum -y install "
cmd += " ".join(packages)
logger.info("Installing packages: %s" % cmd)
command = CommandRunner(cmd).run()
if command.status:
logger.warn("Failed to install packages: %s" % cmd)
@classmethod
def downgrade(cls, packages, rpms=True):
"""
Downgrades a set of packages via RPM or via Yum.
Arguments:
packages -- a list of packages to downgrade
rpms -- if True:
* use RPM to downgrade (replace) the packages
* packages must be a list of URLs to retrieve the RPMs
if False:
* use Yum to downgrade packages
* packages is a list of:
- pkg name with version spec (httpd-2.2.22), or
- pkg name with version-release spec
(httpd-2.2.22-1.fc16)
"""
if rpms:
cls.install(packages)
else:
cmd = "yum -y downgrade "
cmd += " ".join(packages)
logger.info("Downgrading packages: %s" % cmd)
command = Command(cmd).run()
if command.status:
logger.warn("Failed to downgrade packages: %s" % cmd)
class PackagesHandler(object):
_packages = {}
_package_order = ["dpkg", "rpm", "apt", "yum"]
@staticmethod
def _pkgsort(pkg1, pkg2):
order = PackagesHandler._package_order
p1_name = pkg1[0]
p2_name = pkg2[0]
if p1_name in order and p2_name in order:
return cmp(order.index(p1_name), order.index(p2_name))
elif p1_name in order:
return -1
elif p2_name in order:
return 1
else:
return cmp(p1_name.lower(), p2_name.lower())
def __init__(self, packages):
self._packages = packages
def _handle_gem_packages(self, packages):
"""
very basic support for gems
"""
# TODO(asalkeld) support versions
# -b == local & remote install
# -y == install deps
opts = '-b -y'
for pkg_name, versions in packages.iteritems():
if len(versions) > 0:
cmd_str = 'gem install %s --version %s %s' % (opts,
versions[0],
pkg_name)
CommandRunner(cmd_str).run()
else:
CommandRunner('gem install %s %s' % (opts, pkg_name)).run()
def _handle_python_packages(self, packages):
"""
very basic support for easy_install
"""
# TODO(asalkeld) support versions
for pkg_name, versions in packages.iteritems():
cmd_str = 'easy_install %s' % (pkg_name)
CommandRunner(cmd_str).run()
def _handle_yum_packages(self, packages):
"""
Handle installation, upgrade, or downgrade of a set of
packages via yum.
Arguments:
packages -- a package entries map of the form:
"pkg_name" : "version",
"pkg_name" : ["v1", "v2"],
"pkg_name" : []
For each package entry:
* if no version is supplied and the package is already installed, do
nothing
* if no version is supplied and the package is _not_ already
installed, install it
* if a version string is supplied, and the package is already
installed, determine whether to downgrade or upgrade (or do nothing
if version matches installed package)
* if a version array is supplied, choose the highest version from the
array and follow same logic for version string above
"""
# collect pkgs for batch processing at end
installs = []
downgrades = []
# update yum cache
RpmHelper.prepcache()
for pkg_name, versions in packages.iteritems():
ver = RpmHelper.newest_rpm_version(versions)
pkg = "%s-%s" % (pkg_name, ver) if ver else pkg_name
if RpmHelper.rpm_package_installed(pkg):
# FIXME:print non-error, but skipping pkg
pass
elif not RpmHelper.yum_package_available(pkg):
logger.warn("Skipping package '%s'. Not available via yum" %
pkg)
elif not ver:
installs.append(pkg)
else:
current_ver = RpmHelper.rpm_package_version(pkg)
rc = RpmHelper.compare_rpm_versions(current_ver, ver)
if rc < 0:
installs.append(pkg)
elif rc > 0:
downgrades.append(pkg)
if installs:
RpmHelper.install(installs, rpms=False)
if downgrades:
RpmHelper.downgrade(downgrades)
def _handle_rpm_packages(sef, packages):
"""
Handle installation, upgrade, or downgrade of a set of
packages via rpm.
Arguments:
packages -- a package entries map of the form:
"pkg_name" : "url"
For each package entry:
* if the EXACT package is already installed, skip it
* if a different version of the package is installed, overwrite it
* if the package isn't installed, install it
"""
#FIXME: handle rpm installs
pass
def _handle_apt_packages(self, packages):
"""
very basic support for apt
"""
# TODO(asalkeld) support versions
pkg_list = ' '.join([p for p in packages])
cmd_str = 'apt-get -y install %s' % pkg_list
CommandRunner(cmd_str).run()
# map of function pionters to handle different package managers
_package_handlers = {
"yum": _handle_yum_packages,
"rpm": _handle_rpm_packages,
"apt": _handle_apt_packages,
"rubygems": _handle_gem_packages,
"python": _handle_python_packages
}
def _package_handler(self, manager_name):
handler = None
if manager_name in self._package_handlers:
handler = self._package_handlers[manager_name]
return handler
def apply_packages(self):
"""
Install, upgrade, or downgrade packages listed
Each package is a dict containing package name and a list of versions
Install order:
* dpkg
* rpm
* apt
* yum
"""
if not self._packages:
return
packages = sorted(self._packages.iteritems(), PackagesHandler._pkgsort)
for manager, package_entries in packages:
handler = self._package_handler(manager)
if not handler:
logger.warn("Skipping invalid package type: %s" % manager)
else:
handler(self, package_entries)
class FilesHandler(object):
def __init__(self, files):
self._files = files
def apply_files(self):
if not self._files:
return
for fdest, meta in self._files.iteritems():
dest = fdest.encode()
try:
os.makedirs(os.path.dirname(dest))
except OSError as e:
if e.errno == errno.EEXIST:
logger.debug(str(e))
else:
logger.exception(e)
if 'content' in meta:
if isinstance(meta['content'], basestring):
f = open(dest, 'w')
f.write(meta['content'])
f.close()
else:
f = open(dest, 'w')
f.write(json.dumps(meta['content'], indent=4))
f.close()
elif 'source' in meta:
CommandRunner('wget -O %s %s' % (dest, meta['source'])).run()
else:
logger.error('%s %s' % (dest, str(meta)))
continue
uid = -1
gid = -1
if 'owner' in meta:
try:
user_info = pwd.getpwnam(meta['owner'])
uid = user_info[2]
except KeyError as ex:
pass
if 'group' in meta:
try:
group_info = grp.getgrnam(meta['group'])
gid = group_info[2]
except KeyError as ex:
pass
os.chown(dest, uid, gid)
if 'mode' in meta:
os.chmod(dest, int(meta['mode'], 8))
class SourcesHandler(object):
'''
tar, tar+gzip,tar+bz2 and zip
'''
_sources = {}
def __init__(self, sources):
self._sources = sources
def _url_to_tmp_filename(self, url):
sp = url.split('/')
if 'https://github.com' in url:
if 'zipball' == sp[-2]:
return '/tmp/%s-%s.zip' % (sp[-3], sp[-1])
elif 'tarball' == sp[-2]:
return '/tmp/%s-%s.tar.gz' % (sp[-3], sp[-1])
else:
pass
return '/tmp/%s' % (sp[-1])
def _decompress(self, archive, dest_dir):
cmd_str = ''
logger.debug("Decompressing")
(r, ext) = os.path.splitext(archive)
if ext == '.tgz':
cmd_str = 'tar -C %s -xzf %s' % (dest_dir, archive)
elif ext == '.tbz2':
cmd_str = 'tar -C %s -xjf %s' % (dest_dir, archive)
elif ext == '.zip':
cmd_str = 'unzip -d %s %s' % (dest_dir, archive)
elif ext == '.tar':
cmd_str = 'tar -C %s -xf %s' % (dest_dir, archive)
elif ext == '.gz':
(r, ext) = os.path.splitext(r)
if ext:
cmd_str = 'tar -C %s -xzf %s' % (dest_dir, archive)
else:
cmd_str = 'gunzip -c %s > %s' % (archive, dest_dir)
elif ext == 'bz2':
(r, ext) = os.path.splitext(r)
if ext:
cmd_str = 'tar -C %s -xjf %s' % (dest_dir, archive)
else:
cmd_str = 'bunzip2 -c %s > %s' % (archive, dest_dir)
else:
pass
return CommandRunner(cmd_str)
def apply_sources(self):
if not self._sources:
return
for dest, url in self._sources.iteritems():
tmp_name = self._url_to_tmp_filename(url)
cmd_str = 'wget -O %s %s' % (tmp_name, url)
decompress_command = self._decompress(tmp_name, dest)
CommandRunner(cmd_str, decompress_command).run()
try:
os.makedirs(dest)
except OSError as e:
if e.errno == errno.EEXIST:
logger.debug(str(e))
else:
logger.exception(e)
class ServicesHandler(object):
_services = {}
def __init__(self, services, resource=None, hooks=None):
self._services = services
self.resource = resource
self.hooks = hooks
def _handle_sysv_command(self, service, command):
service_exe = "/sbin/service"
enable_exe = "/sbin/chkconfig"
cmd = ""
if "enable" == command:
cmd = "%s %s on" % (enable_exe, service)
elif "disable" == command:
cmd = "%s %s off" % (enable_exe, service)
elif "start" == command:
cmd = "%s %s start" % (service_exe, service)
elif "stop" == command:
cmd = "%s %s stop" % (service_exe, service)
elif "status" == command:
cmd = "%s %s status" % (service_exe, service)
command = CommandRunner(cmd)
command.run()
return command
def _handle_systemd_command(self, service, command):
exe = "/bin/systemctl"
cmd = ""
service = '%s.service' % service
if "enable" == command:
cmd = "%s enable %s" % (exe, service)
elif "disable" == command:
cmd = "%s disable %s" % (exe, service)
elif "start" == command:
cmd = "%s start %s" % (exe, service)
elif "stop" == command:
cmd = "%s stop %s" % (exe, service)
elif "status" == command:
cmd = "%s status %s" % (exe, service)
command = CommandRunner(cmd)
command.run()
return command
def _initialize_service(self, handler, service, properties):
if "enabled" in properties:
enable = to_boolean(properties["enabled"])
if enable:
logger.info("Enabling service %s" % service)
handler(self, service, "enable")
else:
logger.info("Disabling service %s" % service)
handler(self, service, "disable")
if "ensureRunning" in properties:
ensure_running = to_boolean(properties["ensureRunning"])
command = handler(self, service, "status")
running = command.status == 0
if ensure_running and not running:
logger.info("Starting service %s" % service)
handler(self, service, "start")
elif not ensure_running and running:
logger.info("Stopping service %s" % service)
handler(self, service, "stop")
def _monitor_service(self, handler, service, properties):
if "ensureRunning" in properties:
ensure_running = to_boolean(properties["ensureRunning"])
command = handler(self, service, "status")
running = command.status == 0
if ensure_running and not running:
logger.warn("Restarting service %s" % service)
start_cmd = handler(self, service, "start")
if start_cmd.status != 0:
logger.warning('Service %s did not start. STDERR: %s' %
(service, start_cmd.stderr))
return
for h in self.hooks:
self.hooks[h].event('service.restarted',
service, self.resource)
def _monitor_services(self, handler, services):
for service, properties in services.iteritems():
self._monitor_service(handler, service, properties)
def _initialize_services(self, handler, services):
for service, properties in services.iteritems():
self._initialize_service(handler, service, properties)
# map of function pointers to various service handlers
_service_handlers = {
"sysvinit": _handle_sysv_command,
"systemd": _handle_systemd_command
}
def _service_handler(self, manager_name):
handler = None
if manager_name in self._service_handlers:
handler = self._service_handlers[manager_name]
return handler
def apply_services(self):
"""
Starts, stops, enables, disables services
"""
if not self._services:
return
for manager, service_entries in self._services.iteritems():
handler = self._service_handler(manager)
if not handler:
logger.warn("Skipping invalid service type: %s" % manager)
else:
self._initialize_services(handler, service_entries)
def monitor_services(self):
"""
Restarts failed services, and runs hooks.
"""
if not self._services:
return
for manager, service_entries in self._services.iteritems():
handler = self._service_handler(manager)
if not handler:
logger.warn("Skipping invalid service type: %s" % manager)
else:
self._monitor_services(handler, service_entries)
def metadata_server_url():
"""
Return the url to the metadata server.
"""
try:
f = open("/var/lib/cloud/data/cfn-metadata-server")
server_url = f.read().strip()
f.close()
if not server_url[-1] == '/':
server_url += '/'
return server_url
except IOError:
return None
class MetadataServerConnectionError(Exception):
pass
class Metadata(object):
_metadata = None
_init_key = "AWS::CloudFormation::Init"
def __init__(self, stack, resource, access_key=None,
secret_key=None, credentials_file=None, region=None):
self.stack = stack
self.resource = resource
self.access_key = access_key
self.secret_key = secret_key
self.credentials_file = credentials_file
self.region = region
# TODO(asalkeld) is this metadata for the local resource?
self._is_local_metadata = True
self._metadata = None
def metadata_resource_url(self):
server_url = metadata_server_url()
if not server_url:
return
return server_url + 'stacks/%s/resources/%s' % (self.stack,
self.resource)
def remote_metadata(self):
"""
Connect to the metadata server and retreive the metadata from there.
"""
url = self.metadata_resource_url()
if not url:
raise MetadataServerConnectionError()
try:
return urlopen(url).read()
except:
raise MetadataServerConnectionError()
def retrieve(self, meta_str=None):
"""
Read the metadata from the given filename
"""
if meta_str:
self._data = meta_str
else:
try:
self._data = self.remote_metadata()
except MetadataServerConnectionError:
f = open("/var/lib/cloud/data/cfn-init-data")
self._data = f.read()
f.close()
if isinstance(self._data, str):
self._metadata = json.loads(self._data)
else:
self._metadata = self._data
def __str__(self):
return json.dumps(self._metadata)
def _is_valid_metadata(self):
"""
Should find the AWS::CloudFormation::Init json key
"""
is_valid = self._metadata and \
self._init_key in self._metadata and \
self._metadata[self._init_key]
if is_valid:
self._metadata = self._metadata[self._init_key]
return is_valid
def _process_config(self):
"""
Parse and process a config section
* packages
* sources
* users (not yet)
* groups (not yet)
* files
* commands (not yet)
* services
"""
self._config = self._metadata["config"]
PackagesHandler(self._config.get("packages")).apply_packages()
#FIXME: handle sources
SourcesHandler(self._config.get("sources")).apply_sources()
#FIXME: handle users
#FIXME: handle groups
FilesHandler(self._config.get("files")).apply_files()
#FIXME: handle commands
ServicesHandler(self._config.get("services")).apply_services()
def cfn_init(self):
"""
Process the resource metadata
"""
# FIXME: when config sets are implemented, this should select the
# correct config set from the metadata, and send each config in the
# config set to process_config
if not self._is_valid_metadata():
raise Exception("invalid metadata")
else:
self._process_config()
def cfn_hup(self, hooks):
"""
Process the resource metadata
"""
if not self._is_valid_metadata():
raise Exception("invalid metadata")
else:
if self._is_local_metadata:
self._config = self._metadata["config"]
s = self._config.get("services")
sh = ServicesHandler(s, resource=self.resource, hooks=hooks)
sh.monitor_services()

View File

@ -1,28 +0,0 @@
<template>
<name>F16-i386-cfntools-jeos</name>
<os>
<name>Fedora</name>
<version>16</version>
<arch>i386</arch>
<install type='iso'>
<iso>file:/var/lib/libvirt/images/Fedora-16-i386-DVD.iso</iso>
</install>
</os>
<description>Fedora 16</description>
<commands>
<command name='commands'>
yum -y update --skip-broken;yum -y install yum-plugin-fastestmirror;yum -y update;/usr/sbin/useradd ec2-user;echo -e 'ec2-user\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers;yum -y install cloud-init;cat >> /etc/rc.d/rc.local &lt;&lt; EOF;chmod +x /etc/rc.d/rc.local;chmod +x /opt/aws/bin/cfn-*
#!/bin/bash
setenforce 0
EOF
</command>
</commands>
<files>
<file name='/opt/aws/bin/cfn-init' type='base64'></file>
<file name='/opt/aws/bin/cfn-hup' type='base64'></file>
<file name='/opt/aws/bin/cfn-signal' type='base64'></file>
<file name='/opt/aws/bin/cfn_helper.py' type='base64'></file>
<file name='/opt/aws/bin/cfn-get-metadata' type='base64'></file>
<file name='/opt/aws/bin/cfn-push-stats' type='base64'></file>
</files>
</template>

View File

@ -1,60 +0,0 @@
<template>
<name>F16-i386-gold-jeos</name>
<os>
<name>Fedora</name>
<version>16</version>
<arch>i386</arch>
<install type='iso'>
<iso>file:/var/lib/libvirt/images/Fedora-16-i386-DVD.iso</iso>
</install>
</os>
<description>Fedora 16</description>
<commands>
<command name='commands'>
rm -f /etc/yum.repos.d/fedora-updates*;yum -y install cloud-init;cat >> /etc/rc.d/rc.local &lt;&lt; EOF;chmod +x /etc/rc.d/rc.local
#!/bin/bash
setenforce 0
while true; do
gdbus introspect --system --dest org.freedesktop.NetworkManager --object-path /org/freedesktop/NetworkManager/Devices/0 | grep "State = 100"
if [ \$? -eq 0 ]
then
break
fi
sleep 1
done
if [ ! -d /root/.ssh ]; then
mkdir -p /root/.ssh
chmod 700 /root/.ssh
fi
# Fetch public key using HTTP
ATTEMPTS=10
while [ ! -f /root/.ssh/authorized_keys ]; do
curl -f http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key > /tmp/aws-key 2>/dev/null
if [ \$? -eq 0 ]; then
cat /tmp/aws-key >> /root/.ssh/authorized_keys
chmod 0600 /root/.ssh/authorized_keys
restorecon /root/.ssh/authorized_keys
rm -f /tmp/aws-key
echo "Successfully retrieved AWS public key from instance metadata"
else
FAILED=\$((\$FAILED + 1))
if [ \$FAILED -ge \$ATTEMPTS ]; then
echo "Failed to retrieve AWS public key after \$FAILED attempts, quitting"
break
fi
echo "Could not retrieve AWS public key (attempt #\$FAILED/\$ATTEMPTS), retrying in 5 seconds..."
sleep 5
fi
done
while [ ! -f /var/lib/cloud/instance/user-data.txt ]; do
sleep 1
done
base64 -d /var/lib/cloud/instance/user-data.txt > /tmp/startup
chmod +x /tmp/startup
/tmp/startup
EOF
</command>
</commands>
</template>

View File

@ -1,28 +0,0 @@
<template>
<name>F16-x86_64-cfntools-jeos</name>
<os>
<name>Fedora</name>
<version>16</version>
<arch>x86_64</arch>
<install type='iso'>
<iso>file:/var/lib/libvirt/images/Fedora-16-x86_64-DVD.iso</iso>
</install>
</os>
<description>Fedora 16</description>
<commands>
<command name='commands'>
yum -y update --skip-broken;yum -y install yum-plugin-fastestmirror;yum -y update;/usr/sbin/useradd ec2-user;echo -e 'ec2-user\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers;yum -y install cloud-init;cat >> /etc/rc.d/rc.local &lt;&lt; EOF;chmod +x /etc/rc.d/rc.local;chmod +x /opt/aws/bin/cfn-*
#!/bin/bash
setenforce 0
EOF
</command>
</commands>
<files>
<file name='/opt/aws/bin/cfn-init' type='base64'></file>
<file name='/opt/aws/bin/cfn-hup' type='base64'></file>
<file name='/opt/aws/bin/cfn-signal' type='base64'></file>
<file name='/opt/aws/bin/cfn_helper.py' type='base64'></file>
<file name='/opt/aws/bin/cfn-get-metadata' type='base64'></file>
<file name='/opt/aws/bin/cfn-push-stats' type='base64'></file>
</files>
</template>

View File

@ -1,60 +0,0 @@
<template>
<name>F16-x86_64-gold-jeos</name>
<os>
<name>Fedora</name>
<version>16</version>
<arch>x86_64</arch>
<install type='iso'>
<iso>file:/var/lib/libvirt/images/Fedora-16-x86_64-DVD.iso</iso>
</install>
</os>
<description>Fedora 16</description>
<commands>
<command name='commands'>
rm -f /etc/yum.repos.d/fedora-updates*;yum -y install cloud-init;cat >> /etc/rc.d/rc.local &lt;&lt; EOF;chmod +x /etc/rc.d/rc.local
#!/bin/bash
setenforce 0
while true; do
gdbus introspect --system --dest org.freedesktop.NetworkManager --object-path /org/freedesktop/NetworkManager/Devices/0 | grep "State = 100"
if [ \$? -eq 0 ]
then
break
fi
sleep 1
done
if [ ! -d /root/.ssh ]; then
mkdir -p /root/.ssh
chmod 700 /root/.ssh
fi
# Fetch public key using HTTP
ATTEMPTS=10
while [ ! -f /root/.ssh/authorized_keys ]; do
curl -f http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key > /tmp/aws-key 2>/dev/null
if [ \$? -eq 0 ]; then
cat /tmp/aws-key >> /root/.ssh/authorized_keys
chmod 0600 /root/.ssh/authorized_keys
restorecon /root/.ssh/authorized_keys
rm -f /tmp/aws-key
echo "Successfully retrieved AWS public key from instance metadata"
else
FAILED=\$((\$FAILED + 1))
if [ \$FAILED -ge \$ATTEMPTS ]; then
echo "Failed to retrieve AWS public key after \$FAILED attempts, quitting"
break
fi
echo "Could not retrieve AWS public key (attempt #\$FAILED/\$ATTEMPTS), retrying in 5 seconds..."
sleep 5
fi
done
while [ ! -f /var/lib/cloud/instance/user-data.txt ]; do
sleep 1
done
base64 -d /var/lib/cloud/instance/user-data.txt > /tmp/startup
chmod +x /tmp/startup
/tmp/startup
EOF
</command>
</commands>
</template>

View File

@ -1,28 +0,0 @@
<template>
<name>F17-i386-cfntools-jeos</name>
<os>
<name>Fedora</name>
<version>17</version>
<arch>i386</arch>
<install type='iso'>
<iso>file:/var/lib/libvirt/images/Fedora-17-i386-DVD.iso</iso>
</install>
</os>
<description>Fedora 17</description>
<commands>
<command name='commands'>
yum -y update --skip-broken;yum -y install yum-plugin-fastestmirror;yum -y update;/usr/sbin/useradd ec2-user;echo -e 'ec2-user\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers;yum -y install cloud-init;cat >> /etc/rc.d/rc.local &lt;&lt; EOF;chmod +x /etc/rc.d/rc.local;chmod +x /opt/aws/bin/cfn-*
#!/bin/bash
setenforce 0
EOF
</command>
</commands>
<files>
<file name='/opt/aws/bin/cfn-init' type='base64'></file>
<file name='/opt/aws/bin/cfn-hup' type='base64'></file>
<file name='/opt/aws/bin/cfn-signal' type='base64'></file>
<file name='/opt/aws/bin/cfn_helper.py' type='base64'></file>
<file name='/opt/aws/bin/cfn-get-metadata' type='base64'></file>
<file name='/opt/aws/bin/cfn-push-stats' type='base64'></file>
</files>
</template>

View File

@ -1,60 +0,0 @@
<template>
<name>F17-i386-gold-jeos</name>
<os>
<name>Fedora</name>
<version>17</version>
<arch>i386</arch>
<install type='iso'>
<iso>file:/var/lib/libvirt/images/Fedora-17-i386-DVD.iso</iso>
</install>
</os>
<description>Fedora 17</description>
<commands>
<command name='commands'>
rm -f /etc/yum.repos.d/fedora-updates*;yum -y install cloud-init;cat >> /etc/rc.d/rc.local &lt;&lt; EOF;chmod +x /etc/rc.d/rc.local
#!/bin/bash
setenforce 0
while true; do
gdbus introspect --system --dest org.freedesktop.NetworkManager --object-path /org/freedesktop/NetworkManager/Devices/0 | grep "State = 100"
if [ \$? -eq 0 ]
then
break
fi
sleep 1
done
if [ ! -d /root/.ssh ]; then
mkdir -p /root/.ssh
chmod 700 /root/.ssh
fi
# Fetch public key using HTTP
ATTEMPTS=10
while [ ! -f /root/.ssh/authorized_keys ]; do
curl -f http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key > /tmp/aws-key 2>/dev/null
if [ \$? -eq 0 ]; then
cat /tmp/aws-key >> /root/.ssh/authorized_keys
chmod 0600 /root/.ssh/authorized_keys
restorecon /root/.ssh/authorized_keys
rm -f /tmp/aws-key
echo "Successfully retrieved AWS public key from instance metadata"
else
FAILED=\$((\$FAILED + 1))
if [ \$FAILED -ge \$ATTEMPTS ]; then
echo "Failed to retrieve AWS public key after \$FAILED attempts, quitting"
break
fi
echo "Could not retrieve AWS public key (attempt #\$FAILED/\$ATTEMPTS), retrying in 5 seconds..."
sleep 5
fi
done
while [ ! -f /var/lib/cloud/instance/user-data.txt ]; do
sleep 1
done
base64 -d /var/lib/cloud/instance/user-data.txt > /tmp/startup
chmod +x /tmp/startup
/tmp/startup
EOF
</command>
</commands>
</template>

View File

@ -1,28 +0,0 @@
<template>
<name>F17-x86_64-cfntools-jeos</name>
<os>
<name>Fedora</name>
<version>17</version>
<arch>x86_64</arch>
<install type='iso'>
<iso>file:/var/lib/libvirt/images/Fedora-17-x86_64-DVD.iso</iso>
</install>
</os>
<description>Fedora 17</description>
<commands>
<command name='commands'>
yum -y update --skip-broken;yum -y install yum-plugin-fastestmirror;yum -y update;/usr/sbin/useradd ec2-user;echo -e 'ec2-user\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers;yum -y install cloud-init;cat >> /etc/rc.d/rc.local &lt;&lt; EOF;chmod +x /etc/rc.d/rc.local;chmod +x /opt/aws/bin/cfn-*
#!/bin/bash
setenforce 0
EOF
</command>
</commands>
<files>
<file name='/opt/aws/bin/cfn-init' type='base64'></file>
<file name='/opt/aws/bin/cfn-hup' type='base64'></file>
<file name='/opt/aws/bin/cfn-signal' type='base64'></file>
<file name='/opt/aws/bin/cfn_helper.py' type='base64'></file>
<file name='/opt/aws/bin/cfn-get-metadata' type='base64'></file>
<file name='/opt/aws/bin/cfn-push-stats' type='base64'></file>
</files>
</template>

View File

@ -1,60 +0,0 @@
<template>
<name>F17-x86_64-gold-jeos</name>
<os>
<name>Fedora</name>
<version>17</version>
<arch>x86_64</arch>
<install type='iso'>
<iso>file:/var/lib/libvirt/images/Fedora-17-x86_64-DVD.iso</iso>
</install>
</os>
<description>Fedora 17</description>
<commands>
<command name='commands'>
rm -f /etc/yum.repos.d/fedora-updates*;yum -y install cloud-init;cat >> /etc/rc.d/rc.local &lt;&lt; EOF;chmod +x /etc/rc.d/rc.local
#!/bin/bash
setenforce 0
while true; do
gdbus introspect --system --dest org.freedesktop.NetworkManager --object-path /org/freedesktop/NetworkManager/Devices/0 | grep "State = 100"
if [ \$? -eq 0 ]
then
break
fi
sleep 1
done
if [ ! -d /root/.ssh ]; then
mkdir -p /root/.ssh
chmod 700 /root/.ssh
fi
# Fetch public key using HTTP
ATTEMPTS=10
while [ ! -f /root/.ssh/authorized_keys ]; do
curl -f http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key > /tmp/aws-key 2>/dev/null
if [ \$? -eq 0 ]; then
cat /tmp/aws-key >> /root/.ssh/authorized_keys
chmod 0600 /root/.ssh/authorized_keys
restorecon /root/.ssh/authorized_keys
rm -f /tmp/aws-key
echo "Successfully retrieved AWS public key from instance metadata"
else
FAILED=\$((\$FAILED + 1))
if [ \$FAILED -ge \$ATTEMPTS ]; then
echo "Failed to retrieve AWS public key after \$FAILED attempts, quitting"
break
fi
echo "Could not retrieve AWS public key (attempt #\$FAILED/\$ATTEMPTS), retrying in 5 seconds..."
sleep 5
fi
done
while [ ! -f /var/lib/cloud/instance/user-data.txt ]; do
sleep 1
done
base64 -d /var/lib/cloud/instance/user-data.txt > /tmp/startup
chmod +x /tmp/startup
/tmp/startup
EOF
</command>
</commands>
</template>

View File

@ -1,25 +0,0 @@
<template>
<name>U10-x86_64-cfntools-jeos</name>
<os>
<name>Ubuntu</name>
<version>10.04</version>
<arch>x86_64</arch>
<install type='iso'>
<iso>file:/var/lib/libvirt/images/ubuntu-10.04.3-server-amd64.iso</iso>
</install>
</os>
<description>Ubuntu 10.04</description>
<commands>
<command name='commands'>
apt-get -y update;apt-get -y upgrade;apt-get -y install python-argparse;apt-get -y install chkconfig;apt-get -y install cloud-init;/usr/sbin/useradd -m ec2-user;echo -e 'ec2-user\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers;chmod +x /opt/aws/bin/cfn-*
</command>
</commands>
<files>
<file name='/opt/aws/bin/cfn-init' type='base64'></file>
<file name='/opt/aws/bin/cfn-hup' type='base64'></file>
<file name='/opt/aws/bin/cfn-signal' type='base64'></file>
<file name='/opt/aws/bin/cfn_helper.py' type='base64'></file>
<file name='/opt/aws/bin/cfn-get-metadata' type='base64'></file>
<file name='/opt/aws/bin/cfn-push-stats' type='base64'></file>
</files>
</template>

View File

@ -59,220 +59,6 @@ def catch_error(action):
return wrap return wrap
@catch_error('jeos-create')
def jeos_create(options, arguments, jeos_path, cfntools_path):
'''
Create a new JEOS (Just Enough Operating System) image.
Usage: heat jeos-create <distribution> <architecture> <image type>
Distribution: Distribution such as 'F16', 'F17', 'U10', 'D6'.
Architecture: Architecture such as 'i386' 'i686' or 'x86_64'.
Image Type: Image type such as 'gold' or 'cfntools'.
'gold' is a basic gold JEOS.
'cfntools' contains the cfntools helper scripts.
The command must be run as root in order for libvirt to have permissions
to create virtual machines and read the raw DVDs.
'''
# if not running as root, return EPERM to command line
if os.geteuid() != 0:
logging.error("jeos-create must be run as root")
sys.exit(1)
if len(arguments) < 3:
print '\n Please provide the distro, arch, and instance type.'
print ' Usage:'
print ' heat jeos-create <distro> <arch> <instancetype>'
print ' instance type can be:'
print ' gold builds a base image where userdata is used to' \
' initialize the instance'
print ' cfntools builds a base image where AWS CloudFormation' \
' tools are present'
sys.exit(1)
distro = arguments.pop(0)
arch = arguments.pop(0)
instance_type = arguments.pop(0)
images_dir = '/var/lib/libvirt/images'
arches = ('x86_64', 'i386', 'amd64')
arches_str = " | ".join(arches)
instance_types = ('gold', 'cfntools')
instances_str = " | ".join(instance_types)
if not arch in arches:
logging.error('arch %s not supported' % arch)
logging.error('try: heat jeos-create %s [ %s ]' % (distro, arches_str))
sys.exit(1)
if not instance_type in instance_types:
logging.error('A JEOS instance type of %s not supported' %
instance_type)
logging.error('try: heat jeos-create %s %s [ %s ]' %
(distro, arch, instances_str))
sys.exit(1)
src_arch = 'i386'
fedora_match = re.match('F(1[6-7])', distro)
if fedora_match:
if arch == 'x86_64':
src_arch = 'x86_64'
version = fedora_match.group(1)
iso = '%s/Fedora-%s-%s-DVD.iso' % (images_dir, version, arch)
elif distro == 'U10':
if arch == 'amd64':
src_arch = 'x86_64'
iso = '%s/ubuntu-10.04.3-server-%s.iso' % (images_dir, arch)
else:
logging.error('distro %s not supported' % distro)
logging.error('try: F16, F17 or U10')
sys.exit(1)
if not os.access(iso, os.R_OK):
logging.error('*** %s does not exist.' % (iso))
sys.exit(1)
tdl_file = '%s-%s-%s-jeos.tdl' % (distro, arch, instance_type)
tdl_path = os.path.join(jeos_path, tdl_file)
if options.debug:
print "Using tdl: %s" % tdl_path
# Load the cfntools into the cfntool image by encoding them in base64
# and injecting them into the TDL at the appropriate place
if instance_type == 'cfntools':
tdl_xml = etree.parse(tdl_path)
cfn_tools = ['cfn-init', 'cfn-hup', 'cfn-signal',
'cfn-get-metadata', 'cfn_helper.py', 'cfn-push-stats']
for cfnname in cfn_tools:
f = open('%s/%s' % (cfntools_path, cfnname), 'r')
cfscript_e64 = base64.b64encode(f.read())
f.close()
cfnpath = "/template/files/file[@name='/opt/aws/bin/%s']" % cfnname
tdl_xml.xpath(cfnpath)[0].text = cfscript_e64
# TODO(sdake) INSECURE
tdl_xml.write('/tmp/tdl', xml_declaration=True)
tdl_path = '/tmp/tdl'
dsk_filename = '%s/%s-%s-%s-jeos.dsk' % (images_dir, distro,
src_arch, instance_type)
qcow2_filename = '%s/%s-%s-%s-jeos.qcow2' % (images_dir, distro,
arch, instance_type)
image_name = '%s-%s-%s' % (distro, arch, instance_type)
if not os.access(tdl_path, os.R_OK):
logging.error('The tdl for that disto/arch is not available')
sys.exit(1)
creds = dict(username=options.username,
password=options.password,
tenant=options.tenant,
auth_url=options.auth_url,
strategy=options.auth_strategy)
client = glance_client.Client(host="0.0.0.0", port=9292,
use_ssl=False, auth_tok=None, creds=creds)
parameters = {
"filters": {},
"limit": 10,
}
images = client.get_images(**parameters)
image_registered = False
for image in images:
if image['name'] == distro + '-' + arch + '-' + instance_type:
image_registered = True
runoz = options.yes and 'y' or None
if os.access(qcow2_filename, os.R_OK):
while runoz not in ('y', 'n'):
runoz = raw_input('An existing JEOS was found on disk.'
' Do you want to build a fresh JEOS?'
' (y/n) ').lower()
if runoz == 'y':
os.remove(qcow2_filename)
os.remove(dsk_filename)
if image_registered:
client.delete_image(image['id'])
elif runoz == 'n':
answer = None
while answer not in ('y', 'n'):
answer = raw_input('Do you want to register your existing'
' JEOS file with glance? (y/n) ').lower()
if answer == 'n':
logging.info('No action taken')
sys.exit(0)
elif answer == 'y' and image_registered:
answer = None
while answer not in ('y', 'n'):
answer = raw_input('Do you want to delete the '
'existing JEOS in glance?'
' (y/n) ').lower()
if answer == 'n':
logging.info('No action taken')
sys.exit(0)
elif answer == 'y':
client.delete_image(image['id'])
if runoz is None or runoz == 'y':
logging.info('Creating JEOS image (%s) - '
'this takes approximately 10 minutes.' % image_name)
extra_opts = ' '
if options.debug:
extra_opts = ' -d 3 '
ozcmd = "oz-install %s -t 50000 -u %s -x /dev/null" % (extra_opts,
tdl_path)
logging.debug("Running : %s" % ozcmd)
res = os.system(ozcmd)
if res == 256:
sys.exit(1)
if not os.access(dsk_filename, os.R_OK):
logging.error('oz-install did not create the image,'
' check your oz installation.')
sys.exit(1)
logging.info('Converting raw disk image to a qcow2 image.')
os.system("qemu-img convert -c -O qcow2 %s %s" % (dsk_filename,
qcow2_filename))
logging.info('Registering JEOS image (%s) '
'with OpenStack Glance.' % image_name)
image_meta = {'name': image_name,
'is_public': True,
'disk_format': 'qcow2',
'min_disk': 0,
'min_ram': 0,
'owner': options.username,
'container_format': 'bare'}
try:
with open(qcow2_filename) as ifile:
image_meta = client.add_image(image_meta, ifile)
image_id = image_meta['id']
logging.debug(" Added new image with ID: %s" % image_id)
logging.debug(" Returned the following metadata for the new image:")
for k, v in sorted(image_meta.items()):
logging.debug(" %(k)30s => %(v)s" % locals())
except exception.ClientConnectionError, e:
logging.error((" Failed to connect to the Glance API server." +
" Is the server running?" % locals()))
pieces = unicode(e).split('\n')
for piece in pieces:
logging.error(piece)
sys.exit(1)
except Exception, e:
logging.error(" Failed to add image. Got error:")
pieces = unicode(e).split('\n')
for piece in pieces:
logging.error(piece)
logging.warning(" Note: Your image metadata may still be in the " +
"registry, but the image's status will likely be 'killed'.")
class LazyPluggable(object): class LazyPluggable(object):
"""A pluggable backend loaded lazily based on some value.""" """A pluggable backend loaded lazily based on some value."""