2015-08-24 03:49:51 +00:00
|
|
|
# Copyright 2015 Canonical, Ltd.
|
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU Affero General Public License as
|
|
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
|
|
# License, or (at your option) any later version.
|
|
|
|
#
|
|
|
|
# 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 Affero General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2015-09-21 21:40:26 +00:00
|
|
|
import crypt
|
|
|
|
import errno
|
|
|
|
import logging
|
2015-09-02 19:21:14 +00:00
|
|
|
import os
|
2015-09-21 21:40:26 +00:00
|
|
|
import random
|
2015-09-02 19:21:14 +00:00
|
|
|
from subprocess import Popen, PIPE
|
2015-08-24 03:49:51 +00:00
|
|
|
from subiquity.async import Async
|
|
|
|
|
|
|
|
log = logging.getLogger("subiquity.utils")
|
2015-09-03 18:35:07 +00:00
|
|
|
SYS_CLASS_NET = "/sys/class/net/"
|
2015-08-24 03:49:51 +00:00
|
|
|
|
|
|
|
|
2015-09-25 02:30:04 +00:00
|
|
|
def run_command_async(cmd, timeout=None):
|
2015-09-11 18:12:26 +00:00
|
|
|
log.debug('calling Async command: {}'.format(cmd))
|
2015-09-25 02:30:04 +00:00
|
|
|
return Async.pool.submit(run_command, cmd, timeout)
|
2015-08-24 03:49:51 +00:00
|
|
|
|
|
|
|
|
2015-09-25 02:30:04 +00:00
|
|
|
def run_command(command, timeout=None):
|
2015-09-02 19:21:14 +00:00
|
|
|
""" Execute command through system shell
|
|
|
|
:param command: command to run
|
|
|
|
:param timeout: (optional) use 'timeout' to limit time. default 300
|
|
|
|
:type command: str
|
|
|
|
:returns: {status: returncode, output: stdout, err: stderr}
|
|
|
|
:rtype: dict
|
|
|
|
.. code::
|
|
|
|
# Get output of juju status
|
|
|
|
cmd_dict = utils.get_command_output('juju status')
|
2015-08-24 03:49:51 +00:00
|
|
|
"""
|
2015-09-11 18:12:26 +00:00
|
|
|
log.debug('run_command called: {}'.format(command))
|
2015-09-02 19:21:14 +00:00
|
|
|
cmd_env = os.environ.copy()
|
|
|
|
# set consistent locale
|
|
|
|
cmd_env['LC_ALL'] = 'C'
|
|
|
|
if timeout:
|
|
|
|
command = "timeout %ds %s" % (timeout, command)
|
|
|
|
|
|
|
|
try:
|
2015-09-11 20:53:34 +00:00
|
|
|
log.debug('trying Popen...')
|
2015-09-02 19:21:14 +00:00
|
|
|
p = Popen(command, shell=True,
|
2015-09-25 02:30:04 +00:00
|
|
|
stdout=PIPE, stderr=PIPE,
|
2015-09-02 19:21:14 +00:00
|
|
|
bufsize=-1, env=cmd_env, close_fds=True)
|
|
|
|
except OSError as e:
|
|
|
|
if e.errno == errno.ENOENT:
|
2015-09-11 20:53:34 +00:00
|
|
|
log.debug('error!')
|
2015-09-02 19:21:14 +00:00
|
|
|
return dict(ret=127, output="", err="")
|
|
|
|
else:
|
2015-09-11 20:53:34 +00:00
|
|
|
log.debug('error raise!')
|
2015-09-02 19:21:14 +00:00
|
|
|
raise e
|
2015-09-11 20:53:34 +00:00
|
|
|
log.debug('calling communicate()')
|
2015-09-02 19:21:14 +00:00
|
|
|
stdout, stderr = p.communicate()
|
|
|
|
if p.returncode == 126 or p.returncode == 127:
|
|
|
|
stdout = bytes()
|
|
|
|
if not stderr:
|
|
|
|
stderr = bytes()
|
2015-09-11 20:53:34 +00:00
|
|
|
rv = dict(status=p.returncode,
|
|
|
|
output=stdout.decode('utf-8'),
|
|
|
|
err=stderr.decode('utf-8'))
|
|
|
|
log.debug('run_command returning: {}'.format(rv))
|
|
|
|
return rv
|
2015-09-03 16:48:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
def sys_dev_path(devname, path=""):
|
|
|
|
return SYS_CLASS_NET + devname + "/" + path
|
|
|
|
|
|
|
|
|
|
|
|
def read_sys_net(devname, path, translate=None, enoent=None, keyerror=None):
|
|
|
|
try:
|
|
|
|
contents = ""
|
|
|
|
with open(sys_dev_path(devname, path), "r") as fp:
|
|
|
|
contents = fp.read().strip()
|
|
|
|
if translate is None:
|
|
|
|
return contents
|
|
|
|
|
|
|
|
try:
|
|
|
|
return translate.get(contents)
|
|
|
|
except KeyError:
|
2015-09-03 18:35:07 +00:00
|
|
|
log.debug("found unexpected value '%s' in '%s/%s'", contents,
|
2015-09-03 16:48:40 +00:00
|
|
|
devname, path)
|
|
|
|
if keyerror is not None:
|
|
|
|
return keyerror
|
|
|
|
raise
|
|
|
|
except OSError as e:
|
|
|
|
if e.errno == errno.ENOENT and enoent is not None:
|
|
|
|
return enoent
|
|
|
|
raise
|
2015-09-21 21:40:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
## FIXME: replace with passlib and update package deps
|
|
|
|
def crypt_password(passwd, algo='SHA-512'):
|
|
|
|
# encryption algo - id pairs for crypt()
|
|
|
|
algos = {'SHA-512': '$6$', 'SHA-256': '$5$', 'MD5': '$1$', 'DES': ''}
|
|
|
|
if algo not in algos:
|
|
|
|
raise Exception('Invalid algo({}), must be one of: {}. '.format(
|
|
|
|
algo, ','.join(algos.keys())))
|
|
|
|
|
|
|
|
salt_set = ('abcdefghijklmnopqrstuvwxyz'
|
|
|
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
|
|
'0123456789./')
|
|
|
|
salt = 16 * ' '
|
|
|
|
salt = ''.join([random.choice(salt_set) for c in salt])
|
|
|
|
return crypt.crypt(passwd, algos[algo] + salt)
|
2015-09-28 14:45:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
def is_root():
|
|
|
|
""" Returns root or if sudo user exists
|
|
|
|
"""
|
|
|
|
sudo_user = os.getenv('SUDO_USER', None)
|
2015-09-29 21:15:14 +00:00
|
|
|
euid = os.geteuid()
|
2015-09-28 14:45:54 +00:00
|
|
|
|
2015-09-29 21:15:14 +00:00
|
|
|
log.debug('is_root: euid={} sudo_user={}'.format(
|
|
|
|
euid, sudo_user))
|
|
|
|
if euid != 0 or sudo_user is not None:
|
2015-09-28 14:45:54 +00:00
|
|
|
return False
|
|
|
|
return True
|