mirror of
https://abf.rosa.ru/djam/abf-console-client-src.git
synced 2025-02-23 18:02:50 +00:00
Initial commit (version 1.0)
This commit is contained in:
commit
960ab0feea
10 changed files with 670 additions and 0 deletions
35
Makefile
Normal file
35
Makefile
Normal file
|
@ -0,0 +1,35 @@
|
|||
#############################################################################
|
||||
# File : Makefile
|
||||
# Package : abf_console_client
|
||||
# Author : Anton Kirilenko <anton.kirilenko@rosalab.ru>
|
||||
#############################################################################
|
||||
|
||||
|
||||
PYTHON=python
|
||||
PYVER := $(shell $(PYTHON) -c 'import sys; print "%.3s" %(sys.version)')
|
||||
PYSYSDIR := $(shell $(PYTHON) -c 'import sys; print sys.prefix')
|
||||
PYLIBDIR = $(PYSYSDIR)/lib/python$(PYVER)
|
||||
PKGDIR = $(PYLIBDIR)/site-packages
|
||||
|
||||
BINDIR=/usr/bin
|
||||
ETCDIR=/etc
|
||||
MANDIR=/usr/share/man
|
||||
|
||||
|
||||
FILES = abf/console/*.py
|
||||
|
||||
all:
|
||||
@echo "Nothing to do. Run 'make install' or 'make clean'"
|
||||
|
||||
clean:
|
||||
rm -f *~ *.pyc *.pyo
|
||||
|
||||
|
||||
|
||||
install:
|
||||
mkdir -p $(DESTDIR)$(PKGDIR) $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
|
||||
cp -p --parents $(FILES) $(DESTDIR)$(PKGDIR)
|
||||
cp -p "abf.py" $(DESTDIR)$(BINDIR)"/abf"
|
||||
|
||||
|
||||
|
29
abf-console-client.spec
Normal file
29
abf-console-client.spec
Normal file
|
@ -0,0 +1,29 @@
|
|||
Name: abf-console-client
|
||||
Version: 1.0
|
||||
Release: 0
|
||||
Summary: Python API to ABF (https://abf.rosalinux.ru)
|
||||
Group: System/Configuration/Packaging
|
||||
License: GPLv2
|
||||
URL: http://wiki.rosalab.ru/en/index.php/ABF_Console_Client
|
||||
Source0: %{name}-%{version}.tar.gz
|
||||
BuildArch: noarch
|
||||
|
||||
Requires: python-abf
|
||||
|
||||
%description
|
||||
Console client for ABF (https://abf.rosalinux.ru).
|
||||
|
||||
|
||||
%prep
|
||||
%setup -q -n %{name}
|
||||
|
||||
%install
|
||||
rm -rf %{buildroot}
|
||||
make install DESTDIR=$RPM_BUILD_ROOT
|
||||
|
||||
|
||||
%files
|
||||
%defattr(-,root,root,-)
|
||||
%dir %{py_puresitedir}/abf/console
|
||||
%{py_puresitedir}/abf/console/*.py*
|
||||
%{_bindir}/abf
|
279
abf.py
Executable file
279
abf.py
Executable file
|
@ -0,0 +1,279 @@
|
|||
#!/usr/bin/python -tt
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
import os
|
||||
|
||||
|
||||
from abf.console.config import Config
|
||||
from abf.console.log import Log
|
||||
cfg = Config()
|
||||
log = Log('abf')
|
||||
|
||||
|
||||
from abf.console.misc import *
|
||||
|
||||
from abf.model import Models
|
||||
|
||||
|
||||
domain = cfg['main']['domain']
|
||||
login = cfg['user']['login']
|
||||
password = cfg['user']['password']
|
||||
|
||||
#models = Models(domain, login, password)
|
||||
#res = models.platforms['15']
|
||||
#res = models.platforms['17']
|
||||
#res = models.repositories[('18', '24')]
|
||||
#print res
|
||||
#exit()
|
||||
|
||||
|
||||
def parse_command_line():
|
||||
global command_line
|
||||
parser = argparse.ArgumentParser(description='ABF Console CLient')
|
||||
parser.add_argument('-v', '--verbose', action='store_true', help='be verbose')
|
||||
subparsers = parser.add_subparsers(help='sub-command help')
|
||||
|
||||
# get
|
||||
parser_get = subparsers.add_parser('get', help='get help')
|
||||
parser_get.add_argument('project', action='store', help='project name. Format: "project_name" or "group/project_name"')
|
||||
parser_get.add_argument('-b', '--branch', action='store', help='branch to checkout')
|
||||
parser_get.set_defaults(func=get)
|
||||
|
||||
# put
|
||||
parser_get = subparsers.add_parser('put', help='commit changes (with -am "message") and push')
|
||||
parser_get.add_argument('message', action='store', help='A message to commit with')
|
||||
parser_get.set_defaults(func=put)
|
||||
|
||||
# build
|
||||
parser_build = subparsers.add_parser('build', help='build help')
|
||||
parser_build.add_argument('project', action='store', nargs='?', help='project name')
|
||||
parser_build.add_argument('-b', '--branch', action='store', help='Branch to build')
|
||||
parser_build.add_argument('-t', '--tag', action='store', help='Tag to build')
|
||||
parser_build.add_argument('-p', '--target-platform', action='store', help='Platform to build into')
|
||||
parser_build.add_argument('-a', '--arches', action='append', nargs='+', help='Architectures to build, '
|
||||
'can be set more than onece. If not set - use all the available architectures')
|
||||
parser_build.add_argument('-r', '--repository', action='append', nargs='+', help='Repositories to biuld with (platform/repository)')
|
||||
parser_build.set_defaults(func=build)
|
||||
|
||||
# buildstatus
|
||||
parser_build = subparsers.add_parser('buildstatus', help='Get a building task status')
|
||||
parser_build.add_argument('ID', action='store', nargs='?', help='Build list ID')
|
||||
parser_build.add_argument('-l', '--logs', action='store_true', help='Also download logs')
|
||||
parser_build.set_defaults(func=buildstatus)
|
||||
|
||||
command_line = parser.parse_args(sys.argv[1:])
|
||||
|
||||
def get():
|
||||
log.debug('GET started')
|
||||
proj = command_line.project
|
||||
tmp = proj.split('/')
|
||||
if len(tmp) > 2:
|
||||
log.error('Specify a project name as "group_name/project_name" or just "project_name"')
|
||||
exit(1)
|
||||
if len(tmp) == 1:
|
||||
proj = '%s/%s' % (cfg['user']['default_group'], proj)
|
||||
|
||||
uri = "%s/%s.git" % (cfg['user']['git_uri'], proj)
|
||||
cmd = ['git', 'clone', uri]
|
||||
if command_line.branch:
|
||||
cmd += ['-b', command_line.branch]
|
||||
#log.debug('Executing command ' + str(cmd))
|
||||
execute_command(cmd, log, print_to_stdout=True, exit_on_error=True)
|
||||
|
||||
#os.execlp(*cmd)
|
||||
|
||||
def put():
|
||||
log.debug('PUT started')
|
||||
cmd = ['git', 'commit', '-a', '-m', command_line.message]
|
||||
execute_command(cmd, log, print_to_stdout=True, exit_on_error=True)
|
||||
|
||||
log.info('Commited.')
|
||||
cmd = ['git', 'push']
|
||||
execute_command(cmd, log, print_to_stdout=True, exit_on_error=True)
|
||||
log.info('Pushed')
|
||||
|
||||
def build():
|
||||
log.debug('BUILD started')
|
||||
IDs = {
|
||||
'arches':[],
|
||||
'version':None,
|
||||
'target_platform':None,
|
||||
'repositories':[],
|
||||
'platforms': [],
|
||||
}
|
||||
|
||||
|
||||
if command_line.project:
|
||||
tmp = command_line.project.split('/')
|
||||
if len(tmp) != 2:
|
||||
log.error('The project format is "owner_name/project_name"')
|
||||
exit(1)
|
||||
owner_name = tmp[0]
|
||||
project_name = tmp[1]
|
||||
else:
|
||||
owner_name, project_name = get_project_name()
|
||||
if not project_name:
|
||||
log.error('You are not in git repository. Specify the project name!')
|
||||
exit(1)
|
||||
|
||||
if command_line.branch and command_line.tag:
|
||||
log.error('Branch and tag were specified simultaneously!')
|
||||
exit(1)
|
||||
|
||||
log.debug('Project name: %s/%s' % (owner_name, project_name))
|
||||
|
||||
models = Models(domain, login, password)
|
||||
nbp = models.newbuildpages[(owner_name, project_name)]
|
||||
if command_line.arches:
|
||||
arches = []
|
||||
for item in command_line.arches:
|
||||
arches += item
|
||||
arches = list(set(arches))
|
||||
|
||||
for arch in arches:
|
||||
if arch not in nbp.arches.keys():
|
||||
log.error("Error: can not build for arch %s" % arch)
|
||||
exit(1)
|
||||
else:
|
||||
arches = nbp.arches.keys()
|
||||
|
||||
for arch in arches:
|
||||
IDs['arches'].append(nbp.arches[arch]['value'])
|
||||
log.debug('Architectures: %s' % arches)
|
||||
|
||||
# try to resolve a version from branch or tag
|
||||
version = None
|
||||
if command_line.branch:
|
||||
if command_line.branch in nbp.versions['branches']:
|
||||
version = command_line.branch
|
||||
if 'latest_' + command_line.branch in nbp.versions['branches']:
|
||||
version = 'latest_' + command_line.branch
|
||||
if command_line.tag:
|
||||
if command_line.tag in nbp.versions['tags']:
|
||||
version = command_line.tag
|
||||
if (command_line.branch or command_line.tag) and not version:
|
||||
if command_line.branch:
|
||||
log.error('Selected branch (%s) is not correct' % command_line.branch)
|
||||
if command_line.tag:
|
||||
log.error('Selected tag (%s) is not correct' % command_line.tag)
|
||||
exit(1)
|
||||
|
||||
# try to resolve a version fromplatform
|
||||
if (not command_line.branch and not command_line.tag):
|
||||
if not command_line.target_platform:
|
||||
log.error('You have to specify either platform or version (tag or branch), or both of them')
|
||||
exit(1)
|
||||
else:
|
||||
plat = command_line.target_platform.split('/')[0]
|
||||
if ('latest_' + plat) in nbp.versions['branches']:
|
||||
version = 'latest_' + plat
|
||||
else:
|
||||
log.error("Could not resolve version from platform name")
|
||||
exit(1)
|
||||
|
||||
log.debug('Version: %s' % version)
|
||||
|
||||
# If version is selected via command line and correct,
|
||||
# but platform is not selected
|
||||
platform = None
|
||||
if version and not command_line.target_platform:
|
||||
tmp = version
|
||||
if tmp.startswith('latest_'):
|
||||
tmp = tmp[7:]
|
||||
if tmp + '/main' in nbp.target_platforms:
|
||||
platform = tmp + '/main'
|
||||
|
||||
if command_line.target_platform:
|
||||
tmp = command_line.target_platform.split('/')
|
||||
if len(tmp) == 1:
|
||||
tmp.append('main')
|
||||
elif len(tmp) > 2:
|
||||
log.error("Target platform format is 'platform_name' or 'platform_name/project_name'")
|
||||
exit(1)
|
||||
p = tmp[0] + '/' + tmp[1]
|
||||
if p in nbp.target_platforms:
|
||||
platform = p
|
||||
else:
|
||||
log.error("Target platform specified (%s) is not correct!" % (command_line.target_platform))
|
||||
exit(1)
|
||||
|
||||
log.debug('Platform: %s' % platform)
|
||||
|
||||
# try to resolve platform
|
||||
if version and not platform:
|
||||
log.error('Could not resolve platform. Please, specify it.')
|
||||
exit(1)
|
||||
|
||||
if not version and platform:
|
||||
log.error('Could not resolve version (branch or tag). Please, specify it.')
|
||||
exit(1)
|
||||
|
||||
#resolve target platform
|
||||
IDs['target_platform'] = nbp.target_platforms[platform]['value']
|
||||
|
||||
if version in nbp.versions['branches']:
|
||||
IDs['version'] = nbp.versions['branches'][version]['value']
|
||||
if version in nbp.versions['tags']:
|
||||
IDs['version'] = nbp.versions['tags'][version]['value']
|
||||
|
||||
repos = []
|
||||
|
||||
# try to resolve a repository
|
||||
plat = platform.split('/')[0]
|
||||
repo = platform.split('/')[1]
|
||||
if plat in nbp.platforms and repo in nbp.platforms[plat]['repositories']:
|
||||
repos = [(plat, repo)]
|
||||
|
||||
if command_line.repository:
|
||||
repos = []
|
||||
tmp = []
|
||||
for item in command_line.repository:
|
||||
tmp += item
|
||||
for repo in tmp:
|
||||
if len(repo.split('/')) != 2:
|
||||
log.error('Repository format: platform/repository. "%s" is not correct' % repo)
|
||||
exit(2)
|
||||
p = repo.split('/')[0]
|
||||
r = repo.split('/')[1]
|
||||
repos.append((p, r))
|
||||
if not p in nbp.platforms:
|
||||
log.error('Platform specified (%s) is not correct!' % p)
|
||||
exit(2)
|
||||
if not r in nbp.platforms[p]['repositories']:
|
||||
log.error('Repository specified (%s/%s) is not correct!' % (p, r) )
|
||||
exit(2)
|
||||
if not repos:
|
||||
log.error('Repository to build with could not be resolved. Please, specify it.')
|
||||
exit(2)
|
||||
|
||||
for plat, repo in repos:
|
||||
IDs['repositories'].append(nbp.platforms[plat]['repositories'][repo]['value'])
|
||||
IDs['platforms'].append(nbp.platforms[plat]['value'])
|
||||
IDs['platforms'] = list(set(IDs['platforms']))
|
||||
|
||||
nbp.build(IDs)
|
||||
|
||||
def buildstatus():
|
||||
log.debug('BUILDSTATUS started')
|
||||
if not command_line.ID:
|
||||
log.error("Enter the ID, please. It can not be resolved automatically now (not implemented).")
|
||||
exit(1)
|
||||
models = Models(domain, login, password)
|
||||
bl = models.buildlists[command_line.ID]
|
||||
print '%-20s%s' %('Owner:', bl.owner_name)
|
||||
print '%-20s%s' %('Status:', bl.status)
|
||||
print '%-20s%s' %('User:', bl.user_name)
|
||||
print '%-20s%s' %('Build for platform:', bl.platform)
|
||||
print '%-20s%s' %('Repository:', bl.repository)
|
||||
print '%-20s%s' %('Architecture:', bl.arch)
|
||||
print '%-20s%s' %('Notified at:', bl.notified_at)
|
||||
print '%-20s%s' %('Packages:', ', '.join(bl.packages))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parse_command_line()
|
||||
if command_line.verbose:
|
||||
Log.set_verbose()
|
||||
command_line.func()
|
0
abf/__init__.py
Normal file
0
abf/__init__.py
Normal file
1
abf/api
Symbolic link
1
abf/api
Symbolic link
|
@ -0,0 +1 @@
|
|||
/home/flid/git/abf/akirilenko/python-abf/abf/api/
|
0
abf/console/__init__.py
Normal file
0
abf/console/__init__.py
Normal file
172
abf/console/config.py
Normal file
172
abf/console/config.py
Normal file
|
@ -0,0 +1,172 @@
|
|||
import ConfigParser
|
||||
import os
|
||||
from abf.console.misc import mkdirs, ask_user
|
||||
|
||||
#####################################################
|
||||
# USAGE:
|
||||
#
|
||||
# from abf.console.config import Config
|
||||
#
|
||||
# cfg = Config()
|
||||
# cfg['aaa']['bbb'] = 'ccc'
|
||||
# print cfg['aaa']['bbb']
|
||||
# print cfg['aaa'].pop('bbb')
|
||||
# print cfg.pop('aaa')
|
||||
#####################################################
|
||||
|
||||
class Section(dict):
|
||||
def __init__(self, config, conf_path, section):
|
||||
self.section = section
|
||||
self.config = config
|
||||
self.conf_path = conf_path
|
||||
if not section in self.config.sections():
|
||||
self.config.add_section(self.section)
|
||||
self.save()
|
||||
|
||||
def save(self):
|
||||
with open(self.conf_path, 'wb') as configfile:
|
||||
self.config.write(configfile)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
'''NOTE: value is ignored'''
|
||||
if key in self and self[key] == value:
|
||||
return
|
||||
super(Section, self).__setitem__(key, value)
|
||||
self.config.set(self.section, key, value)
|
||||
self.save()
|
||||
|
||||
def __getitem__(self, key):
|
||||
if super(Section, self).__contains__(key):
|
||||
return super(Section, self).__getitem__(key)
|
||||
res = self.config.get(section, opt)
|
||||
|
||||
def pop(self, key, init=None):
|
||||
if init is not None and key not in self:
|
||||
return init
|
||||
res = super(Section, self).pop(key, init)
|
||||
|
||||
self.config.remove_option(self.section, key)
|
||||
self.save()
|
||||
return res
|
||||
|
||||
class Config(dict):
|
||||
default_url = 'https://abf.rosalinux.ru'
|
||||
default_log_path = '/var/log/abf.log'
|
||||
def __init__(self, conf_path='~/.abfcfg'):
|
||||
self.conf_path = os.path.expanduser(conf_path)
|
||||
init = False
|
||||
if not os.path.isfile(self.conf_path):
|
||||
mkdirs(os.path.dirname(self.conf_path))
|
||||
init = True
|
||||
|
||||
self.config = ConfigParser.RawConfigParser()
|
||||
self.config.read(self.conf_path)
|
||||
|
||||
sections = self.config.sections()
|
||||
for section in sections:
|
||||
self[section] = []
|
||||
opts = self.config.options(section)
|
||||
for opt in opts:
|
||||
self[section][opt] = self.config.get(section, opt)
|
||||
|
||||
if init:
|
||||
self.first_start()
|
||||
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
'''NOTE: value is ignored'''
|
||||
if super(Config, self).__contains__(key):
|
||||
return
|
||||
super(Config, self).__setitem__(key, Section(self.config, self.conf_path, key))
|
||||
|
||||
def __getitem__(self, key):
|
||||
if not super(Config, self).__contains__(key):
|
||||
self[key] = []
|
||||
return super(Config, self).__getitem__(key)
|
||||
|
||||
def pop(self, section, init=None):
|
||||
if init is not None and section not in self:
|
||||
return init
|
||||
res = super(Config, self).pop(section, init)
|
||||
self.config.remove_section(section)
|
||||
res.save()
|
||||
return res
|
||||
|
||||
def first_start(self):
|
||||
print("First start")
|
||||
#self['main'] = []
|
||||
#self['user'] = []
|
||||
|
||||
done = False
|
||||
while not done:
|
||||
domain = ask_user('ABF domain [%s]:' % Config.default_url, can_be_empty=True)
|
||||
domain = domain or Config.default_url
|
||||
if domain and domain.endswith('/'):
|
||||
domain = domain[:-1] # remove trailing '/'
|
||||
parts = domain.split('//')
|
||||
if len(parts) == 1:
|
||||
print 'No protocol part specified (http://, https://, etc.)'
|
||||
continue
|
||||
if len(parts) > 2:
|
||||
print 'Double slashe must present only once (in a ptocol part)'
|
||||
continue
|
||||
done = True
|
||||
|
||||
self['main']['domain'] = domain
|
||||
|
||||
user = ask_user('User:', can_be_empty=False)
|
||||
self['user']['login'] = user
|
||||
|
||||
password = ask_user('Password:', can_be_empty=False)
|
||||
self['user']['password'] = password
|
||||
|
||||
git_uri = "%(protocol)s//%(user)s@%(domain)s" % \
|
||||
dict(protocol=parts[0], user=user, domain=parts[1])
|
||||
|
||||
self['user']['git_uri'] = git_uri
|
||||
|
||||
res = ask_user('Default group [%s]:' % user, can_be_empty=True)
|
||||
self['user']['default_group'] = res or user
|
||||
|
||||
#configure logging
|
||||
self['formatters']['keys'] = 'verbose,simple'
|
||||
self['formatter_verbose']['format'] = '%(asctime)s %(levelname)-7s in %(filename)s:%(funcName)s:%(lineno)d: %(message)s'
|
||||
self['formatter_simple']['format'] = '%(message)s'
|
||||
|
||||
self['loggers']['keys'] = 'root,abf,beaker'
|
||||
self['logger_root']['handlers'] = 'verbose'
|
||||
self['logger_root']['propagate'] = '1'
|
||||
self['logger_root']['level'] = 'DEBUG'
|
||||
self['logger_root']['qualname'] = ''
|
||||
self['logger_abf']['handlers'] = 'main'
|
||||
self['logger_abf']['propagate'] = '0'
|
||||
self['logger_abf']['level'] = 'DEBUG'
|
||||
self['logger_abf']['qualname'] = 'abf'
|
||||
self['logger_models']['handlers'] = 'main'
|
||||
self['logger_models']['propagate'] = '0'
|
||||
self['logger_models']['level'] = 'DEBUG'
|
||||
self['logger_models']['qualname'] = 'models'
|
||||
|
||||
self['logger_beaker']['handlers'] = 'verbose'
|
||||
self['logger_beaker']['propagate'] = '1'
|
||||
self['logger_beaker']['level'] = 'ERROR'
|
||||
self['logger_beaker']['qualname'] = 'beaker'
|
||||
|
||||
self['handlers']['keys'] = 'verbose,main'
|
||||
self['handler_verbose']['level'] = 'DEBUG'
|
||||
self['handler_verbose']['class'] = 'StreamHandler'
|
||||
self['handler_verbose']['formatter'] = 'verbose'
|
||||
self['handler_verbose']['args'] = '()'
|
||||
self['handler_main']['level'] = 'WARNING'
|
||||
self['handler_main']['class'] = 'StreamHandler'
|
||||
self['handler_main']['formatter'] = 'simple'
|
||||
self['handler_main']['args'] = '()'
|
||||
|
||||
print('Initial configuration have been completed')
|
||||
exit()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
17
abf/console/log.py
Normal file
17
abf/console/log.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
import os
|
||||
import logging
|
||||
import logging.config
|
||||
|
||||
class Log:
|
||||
''' Read the configuration file and create the logging object.'''
|
||||
@staticmethod
|
||||
def set_verbose():
|
||||
logging.getLogger("abf").propagate = 1
|
||||
logging.getLogger("models").propagate = 1
|
||||
|
||||
def __init__(self, name=''):
|
||||
logging.config.fileConfig(os.path.expanduser('~/.abfcfg'))
|
||||
self.log = logging.getLogger(name)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.log, attr)
|
136
abf/console/misc.py
Normal file
136
abf/console/misc.py
Normal file
|
@ -0,0 +1,136 @@
|
|||
import os
|
||||
import sys
|
||||
import time
|
||||
import select
|
||||
import subprocess
|
||||
import fcntl
|
||||
|
||||
def mkdirs(path):
|
||||
''' the equivalent of mkdir -p path'''
|
||||
if os.path.exists(path):
|
||||
return
|
||||
path = os.path.normpath(path)
|
||||
items = path.split('/')
|
||||
p = ''
|
||||
for item in items:
|
||||
p += '/' + item
|
||||
if not os.path.isdir(p):
|
||||
os.mkdir(p)
|
||||
|
||||
def ask_user(prompt, can_be_empty=False, variants=None):
|
||||
while True:
|
||||
sys.stdout.write(prompt)
|
||||
sys.stdout.flush()
|
||||
res = sys.stdin.readline()
|
||||
res = res.strip()
|
||||
if not can_be_empty and not res:
|
||||
continue
|
||||
|
||||
if variants:
|
||||
if res in variants:
|
||||
break
|
||||
else:
|
||||
continue
|
||||
break
|
||||
|
||||
return res
|
||||
|
||||
class CommandTimeoutExpired(Exception):
|
||||
pass
|
||||
|
||||
class ReturnCodeNotZero(Exception):
|
||||
def __init__(self, message, code):
|
||||
super(ReturnCodeNotZero, self).__init__(message)
|
||||
self.code = code
|
||||
|
||||
def get_project_name():
|
||||
try:
|
||||
output = execute_command(['git', 'remote', 'show', 'origin', '-n'])
|
||||
|
||||
for line in output.split('\n'):
|
||||
if line.startswith(' Fetch URL:'):
|
||||
project_name = line.split('/')[-1][:-4]
|
||||
owner_name = line.split('/')[-2]
|
||||
return (owner_name, project_name)
|
||||
return None
|
||||
except ReturnCodeNotZero:
|
||||
return None
|
||||
|
||||
def execute_command(command, log=None, shell=False, cwd=None, timeout=0, raiseExc=True, print_to_stdout=False, exit_on_error=False):
|
||||
output = ""
|
||||
start = time.time()
|
||||
try:
|
||||
child = None
|
||||
if log:
|
||||
log.debug("Executing command: %s" % command)
|
||||
child = subprocess.Popen(
|
||||
command,
|
||||
shell=shell,
|
||||
bufsize=0, close_fds=True,
|
||||
stdin=open("/dev/null", "r"),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE
|
||||
)
|
||||
# use select() to poll for output so we dont block
|
||||
output = logOutput([child.stdout, child.stderr],
|
||||
start, timeout, print_to_stdout=print_to_stdout)
|
||||
except:
|
||||
# kill children if they arent done
|
||||
if child is not None and child.returncode is None:
|
||||
os.killpg(child.pid, 9)
|
||||
try:
|
||||
if child is not None:
|
||||
os.waitpid(child.pid, 0)
|
||||
except:
|
||||
pass
|
||||
raise
|
||||
|
||||
# wait until child is done, kill it if it passes timeout
|
||||
niceExit=1
|
||||
while child.poll() is None:
|
||||
if (time.time() - start)>timeout and timeout!=0:
|
||||
niceExit=0
|
||||
os.killpg(child.pid, 15)
|
||||
if (time.time() - start)>(timeout+1) and timeout!=0:
|
||||
niceExit=0
|
||||
os.killpg(child.pid, 9)
|
||||
if not niceExit and raiseExc:
|
||||
raise CommandTimeoutExpired("Timeout(%s) expired for command:\n # %s\n%s" % (timeout, command, output))
|
||||
|
||||
if log:
|
||||
log.debug("Child returncode was: %s" % str(child.returncode))
|
||||
if child.returncode:
|
||||
if exit_on_error:
|
||||
exit(child.returncode)
|
||||
if raiseExc:
|
||||
raise ReturnCodeNotZero("Command failed.\nReturn code: %s\nOutput: %s" % (child.returncode, output), child.returncode)
|
||||
|
||||
return output
|
||||
|
||||
def logOutput(fds, start=0, timeout=0, print_to_stdout=False):
|
||||
done = 0
|
||||
output = ''
|
||||
|
||||
# set all fds to nonblocking
|
||||
for fd in fds:
|
||||
flags = fcntl.fcntl(fd, fcntl.F_GETFL)
|
||||
if not fd.closed:
|
||||
fcntl.fcntl(fd, fcntl.F_SETFL, flags| os.O_NONBLOCK)
|
||||
|
||||
while not done:
|
||||
if (time.time() - start)>timeout and timeout!=0:
|
||||
done = 1
|
||||
break
|
||||
|
||||
i_rdy,o_rdy,e_rdy = select.select(fds,[],[],1)
|
||||
for s in i_rdy:
|
||||
# slurp as much input as is ready
|
||||
string = s.read()
|
||||
if string == '':
|
||||
done = 1
|
||||
continue
|
||||
else:
|
||||
if print_to_stdout:
|
||||
print string
|
||||
output += string
|
||||
return output
|
1
abf/model.py
Symbolic link
1
abf/model.py
Symbolic link
|
@ -0,0 +1 @@
|
|||
/home/flid/git/abf/akirilenko/python-abf/abf/model.py
|
Loading…
Add table
Reference in a new issue