Commit 94fe5795 authored by Andrii Salnikov's avatar Andrii Salnikov

LSC fetching done

OS package management to deploy IGTF CA (start of implementation)
parent b152e3eb
......@@ -48,7 +48,6 @@ class JobsControl(ComponentControl):
@staticmethod
def __run_gmjobs(args, stderr=False):
__GMJOBS = [ARC_LIBEXEC_DIR + '/gm-jobs']
__GMJOBS = ['ssh', 'arc6.grid.org.ua'] + __GMJOBS
loglevel = logging.getLogger('ARCCTL').getEffectiveLevel()
__GMJOBS += ['-x', {50: 'FATAL', 40: 'ERROR', 30: 'WARNING', 20: 'INFO', 10: 'DEBUG'}[loglevel]]
if stderr:
......@@ -374,14 +373,14 @@ class JobsControl(ComponentControl):
jobs_attr.add_argument('attr', help='Attribute name', nargs='?')
jobs_kill = jobs_actions.add_parser('kill', help='Cancel job')
jobs_kill.add_argument('jobid', nargs='*', help='Job ID').completer = complete_job_id
jobs_kill.add_argument('jobid', nargs='+', help='Job ID').completer = complete_job_id
jobs_killall = jobs_actions.add_parser('killall', help='Cancel all jobs')
jobs_killall.add_argument('-s', '--state', help='Filter jobs by state', action='append', choices=__JOB_STATES)
jobs_killall.add_argument('-o', '--owner', help='Filter jobs by owner').completer = complete_job_owner
jobs_clean = jobs_actions.add_parser('clean', help='Clean job')
jobs_clean.add_argument('jobid', nargs='*', help='Job ID').completer = complete_job_id
jobs_clean.add_argument('jobid', nargs='+', help='Job ID').completer = complete_job_id
jobs_cleanall = jobs_actions.add_parser('cleanall', help='Clean all jobs')
jobs_cleanall.add_argument('-s', '--state', help='Filter jobs by state', action='append', choices=__JOB_STATES)
......
import subprocess
import logging
import sys
class OSPackageManagement(object):
"""This class aimed to handle both yum (RedHat) and apt (Debian) cases"""
def __init__(self):
self.command_base = [] # placeholder to include command's prefix, like ssh to host
self.logger = logging.getLogger('ARCCTL.OSPackageManagement')
# detect yum (will also works with dnf wrapper)
try:
yum_output = subprocess.Popen(self.command_base + ['yum', '--version'],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout = yum_output.communicate()
if yum_output.returncode == 0:
self.pm_version = stdout[0].split('\n')[0]
self.pm = 'yum'
return
except OSError:
pass
# detect apt
try:
apt_output = subprocess.Popen(self.command_base + ['apt-get', '--version'],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout = apt_output.communicate()
if apt_output.returncode == 0:
self.pm_version = stdout[0].split('\n')[0].replace('apt ', '')
self.pm = 'apt'
return
except OSError:
pass
self.logger.error('Cannot find both yum and apt-get to manage OS packages.')
sys.exit(1)
def version(self):
print '{} version {}'.format(self.pm, self.pm_version)
......@@ -455,11 +455,11 @@ class RTEControl(ComponentControl):
rte_actions = rte_ctl.add_subparsers(title='RunTime Environments Actions', dest='action',
metavar='ACTION', help='DESCRIPTION')
rte_enable = rte_actions.add_parser('enable', help='Enable RTE to be used by A-REX')
rte_enable.add_argument('rte', nargs='*', help='RTE name').completer = complete_rte_name
rte_enable.add_argument('rte', nargs='+', help='RTE name').completer = complete_rte_name
rte_enable.add_argument('-f', '--force', help='Force RTE enabling', action='store_true')
rte_disable = rte_actions.add_parser('disable', help='Disable RTE to be used by A-REX')
rte_disable.add_argument('rte', nargs='*', help='RTE name').completer = complete_rte_name
rte_disable.add_argument('rte', nargs='+', help='RTE name').completer = complete_rte_name
rte_list = rte_actions.add_parser('list', help='List RunTime Environments')
rte_list.add_argument('-l', '--long', help='Detailed listing of RTEs', action='store_true')
......@@ -471,11 +471,11 @@ class RTEControl(ComponentControl):
rte_list_types.add_argument('-u', '--user', help='List available user-defined RTEs', action='store_true')
rte_default = rte_actions.add_parser('default', help='Transparently use RTE for every A-REX job')
rte_default.add_argument('rte', nargs='*', help='RTE name').completer = complete_rte_name
rte_default.add_argument('rte', nargs='+', help='RTE name').completer = complete_rte_name
rte_default.add_argument('-f', '--force', help='Force RTE enabling', action='store_true')
rte_undefault = rte_actions.add_parser('undefault', help='Remove RTE from transparent A-REX usage')
rte_undefault.add_argument('rte', nargs='*', help='RTE name').completer = complete_rte_name
rte_undefault.add_argument('rte', nargs='+', help='RTE name').completer = complete_rte_name
rte_cat = rte_actions.add_parser('cat', help='Print the content of RTE file')
rte_cat.add_argument('rte', help='RTE name').completer = complete_rte_name
......
......@@ -6,6 +6,7 @@ import ssl
import re
import subprocess
import xml.etree.ElementTree as ET
from OSPackage import OSPackageManagement
class ThirdPartyControl(ComponentControl):
......@@ -53,10 +54,36 @@ class ThirdPartyControl(ComponentControl):
(hostname, port) = self.__get_socket_from_url(url)
# try to connect using openssl
try:
subprocess.call(['openssl', 's_client', '-connect'] + ['{}:{}'.format(hostname, port)])
# TODO: parse output for DN
s_client = subprocess.Popen(['openssl', 's_client', '-connect'] + ['{}:{}'.format(hostname, port)],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in iter(s_client.stdout.readline, ''):
if line.startswith('subject='):
dn = line.replace('subject=', '')
if line.startswith('issuer='):
ca = line.replace('issuer=', '')
break
if dn and ca:
return {hostname: {'dn': dn.rstrip(), 'ca': ca.rstrip()}}
self.logger.error('Failed to get DN and CA with OpenSSL SSL/TLS bind to %s:%s.', hostname, port)
sys.exit(1)
except OSError:
self.logger.error('Failed to find "openssl" command. OpenSSL installation is required to use this feature.')
sys.exit(1)
@staticmethod
def __x500_to_DN(attr):
x500_map = {
'domainComponent': 'DC',
'countryName': 'C',
'stateOrProvinceName': 'ST',
'locality': 'L',
'organizationName': 'O',
'organizationalUnitName': 'OU',
'commonName': 'CN'
}
if attr in x500_map:
return x500_map[attr]
return attr
def __get_ssl_cert(self, url):
(hostname, port) = self.__get_socket_from_url(url)
......@@ -84,18 +111,43 @@ class ThirdPartyControl(ComponentControl):
except:
self.logger.error('Unexpected error: %s', sys.exc_info()[0])
sys.exit(1)
# TODO: convert cert dict to DN
if 'subject' not in cert or 'issuer' not in cert:
self.logger.error('Failed to get DN and CA with OpenSSL SSL/TLS bind to %s:%s.', hostname, port)
sys.exit(1)
# convert X500 output to DN
dn = ''
ca = ''
for (k, v), in cert['subject']:
dn += '/{}={}'.format(self.__x500_to_DN(k), v)
for (k, v), in cert['issuer']:
ca += '/{}={}'.format(self.__x500_to_DN(k), v)
return {hostname: {'dn': dn, 'ca': ca}}
def lsc_deploy(self, args):
voms_creds = {}
# fetch VOMS server DN and CA with some of the methods
if args.egi_vo:
print self.__egi_get_voms(args.vo)
voms_creds.update(self.__egi_get_voms(args.vo))
elif args.voms:
if args.openssl:
for vomsurl in args.voms:
self.__get_ssl_cert_openssl(vomsurl)
voms_creds.update(self.__get_ssl_cert_openssl(vomsurl))
else:
for vomsurl in args.voms:
self.__get_ssl_cert(vomsurl)
voms_creds.update(self.__get_ssl_cert(vomsurl))
if not voms_creds:
self.logger.error('There are no VOMS server credentials found. Will not create LSC file.')
sys.exit(1)
# create vomsdir for LSC
vomses_dir = '/etc/grid-security/vomsdir/{}'.format(args.vo)
if not os.path.exists(vomses_dir):
self.logger.debug('Making vomses directory %s to hold LSC file(s)', vomses_dir)
os.makedirs(vomses_dir, mode=0755)
for host, creds in voms_creds.iteritems():
lsc_file = '{}/{}.lsc'.format(vomses_dir, host)
with open(lsc_file, 'w') as lsc_f:
self.logger.info('Creating LSC file: %s', lsc_file)
lsc_f.write('{dn}\n{ca}'.format(**creds))
def enable_cacerts_repo(self, repotype):
# Detect apt vs yum
......@@ -104,9 +156,15 @@ class ThirdPartyControl(ComponentControl):
# TODO: Nordugrid-Repo (suggest to follow URL, thus there is to much versioning)
pass
def igtf_deploy(self, bundle):
pm = OSPackageManagement()
pm.version()
def control(self, args):
if args.action == 'voms-lsc':
self.lsc_deploy(args)
elif args.action == 'igtf-ca':
self.igtf_deploy(args.bundle)
else:
self.logger.critical('Unsupported third party deployment action %s', args.action)
sys.exit(1)
......@@ -118,13 +176,18 @@ class ThirdPartyControl(ComponentControl):
deploy_actions = deploy_ctl.add_subparsers(title='Deployment Actions', dest='action',
metavar='ACTION', help='DESCRIPTION')
deploy_voms_lsc = deploy_actions.add_parser('voms-lsc', help='Deploy VOMS list-of-certificates files')
deploy_voms_lsc.add_argument('vo', help='VO Name')
deploy_voms_sources = deploy_voms_lsc.add_mutually_exclusive_group(required=True)
deploy_voms_sources.add_argument_group('voms-admin', 'voms-admin certificate query')
deploy_voms_sources.add_argument('-v', '--voms', nargs='*', help='VOMS-Admin URL')
deploy_voms_sources.add_argument('-v', '--voms', help='VOMS-Admin URL', action='append')
deploy_voms_sources.add_argument('-e', '--egi-vo', help='Fecth information from EGI VOs database',
action='store_true')
deploy_voms_lsc.add_argument('-o', '--openssl', action='store_true',
help='Use external OpenSSL command instead of native python SSL')
igtf_ca = deploy_actions.add_parser('igtf-ca', help='Deploy IGTF CA certificates')
igtf_ca.add_argument('bundle', help='IGTF CA bundle name', nargs='+',
choices=['classic', 'iota', 'mics', 'slcs'])
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment