summaryrefslogtreecommitdiff
path: root/cloudinit/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/__init__.py')
-rw-r--r--cloudinit/__init__.py309
1 files changed, 176 insertions, 133 deletions
diff --git a/cloudinit/__init__.py b/cloudinit/__init__.py
index 6a59a23f..9c02ff8a 100644
--- a/cloudinit/__init__.py
+++ b/cloudinit/__init__.py
@@ -18,94 +18,35 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
-datadir = '/var/lib/cloud/data'
-semdir = '/var/lib/cloud/sem'
-pluginsdir = datadir + '/plugins'
-cachedir = datadir + '/cache'
-userdata_raw = datadir + '/user-data.txt'
-userdata = datadir + '/user-data.txt.i'
-user_scripts_dir = datadir + "/scripts"
-boothooks_dir = datadir + "/boothooks"
-cloud_config = datadir + '/cloud-config.txt'
-#cloud_config = '/tmp/cloud-config.txt'
-data_source_cache = cachedir + '/obj.pkl'
+varlibdir = '/var/lib/cloud'
+cur_instance_link = varlibdir + "/instance"
+boot_finished = cur_instance_link + "/boot-finished"
system_config = '/etc/cloud/cloud.cfg'
+seeddir = varlibdir + "/seed"
cfg_env_name = "CLOUD_CFG"
cfg_builtin = """
+log_cfgs: [ ]
cloud_type: auto
-user: ubuntu
-disable_root: 1
-
-cloud_config_modules:
- - mounts
- - ssh-import-id
- - ssh
- - grub-dpkg
- - apt-update-upgrade
- - puppet
- - updates-check
- - disable-ec2-metadata
- - runcmd
- - byobu
-
-log_cfg: built_in
+def_log_file: /var/log/cloud-init.log
+syslog_fix_perms: syslog:adm
"""
-
-def_log_file = '/var/log/cloud-init.log'
logger_name = "cloudinit"
-built_in_log_base = """
-[loggers]
-keys=root,cloudinit
-
-[handlers]
-keys=consoleHandler,cloudLogHandler
-
-[formatters]
-keys=simpleFormatter,arg0Formatter
-
-[logger_root]
-level=DEBUG
-handlers=consoleHandler,cloudLogHandler
-
-[logger_cloudinit]
-level=DEBUG
-qualname=cloudinit
-handlers=
-propagate=1
-
-[handler_consoleHandler]
-class=StreamHandler
-level=WARNING
-formatter=arg0Formatter
-args=(sys.stderr,)
-
-[formatter_arg0Formatter]
-format=%(asctime)s - %(filename)s[%(levelname)s]: %(message)s
-
-[formatter_simpleFormatter]
-format=[CLOUDINIT] %(asctime)s - %(filename)s[%(levelname)s]: %(message)s
-datefmt=
-
-"""
-
-built_in_log_clougLogHandlerLog="""
-[handler_cloudLogHandler]
-class=FileHandler
-level=DEBUG
-formatter=simpleFormatter
-args=('__CLOUDINIT_LOGGER_FILE__',)
-"""
-
-built_in_log_cloudLogHandlerSyslog= """
-[handler_cloudLogHandler]
-class=handlers.SysLogHandler
-level=DEBUG
-formatter=simpleFormatter
-args=("/dev/log", handlers.SysLogHandler.LOG_USER)
-"""
-
+pathmap = {
+ "handlers" : "/handlers",
+ "scripts" : "/scripts",
+ "sem" : "/sem",
+ "boothooks" : "/boothooks",
+ "userdata_raw" : "/user-data.txt",
+ "userdata" : "/user-data-raw.txt.i",
+ "obj_pkl" : "/obj.pkl",
+ "cloud_config" : "/cloud-config.txt",
+ "datadir" : "/data",
+ None : "",
+}
+
+parsed_cfgs = { }
import os
from configobj import ConfigObj
@@ -121,43 +62,44 @@ import util
import logging
import logging.config
import StringIO
+import glob
class NullHandler(logging.Handler):
- def emit(self,record): pass
+ def emit(self,record): pass
log = logging.getLogger(logger_name)
log.addHandler(NullHandler())
def logging_set_from_cfg_file(cfg_file=system_config):
- logging_set_from_cfg(util.get_base_cfg(cfg_file,cfg_builtin))
+ logging_set_from_cfg(util.get_base_cfg(cfg_file,cfg_builtin,parsed_cfgs))
+
+def logging_set_from_cfg(cfg):
+ log_cfgs = []
+ logcfg=util.get_cfg_option_str(cfg, "log_cfg", False)
+ if logcfg:
+ # if there is a 'logcfg' entry in the config, respect
+ # it, it is the old keyname
+ log_cfgs = [ logcfg ]
+ elif "log_cfgs" in cfg:
+ for cfg in cfg['log_cfgs']:
+ if isinstance(cfg,list):
+ log_cfgs.append('\n'.join(cfg))
+ else:
+ log_cfgs.append()
+
+ if not len(log_cfgs):
+ sys.stderr.write("Warning, no logging configured\n")
+ return
-def logging_set_from_cfg(cfg, logfile=None):
- if logfile is None:
+ for logcfg in log_cfgs:
try:
- open(def_log_file,"a").close()
- logfile = def_log_file
- except IOError as e:
- if e.errno == errno.EACCES:
- logfile = "/dev/null"
- else: raise
-
- logcfg=util.get_cfg_option_str(cfg, "log_cfg", "built_in")
- failsafe = "%s\n%s" % (built_in_log_base, built_in_log_clougLogHandlerLog)
- builtin = False
- if logcfg.lower() == "built_in":
- logcfg = "%s\n%s" % (built_in_log_base, built_in_log_cloudLogHandlerSyslog)
- builtin = True
-
- logcfg=logcfg.replace("__CLOUDINIT_LOGGER_FILE__",logfile)
- try:
- logging.config.fileConfig(StringIO.StringIO(logcfg))
- return
- except:
- if not builtin:
- sys.stderr.write("Warning, setting config.fileConfig failed\n")
+ logging.config.fileConfig(StringIO.StringIO(logcfg))
+ return
+ except:
+ pass
+
+ raise Exception("no valid logging found\n")
- failsafe=failsafe.replace("__CLOUDINIT_LOGGER_FILE__",logfile)
- logging.config.fileConfig(StringIO.StringIO(failsafe))
import DataSourceEc2
import DataSourceNoCloud
@@ -174,7 +116,6 @@ class CloudInit:
"all": ( "nocloud-net", "ec2" ),
"local" : ( "nocloud", ),
}
-
cfg = None
part_handlers = { }
old_conffile = '/etc/ec2-init/ec2-config.cfg'
@@ -196,7 +137,7 @@ class CloudInit:
if self.cfg:
return(self.cfg)
- conf = util.get_base_cfg(system_config,cfg_builtin)
+ conf = util.get_base_cfg(self.sysconfig,cfg_builtin, parsed_cfgs)
# support reading the old ConfigObj format file and merging
# it into the yaml dictionary
@@ -212,7 +153,11 @@ class CloudInit:
def restore_from_cache(self):
try:
- f=open(data_source_cache, "rb")
+ # we try to restore from a current link and static path
+ # by using the instance link, if purge_cache was called
+ # the file wont exist
+ cache = get_ipath_cur('obj_pkl')
+ f=open(cache, "rb")
data = cPickle.load(f)
self.datasource = data
return True
@@ -220,16 +165,17 @@ class CloudInit:
return False
def write_to_cache(self):
+ cache = self.get_ipath("obj_pkl")
try:
- os.makedirs(os.path.dirname(data_source_cache))
+ os.makedirs(os.path.dirname(cache))
except OSError as e:
if e.errno != errno.EEXIST:
return False
try:
- f=open(data_source_cache, "wb")
+ f=open(cache, "wb")
data = cPickle.dump(self.datasource,f)
- os.chmod(data_source_cache,0400)
+ os.chmod(cache,0400)
return True
except:
return False
@@ -252,6 +198,7 @@ class CloudInit:
for ds in cfglist.split(','):
dslist.append(strip(ds).tolower())
+ log.debug("searching for data source in [%s]" % str(dslist))
for ds in dslist:
if ds not in self.datasource_map:
log.warn("data source %s not found in map" % ds)
@@ -270,27 +217,48 @@ class CloudInit:
log.debug("did not find data source from %s" % dslist)
raise DataSourceNotFoundException("Could not find data source")
+ def set_cur_instance(self):
+ try:
+ os.unlink(cur_instance_link)
+ except OSError, e:
+ if e.errno != errno.ENOENT: raise
+
+ os.symlink("./instances/%s" % self.get_instance_id(), cur_instance_link)
+ idir = self.get_ipath()
+ dlist = []
+ for d in [ "handlers", "scripts", "sem" ]:
+ dlist.append("%s/%s" % (idir, d))
+
+ util.ensure_dirs(dlist)
+
def get_userdata(self):
return(self.datasource.get_userdata())
+ def get_userdata_raw(self):
+ return(self.datasource.get_userdata_raw())
+
+ def get_instance_id(self):
+ return(self.datasource.get_instance_id())
+
def update_cache(self):
self.write_to_cache()
self.store_userdata()
def store_userdata(self):
- util.write_file(userdata_raw, self.datasource.get_userdata_raw(), 0600)
- util.write_file(userdata, self.datasource.get_userdata(), 0600)
+ util.write_file(self.get_ipath('userdata_raw'),
+ self.datasource.get_userdata_raw(), 0600)
+ util.write_file(self.get_ipath('userdata'),
+ self.datasource.get_userdata(), 0600)
def initctl_emit(self):
+ cc_path = get_ipath_cur('cloud_config')
subprocess.Popen(['initctl', 'emit', 'cloud-config',
- '%s=%s' % (cfg_env_name,cloud_config)]).communicate()
+ '%s=%s' % (cfg_env_name,cc_path)]).communicate()
def sem_getpath(self,name,freq):
- freqtok = freq
if freq == 'once-per-instance':
- freqtok = self.datasource.get_instance_id()
-
- return("%s/%s.%s" % (semdir,name,freqtok))
+ return("%s/%s" % (self.get_ipath("sem"),name))
+ return("%s/%s.%s" % (get_cpath("sem"), name, freq))
def sem_has_run(self,name,freq):
if freq == "always": return False
@@ -349,9 +317,40 @@ class CloudInit:
self.sem_clear(semname,freq)
raise
+ # get_ipath : get the instance path for a name in pathmap
+ # (/var/lib/cloud/instances/<instance>/name)<name>)
+ def get_ipath(self, name=None):
+ return("%s/instances/%s%s"
+ % (varlibdir,self.get_instance_id(), pathmap[name]))
+
def consume_userdata(self):
self.get_userdata()
data = self
+
+ cdir = get_cpath("handlers")
+ idir = self.get_ipath("handlers")
+
+ # add the path to the plugins dir to the top of our list for import
+ # instance dir should be read before cloud-dir
+ sys.path.insert(0,cdir)
+ sys.path.insert(0,idir)
+
+ # add handlers in cdir
+ for fname in glob.glob("%s/*.py" % cdir):
+ if not os.path.isfile(fname): continue
+ modname = os.path.basename(fname)[0:-3]
+ try:
+ mod = __import__(modname)
+ lister = getattr(mod, "list_types")
+ handler = getattr(mod, "handle_part")
+ mtypes = lister()
+ for mtype in mtypes:
+ self.part_handlers[mtype]=handler
+ log.debug("added handler for [%s] from %s" % (mtypes,fname))
+ except:
+ log.warn("failed to initialize handler in %s" % fname)
+ util.logexc(log)
+
# give callbacks opportunity to initialize
for ctype, func in self.part_handlers.items():
func(data, "__begin__",None,None)
@@ -368,16 +367,13 @@ class CloudInit:
self.handlercount = 0
return
- # add the path to the plugins dir to the top of our list for import
- if self.handlercount == 0:
- sys.path.insert(0,pluginsdir)
-
self.handlercount=self.handlercount+1
- # write content to pluginsdir
+ # write content to instance's handlerdir
+ handlerdir = self.get_ipath("handler")
modname = 'part-handler-%03d' % self.handlercount
modfname = modname + ".py"
- util.write_file("%s/%s" % (pluginsdir,modfname), payload, 0600)
+ util.write_file("%s/%s" % (handlerdir,modfname), payload, 0600)
try:
mod = __import__(modname)
@@ -402,7 +398,9 @@ class CloudInit:
return
filename=filename.replace(os.sep,'_')
- util.write_file("%s/%s" % (user_scripts_dir,filename), payload, 0700)
+ scriptsdir = get_ipath_cur('scripts')
+ util.write_file("%s/%s/%s" %
+ (scriptsdir,self.get_instance_id(),filename), payload, 0700)
def handle_upstart_job(self,data,ctype,filename,payload):
if ctype == "__end__" or ctype == "__begin__": return
@@ -416,6 +414,7 @@ class CloudInit:
self.cloud_config_str=""
return
if ctype == "__end__":
+ cloud_config = self.get_ipath("cloud_config")
util.write_file(cloud_config, self.cloud_config_str, 0600)
## this could merge the cloud config with the system config
@@ -452,6 +451,7 @@ class CloudInit:
elif start != 0:
payload=payload[start:]
+ boothooks_dir = self.get_ipath("boothooks")
filepath = "%s/%s" % (boothooks_dir,filename)
util.write_file(filepath, payload, 0700)
try:
@@ -480,15 +480,58 @@ class CloudInit:
def device_name_to_device(self,name):
return(self.datasource.device_name_to_device(name))
+ # I really don't know if this should be here or not, but
+ # I needed it in cc_update_hostname, where that code had a valid 'cloud'
+ # reference, but did not have a cloudinit handle
+ # (ie, no cloudinit.get_cpath())
+ def get_cpath(self,name=None):
+ return(get_cpath(name))
+
+
+def initfs():
+ subds = [ 'scripts/per-instance', 'scripts/per-once', 'scripts/per-boot',
+ 'seed', 'instances', 'handlers', 'sem', 'data' ]
+ dlist = [ ]
+ for subd in subds:
+ dlist.append("%s/%s" % (varlibdir, subd))
+ util.ensure_dirs(dlist)
+
+ cfg = util.get_base_cfg(system_config,cfg_builtin,parsed_cfgs)
+ log_file = None
+ if 'def_log_file' in cfg:
+ log_file = cfg['def_log_file']
+ fp = open(log_file,"ab")
+ fp.close()
+ if log_file and 'syslog' in cfg:
+ perms = cfg['syslog']
+ (u,g) = perms.split(':',1)
+ if u == "-1" or u == "None": u = None
+ if g == "-1" or g == "None": g = None
+ util.chownbyname(log_file, u, g)
def purge_cache():
- try:
- os.unlink(data_source_cache)
- except OSError as e:
- if e.errno != errno.ENOENT: return(False)
- except:
- return(False)
+ rmlist = ( boot_finished , cur_instance_link )
+ for f in rmlist:
+ try:
+ os.unlink(f)
+ except OSError as e:
+ if e.errno == errno.ENOENT: continue
+ return(False)
+ except:
+ return(False)
return(True)
+# get_ipath_cur: get the current instance path for an item
+def get_ipath_cur(name=None):
+ return("%s/instance/%s" % (varlibdir, pathmap[name]))
+
+# get_cpath : get the "clouddir" (/var/lib/cloud/<name>)
+# for a name in dirmap
+def get_cpath(name=None):
+ return("%s%s" % (varlibdir, pathmap[name]))
+
+def get_base_cfg():
+ return(util.get_base_cfg(system_config,cfg_builtin,parsed_cfgs))
+
class DataSourceNotFoundException(Exception):
pass