new: FreeBSD module to support cloud-init on the FBSD10 platform. In its
current form its still missing some modules though. Supported: -SSH-keys -growpart -growfs -adduser -powerstate
This commit is contained in:
@@ -22,6 +22,7 @@ import os
|
|||||||
import os.path
|
import os.path
|
||||||
import re
|
import re
|
||||||
import stat
|
import stat
|
||||||
|
import sys
|
||||||
|
|
||||||
from cloudinit import log as logging
|
from cloudinit import log as logging
|
||||||
from cloudinit.settings import PER_ALWAYS
|
from cloudinit.settings import PER_ALWAYS
|
||||||
@@ -137,6 +138,35 @@ class ResizeGrowPart(object):
|
|||||||
|
|
||||||
return (before, get_size(partdev))
|
return (before, get_size(partdev))
|
||||||
|
|
||||||
|
class ResizeGpart(object):
|
||||||
|
def available(self):
|
||||||
|
if not os.path.exists('/usr/local/sbin/gpart'):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def resize(self, diskdev, partnum, partdev):
|
||||||
|
"""
|
||||||
|
GPT disks store metadata at the beginning (primary) and at the
|
||||||
|
end (secondary) of the disk. When launching an image with a
|
||||||
|
larger disk compared to the original image, the secondary copy
|
||||||
|
is lost. Thus, the metadata will be marked CORRUPT, and need to
|
||||||
|
be recovered.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
util.subp(["gpart", "recover", diskdev])
|
||||||
|
except util.ProcessExecutionError as e:
|
||||||
|
if e.exit_code != 0:
|
||||||
|
util.logexc(LOG, "Failed: gpart recover %s", diskdev)
|
||||||
|
raise ResizeFailedException(e)
|
||||||
|
|
||||||
|
before = get_size(partdev)
|
||||||
|
try:
|
||||||
|
util.subp(["gpart", "resize", "-i", partnum, diskdev])
|
||||||
|
except util.ProcessExecutionError as e:
|
||||||
|
util.logexc(LOG, "Failed: gpart resize -i %s %s", partnum, diskdev)
|
||||||
|
raise ResizeFailedException(e)
|
||||||
|
|
||||||
|
return (before, get_size(partdev))
|
||||||
|
|
||||||
def get_size(filename):
|
def get_size(filename):
|
||||||
fd = os.open(filename, os.O_RDONLY)
|
fd = os.open(filename, os.O_RDONLY)
|
||||||
@@ -156,6 +186,12 @@ def device_part_info(devpath):
|
|||||||
bname = os.path.basename(rpath)
|
bname = os.path.basename(rpath)
|
||||||
syspath = "/sys/class/block/%s" % bname
|
syspath = "/sys/class/block/%s" % bname
|
||||||
|
|
||||||
|
# FreeBSD doesn't know of sysfs so just get everything we need from
|
||||||
|
# the device, like /dev/vtbd0p2.
|
||||||
|
if sys.platform.startswith('freebsd'):
|
||||||
|
m = re.search('^(/dev/.+)p([0-9])$', devpath)
|
||||||
|
return (m.group(1), m.group(2))
|
||||||
|
|
||||||
if not os.path.exists(syspath):
|
if not os.path.exists(syspath):
|
||||||
raise ValueError("%s had no syspath (%s)" % (devpath, syspath))
|
raise ValueError("%s had no syspath (%s)" % (devpath, syspath))
|
||||||
|
|
||||||
@@ -206,7 +242,7 @@ def resize_devices(resizer, devices):
|
|||||||
"stat of '%s' failed: %s" % (blockdev, e),))
|
"stat of '%s' failed: %s" % (blockdev, e),))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not stat.S_ISBLK(statret.st_mode):
|
if not stat.S_ISBLK(statret.st_mode) and not stat.S_ISCHR(statret.st_mode):
|
||||||
info.append((devent, RESIZE.SKIPPED,
|
info.append((devent, RESIZE.SKIPPED,
|
||||||
"device '%s' not a block device" % blockdev,))
|
"device '%s' not a block device" % blockdev,))
|
||||||
continue
|
continue
|
||||||
@@ -281,4 +317,4 @@ def handle(_name, cfg, _cloud, log, _args):
|
|||||||
|
|
||||||
# LP: 1212444 FIXME re-order and favor ResizeParted
|
# LP: 1212444 FIXME re-order and favor ResizeParted
|
||||||
#RESIZERS = (('growpart', ResizeGrowPart),)
|
#RESIZERS = (('growpart', ResizeGrowPart),)
|
||||||
RESIZERS = (('growpart', ResizeGrowPart), ('parted', ResizeParted))
|
RESIZERS = (('growpart', ResizeGrowPart), ('parted', ResizeParted), ('gpart', ResizeGpart))
|
||||||
|
|||||||
@@ -23,12 +23,34 @@ import errno
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
frequency = PER_INSTANCE
|
frequency = PER_INSTANCE
|
||||||
|
|
||||||
EXIT_FAIL = 254
|
EXIT_FAIL = 254
|
||||||
|
|
||||||
|
#
|
||||||
|
# Returns the cmdline for the given process id.
|
||||||
|
#
|
||||||
|
|
||||||
|
def givecmdline(pid):
|
||||||
|
# Check if this pid still exists by sending it the harmless 0 signal.
|
||||||
|
try:
|
||||||
|
os.kill(pid, 0)
|
||||||
|
except OSError:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
# Example output from procstat -c 16357
|
||||||
|
# PID COMM ARGS
|
||||||
|
# 1 init /bin/init --
|
||||||
|
if sys.platform.startswith('freebsd'):
|
||||||
|
(output, _err) = util.subp(['procstat', '-c', str(pid)])
|
||||||
|
line = output.splitlines()[1]
|
||||||
|
m = re.search('\d+ (\w|\.|-)+\s+(/\w.+)', line)
|
||||||
|
return m.group(2)
|
||||||
|
else:
|
||||||
|
return util.load_file("/proc/%s/cmdline" % pid)
|
||||||
|
|
||||||
def handle(_name, cfg, _cloud, log, _args):
|
def handle(_name, cfg, _cloud, log, _args):
|
||||||
|
|
||||||
@@ -42,8 +64,8 @@ def handle(_name, cfg, _cloud, log, _args):
|
|||||||
return
|
return
|
||||||
|
|
||||||
mypid = os.getpid()
|
mypid = os.getpid()
|
||||||
cmdline = util.load_file("/proc/%s/cmdline" % mypid)
|
|
||||||
|
|
||||||
|
cmdline = givecmdline(mypid)
|
||||||
if not cmdline:
|
if not cmdline:
|
||||||
log.warn("power_state: failed to get cmdline of current process")
|
log.warn("power_state: failed to get cmdline of current process")
|
||||||
return
|
return
|
||||||
@@ -119,8 +141,6 @@ def run_after_pid_gone(pid, pidcmdline, timeout, log, func, args):
|
|||||||
msg = None
|
msg = None
|
||||||
end_time = time.time() + timeout
|
end_time = time.time() + timeout
|
||||||
|
|
||||||
cmdline_f = "/proc/%s/cmdline" % pid
|
|
||||||
|
|
||||||
def fatal(msg):
|
def fatal(msg):
|
||||||
if log:
|
if log:
|
||||||
log.warn(msg)
|
log.warn(msg)
|
||||||
@@ -134,16 +154,14 @@ def run_after_pid_gone(pid, pidcmdline, timeout, log, func, args):
|
|||||||
break
|
break
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cmdline = ""
|
cmdline = givecmdline(pid)
|
||||||
with open(cmdline_f) as fp:
|
|
||||||
cmdline = fp.read()
|
|
||||||
if cmdline != pidcmdline:
|
if cmdline != pidcmdline:
|
||||||
msg = "cmdline changed for %s [now: %s]" % (pid, cmdline)
|
msg = "cmdline changed for %s [now: %s]" % (pid, cmdline)
|
||||||
break
|
break
|
||||||
|
|
||||||
except IOError as ioerr:
|
except IOError as ioerr:
|
||||||
if ioerr.errno in known_errnos:
|
if ioerr.errno in known_errnos:
|
||||||
msg = "pidfile '%s' gone [%d]" % (cmdline_f, ioerr.errno)
|
msg = "pidfile gone [%d]" % ioerr.errno
|
||||||
else:
|
else:
|
||||||
fatal("IOError during wait: %s" % ioerr)
|
fatal("IOError during wait: %s" % ioerr)
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ def _resize_ext(mount_point, devpth): # pylint: disable=W0613
|
|||||||
def _resize_xfs(mount_point, devpth): # pylint: disable=W0613
|
def _resize_xfs(mount_point, devpth): # pylint: disable=W0613
|
||||||
return ('xfs_growfs', devpth)
|
return ('xfs_growfs', devpth)
|
||||||
|
|
||||||
|
|
||||||
|
def _resize_ufs(mount_point, devpth): # pylint: disable=W0613
|
||||||
|
return ('growfs', devpth)
|
||||||
|
|
||||||
# Do not use a dictionary as these commands should be able to be used
|
# Do not use a dictionary as these commands should be able to be used
|
||||||
# for multiple filesystem types if possible, e.g. one command for
|
# for multiple filesystem types if possible, e.g. one command for
|
||||||
# ext2, ext3 and ext4.
|
# ext2, ext3 and ext4.
|
||||||
@@ -46,6 +50,7 @@ RESIZE_FS_PREFIXES_CMDS = [
|
|||||||
('btrfs', _resize_btrfs),
|
('btrfs', _resize_btrfs),
|
||||||
('ext', _resize_ext),
|
('ext', _resize_ext),
|
||||||
('xfs', _resize_xfs),
|
('xfs', _resize_xfs),
|
||||||
|
('ufs', _resize_ufs),
|
||||||
]
|
]
|
||||||
|
|
||||||
NOBLOCK = "noblock"
|
NOBLOCK = "noblock"
|
||||||
@@ -91,7 +96,7 @@ def handle(name, cfg, _cloud, log, args):
|
|||||||
raise exc
|
raise exc
|
||||||
return
|
return
|
||||||
|
|
||||||
if not stat.S_ISBLK(statret.st_mode):
|
if not stat.S_ISBLK(statret.st_mode) and not stat.S_ISCHR(statret.st_mode):
|
||||||
if util.is_container():
|
if util.is_container():
|
||||||
log.debug("device '%s' not a block device in container."
|
log.debug("device '%s' not a block device in container."
|
||||||
" cannot resize: %s" % (devpth, info))
|
" cannot resize: %s" % (devpth, info))
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ from cloudinit.distros.parsers import hosts
|
|||||||
OSFAMILIES = {
|
OSFAMILIES = {
|
||||||
'debian': ['debian', 'ubuntu'],
|
'debian': ['debian', 'ubuntu'],
|
||||||
'redhat': ['fedora', 'rhel'],
|
'redhat': ['fedora', 'rhel'],
|
||||||
|
'freebsd': ['freebsd'],
|
||||||
'suse': ['sles']
|
'suse': ['sles']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
208
cloudinit/distros/freebsd.py
Normal file
208
cloudinit/distros/freebsd.py
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
# vi: ts=4 expandtab
|
||||||
|
#
|
||||||
|
# Copyright (C) 2012 Canonical Ltd.
|
||||||
|
# Copyright (C) 2012, 2013 Hewlett-Packard Development Company, L.P.
|
||||||
|
# Copyright (C) 2012 Yahoo! Inc.
|
||||||
|
#
|
||||||
|
# Author: Scott Moser <scott.moser@canonical.com>
|
||||||
|
# Author: Juerg Haefliger <juerg.haefliger@hp.com>
|
||||||
|
# Author: Joshua Harlow <harlowja@yahoo-inc.com>
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License version 3, as
|
||||||
|
# published by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from cloudinit import distros
|
||||||
|
from cloudinit import helpers
|
||||||
|
from cloudinit import log as logging
|
||||||
|
from cloudinit import netinfo
|
||||||
|
from cloudinit import ssh_util
|
||||||
|
from cloudinit import util
|
||||||
|
|
||||||
|
from cloudinit.settings import PER_INSTANCE
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class Distro(distros.Distro):
|
||||||
|
def __init__(self, name, cfg, paths):
|
||||||
|
distros.Distro.__init__(self, name, cfg, paths)
|
||||||
|
# This will be used to restrict certain
|
||||||
|
# calls from repeatly happening (when they
|
||||||
|
# should only happen say once per instance...)
|
||||||
|
self._runner = helpers.Runners(paths)
|
||||||
|
self.osfamily = 'freebsd'
|
||||||
|
|
||||||
|
def updatercconf(self, key, value):
|
||||||
|
LOG.debug("updatercconf: %s => %s" % (key, value))
|
||||||
|
conf = {}
|
||||||
|
configchanged = False
|
||||||
|
with open("/etc/rc.conf") as file:
|
||||||
|
for line in file:
|
||||||
|
tok = line.split('=')
|
||||||
|
# TODO: Handle keys with spaces, make this a bit more robust.
|
||||||
|
if tok[0] == key:
|
||||||
|
if tok[1] != value:
|
||||||
|
conf[tok[0]] = value
|
||||||
|
LOG.debug("[rc.conf]: Value %s for key %s needs to be changed" % (value, key))
|
||||||
|
configchanged = True
|
||||||
|
else:
|
||||||
|
conf[tok[0]] = tok[1].rstrip()
|
||||||
|
|
||||||
|
if configchanged:
|
||||||
|
LOG.debug("Writing new /etc/rc.conf file")
|
||||||
|
with open ('/etc/rc.conf', 'w') as file:
|
||||||
|
for keyval in conf.items():
|
||||||
|
file.write("%s=%s\n" % keyval)
|
||||||
|
|
||||||
|
def _read_hostname():
|
||||||
|
return
|
||||||
|
|
||||||
|
def _read_system_hostname():
|
||||||
|
return
|
||||||
|
|
||||||
|
def _select_hostname(self, hostname, fqdn):
|
||||||
|
if not hostname:
|
||||||
|
return fqdn
|
||||||
|
return hostname
|
||||||
|
|
||||||
|
def _write_hostname(self, your_hostname, out_fn):
|
||||||
|
self.updatercconf('hostname', your_hostname)
|
||||||
|
|
||||||
|
def create_group(self, name, members):
|
||||||
|
group_add_cmd = ['pw', '-n', name]
|
||||||
|
if util.is_group(name):
|
||||||
|
LOG.warn("Skipping creation of existing group '%s'" % name)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
util.subp(group_add_cmd)
|
||||||
|
LOG.info("Created new group %s" % name)
|
||||||
|
except Exception:
|
||||||
|
util.logexc("Failed to create group %s", name)
|
||||||
|
|
||||||
|
if len(members) > 0:
|
||||||
|
for member in members:
|
||||||
|
if not util.is_user(member):
|
||||||
|
LOG.warn("Unable to add group member '%s' to group '%s'"
|
||||||
|
"; user does not exist.", member, name)
|
||||||
|
continue
|
||||||
|
util.subp(['pw', 'usermod', '-n', name, '-G', member])
|
||||||
|
LOG.info("Added user '%s' to group '%s'" % (member, name))
|
||||||
|
|
||||||
|
def add_user(self, name, **kwargs):
|
||||||
|
if util.is_user(name):
|
||||||
|
LOG.info("User %s already exists, skipping." % name)
|
||||||
|
return False
|
||||||
|
|
||||||
|
adduser_cmd = ['pw', 'useradd', '-n', name]
|
||||||
|
log_adduser_cmd = ['pw', 'useradd', '-n', name]
|
||||||
|
|
||||||
|
adduser_opts = {
|
||||||
|
"homedir": '-d',
|
||||||
|
"gecos": '-c',
|
||||||
|
"primary_group": '-g',
|
||||||
|
"groups": '-G',
|
||||||
|
"passwd": '-h',
|
||||||
|
"shell": '-s',
|
||||||
|
"inactive": '-E',
|
||||||
|
}
|
||||||
|
adduser_flags = {
|
||||||
|
"no_user_group": '--no-user-group',
|
||||||
|
"system": '--system',
|
||||||
|
"no_log_init": '--no-log-init',
|
||||||
|
}
|
||||||
|
|
||||||
|
redact_opts = ['passwd']
|
||||||
|
|
||||||
|
for key, val in kwargs.iteritems():
|
||||||
|
if key in adduser_opts and val and isinstance(val, str):
|
||||||
|
adduser_cmd.extend([adduser_opts[key], val])
|
||||||
|
|
||||||
|
# Redact certain fields from the logs
|
||||||
|
if key in redact_opts:
|
||||||
|
log_adduser_cmd.extend([adduser_opts[key], 'REDACTED'])
|
||||||
|
else:
|
||||||
|
log_adduser_cmd.extend([adduser_opts[key], val])
|
||||||
|
|
||||||
|
elif key in adduser_flags and val:
|
||||||
|
adduser_cmd.append(adduser_flags[key])
|
||||||
|
log_adduser_cmd.append(adduser_flags[key])
|
||||||
|
|
||||||
|
if 'no_create_home' in kwargs or 'system' in kwargs:
|
||||||
|
adduser_cmd.append('-d/nonexistent')
|
||||||
|
log_adduser_cmd.append('-d/nonexistent')
|
||||||
|
else:
|
||||||
|
adduser_cmd.append('-d/usr/home/%s' % name)
|
||||||
|
adduser_cmd.append('-m')
|
||||||
|
log_adduser_cmd.append('-d/usr/home/%s' % name)
|
||||||
|
log_adduser_cmd.append('-m')
|
||||||
|
|
||||||
|
# Run the command
|
||||||
|
LOG.info("Adding user %s", name)
|
||||||
|
try:
|
||||||
|
util.subp(adduser_cmd, logstring=log_adduser_cmd)
|
||||||
|
except Exception as e:
|
||||||
|
util.logexc(LOG, "Failed to create user %s", name)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
# TODO:
|
||||||
|
def set_passwd(self, name, **kwargs):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def lock_passwd(self, name):
|
||||||
|
try:
|
||||||
|
util.subp(['pw', 'usermod', name, '-h', '-'])
|
||||||
|
except Exception as e:
|
||||||
|
util.logexc(LOG, "Failed to lock user %s", name)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
# TODO:
|
||||||
|
def write_sudo_rules(self, name, rules, sudo_file=None):
|
||||||
|
LOG.debug("[write_sudo_rules] Name: %s" % name)
|
||||||
|
|
||||||
|
def create_user(self, name, **kwargs):
|
||||||
|
self.add_user(name, **kwargs)
|
||||||
|
|
||||||
|
# Set password if plain-text password provided and non-empty
|
||||||
|
if 'plain_text_passwd' in kwargs and kwargs['plain_text_passwd']:
|
||||||
|
self.set_passwd(name, kwargs['plain_text_passwd'])
|
||||||
|
|
||||||
|
# Default locking down the account. 'lock_passwd' defaults to True.
|
||||||
|
# lock account unless lock_password is False.
|
||||||
|
if kwargs.get('lock_passwd', True):
|
||||||
|
self.lock_passwd(name)
|
||||||
|
|
||||||
|
# Configure sudo access
|
||||||
|
if 'sudo' in kwargs:
|
||||||
|
self.write_sudo_rules(name, kwargs['sudo'])
|
||||||
|
|
||||||
|
# Import SSH keys
|
||||||
|
if 'ssh_authorized_keys' in kwargs:
|
||||||
|
keys = set(kwargs['ssh_authorized_keys']) or []
|
||||||
|
ssh_util.setup_user_keys(keys, name, options=None)
|
||||||
|
|
||||||
|
def _write_network(self, settings):
|
||||||
|
return
|
||||||
|
|
||||||
|
def apply_locale():
|
||||||
|
return
|
||||||
|
|
||||||
|
def install_packages():
|
||||||
|
return
|
||||||
|
|
||||||
|
def package_command():
|
||||||
|
return
|
||||||
|
|
||||||
|
def set_timezone():
|
||||||
|
return
|
||||||
|
|
||||||
|
def update_package_sources():
|
||||||
|
return
|
||||||
|
|
||||||
@@ -34,6 +34,7 @@ def netdev_info(empty=""):
|
|||||||
continue
|
continue
|
||||||
if line[0] not in ("\t", " "):
|
if line[0] not in ("\t", " "):
|
||||||
curdev = line.split()[0]
|
curdev = line.split()[0]
|
||||||
|
# TODO: up/down detection fails on FreeBSD
|
||||||
devs[curdev] = {"up": False}
|
devs[curdev] = {"up": False}
|
||||||
for field in fields:
|
for field in fields:
|
||||||
devs[curdev][field] = ""
|
devs[curdev][field] = ""
|
||||||
@@ -46,21 +47,32 @@ def netdev_info(empty=""):
|
|||||||
fieldpost = "6"
|
fieldpost = "6"
|
||||||
|
|
||||||
for i in range(len(toks)):
|
for i in range(len(toks)):
|
||||||
if toks[i] == "hwaddr":
|
if toks[i] == "hwaddr" or toks[i] == "ether":
|
||||||
try:
|
try:
|
||||||
devs[curdev]["hwaddr"] = toks[i + 1]
|
devs[curdev]["hwaddr"] = toks[i + 1]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
for field in ("addr", "bcast", "mask"):
|
|
||||||
|
"""
|
||||||
|
Couple the different items we're interested in with the correct field
|
||||||
|
since FreeBSD/CentOS/Fedora differ in the output.
|
||||||
|
"""
|
||||||
|
|
||||||
|
ifconfigfields = {
|
||||||
|
"addr:":"addr", "inet":"addr",
|
||||||
|
"bcast:":"bcast", "broadcast":"bcast",
|
||||||
|
"mask:":"mask", "netmask":"mask"
|
||||||
|
}
|
||||||
|
for origfield, field in ifconfigfields.items():
|
||||||
target = "%s%s" % (field, fieldpost)
|
target = "%s%s" % (field, fieldpost)
|
||||||
if devs[curdev].get(target, ""):
|
if devs[curdev].get(target, ""):
|
||||||
continue
|
continue
|
||||||
if toks[i] == "%s:" % field:
|
if toks[i] == "%s" % origfield:
|
||||||
try:
|
try:
|
||||||
devs[curdev][target] = toks[i + 1]
|
devs[curdev][target] = toks[i + 1]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
elif toks[i].startswith("%s:" % field):
|
elif toks[i].startswith("%s" % origfield):
|
||||||
devs[curdev][target] = toks[i][len(field) + 1:]
|
devs[curdev][target] = toks[i][len(field) + 1:]
|
||||||
|
|
||||||
if empty != "":
|
if empty != "":
|
||||||
@@ -71,17 +83,38 @@ def netdev_info(empty=""):
|
|||||||
|
|
||||||
return devs
|
return devs
|
||||||
|
|
||||||
|
#
|
||||||
|
# Use netstat instead of route since that produces more portable output.
|
||||||
|
#
|
||||||
|
|
||||||
def route_info():
|
def route_info():
|
||||||
(route_out, _err) = util.subp(["route", "-n"])
|
(route_out, _err) = util.subp(["netstat", "-rn"])
|
||||||
routes = []
|
routes = []
|
||||||
entries = route_out.splitlines()[1:]
|
entries = route_out.splitlines()[1:]
|
||||||
for line in entries:
|
for line in entries:
|
||||||
if not line:
|
if not line:
|
||||||
continue
|
continue
|
||||||
toks = line.split()
|
toks = line.split()
|
||||||
if len(toks) < 8 or toks[0] == "Kernel" or toks[0] == "Destination":
|
|
||||||
|
"""
|
||||||
|
FreeBSD shows 6 items in the routing table:
|
||||||
|
Destination Gateway Flags Refs Use Netif Expire
|
||||||
|
default 10.65.0.1 UGS 0 34920 vtnet0
|
||||||
|
|
||||||
|
Linux netstat shows 2 more:
|
||||||
|
Destination Gateway Genmask Flags MSS Window irtt Iface
|
||||||
|
0.0.0.0 10.65.0.1 0.0.0.0 UG 0 0 0 eth0
|
||||||
|
"""
|
||||||
|
|
||||||
|
if len(toks) < 6 or toks[0] == "Kernel" or toks[0] == "Destination" or toks[0] == "Internet" or toks[0] == "Internet6" or toks[0] == "Routing":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if len(toks) < 8:
|
||||||
|
toks.append("-")
|
||||||
|
toks.append("-")
|
||||||
|
toks[7] = toks[5]
|
||||||
|
toks[5] = "-"
|
||||||
|
|
||||||
entry = {
|
entry = {
|
||||||
'destination': toks[0],
|
'destination': toks[0],
|
||||||
'gateway': toks[1],
|
'gateway': toks[1],
|
||||||
@@ -92,6 +125,7 @@ def route_info():
|
|||||||
'use': toks[6],
|
'use': toks[6],
|
||||||
'iface': toks[7],
|
'iface': toks[7],
|
||||||
}
|
}
|
||||||
|
|
||||||
routes.append(entry)
|
routes.append(entry)
|
||||||
return routes
|
return routes
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ class DataSource(object):
|
|||||||
# when the kernel named them 'vda' or 'xvda'
|
# when the kernel named them 'vda' or 'xvda'
|
||||||
# we want to return the correct value for what will actually
|
# we want to return the correct value for what will actually
|
||||||
# exist in this instance
|
# exist in this instance
|
||||||
mappings = {"sd": ("vd", "xvd")}
|
mappings = {"sd": ("vd", "xvd", "vtb")}
|
||||||
for (nfrom, tlist) in mappings.iteritems():
|
for (nfrom, tlist) in mappings.iteritems():
|
||||||
if not short_name.startswith(nfrom):
|
if not short_name.startswith(nfrom):
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ from StringIO import StringIO
|
|||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import copy as obj_copy
|
import copy as obj_copy
|
||||||
|
import ctypes
|
||||||
import errno
|
import errno
|
||||||
import glob
|
import glob
|
||||||
import grp
|
import grp
|
||||||
@@ -36,6 +37,7 @@ import os.path
|
|||||||
import platform
|
import platform
|
||||||
import pwd
|
import pwd
|
||||||
import random
|
import random
|
||||||
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import socket
|
import socket
|
||||||
import stat
|
import stat
|
||||||
@@ -1300,11 +1302,25 @@ def mounts():
|
|||||||
mounted = {}
|
mounted = {}
|
||||||
try:
|
try:
|
||||||
# Go through mounts to see what is already mounted
|
# Go through mounts to see what is already mounted
|
||||||
mount_locs = load_file("/proc/mounts").splitlines()
|
if os.path.exists("/proc/mounts"):
|
||||||
|
mount_locs = load_file("/proc/mounts").splitlines()
|
||||||
|
method = 'proc'
|
||||||
|
else:
|
||||||
|
(mountoutput, _err) = subp("mount")
|
||||||
|
mount_locs = mountoutput.splitlines()
|
||||||
|
method = 'mount'
|
||||||
for mpline in mount_locs:
|
for mpline in mount_locs:
|
||||||
# Format at: man fstab
|
# Linux: /dev/sda1 on /boot type ext4 (rw,relatime,data=ordered)
|
||||||
|
# FreeBSD: /dev/vtbd0p2 on / (ufs, local, journaled soft-updates)
|
||||||
try:
|
try:
|
||||||
(dev, mp, fstype, opts, _freq, _passno) = mpline.split()
|
if method == 'proc' and len(mpline) == 6:
|
||||||
|
(dev, mp, fstype, opts, _freq, _passno) = mpline.split()
|
||||||
|
elif method == 'mount':
|
||||||
|
m = re.search('^(/dev/[\S]+) on (/.*) \((.+), .+, (.+)\)$', mpline)
|
||||||
|
dev = m.group(1)
|
||||||
|
mp = m.group(2)
|
||||||
|
fstype = m.group(3)
|
||||||
|
opts = m.group(4)
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
# If the name of the mount point contains spaces these
|
# If the name of the mount point contains spaces these
|
||||||
@@ -1315,9 +1331,9 @@ def mounts():
|
|||||||
'mountpoint': mp,
|
'mountpoint': mp,
|
||||||
'opts': opts,
|
'opts': opts,
|
||||||
}
|
}
|
||||||
LOG.debug("Fetched %s mounts from %s", mounted, "/proc/mounts")
|
LOG.debug("Fetched %s mounts from %s", mounted, method)
|
||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
logexc(LOG, "Failed fetching mount points from /proc/mounts")
|
logexc(LOG, "Failed fetching mount points")
|
||||||
return mounted
|
return mounted
|
||||||
|
|
||||||
|
|
||||||
@@ -1403,11 +1419,22 @@ def time_rfc2822():
|
|||||||
def uptime():
|
def uptime():
|
||||||
uptime_str = '??'
|
uptime_str = '??'
|
||||||
try:
|
try:
|
||||||
contents = load_file("/proc/uptime").strip()
|
if os.path.exists("/proc/uptime"):
|
||||||
if contents:
|
contents = load_file("/proc/uptime").strip()
|
||||||
uptime_str = contents.split()[0]
|
if contents:
|
||||||
|
uptime_str = contents.split()[0]
|
||||||
|
else:
|
||||||
|
libc = ctypes.CDLL('/lib/libc.so.7')
|
||||||
|
size = ctypes.c_size_t()
|
||||||
|
buf = ctypes.c_int()
|
||||||
|
size.value = ctypes.sizeof(buf)
|
||||||
|
libc.sysctlbyname("kern.boottime", ctypes.byref(buf), ctypes.byref(size), None, 0)
|
||||||
|
now = time.time()
|
||||||
|
bootup = buf.value
|
||||||
|
uptime_str = now - bootup
|
||||||
|
|
||||||
except:
|
except:
|
||||||
logexc(LOG, "Unable to read uptime from /proc/uptime")
|
logexc(LOG, "Unable to read uptime")
|
||||||
return uptime_str
|
return uptime_str
|
||||||
|
|
||||||
|
|
||||||
@@ -1746,6 +1773,18 @@ def parse_mtab(path):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def parse_mount(path):
|
||||||
|
(mountoutput, _err) = subp("mount")
|
||||||
|
mount_locs = mountoutput.splitlines()
|
||||||
|
for line in mount_locs:
|
||||||
|
m = re.search('^(/dev/[\S]+) on (/.*) \((.+), .+, (.+)\)$', line)
|
||||||
|
devpth = m.group(1)
|
||||||
|
mount_point = m.group(2)
|
||||||
|
fs_type = m.group(3)
|
||||||
|
if mount_point == path:
|
||||||
|
return devpth, fs_type, mount_point
|
||||||
|
return None
|
||||||
|
|
||||||
def get_mount_info(path, log=LOG):
|
def get_mount_info(path, log=LOG):
|
||||||
# Use /proc/$$/mountinfo to find the device where path is mounted.
|
# Use /proc/$$/mountinfo to find the device where path is mounted.
|
||||||
# This is done because with a btrfs filesystem using os.stat(path)
|
# This is done because with a btrfs filesystem using os.stat(path)
|
||||||
@@ -1779,8 +1818,10 @@ def get_mount_info(path, log=LOG):
|
|||||||
if os.path.exists(mountinfo_path):
|
if os.path.exists(mountinfo_path):
|
||||||
lines = load_file(mountinfo_path).splitlines()
|
lines = load_file(mountinfo_path).splitlines()
|
||||||
return parse_mount_info(path, lines, log)
|
return parse_mount_info(path, lines, log)
|
||||||
else:
|
elif os.path.exists("/etc/mtab"):
|
||||||
return parse_mtab(path)
|
return parse_mtab(path)
|
||||||
|
else:
|
||||||
|
return parse_mount(path)
|
||||||
|
|
||||||
|
|
||||||
def which(program):
|
def which(program):
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ if [ ! -e "$CHNG_LOG" ]; then
|
|||||||
fail "Unable to find 'ChangeLog' file located at '$CHNG_LOG'"
|
fail "Unable to find 'ChangeLog' file located at '$CHNG_LOG'"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
VERSION=$(sed -n '/^[0-9]\+[.][0-9]\+[.][0-9]\+:/ {s/://; p; :a;n; ba; }' \
|
VERSION=$(grep -m1 -o -E '^[0-9]+(\.[0-9]+)+' \
|
||||||
"$CHNG_LOG") &&
|
"$CHNG_LOG") &&
|
||||||
[ -n "$VERSION" ] ||
|
[ -n "$VERSION" ] ||
|
||||||
fail "failed to get version from '$CHNG_LOG'"
|
fail "failed to get version from '$CHNG_LOG'"
|
||||||
|
|||||||
Reference in New Issue
Block a user