diff --git a/CHANGES b/CHANGES index 59b2bc5..9b759ad 100644 --- a/CHANGES +++ b/CHANGES @@ -31,6 +31,7 @@ It also looks at the older `chapi.log.1` and `error.log.1`. (#360) * `geni-check-errors` now also ignores collector tool speaksfor. (#361) * Add iMinds w-iLab.t and Virtual Wall 1 aggregates (#367) + * Migrate management scripts from geni-portal to geni-ch (#101) * Add Kaiserslautern OpenGENI aggreate (#374) == 1.28 == diff --git a/bin/Makefile.am b/bin/Makefile.am index dbb6ad5..8dc452d 100644 --- a/bin/Makefile.am +++ b/bin/Makefile.am @@ -61,14 +61,35 @@ dist_pkgdata_DATA = \ ch-error-log-out.txt \ chapi-log-out.txt +dist_bin_SCRIPTS = \ + geni-add-member-attribute \ + geni-add-project-member \ + geni-add-trusted-tool \ + geni-assert-email \ + geni-delete-outside-cert \ + geni-disable-user \ + geni-enable-user \ + geni-ops-report \ + geni-remove-member-attribute \ + geni-remove-project-member \ + gmoc_list_contacts \ + gmoc_list_slices \ + report_genich_relations + dist_sbin_SCRIPTS = \ - geni-add-member-privilege \ geni-create-ma-crl \ + geni-init-ca \ + geni-init-services \ geni-list-member-projects \ geni-list-pending-requests \ geni-revoke-member-certificate \ geni-revoke-member-privilege +if INSTALL_GITHASH +dist_pkgsysconf_DATA = geni-ch-githash +endif + + # GPO Lab # Install in /usr/local/lib, not /usr/lib localpythondir = $(subst /usr/lib,/usr/local/lib,$(pythondir)) diff --git a/bin/archive_log_entry_tables.sh b/bin/archive_log_entry_tables.sh new file mode 100755 index 0000000..4b7212e --- /dev/null +++ b/bin/archive_log_entry_tables.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Moved expired data from logging_entry to logging_entry_old +# Moved associated data from logging_entry_attribute to logging_entry_attribute_old +# +# Usage: archive_log_entry_table.sh expired_time + +if [ $# -ne 1 ]; then + echo "Usage: archive_log_entry_tables.sh time (ex. '2014-03-10')" + exit +else + + EXPIRED_TIME=$1 + echo "BEGIN;" > /tmp/log_entry_mod.sql + echo "insert into logging_entry_old (select * from logging_entry where event_time < '$EXPIRED_TIME');" >> /tmp/log_entry_mod.sql + echo "insert into logging_entry_attribute_old select * from logging_entry_attribute where event_id in (select id from logging_entry where event_time < '$EXPIRED_TIME');" >> /tmp/log_entry_mod.sql + echo "delete from logging_entry_attribute where event_id in (select id from logging_entry where event_time < '$EXPIRED_TIME');" >> /tmp/log_entry_mod.sql + echo "delete from logging_entry where event_time < '$EXPIRED_TIME';" >> /tmp/log_entry_mod.sql + echo "COMMIT;" >> /tmp/log_entry_mod.sql + psql -U portal -h localhost portal -f /tmp/log_entry_mod.sql +fi diff --git a/bin/archive_sa_entry_tables.sh b/bin/archive_sa_entry_tables.sh new file mode 100755 index 0000000..b240e21 --- /dev/null +++ b/bin/archive_sa_entry_tables.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# Moved expired data from logging_entry to logging_entry_old +# Moved associated data from logging_entry_attribute to logging_entry_attribute_old +# +# Usage: archive_sa_entry_table.sh expired_time + +if [ $# -ne 1 ]; then + echo "Usage: archive_sa_entry_tables.sh time (ex. '2014-03-10')" + exit +else + EXPIRED_TIME=$1 + echo "BEGIN;" > /tmp/slice_tables_mod.sql + echo "insert into sa_slice_member_old (select * from sa_slice_member where slice_id in (select slice_id from sa_slice where expiration < '$EXPIRED_TIME')); " >> /tmp/slice_tables_mod.sql + echo "insert into sa_slice_old (select * from sa_slice where expiration < '$EXPIRED_TIME'); " >> /tmp/slice_tables_mod.sql + echo "delete from sa_slice_member where slice_id in (select slice_id from sa_slice where expiration < '$EXPIRED_TIME'); " >> /tmp/slice_tables_mod.sql + echo "delete from sa_slice where expiration < '$EXPIRED_TIME'; " >> /tmp/slice_tables_mod.sql + echo "COMMIT;" >> /tmp/slice_tables_mod.sql + psql -U portal -h localhost portal -f /tmp/slice_tables_mod.sql +fi diff --git a/bin/check_existence_of_accounts b/bin/check_existence_of_accounts new file mode 100755 index 0000000..c334bc3 --- /dev/null +++ b/bin/check_existence_of_accounts @@ -0,0 +1,202 @@ +#!/usr/bin/env python +# -*- mode:python -*- + +#---------------------------------------------------------------------- +# Copyright (c) 2013-2015 Raytheon BBN Technologies +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and/or hardware specification (the "Work") to +# deal in the Work without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Work, and to permit persons to whom the Work +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Work. +# +# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS +# IN THE WORK. +#---------------------------------------------------------------------- + +import csv +import optparse +import sys + +check_list_file_default = 'tutorial.csv' +accounts_list_file_default = 'accounts.csv' +output_list_file_default = 'output.csv' + +check_fieldnames = ['email', 'first_name', 'last_name'] +accounts_fieldnames = ['contact_urn','contact_email','contact_lastname','contact_givenname','owner_id'] +output_fieldnames = ['email', 'name'] + +class Person( object ): + def __init__( self, first_name=None, last_name=None, email=None ): + self.first_name = first_name + self.last_name = last_name + self.email = email + def __str__( self ): + return "%s %s (%s)" % (self.first_name, self.last_name, self.email) + +def main(): + usage = "%prog --check-file tutorial.csv \\\n" \ + "\t\t --accounts-file accounts.csv " \ + "--output-file output.csv\n\n" \ + "Arguments:\n" \ + "\t--check-file is a CSV of the accounts to check for. Each line is of "\ + "\t\t\tthe form: \n" \ + "\t\t\temail, first name, last name\n" \ + "\t--accounts-file is the output of running: \n" \ + "\t\tsudo -u www-data /usr/local/bin/geni-ops-report -u portal -P /usr/sysadmin/etc/portal_password -g > accounts.csv\n" \ + "\t--output-file contains three parts:\n" \ + "\t\t* People who HAVE accounts reported in the bulk upload format: \n"\ + "\t\t\temail, first_name last_name\n" \ + "\t\t* People who DO NOT have accounts reported in the same format \n"\ + "\t\t as --check-file\n" \ + "\t\t* People who MAY have accounts and possible matches: \n" \ + "\t\t\t-People being looked up are listed in the same format \n"\ + "\t\t\t as --check-file\n" \ + "\t\t\t-Accounts which may be a match are listed in the bulk \n"\ + "\t\t\t upload format\n" \ + "\nThe idea is that a file containing people for whom their was no account can be"\ + "\npassed back into the script and compared to an updated --accounts-file at a "\ + "\nlater time." + parser = optparse.OptionParser( usage=usage ) + parser.add_option("-c", "--check-file", + help="File containing a list of people to check if they have accounts (in CSV)", metavar="FILE", default=check_list_file_default, dest='check_list_file') + parser.add_option("-a", "--accounts-file", + help="File containing a list of accounts (in CSV)", metavar="FILE", default=accounts_list_file_default, dest='accounts_list_file') + parser.add_option("-o", "--output-file", + help="File containing a list of users from check-file which have accounts. Output in a CSV suitable for use with bulk_upload.", metavar="FILE", default=output_list_file_default, dest='output_list_file') + parser.add_option("-q", "--quiet", + help="Print less output", action='store_false', + default=True, dest='verbose') + + if sys.argv is None: + # prints to stderr + parser.print_help() + exit(-1) + (options, args) = parser.parse_args(sys.argv) + check_list_file = options.check_list_file + accounts_list_file = options.accounts_list_file + output_list_file = options.output_list_file + + # read the two input files + check_list = open(check_list_file) + check = csv.DictReader( check_list, fieldnames=check_fieldnames) + accounts_list = open(accounts_list_file) + accounts = csv.DictReader( accounts_list, fieldnames=accounts_fieldnames) + + check_set = set() # the set of people who's accounts you are looking up + for row in check: + check_set.add(Person( row['first_name'].strip().lower(), row['last_name'].strip().lower(), email=row['email'].strip().lower() )) + + + accounts_set = set() # the set of people who have accounts + accounts_by_email = {} # dict keyed by email where value a member of the above set + accounts_by_lastname = {} # dict keyed by lastname where value a member of the above set + for row in accounts: + first_name = row['contact_givenname'].strip().lower() + last_name = row['contact_lastname'].strip().lower() + email = email=row['contact_email'].strip().lower() + account_obj = Person( first_name, last_name, email=email) + accounts_set.add( account_obj ) + accounts_by_email[email] = account_obj +# accounts_by_lastname[last_name] = account_obj + if not accounts_by_lastname.has_key(last_name): + accounts_by_lastname[ last_name ] = set() + accounts_by_lastname[ last_name ].add( account_obj ) + + + output_set = set() # the set of people who we are looking up who HAVE accounts + output_options = {} # the set of people we are looking up who MAY have accounts + output_none = [] # the set of people we are looking who DO NOT have accounts (that we can find at least) + check_by_email = {} # dict keyed by email where value is a a member of check_set + for check_user in check_set: + check_by_email[check_user.email] = check_user + # People who HAVE accounts (verified by email) + if accounts_by_email.has_key(check_user.email): + account_obj = accounts_by_email[check_user.email] + print_obj = Person( check_user.first_name, check_user.last_name, account_obj.email ) + output_set.add( print_obj ) + elif accounts_by_lastname.has_key(check_user.last_name): + account_obj_list = accounts_by_lastname[check_user.last_name] + for account_obj in account_obj_list: + if account_obj.first_name == check_user.first_name: + # People who HAVE have accounts (last name and first name match) + print_obj = Person( check_user.first_name, check_user.last_name, account_obj.email ) + output_set.add( print_obj) + if output_options.has_key(check_user.email): + del output_options[ check_user.email ] + break + else: + if not output_options.has_key(check_user.email): + output_options[ check_user.email ] = set() + # People who MAY have accounts (last name matches) + output_options[ check_user.email ].add( account_obj ) + else: + # People who DO NOT have accounts (none of email, last name, first name matches) + output_none.append(check_user.email) + check_list.close() + accounts_list.close() + + # write the output to files + with open(output_list_file, 'wb') as outfile: + writer = csv.writer(outfile) + # People who HAVE accounts (verified by email OR last name and first name match) + for person in output_set: + writer.writerow( [person.email, person.first_name+" "+person.last_name] ) + # People who MAY have accounts (last name matches) + for key, output2 in output_options.items(): + person2 = check_by_email[key] + if len(output2)>0: + print >>outfile, "# No obvious entry found for..." + writer.writerow( [key, person2.first_name, person2.last_name] ) + print >>outfile, "# These are possible fits for the above person:" + for person in output2: + writer.writerow( ["#"+person.email, person.first_name+" "+person.last_name] ) + # People who DO NOT have accounts (none of email, last name, first name matches) + if len(output_none) > 0: + print >>outfile, "# These people have no account:" + for output3 in output_none: + person3 = check_by_email[output3] + writer.writerow( [person3.email, person3.first_name, person3.last_name] ) + + # print the output to the screen + if options.verbose: + print "="*80 + print " Names to check " + for person in check_set: + print person + + #print "="*80 + #print " Accounts " + #for account in accounts_set: + # print account + + print "="*80 + print " Output " + for output in output_set: + print output + print "="*80 + print " Output Options " + for key, output2 in output_options.items(): + person = check_by_email[key] + if len(output2)>0: + print "# Possible values for "+person.first_name+" "+person.last_name+" ("+key+"):" + for person2 in output2: + print person2 + print "# These people have no account:" + for output3 in output_none: + person3 = check_by_email[output3] + print person3.first_name+" "+person3.last_name+" ("+person3.email+") " + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bin/create_standard_services.sh b/bin/create_standard_services.sh new file mode 100755 index 0000000..4e49c23 --- /dev/null +++ b/bin/create_standard_services.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# To create service certs and SQL: +# bin/geni-init-services --sql services.sql /etc/geni-ch/ch-services.ini + +# To add the services to the database (see above) +# psql -U portal -h localhost -f services.sql portal + +# To add the portal: +# geni-add-trusted-tool -d portal --host localhost -u portal portal \ +# urn:publicid:IDN+ch.geni.net+authority+portal + + +echo +echo "Warning: create_standard_services.sh is obsolete." +echo +echo " Use 'geni-init-services' to create service certificates and SQL." +echo " Use 'geni-add-trusted-tool' to add the portal to the database." +echo diff --git a/bin/delete_member.sh b/bin/delete_member.sh new file mode 100755 index 0000000..6dae70b --- /dev/null +++ b/bin/delete_member.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# Delete any reference to an account of a given member by member_id +# If second argument ('data_only') is set to non-zero, +# only delete CS, PA, SA and LOGGING data but not MA data +# If second argument ('data_only') is zero, delete MA data as well +# +# Usage: delete_member.sh member_id [data_only=1] + +if [ $# -lt 1 ]; then + echo "Usage: delete_member.sh member_id [data_only=1]" + exit +else + MEMBER_ID=$1 + DATA_ONLY=1; + if [ $# -gt 1 ]; then + DATA_ONLY=$2; + fi + if [ $DATA_ONLY -eq 0 ]; then + echo "delete from ma_member_attribute where member_id = '$MEMBER_ID'" | psql -U portal -h localhost portal + echo "delete from ma_member_privilege where member_id = '$MEMBER_ID'" | psql -U portal -h localhost portal + echo "delete from ma_inside_key where member_id = '$MEMBER_ID'" | psql -U portal -h localhost portal + echo "delete from ma_ssh_key where member_id = '$MEMBER_ID'" | psql -U portal -h localhost portal + echo "delete from ma_member where member_id = '$MEMBER_ID'" | psql -U portal -h localhost portal + fi + echo "delete from pa_project_member where member_id = '$MEMBER_ID'" | psql -U portal -h localhost portal + echo "delete from sa_slice_member where member_id = '$MEMBER_ID'" | psql -U portal -h localhost portal + echo "delete from logging_entry_attribute where event_id in (select id from logging_entry where user_id = '$MEMBER_ID')" | psql -U portal -h localhost portal + echo "delete from logging_entry where user_id = '$MEMBER_ID'" | psql -U portal -h localhost portal +fi diff --git a/bin/delete_project.sh b/bin/delete_project.sh new file mode 100755 index 0000000..0e08cd9 --- /dev/null +++ b/bin/delete_project.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# Delete any reference to a project of given project_id +# in LOGGING, CS, SA, PA data +# +# Usage: delete_project.sh project_id + +if [ $# -lt 1 ]; then + echo "Usage: delete_project.sh project_id" + exit +else + PROJECT_ID=$1 + echo "delete from sa_slice_member where slice_id in (select slice_id from sa_slice where project_id = '$PROJECT_ID')" | psql -U portal -h localhost portal + echo "delete from sa_slice where project_id = '$PROJECT_ID'" | psql -U portal -h localhost portal + echo "delete from pa_project_member where project_id='$PROJECT_ID'" | psql -U portal -h localhost portal + echo "delete from pa_project where project_id = '$PROJECT_ID'" | psql -U portal -h localhost portal + echo "delete from logging_entry where id in (select event_id from logging_entry_attribute where attribute_name = 'PROJECT' and attribute_value = '$PROJECT_ID')" | psql -U portal -h localhost portal + echo "delete from logging_entry_attribute where attribute_name = 'PROJECT' and attribute_value = '$PROJECT_ID'" | psql -U portal -h localhost portal +fi diff --git a/bin/delete_slice.sh b/bin/delete_slice.sh new file mode 100755 index 0000000..5304359 --- /dev/null +++ b/bin/delete_slice.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Delete any reference to a slice of given slice_id +# in LOGGING, CS, SA data +# +# Usage: delete_slice.sh slice_id + +if [ $# -lt 1 ]; then + echo "Usage: delete_slice.sh slice_id" + exit +else + SLICE_ID=$1 + echo "delete from sa_slice_member where slice_id='$SLICE_ID'" | psql -U portal -h localhost portal + echo "delete from sa_slice where slice_id = '$SLICE_ID'" | psql -U portal -h localhost portal + echo "delete from logging_entry where id in (select event_id from logging_entry_attribute where attribute_name = 'SLICE' and attribute_value = '$SLICE_ID')" | psql -U portal -h localhost portal + echo "delete from logging_entry_attribute where attribute_name = 'SLICE' and attribute_value = '$SLICE_ID'" | psql -U portal -h localhost portal +fi + + + + diff --git a/bin/geni-add-member-attribute b/bin/geni-add-member-attribute new file mode 100755 index 0000000..fd22660 --- /dev/null +++ b/bin/geni-add-member-attribute @@ -0,0 +1,136 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +# +#---------------------------------------------------------------------- +# Copyright (c) 2013-2015 Raytheon BBN Technologies +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and/or hardware specification (the "Work") to +# deal in the Work without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Work, and to permit persons to whom the Work +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Work. +# +# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS +# IN THE WORK. +#---------------------------------------------------------------------- + +#---------------------------------------------------------------------- +# +# Add an arbitrary attribute to the given member in the ma_member_attribute table +# +# Communicates with a GENI Member Authority via its public API +# +#---------------------------------------------------------------------- + +import sys +import logging +import optparse +import psycopg2 +import psycopg2.extras +from urlparse import urlparse +import uuid +from chapiclient import chapi + +def init_logging(options): + level = logging.INFO + if options.debug: + level = logging.DEBUG + logging.basicConfig(level=level) + +def parse_args(argv): + parser = optparse.OptionParser(usage="Add given attribute with optional specific value to the given member if not already present") + parser.add_option("--debug", action="store_true", default=False, + help="enable debugging output") + parser.add_option("-k", "--keyfile", metavar="FILE", + help="Invoker's private key") + parser.add_option("-c", "--certfile", metavar="FILE", + help="Invoker's GENI certificate") + parser.add_option("-u", "--url", help="base URL (https://chSomething.something)") + parser.add_option("-m", "--member", help="member id (a UUID or username)") + parser.add_option("-a", "--attribute", help="Name of attribute to add") + parser.add_option("--value", default="true", help="Value of attribute to add if important") + options,args = parser.parse_args() + if not (options.keyfile and options.certfile and options.url and options.attribute + and options.member): + parser.print_usage() + raise Exception("Missing some required arguments") + return options,args + +def verify_url(url): + parsed = urlparse(url) + if (parsed.scheme in ('http', 'https') + and parsed.netloc + and parsed.path): + return parsed.geturl() + else: + raise Exception("Invalid url %r" % (url)) + +def load_cert(certfile): + f = open(certfile) + cert = f.read() + f.close() + # Can we do additional tests to verify that this is an x509 cert? + # Or will that be taken care of downstream, by the ch_interface? + return cert + +def load_private_key(certfile): + f = open(certfile) + key = f.read() + f.close() + # Can we do additional tests to verify that this is a private key? + # Or will that be taken care of downstream, by the ch_interface? + return key + +def main(argv=None): + if argv is None: + argv = sys.argv + try: + options,args = parse_args(argv) + init_logging(options) + url = verify_url(chapi.service_url(options.url, 'MA')) + cert = load_cert(options.certfile) + pkey = load_private_key(options.keyfile) + except Exception as e: + sys.stderr.write(str(e) + "\n") + return 1 + logger = logging.getLogger() + member_urn = chapi.find_member_urn(options.member, url, cert, pkey) + if member_urn is None: + print "ERROR: Member %s not found" % options.member + return 1 + proxy = chapi.make_proxy(url, cert, pkey) + result = proxy.add_member_attribute(member_urn, options.attribute, options.value, 'f', [], dict()) + + print "%r" % (result) + if not 'code' in result: + msg = "Invalid response from server: %r" % (result) + sys.stderr.write(msg + "\n") + return 1 + status = result['code'] + if not status == 0: + if 'output' in result: + msg = "Error: %s" % (result['output']) + else: + msg = "Error %d, no reason given" % (status) + sys.stderr.write(msg + "\n") + return status + if result['value'] is None or result['value'] != options.value: + # set + print "For member %s added attribute %s=%s" % (options.member, options.attribute, options.value) + else: + # already set + print "User %s already has attribute %s=%s" % (options.member, options.attribute, options.value) + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bin/geni-add-project-member b/bin/geni-add-project-member new file mode 100755 index 0000000..28fe0d2 --- /dev/null +++ b/bin/geni-add-project-member @@ -0,0 +1,148 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +# +#---------------------------------------------------------------------- +# Copyright (c) 2013-2015 Raytheon BBN Technologies +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and/or hardware specification (the "Work") to +# deal in the Work without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Work, and to permit persons to whom the Work +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Work. +# +# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS +# IN THE WORK. +#---------------------------------------------------------------------- + +#---------------------------------------------------------------------- +# +# Add a GENI user to a GENI project. +# +# Communicates with a GENI Project Authority via its public API to add +# a new member to a project. Status indicates success or failure. +# +#---------------------------------------------------------------------- + +import sys +import logging +import optparse +from urlparse import urlparse +import uuid +from chapiclient import chapi + +def init_logging(options): + level = logging.INFO + if options.debug: + level = logging.DEBUG + logging.basicConfig(level=level) + +def parse_args(argv): + parser = optparse.OptionParser(usage="Add given user to the given project with given role or Auditor") + parser.add_option("--debug", action="store_true", default=False, + help="enable debugging output") + parser.add_option("-k", "--keyfile", metavar="FILE", + help="Invoker's private key") + parser.add_option("-c", "--certfile", metavar="FILE", + help="Invoker's GENI certificate") + parser.add_option("-u", "--url", help="base authority URL, requires SA and MA (https://chSOMETHING)") + parser.add_option("-p", "--project", help="project id (UUID) or name") + parser.add_option("-m", "--member", help="member id (UUID) or username") + parser.add_option("--role", type="choice", + choices=["LEAD", "ADMIN", "MEMBER", "AUDITOR", + "OPERATOR"], + default="AUDITOR", + help="role within project, default auditor") + options,args = parser.parse_args() + if not (options.keyfile and options.certfile and options.url + and options.project and options.member and options.role): + parser.print_usage() + raise Exception("Missing some required arguments") + return options,args + +def verify_url(url): + parsed = urlparse(url) + if (parsed.scheme in ('http', 'https') + and parsed.netloc + #and parsed.path + ): + return parsed.geturl() + else: + raise Exception("Invalid url %r" % (url)) + +def load_cert(certfile): + f = open(certfile) + cert = f.read() + f.close() + # Can we do additional tests to verify that this is an x509 cert? + # Or will that be taken care of downstream, by the ch_interface? + return cert + +def load_private_key(certfile): + f = open(certfile) + key = f.read() + f.close() + # Can we do additional tests to verify that this is a private key? + # Or will that be taken care of downstream, by the ch_interface? + return key + +def main(argv=None): + if argv is None: + argv = sys.argv + try: + options,args = parse_args(argv) + init_logging(options) + url = verify_url(options.url) + saurl = verify_url(chapi.service_url(options.url, 'SA')) + maurl = verify_url(chapi.service_url(options.url, 'MA')) + cert = load_cert(options.certfile) + pkey = load_private_key(options.keyfile) + project_urn = chapi.find_project_urn(options.project, saurl, cert, pkey) + member_urn = chapi.find_member_urn(options.member, maurl, cert, pkey) + except Exception as e: + sys.stderr.write(str(e) + "\n") + return 1 + if project_urn is None: + sys.stderr.write("Unknown project %s\n" % options.project) + return 1 + if member_urn is None: + sys.stderr.write("Unknown member %s\n" % options.member) + return 1 + + proxy = chapi.make_proxy(saurl, cert, pkey) + result = proxy.modify_project_membership(project_urn, [], {'members_to_add': [ + {'PROJECT_MEMBER':member_urn, + 'PROJECT_ROLE':options.role + } + ]}) + + # print "%r" % (result) + if not 'code' in result: + msg = "Invalid response from server: %r" % (result) + sys.stderr.write(msg + "\n") + return 1 + status = result['code'] + if not status == 0: + if 'output' in result: + msg = "Error: %s" % (result['output']) + else: + msg = "Error %d, no reason given" % (status) + sys.stderr.write(msg + "\n") + return status + + sys.stderr.write("Added %s to %s as %s\n" % (options.member, options.project, options.role)) + return 0 + + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bin/geni-add-trusted-tool b/bin/geni-add-trusted-tool new file mode 100755 index 0000000..f119ac8 --- /dev/null +++ b/bin/geni-add-trusted-tool @@ -0,0 +1,104 @@ +#!/usr/bin/env python +# -*- Mode: python -*- + +#---------------------------------------------------------------------- +# Copyright (c) 2013-2015 Raytheon BBN Technologies +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and/or hardware specification (the "Work") to +# deal in the Work without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Work, and to permit persons to whom the Work +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Work. +# +# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS +# IN THE WORK. +#---------------------------------------------------------------------- +import sys +import logging +import optparse +import psycopg2 + +def init_logging(options): + level = logging.INFO + if options.debug: + level = logging.DEBUG + logging.basicConfig(level=level) + +def get_database_password(options): + if options.password: + return options.password + if options.password_file: + f = open(options.password_file, 'r') + password = f.readlines()[0].strip() + f.close() + return password + return None + +def parse_args(argv): + usage = "usage: %prog [options] client_name client_urn" + parser = optparse.OptionParser(usage=usage) + parser.add_option("--debug", action="store_true", default=False, + help="enable debugging output") + parser.add_option("-d", "--database", default='portal', + help="database name") + parser.add_option("--host", default='localhost', + help="database host") + parser.add_option("-u", "--user", default='portal', + help="database user") + parser.add_option("-p", "--password", + help="database password") + parser.add_option("-P", "--password-file", + help="file containing database password") + options,args = parser.parse_args() + if len(args) < 2: + parser.print_usage() + sys.exit(2) + return options,args + +def main(argv=None): + if argv is None: + argv = sys.argv + try: + options,args = parse_args(argv) + init_logging(options) + except Exception as e: + sys.stderr.write(str(e) + "\n") + return 1 + client_name = args[0] + client_urn = args[1] + connect_args = dict(database=options.database, + user=options.user, + host=options.host) + password = get_database_password(options) + if password: + connect_args['password'] = password + + # FIXME: Is this entry already in the DB? Then bail + + query = 'INSERT INTO ma_client (client_name, client_urn) VALUES (%s, %s)' + try: + conn = psycopg2.connect(**connect_args) + cur = conn.cursor() + cur.execute(query, (client_name, client_urn)) + # FIXME: Check: did this do an insert? + cur.close() + conn.commit() + except psycopg2.Error as e: + msg = "Trusted tool NOT added. Database error: %s\n" % (str(e)) + sys.stderr.write(msg) + return 1 + sys.stderr.write("Added %s as a trusted tool\n" % client_name) + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bin/geni-assert-email b/bin/geni-assert-email new file mode 100755 index 0000000..d444b71 --- /dev/null +++ b/bin/geni-assert-email @@ -0,0 +1,37 @@ +#!/bin/bash +# Assert an email address for an eppn. For use when an IdP sends us +# the eppn but no the email address for a user, and the portal sends +# us email. From the email, the eppn is in the 'Subject' field and the +# email address is in the 'From' field. +# After running this script, reply to the email, asking them to try again. +# +# Usage: assert_email.sh + +if [ $# -ne 2 ]; then + echo "Usage: assert_email.sh " + exit +else + EPPN=$1 + EMAIL=$2 + USER=`whoami` + + ASSERTER=`psql -U portal -h localhost portal -q -t -c "select distinct member_id from ma_member_attribute where value = '$USER'"` + ASSERTER=`echo $ASSERTER | tr -d ' '` + if [ -z "$ASSERTER" ]; then + echo "Could not find member_id for asserter for user $USER" + exit + fi + + echo "$USER asserting email $EMAIL for eppn $EPPN" + echo + res1=`echo "insert into km_asserted_attribute (eppn, name, value, asserter_id) select '$EPPN', 'mail', '$EMAIL', '$ASSERTER' where not exists (select 1 from km_asserted_attribute where name='mail' and eppn='$EPPN')" | psql -U portal -h localhost portal` + res=`echo $res1 | tr -d ' '` + if [ "$res" = "INSERT01" ]; then + echo "1 row inserted" + elif [ "$res" = "INSERT00" ]; then + res2=`echo "select value || ', asserted by: ' || asserter_id from km_asserted_attribute where name='mail' and eppn='$EPPN'" | psql -U portal -h localhost portal -q -t` + echo " ** EPPN $EPPN already has email:$res2" + else + echo " ** Unknown error: $res1" + fi +fi \ No newline at end of file diff --git a/bin/geni-ch-githash b/bin/geni-ch-githash new file mode 100644 index 0000000..e4e90e8 --- /dev/null +++ b/bin/geni-ch-githash @@ -0,0 +1 @@ +57b63abd05728f21bd0d84cf2542c20ef8bc8488 diff --git a/bin/geni-delete-outside-cert b/bin/geni-delete-outside-cert new file mode 100755 index 0000000..c6ccc3d --- /dev/null +++ b/bin/geni-delete-outside-cert @@ -0,0 +1,159 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +# +#---------------------------------------------------------------------- +# Copyright (c) 2013-2015 Raytheon BBN Technologies +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and/or hardware specification (the "Work") to +# deal in the Work without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Work, and to permit persons to whom the Work +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Work. +# +# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS +# IN THE WORK. +#---------------------------------------------------------------------- + +#---------------------------------------------------------------------- +# +# Delete any outside cert associated with the given member +# +# Communicates with a GENI Member Authority via its public API +# +#---------------------------------------------------------------------- + +import sys +import logging +import optparse +import psycopg2 +import psycopg2.extras +from urlparse import urlparse +import uuid +from chapiclient import chapi + +def init_logging(options): + level = logging.INFO + if options.debug: + level = logging.DEBUG + logging.basicConfig(level=level) + +def parse_args(argv): + parser = optparse.OptionParser(usage="Delete any outside cert for this member") + parser.add_option("--debug", action="store_true", default=False, + help="enable debugging output") + parser.add_option("-k", "--keyfile", metavar="FILE", + help="Invoker's private key") + parser.add_option("-c", "--certfile", metavar="FILE", + help="Invoker's GENI certificate") + parser.add_option("-l", "--url", help="base authority URL (https://chsomething)") + parser.add_option("-m", "--member", help="member id (a UUID or username)") + parser.add_option("-d", "--database", default='portal', + help="database name") + parser.add_option("--host", default='localhost', + help="database host") + parser.add_option("-u", "--user", default='portal', + help="database user") + parser.add_option("-p", "--password", + help="database password") + parser.add_option("-P", "--password-file", + help="file containing database password") + options,args = parser.parse_args() + if not (options.keyfile and options.certfile and options.url + and options.member and (options.password or options.password_file)): + parser.print_usage() + raise Exception("Missing some required arguments") + return options,args + +def verify_url(url): + parsed = urlparse(url) + if (parsed.scheme in ('http', 'https') + and parsed.netloc + and parsed.path): + return parsed.geturl() + else: + raise Exception("Invalid url %r" % (url)) + +def load_cert(certfile): + f = open(certfile) + cert = f.read() + f.close() + # Can we do additional tests to verify that this is an x509 cert? + # Or will that be taken care of downstream, by the ch_interface? + return cert + +def load_private_key(certfile): + f = open(certfile) + key = f.read() + f.close() + # Can we do additional tests to verify that this is a private key? + # Or will that be taken care of downstream, by the ch_interface? + return key + +def get_database_password(options): + if options.password: + return options.password + if options.password_file: + f = open(options.password_file, 'r') + password = f.readlines()[0].strip() + f.close() + return password + +def main(argv=None): + if argv is None: + argv = sys.argv + try: + options,args = parse_args(argv) + init_logging(options) + url = verify_url(chapi.service_url(options.url, 'MA')) + cert = load_cert(options.certfile) + pkey = load_private_key(options.keyfile) + except Exception as e: + sys.stderr.write(str(e) + "\n") + return 1 + logger = logging.getLogger() + member_id = chapi.find_member_id(options.member, url, logger, cert, pkey) + if member_id is None: + print "ERROR: Member %s not found" % options.member + return 1 + + password = get_database_password(options) + conn = psycopg2.connect(database=options.database, + user=options.user, + password=password, + host=options.host) + +# print "found member_id %s" % member_id + cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) + sql = "delete from ma_outside_cert where member_id = %s;" + data = (member_id,) + try: + cur.execute(sql, data) + print "Did: %s" % cur.query + try: + print "Result: %s" % cur.fetchone() + # FIXME: Based on fetchone can we tell if this really worked? + except: + pass + except Exception, e: + print cur.query + print "Doing %s: Got %s: %s" % (sql, e.pgcode, e.pgerror) +# sql = "delete from ma_outside_cert where member_id = '%s';" % member_id +# cur.execute(sql) + conn.commit() + cur.close() + conn.close() + + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bin/geni-disable-user b/bin/geni-disable-user new file mode 100755 index 0000000..96c6cc2 --- /dev/null +++ b/bin/geni-disable-user @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +# +#---------------------------------------------------------------------- +# Copyright (c) 2013-2015 Raytheon BBN Technologies +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and/or hardware specification (the "Work") to +# deal in the Work without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Work, and to permit persons to whom the Work +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Work. +# +# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS +# IN THE WORK. +#---------------------------------------------------------------------- + +#---------------------------------------------------------------------- +# +# Disable the given user so they can do no operations +# +# Communicates with a GENI Member Authority via its public API +# +#---------------------------------------------------------------------- + +import sys +import logging +import optparse +import psycopg2 +import psycopg2.extras +from urlparse import urlparse +import uuid +from chapiclient import chapi + +def init_logging(options): + level = logging.INFO + if options.debug: + level = logging.DEBUG + logging.basicConfig(level=level) + +def parse_args(argv): + parser = optparse.OptionParser(usage="Disable the given user") + parser.add_option("--debug", action="store_true", default=False, + help="enable debugging output") + parser.add_option("-k", "--keyfile", metavar="FILE", + help="Invoker's private key") + parser.add_option("-c", "--certfile", metavar="FILE", + help="Invoker's GENI certificate") + parser.add_option("-u", "--url", help="base authority URL (https://chSOMETHING)") + parser.add_option("-m", "--member", help="member id (a UUID or username)") + options,args = parser.parse_args() + if not (options.keyfile and options.certfile and options.url + and options.member): + parser.print_usage() + raise Exception("Missing some required arguments") + return options,args + +def verify_url(url): + parsed = urlparse(url) + if (parsed.scheme in ('http', 'https') + and parsed.netloc + and parsed.path): + return parsed.geturl() + else: + raise Exception("Invalid url %r" % (url)) + +def load_cert(certfile): + f = open(certfile) + cert = f.read() + f.close() + # Can we do additional tests to verify that this is an x509 cert? + # Or will that be taken care of downstream, by the ch_interface? + return cert + +def load_private_key(certfile): + f = open(certfile) + key = f.read() + f.close() + # Can we do additional tests to verify that this is a private key? + # Or will that be taken care of downstream, by the ch_interface? + return key + +def main(argv=None): + if argv is None: + argv = sys.argv + try: + options,args = parse_args(argv) + init_logging(options) + url = verify_url(chapi.service_url(options.url, 'MA')) + cert = load_cert(options.certfile) + pkey = load_private_key(options.keyfile) + except Exception as e: + sys.stderr.write(str(e) + "\n") + return 1 + + logger = logging.getLogger() + member_urn = chapi.find_member_urn(options.member, url, cert, pkey) + if member_urn is None: + print "ERROR: Member %s not found" % options.member + return 1 + + proxy = chapi.make_proxy(url, cert, pkey) + result = proxy.enable_user(member_urn, False, [], dict()) + + if not 'code' in result: + raise Exception("Could not disable user %s. Result: %s" % + (options.member, result)) + status = result['code'] + if not status == 0: + if result.has_key('value'): + raise Exception("Could not disable user %s. Error %d: %s" % + (options.member, result['code'], result['output'])) + else: + raise Exception("Could not disable user %s" % (options.member)) + was = result['value'] + if was: + sys.stderr.write("Disabled user %s\n" % options.member) + else: + sys.stderr.write("User %s already disabled\n" % options.member) + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bin/geni-enable-user b/bin/geni-enable-user new file mode 100755 index 0000000..723b19c --- /dev/null +++ b/bin/geni-enable-user @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +# +#---------------------------------------------------------------------- +# Copyright (c) 2013-2015 Raytheon BBN Technologies +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and/or hardware specification (the "Work") to +# deal in the Work without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Work, and to permit persons to whom the Work +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Work. +# +# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS +# IN THE WORK. +#---------------------------------------------------------------------- + +#---------------------------------------------------------------------- +# +# Re enable a disabled GENI user +# +# Communicates with a GENI Member Authority via its public API +# +#---------------------------------------------------------------------- + +import sys +import logging +import optparse +import psycopg2 +import psycopg2.extras +from urlparse import urlparse +import uuid +from chapiclient import chapi + +def init_logging(options): + level = logging.INFO + if options.debug: + level = logging.DEBUG + logging.basicConfig(level=level) + +def parse_args(argv): + parser = optparse.OptionParser(usage="Re enable the given member") + parser.add_option("--debug", action="store_true", default=False, + help="enable debugging output") + parser.add_option("-k", "--keyfile", metavar="FILE", + help="Invoker's private key") + parser.add_option("-c", "--certfile", metavar="FILE", + help="Invoker's GENI certificate") + parser.add_option("-u", "--url", help="base authority URL (https://chSOMETHING)") + parser.add_option("-m", "--member", help="member id (a UUID or username)") + options,args = parser.parse_args() + if not (options.keyfile and options.certfile and options.url + and options.member): + parser.print_usage() + raise Exception("Missing some required arguments") + return options,args + +def verify_url(url): + parsed = urlparse(url) + if (parsed.scheme in ('http', 'https') + and parsed.netloc + and parsed.path): + return parsed.geturl() + else: + raise Exception("Invalid url %r" % (url)) + +def load_cert(certfile): + f = open(certfile) + cert = f.read() + f.close() + # Can we do additional tests to verify that this is an x509 cert? + # Or will that be taken care of downstream, by the ch_interface? + return cert + +def load_private_key(certfile): + f = open(certfile) + key = f.read() + f.close() + # Can we do additional tests to verify that this is a private key? + # Or will that be taken care of downstream, by the ch_interface? + return key + +def main(argv=None): + if argv is None: + argv = sys.argv + try: + options,args = parse_args(argv) + init_logging(options) + url = verify_url(chapi.service_url(options.url, 'MA')) + cert = load_cert(options.certfile) + pkey = load_private_key(options.keyfile) + except Exception as e: + sys.stderr.write(str(e) + "\n") + return 1 + + logger = logging.getLogger() + member_urn = chapi.find_member_urn(options.member, url, cert, pkey) + if member_urn is None: + print "ERROR: Member %s not found" % options.member + return 1 + + proxy = chapi.make_proxy(url, cert, pkey) + result = proxy.enable_user(member_urn, True, [], dict()) + + if not 'code' in result: + raise Exception("Could not enable user %s. Result: %s" % + (options.member, result)) + status = result['code'] + if not status == 0: + if result.has_key('value'): + raise Exception("Could not enable user %s. Error %d: %s" % + (options.member, result['code'], result['output'])) + else: + raise Exception("Could not enable user %s" % options.member) + was = result['value'] + if was: + sys.stderr.write("User %s already enabled\n" % options.member) + else: + sys.stderr.write("Re-enabled user %s\n" % options.member) + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bin/geni-init-ca b/bin/geni-init-ca new file mode 100755 index 0000000..b380c2f --- /dev/null +++ b/bin/geni-init-ca @@ -0,0 +1,162 @@ +#!/usr/bin/env python +# -*- Mode: python -*- + +#---------------------------------------------------------------------- +# Copyright (c) 2013-2015 Raytheon BBN Technologies +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and/or hardware specification (the "Work") to +# deal in the Work without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Work, and to permit persons to whom the Work +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Work. +# +# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS +# IN THE WORK. +#---------------------------------------------------------------------- + +import optparse +import os.path +import subprocess +import sys +import tempfile +import ConfigParser + +# Constants +EXT_NAME = 'v3_geni_ca' + + +class Config(object): + pass + +def parse_ini_config(parser): + SECTION_CA = 'ca' + OPTION_CONF = 'conf' + OPTION_CERT = 'cert' + OPTION_KEY = 'key' + OPTION_AUTHORITY = 'authority' + if not parser.has_section(SECTION_CA): + msg = 'No section "%s" found. Exiting.\n' % (SECTION_CA) + sys.stderr.write(msg) + return None + config = Config() + for opt in (OPTION_CONF, OPTION_CERT, OPTION_KEY, OPTION_AUTHORITY): + if not parser.has_option(SECTION_CA, opt): + msg = 'Section "%s" is missing option "%s". Exiting.\n' + msg = msg % (SECTION_CA, opt) + sys.stderr.write(msg) + return None + setattr(config, opt, parser.get(SECTION_CA, opt)) + return config + +def load_ini_config(fname): + parser = ConfigParser.SafeConfigParser() + result = parser.read(fname) + config = parse_ini_config(parser) + return config + +def parse_args(argv): + usage = "usage: %prog [options] config.ini" + parser = optparse.OptionParser(usage=usage) + parser.add_option("--force", action="store_true", default=False, + help="Force creation of CA certificate and key") + parser.add_option("--debug", action="store_true", default=False, + help="Enable debugging information") + (options, args) = parser.parse_args(argv) + if len(args) < 1: + parser.print_help() + raise Exception() + return (options, args) + +def mk_openssl_conf(urn, config): + extension_template = """[%s] +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer:always +subjectAltName=email:copy,%s +basicConstraints = CA:true +""" + extension = extension_template % (EXT_NAME, urn) + + # Copy the conf file's contents into memory + with open(config.conf, 'r') as f: + conf_data = f.read() + # Substitute the right path in the template + # configuration file + conf_data = conf_data.replace('/usr/share/geni-ch/CA', config.conf) + + # open a temp file + (fd, ca_conf) = tempfile.mkstemp() + f = os.fdopen(fd, 'w') + # write the conf file contents + f.write(conf_data) + # write the extension contents + f.write('\n'); + f.write(extension) + f.close() + return ca_conf + + +def init_ca(config, options): + if os.path.exists(config.cert) and not options.force: + msg = "Cowardly refusing to overwrite %s.\n" % (config.cert) + sys.stderr.write(msg) + return False + if os.path.exists(config.key) and not options.force: + msg = "Cowardly refusing to overwrite %s.\n" % (config.key) + sys.stderr.write(msg) + return False + # Construct the URN + urn_template = "URI:urn:publicid:IDN+%s+authority+ca" + urn = urn_template % (config.authority) + + ca_conf = mk_openssl_conf(urn, config) + + # Use the file (via subprocess) + cmd = ['/usr/bin/openssl', 'req', '-x509', '-nodes', + '-days', '1825', + '-subj', '/CN=%s' % (config.authority), + '-newkey', 'rsa:1024', + '-keyout', config.key, + '-out', config.cert, + '-config', ca_conf, + '-extensions', EXT_NAME] + # I would prefer subprocess.check_output but it is not + # available in Python 2.6, which is the version on Ubuntu 10.04. + returncode = subprocess.call(cmd) + + if options.debug: + print "openssl conf data is in %s" % (ca_conf) + else: + os.unlink(ca_conf) + + return returncode == 0 + +def main(argv=None): + # do initial setup & process the user's call + if argv is None: + argv = sys.argv[1:] + options, args = parse_args(argv) + config_fname = args[0] + config = load_ini_config(config_fname) + if not config: + raise Exception("Invalid configuration") + if init_ca(config, options): + return 0 + return 1 + +if __name__ == "__main__": + try: + sys.exit(main()) + except Exception as e: + # print stack trace + print e + sys.exit(1) diff --git a/bin/geni-init-services b/bin/geni-init-services new file mode 100755 index 0000000..687ac73 --- /dev/null +++ b/bin/geni-init-services @@ -0,0 +1,411 @@ +#!/usr/bin/env python +# -*- Mode: python -*- + +#---------------------------------------------------------------------- +# Copyright (c) 2013-2015 Raytheon BBN Technologies +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and/or hardware specification (the "Work") to +# deal in the Work without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Work, and to permit persons to whom the Work +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Work. +# +# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS +# IN THE WORK. +#---------------------------------------------------------------------- + +import optparse +import os.path +import subprocess +import sys +import tempfile +import uuid +import ConfigParser + +# Constants +EXT_NAME = 'v3_geni_ca' + +# INI file constants +SECTION_CA = 'ca' +SECTION_SERVICES = 'services' +OPTION_CONF = 'conf' +OPTION_CERT = 'cert' +OPTION_KEY = 'key' +OPTION_AUTHORITY = 'authority' +OPTION_EMAIL = 'email' +OPTION_SERVICES = 'services' +OPTION_CA = 'ca' +OPTION_URL = 'url' +OPTION_TYPE = 'type' + + +class Service(object): + def __init__(self): + self.name = 'Unknown' + self.cert_file = None + self.key_file = None + self.ca = False + self.url = None + self.type = 0 + self.authority = None + self.email = None + self.urn = None + + def parse_from_ini(self, parser, name): + """Parse a service record from an ini file. A service record + has 5 fields: cert, key, ca, url, type. All are optional. + cert: location of the service's certificate in the file + system. + Default: None (do not create a certificate) + key: location of the service's private key in the file + system. Ignored if 'cert' is missing. + Default: value of 'cert' (put key in same file as cert) + ca: Indicates whether the service's certificate is a + certificate authority (CA). Ignored if 'cert' is + missing. + Default: False (service's certificate is not a CA) + url: The service's URL. Used when generating the SQL + record. + Default: None (service does not have a URL). + type: The integer type for this service. The types are + derived from the database code. + Default: 0 (service is not one of the known types. + """ + self.name = name + if parser.has_option(name, OPTION_CERT): + if not parser.has_option(name, OPTION_AUTHORITY): + msg = "No authority option found for service %s." % (name) + sys.stderr.write(msg) + return False + self.authority = parser.get(name, OPTION_AUTHORITY) + if not parser.has_option(name, OPTION_EMAIL): + msg = "No email option found for service %s." % (name) + sys.stderr.write(msg) + return False + self.email = parser.get(name, OPTION_EMAIL) + self.cert_file = parser.get(name, OPTION_CERT) + self.key_file = self.cert_file + if parser.has_option(name, OPTION_KEY): + self.key_file = parser.get(name, OPTION_KEY) + if parser.has_option(name, OPTION_CA): + self.ca = parser.getboolean(name, OPTION_CA) + if parser.has_option(name, OPTION_URL): + self.url = parser.get(name, OPTION_URL) + if parser.has_option(name, OPTION_TYPE): + self.type = parser.getint(name, OPTION_TYPE) + # Establish URN and UUID as well. + if self.authority: + self.urn = "URI:urn:publicid:IDN+%s+authority+%s" % (self.authority, + self.name) + self.uuid = uuid.uuid4() + return True + +class Config(object): + pass + +def parse_ca_ini_config(parser): + if not parser.has_section(SECTION_CA): + msg = 'No section "%s" found. Exiting.\n' % (SECTION_CA) + sys.stderr.write(msg) + return None + config = Config() + for opt in (OPTION_CONF, OPTION_CERT, OPTION_KEY, OPTION_AUTHORITY): + if not parser.has_option(SECTION_CA, opt): + msg = 'Section "%s" is missing option "%s". Exiting.\n' + msg = msg % (SECTION_CA, opt) + sys.stderr.write(msg) + return None + setattr(config, opt, parser.get(SECTION_CA, opt)) + return config + +def load_ca_ini_config(fname): + parser = ConfigParser.SafeConfigParser() + result = parser.read(fname) + config = parse_ca_ini_config(parser) + return config + +def parse_service_ini_config(parser, name): + if not parser.has_section(name): + msg = 'No service section "%s" found. Exiting.\n' % (name) + sys.stderr.write(msg) + return None + service = Service() + if service.parse_from_ini(parser, name): + return service + else: + return None + +def parse_svc_ini_config(parser): + if not parser.has_section(SECTION_SERVICES): + msg = 'No section "%s" found. Exiting.\n' % (SECTION_SERVICES) + sys.stderr.write(msg) + return None + if not parser.has_option(SECTION_SERVICES, OPTION_SERVICES): + msg = 'Section "%s" is missing option "%s". Exiting.\n' + msg = msg % (SECTION_SERVICES, OPTION_SERVICES) + sys.stderr.write(msg) + return None + service_names = parser.get(SECTION_SERVICES, OPTION_SERVICES) + service_names = [s.strip() for s in service_names.split(',')] + services = [] + for name in service_names: + service = parse_service_ini_config(parser, name) + if not service: + # An error has occurred. Stop. + return None + services.append(service) + config = Config() + setattr(config, 'services', services) + return config + +def load_svc_ini_config(fname): + parser = ConfigParser.SafeConfigParser() + result = parser.read(fname) + config = parse_svc_ini_config(parser) + return config + +def parse_args(argv): + usage = "usage: %prog [options] config.ini" + parser = optparse.OptionParser(usage=usage) + parser.add_option("--force", action="store_true", default=False, + help="Force creation of CA certificate and key") + parser.add_option("-s", "--sql", default=None, + help="Write SQL to FILE") + parser.add_option("--debug", action="store_true", default=False, + help="Enable debugging information") + (options, args) = parser.parse_args(argv) + if len(args) < 1: + parser.print_help() + raise Exception() + return (options, args) + +def mk_openssl_conf(urn, config): + extension_template = """[%s] +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer:always +subjectAltName=email:copy,%s +basicConstraints = CA:true +""" + extension = extension_template % (EXT_NAME, urn) + + # Copy the conf file's contents into memory + with open(config.conf, 'r') as f: + conf_data = f.read() + # Substitute the right path in the template + # configuration file + conf_data = conf_data.replace('/usr/share/geni-ch/CA', config.conf) + + # open a temp file + (fd, ca_conf) = tempfile.mkstemp() + f = os.fdopen(fd, 'w') + # write the conf file contents + f.write(conf_data) + # write the extension contents + f.write('\n'); + f.write(extension) + f.close() + return ca_conf + + +def init_ca(config, options): + if os.path.exists(config.cert) and not options.force: + msg = "Cowardly refusing to overwrite %s.\n" % (config.cert) + sys.stderr.write(msg) + return False + if os.path.exists(config.key) and not options.force: + msg = "Cowardly refusing to overwrite %s.\n" % (config.key) + sys.stderr.write(msg) + return False + # Construct the URN + urn_template = "URI:urn:publicid:IDN+%s+authority+ca" + urn = urn_template % (config.authority) + + ca_conf = mk_openssl_conf(urn, config) + + # Use the file (via subprocess) + cmd = ['/usr/bin/openssl', 'req', '-x509', '-nodes', + '-days', '1825', + '-subj', '/CN=%s' % (config.authority), + '-newkey', 'rsa:1024', + '-keyout', config.key, + '-out', config.cert, + '-config', ca_conf, + '-extensions', EXT_NAME] + # I would prefer subprocess.check_output but it is not + # available in Python 2.6, which is the version on Ubuntu 10.04. + returncode = subprocess.call(cmd) + + if options.debug: + print "openssl conf data is in %s" % (ca_conf) + else: + os.unlink(ca_conf) + + return returncode == 0 + +def create_req(req_file, key_file, subject, options): + # Use the file (via subprocess) + cmd = ['/usr/bin/openssl', 'req', '-new', '-nodes', + '-newkey', 'rsa:1024', + '-subj', subject, + '-keyout', key_file, + '-out', req_file] + # I would prefer subprocess.check_output but it is not + # available in Python 2.6, which is the version on Ubuntu 10.04. + returncode = subprocess.call(cmd) + return returncode == 0 + +def sign_service_req(req_file, service, ca_config, options): + # The authority URL for more info about the CA, I suppose. + auth_url_tmpl = 'https://%s/cainfo.html' + auth_url = auth_url_tmpl % (service.authority) + uuid_tmpl = 'URI:urn:uuid:%s' + uuid = uuid_tmpl % (service.uuid) + ext_name = 'v3_auth' + (ext_fd, ext_file) = tempfile.mkstemp() + f = os.fdopen(ext_fd, 'w') + f.write('[%s]\n' % (ext_name)) + f.write('subjectKeyIdentifier=hash\n') + f.write('authorityKeyIdentifier=keyid:always,issuer:always\n') + f.write('authorityInfoAccess=2.25.305821105408246119474742976030998643995') + f.write(';URI:%s\n' % (auth_url)) + f.write('subjectAltName=email:copy,%s,%s\n' % (service.urn, uuid)) + f.write('basicConstraints=CA:%s' % (str(bool(service.ca)).lower())) + f.close() + + ca_conf = ca_config.conf + ca_cert_file = ca_config.cert + ca_key_file = ca_config.key + cmd = ['/usr/bin/openssl', 'ca', + '-config', ca_conf, + '-policy', 'policy_anything', + '-batch', + '-notext', + '-extfile', ext_file, + '-extensions', ext_name, + '-days', '1825', + '-in', req_file, + '-out', service.cert_file, + '-cert', ca_cert_file, + '-keyfile', ca_key_file] + # I would prefer subprocess.check_output but it is not + # available in Python 2.6, which is the version on Ubuntu 10.04. + returncode = subprocess.call(cmd) + # Done with ext file + os.unlink(ext_file) + return returncode == 0 + + +def create_service_cert(service, ca_config, options): + subject_tmpl = '/O=%s/OU=authority/OU=%s/CN=%s/emailAddress=%s' + subject = subject_tmpl % (service.authority, service.name, + str(service.uuid), service.email) + (fd, req_file) = tempfile.mkstemp() + os.close(fd) + if not create_req(req_file, service.key_file, subject, options): + msg = 'Failed to create certificate request for service %s' + msg = msg % (service.name) + sys.stderr.write(msg) + return False + if options.debug: + print "Created req file %s" % (req_file) + + sign_result = sign_service_req(req_file, service, ca_config, options) + if not sign_result: + msg = 'Failed to sign certificate request for service %s' + msg = msg % (service.name) + sys.stderr.write(msg) + return False + + # Remove the req file + os.unlink(req_file) + return True + +def init_services(svc_config, ca_config, options): + for svc in svc_config.services: + if not svc.cert_file: + continue + if os.path.isfile(svc.cert_file) and not options.force: + if options.debug: + sys.stderr.write("Skipping service %s: certificate exists.\n" + % svc.name) + continue + if not create_service_cert(svc, ca_config, options): + # A message has already been printed by the lower layer(s) + # Stop here, do not continue + return False + return True + +def sql_string_or_null(s): + if s is None: + return 'NULL' + else: + return "%r" % (str(s)) + +def sql_service_insert(service): + if service.type is 0: + return '' + insert_tmpl = 'insert into service_registry' + insert_tmpl += ' (service_type, service_url, service_urn, service_cert' + insert_tmpl += ', service_name) values' + insert_tmpl += ' (%d, %s, %s, %s, %s);\n' + insert_stmt = insert_tmpl % (service.type, + # URL cannot be NULL, so use empty string + sql_string_or_null(service.url or ''), + sql_string_or_null(service.urn), + sql_string_or_null(service.cert_file), + sql_string_or_null(service.name)) + return insert_stmt + +def generate_sql(sql_filename, svc_config, ca_config, options): + setattr(ca_config, 'url', None) + setattr(ca_config, 'urn', None) + setattr(ca_config, 'type', 7) + setattr(ca_config, 'cert_file', ca_config.cert) + setattr(ca_config, 'name', 'ca') + sql_services = list(svc_config.services) + sql_services.append(ca_config) + with open(sql_filename, 'w') as f: + for svc in sql_services: + stmt = sql_service_insert(svc) + f.write(stmt) + +def main(argv=None): + # do initial setup & process the user's call + if argv is None: + argv = sys.argv[1:] + options, args = parse_args(argv) + config_fname = args[0] + ca_config = load_ca_ini_config(config_fname) + if not ca_config: + raise Exception("Invalid configuration") +# if not init_ca(ca_config, options): + # init_ca has failed. Stop and return an error. +# return 1 + svc_config = load_svc_ini_config(config_fname) + if not svc_config: + raise Exception("Invalid configuration") + if not init_services(svc_config, ca_config, options): + # init_services has failed. Stop and return an error. + return 1 + # Everything succeeded, so return success. + if options.sql: + generate_sql(options.sql, svc_config, ca_config, options) + return 0 + +if __name__ == "__main__": + try: + sys.exit(main()) + except Exception as e: + # print stack trace + print e + sys.exit(1) diff --git a/bin/geni-ops-report b/bin/geni-ops-report new file mode 100755 index 0000000..bb33943 --- /dev/null +++ b/bin/geni-ops-report @@ -0,0 +1,410 @@ +#!/usr/bin/env python +# -*- mode:python -*- + +#---------------------------------------------------------------------- +# Copyright (c) 2013-2015 Raytheon BBN Technologies +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and/or hardware specification (the "Work") to +# deal in the Work without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Work, and to permit persons to whom the Work +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Work. +# +# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS +# IN THE WORK. +#---------------------------------------------------------------------- + +import sys +import logging +import optparse +import csv +import psycopg2 +import psycopg2.extras + +class Member(object): + members = dict() + attrs = [] + + @classmethod + def find(cls, id): + if not id in cls.members: + m = cls(id) + cls.members[id] = m + return cls.members[id] + + @classmethod + def delete_member(cls, id): + del cls.members[id] + + @classmethod + def all_members(cls): + return cls.members.values() + + @classmethod + def sorted_members(cls): + return [cls.members[id] for id in sorted(cls.members.keys())] + + def __init__(self, id): + self.id = id + for attr in self.attrs: + setattr(self, attr, '') + self.project_lead = False + self.operator = False + self.last_seen = None + + def parsed_firstname(self): + """Get a usable firstname for a member, or fall back to email""" + if self.first_name: + return self.first_name + return self.email_address + + def parsed_lastname(self): + """Get a usable lastname for a member, or fall back to email""" + if self.last_name: + return self.last_name + return self.email_address + +def note_last_seen(conn): + DB_TS_FORMAT = '%Y-%m-%d %H:%M:%S.%f' + query = "select member_id, ts from last_seen" + cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) + cur.execute(query) + fetchsize = 5 + rows = cur.fetchmany(fetchsize) + while rows: + for r in rows: + mid = r['member_id'] + if mid in Member.members: + m = Member.find(r['member_id']) + m.last_seen = r['ts'] + # last_seen is a datetime.datetime. + # Remove the microsecond, we don't care + m.last_seen = m.last_seen.replace(microsecond=0) + rows = cur.fetchmany(fetchsize) + cur.close() + +def load_members(conn): + query = "select * from ma_member_attribute order by member_id" + cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) + cur.execute(query) + fetchsize = 5 + rows = cur.fetchmany(fetchsize) + while rows: + for r in rows: + m = Member.find(r['member_id']) + setattr(m, r['name'], r['value']) + rows = cur.fetchmany(fetchsize) + cur.close() + # Remove members who have been disabled from this report + disabled_ids = [mem.id for mem in Member.members.values() \ + if mem.member_enabled == 'n'] + for disabled_id in disabled_ids: Member.delete_member(disabled_id) + # Get the last seen timestamp for each member + note_last_seen(conn) + +def all_member_attrs(conn): + query = "select distinct name from ma_member_attribute" + cur = conn.cursor() + cur.execute(query) + rows = cur.fetchall() + cur.close() + return [r[0] for r in rows] + +def mark_project_leads(conn): + query = ("select member_id, value" + + " from ma_member_attribute" + + " where name = 'PROJECT_LEAD'" + + " order by member_id") + cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) + cur.execute(query) + fetchsize = 5 + rows = cur.fetchmany(fetchsize) + while rows: + for r in rows: + mid = r['member_id'] + if mid in Member.members: + m = Member.find(mid) + m.project_lead = True + rows = cur.fetchmany(fetchsize) + cur.close() + +def mark_operators(conn): + query = ("select member_id, value" + + " from ma_member_attribute" + + " where name = 'OPERATOR'" + + " order by member_id") + cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) + cur.execute(query) + fetchsize = 5 + rows = cur.fetchmany(fetchsize) + while rows: + for r in rows: + mid = r['member_id'] + if mid in Member.members: + m = Member.find(mid) + m.operator = True + rows = cur.fetchmany(fetchsize) + cur.close() + +def print_members_csv(members): + def ident(x): return x + def xform_bool(x): return 'Y' if x else '' + m_attrs = (('id', ident), ('eppn', ident), ('first_name', ident), + ('last_name', ident), ('project_lead', xform_bool), + ('operator', xform_bool), ('last_seen', ident)) + csvout = csv.writer(sys.stdout) + csvout.writerow([x for (x, y) in m_attrs]) + for m in Member.sorted_members(): + vals = [fn(getattr(m, k)) for (k, fn) in m_attrs] + csvout.writerow(vals) + +def print_members_gmoc_csv(members): + def ident(x): return x + m_attrnames = ('contact_urn', 'contact_email', 'contact_lastname', + 'contact_givenname', 'owner_id') + csvout = csv.writer(sys.stdout, lineterminator='\n') + csvout.writerow(m_attrnames) + for m in Member.sorted_members(): + urn = getattr(m, 'urn') + email = getattr(m, 'email_address') + uuid = getattr(m, 'id') + lastname = m.parsed_lastname() + firstname = m.parsed_firstname() + vals = [urn, email, lastname, firstname, uuid] + csvout.writerow(vals) + +def load_projects(conn): + query = ("select project_id, project_name, value, creation" + + " from pa_project p, ma_member_attribute m" + + " where p.lead_id = m.member_id" + + " and m.name = 'eppn'" + + " order by creation") + cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) + cur.execute(query) + fetchsize = 5 + result = [] + hdr = ["project id", "project name", "eppn", "creation"] + result.append(hdr) + rows = cur.fetchmany(fetchsize) + while rows: + for r in rows: + result.append(r) + rows = cur.fetchmany(fetchsize) + cur.close() + return result + +def load_projects_for_audit(conn): + query = ("select project_id, project_name, lead_id, creation" + + " from pa_project") + cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) + cur.execute(query) + fetchsize = 5 + result = [] + rows = cur.fetchmany(fetchsize) + while rows: + for r in rows: + result.append({'id': r[0], 'name': r[1], 'lead_id': r[2], + 'creation': r[3], }) + rows = cur.fetchmany(fetchsize) + cur.close() + return result + +def read_audit_inventory(audit_file): + f = open(audit_file, 'r') + inventory = { + 'operator': [], + 'projlead': [], + } + for line in f.readlines(): + sline = line.strip() + if not sline: continue + if sline.startswith('#'): continue + [privtype, username] = line.split() + if not inventory.has_key(privtype): + raise ValueError, "Syntax error in %s: unknown privilege type %s" \ + % (audit_file, privtype) + if username in inventory[privtype]: + raise ValueError, "Syntax error in %s: multiple entries for ID %s" \ + % (audit_file, username) + inventory[privtype].append(username) + f.close() + return inventory + +def audit_member_info(m): + info = "%s (%s): %s %s <%s>" % \ + (m.username, m.id, m.first_name, m.last_name, m.email_address) + return info + +def audit_members(inventory, projects): + operators_seen = [] + projleads_seen = [] + projlead_ids = [] + errors = { + 'unexpected_operators': [], + 'unexpected_projleads': [], + 'unexpected_project_lead_ids': [], + 'missing_operators': [], + 'missing_projleads': [], + } + + # iterate over members. look for unexpected operators and leads, + # and store all operators and leads we see. + for member in Member.sorted_members(): + info = audit_member_info(member) + username = member.username + if member.operator: + if not username in inventory['operator']: + errors['unexpected_operators'].append(info) + operators_seen.append(username) + if member.project_lead: + if not (username in inventory['operator'] or + username in inventory['projlead']): + errors['unexpected_projleads'].append(info) + projleads_seen.append(username) + projlead_ids.append(member.id) + + # iterate over projects. look for any projects whose lead ID wasn't + # seen as a project lead + for project in projects: + if not project['lead_id'] in projlead_ids: + info = ("%(name)s (%(id)s): lead_id=%(lead_id)s," + + " created=%(creation)s") % project + errors['unexpected_project_lead_ids'].append(info) + + # look for any operators or leads we expected, but didn't see + for operator in inventory['operator']: + if not operator in operators_seen: + errors['missing_operators'].append(operator) + for projlead in inventory['projlead']: + if not projlead in projleads_seen: + errors['missing_projleads'].append(projlead) + + return errors + +def print_audit_report(errors): + any_errors = False + for errtype in errors.keys(): + if len(errors[errtype]) > 0: + any_errors = True + if any_errors: + print """Mismatches found between portal state and infra's inventory. +For guidance on responding to this report, see: + http://groups.geni.net/syseng/wiki/OpsPortalUserManagement +""" + if len(errors['unexpected_project_lead_ids']) > 0: + print """ERROR: projects have lead_id who does not have the project +lead privilege! Investigate this: + %s\n""" % '\n '.join(errors['unexpected_project_lead_ids']) + + if len(errors['unexpected_operators']) > 0: + print """ERROR: unexpected users have the portal operator privilege! +Investigate this: + %s\n""" % '\n '.join(errors['unexpected_operators']) + + if len(errors['unexpected_projleads']) > 0: + print """Unknown users have the portal project creation privilege. +Double-check that these users are real people, then inventory them: + %s\n""" % '\n '.join(errors['unexpected_projleads']) + + if len(errors['missing_operators']) > 0: + print """These users are listed infra's inventory as operators, but do +not have operator privileges on the portal. Update the inventory (or +investigate if this is unexpected): + %s\n""" % '\n '.join(errors['missing_operators']) + + if len(errors['missing_projleads']) > 0: + print """These users are listed infra's inventory as project +leads, but do not have project creation privileges on the portal. +Update the inventory (or investigate if this is unexpected): + %s\n""" % '\n '.join(errors['missing_projleads']) + +def init_logging(options): + level = logging.INFO + if options.debug: + level = logging.DEBUG + logging.basicConfig(level=level) + +def get_database_password(options): + if options.password: + return options.password + if options.password_file: + f = open(options.password_file, 'r') + password = f.readlines()[0].strip() + f.close() + return password + +def parse_args(argv): + parser = optparse.OptionParser() + parser.add_option("--debug", action="store_true", default=False, + help="enable debugging output") + parser.add_option("-d", "--database", default='portal', + help="database name") + parser.add_option("-g", "--gmoc-format", action="store_true", + default=False, + help="produce output in GMOC contact reporting format") + parser.add_option("--host", default='localhost', + help="database host") + parser.add_option("-u", "--user", default='portal', + help="database user") + parser.add_option("-p", "--password", + help="database password") + parser.add_option("-P", "--password-file", + help="file containing database password") + parser.add_option("-A", "--audit-file", + help="audit users against inventory file") + options,args = parser.parse_args() + if not (options.password or options.password_file): + parser.print_help() + raise Exception("Missing some required arguments") + return options,args + +def main(argv=None): + if argv is None: + argv = sys.argv + try: + options,args = parse_args(argv) + init_logging(options) + except Exception as e: + sys.stderr.write(str(e) + "\n") + return 1 + password = get_database_password(options) + # Member dict, id -> Member + members = dict() + conn = psycopg2.connect(database=options.database, + user=options.user, + password=password, + host=options.host) + attrs = all_member_attrs(conn) + Member.attrs = attrs + load_members(conn) + mark_project_leads(conn) + mark_operators(conn) + + # Output data depending on requested action + if options.audit_file: + projects = load_projects_for_audit(conn) + inventory = read_audit_inventory(options.audit_file) + errors = audit_members(inventory, projects) + print_audit_report(errors) + elif options.gmoc_format: + print_members_gmoc_csv(Member.sorted_members) + else: + projects = load_projects(conn) + print_members_csv(Member.sorted_members) + csvout = csv.writer(sys.stdout) + csvout.writerows(projects) + + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bin/geni-remove-member-attribute b/bin/geni-remove-member-attribute new file mode 100755 index 0000000..4726381 --- /dev/null +++ b/bin/geni-remove-member-attribute @@ -0,0 +1,155 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +# +#---------------------------------------------------------------------- +# Copyright (c) 2013-2015 Raytheon BBN Technologies +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and/or hardware specification (the "Work") to +# deal in the Work without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Work, and to permit persons to whom the Work +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Work. +# +# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS +# IN THE WORK. +#---------------------------------------------------------------------- + +#---------------------------------------------------------------------- +# +# Remove an arbitrary attribute from the given member in the ma_member_attribute table +# +# Communicates with a GENI Member Authority via its public API +# +#---------------------------------------------------------------------- + +import sys +import logging +import optparse +import psycopg2 +import psycopg2.extras +from urlparse import urlparse +import uuid +from chapiclient import chapi + +def init_logging(options): + level = logging.INFO + if options.debug: + level = logging.DEBUG + logging.basicConfig(level=level) + +def parse_args(argv): + parser = optparse.OptionParser(usage="Remove given attribute with optionally specific value from given member") + parser.add_option("--debug", action="store_true", default=False, + help="enable debugging output") + parser.add_option("-k", "--keyfile", metavar="FILE", + help="Invoker's private key") + parser.add_option("-c", "--certfile", metavar="FILE", + help="Invoker's GENI certificate") + parser.add_option("-u", "--url", help="base URL (https://chSOMETHING)") + parser.add_option("-m", "--member", help="member id (a UUID or username)") + parser.add_option("-a", "--attribute", help="Name of attribute to remove") + parser.add_option("--value", help="Value of attribute to remove if important") + options,args = parser.parse_args() + if not (options.keyfile and options.certfile and options.url and options.attribute + and options.member): + parser.print_usage() + raise Exception("Missing some required arguments") + return options,args + +def verify_url(url): + parsed = urlparse(url) + if (parsed.scheme in ('http', 'https') + and parsed.netloc + and parsed.path): + return parsed.geturl() + else: + raise Exception("Invalid url %r" % (url)) + +def load_cert(certfile): + f = open(certfile) + cert = f.read() + f.close() + # Can we do additional tests to verify that this is an x509 cert? + # Or will that be taken care of downstream, by the ch_interface? + return cert + +def load_private_key(certfile): + f = open(certfile) + key = f.read() + f.close() + # Can we do additional tests to verify that this is a private key? + # Or will that be taken care of downstream, by the ch_interface? + return key + +def main(argv=None): + if argv is None: + argv = sys.argv + try: + options,args = parse_args(argv) + init_logging(options) + url = verify_url(chapi.service_url(options.url, 'MA')) + cert = load_cert(options.certfile) + pkey = load_private_key(options.keyfile) + except Exception as e: + sys.stderr.write(str(e) + "\n") + return 1 + logger = logging.getLogger() + member_urn = chapi.find_member_urn(options.member, url, cert, pkey) + if member_urn is None: + print "ERROR: Member %s not found" % options.member + return 1 + + # Forbid removing the important attributes, or at least warn + if options.attribute.strip == "": + print "ERROR: Empty attribute name" + return 1 + if options.attribute in ("member_enabled", "OPERATOR", "PROJECT_LEAD"): + print "ERROR: Use other scripts to remove that attribute" + return 1 + if options.attribute in ("username", "email_address", "eppn"): + print "WARN: You are removing a core attribute!" + raw_input("Press enter to continue or Ctrl-C to cancel ....\n") + if options.attribute in ("first_name", "last_name", "displayName"): + print "WARN: You are removing a name attribute" + raw_input("Press enter to continue or Ctrl-C to cancel ....\n") + + proxy = chapi.make_proxy(url, cert, pkey) + result = proxy.remove_member_attribute(member_urn, + options.attribute, [], dict(), options.value) + + print "%r" % (result) + if not 'code' in result: + msg = "Invalid response from server: %r" % (result) + sys.stderr.write(msg + "\n") + return 1 + status = result['code'] + if not status == 0: + if 'output' in result: + msg = "Error: %s" % (result['output']) + else: + msg = "Error %d, no reason given" % (status) + sys.stderr.write(msg + "\n") + return status + if result['value'] is None: + if options.value is None: + print "User %s did not have attribute %s" % (options.member, options.attribute) + else: + print "User %s did not have attribute %s=%s" % (options.member, options.attribute, options.value) + else: + print "Removed user %s attribute %s=%s" % (options.member, + options.attribute, result['value']) + + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bin/geni-remove-project-member b/bin/geni-remove-project-member new file mode 100755 index 0000000..2122da5 --- /dev/null +++ b/bin/geni-remove-project-member @@ -0,0 +1,148 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +# +#---------------------------------------------------------------------- +# Copyright (c) 2013-2015 Raytheon BBN Technologies +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and/or hardware specification (the "Work") to +# deal in the Work without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Work, and to permit persons to whom the Work +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Work. +# +# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS +# IN THE WORK. +#---------------------------------------------------------------------- + +#---------------------------------------------------------------------- +# +# Remove a GENI user from a GENI project. +# +# Communicates with a GENI Project Authority via its public API to +# remove a member from a project. Status indicates success or failure. +# +#---------------------------------------------------------------------- + +import sys +import logging +import optparse +from urlparse import urlparse +import uuid +from chapiclient import chapi + +def init_logging(options): + level = logging.INFO + if options.debug: + level = logging.DEBUG + logging.basicConfig(level=level) + +def check_result(result): + if not 'code' in result: + msg = "Invalid response from server: %r" % (result) + sys.stderr.write(msg + "\n") + exit(1) + status = result['code'] + if not status == 0: + if 'output' in result: + msg = "Error: %s" % (result['output']) + else: + msg = "Error %d, no reason given" % (status) + sys.stderr.write(msg + "\n") + exit(1) + return status + +def parse_args(argv): + parser = optparse.OptionParser(usage="Remove given member from given project") + parser.add_option("--debug", action="store_true", default=False, + help="enable debugging output") + parser.add_option("-k", "--keyfile", metavar="FILE", + help="Invoker's private key") + parser.add_option("-c", "--certfile", metavar="FILE", + help="Invoker's GENI certificate") + parser.add_option("-u", "--url", help="base authority URL, requires SA and MA (https://chSOMETHING)") + parser.add_option("-p", "--project", help="project id (UUID) or name") + parser.add_option("-m", "--member", help="member id (UUID) or username") + options,args = parser.parse_args() + if not (options.keyfile and options.certfile and options.url + and options.project and options.member): + parser.print_usage() + raise Exception("Missing some required arguments") + return options,args + +def verify_url(url): + parsed = urlparse(url) + if (parsed.scheme in ('http', 'https') + and parsed.netloc + #and parsed.path + ): + return parsed.geturl() + else: + raise Exception("Invalid url %r" % (url)) + +def load_cert(certfile): + f = open(certfile) + cert = f.read() + f.close() + # Can we do additional tests to verify that this is an x509 cert? + # Or will that be taken care of downstream, by the ch_interface? + return cert + +def load_private_key(certfile): + f = open(certfile) + key = f.read() + f.close() + # Can we do additional tests to verify that this is a private key? + # Or will that be taken care of downstream, by the ch_interface? + return key + +def main(argv=None): + if argv is None: + argv = sys.argv + try: + options,args = parse_args(argv) + init_logging(options) + url = verify_url(options.url) + saurl = verify_url(chapi.service_url(options.url, 'SA')) + maurl = verify_url(chapi.service_url(options.url, 'MA')) + cert = load_cert(options.certfile) + pkey = load_private_key(options.keyfile) + project_urn = chapi.find_project_urn(options.project, saurl, cert, pkey) + member_urn = chapi.find_member_urn(options.member, maurl, cert, pkey) + except Exception as e: + sys.stderr.write(str(e) + "\n") + return 1 + if project_urn is None: + sys.stderr.write("Unknown project %s\n" % options.project) + return 1 + if member_urn is None: + sys.stderr.write("Unknown member %s\n" % options.member) + return 1 + proxy = chapi.make_proxy(saurl, cert, pkey) + result = proxy.modify_project_membership(project_urn, [], {'members_to_remove': [member_urn]}) + print "%r" % (result) + if not 'code' in result: + msg = "Invalid response from server: %r" % (result) + sys.stderr.write(msg + "\n") + return 1 + status = result['code'] + if not status == 0: + if 'output' in result: + msg = "Error: %s" % (result['output']) + else: + sys.stderr.write("Can't remove lead from project unless there is an authorized administrator to take the lead role.\n"); + return(1) + sys.stderr.write("Removed %s from %s\n" % (options.member, options.project)) + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bin/geni-update-cert-expiration b/bin/geni-update-cert-expiration new file mode 100644 index 0000000..650f155 --- /dev/null +++ b/bin/geni-update-cert-expiration @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +# +#---------------------------------------------------------------------- +# Copyright (c) 2014-2015 Raytheon BBN Technologies +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and/or hardware specification (the "Work") to +# deal in the Work without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Work, and to permit persons to whom the Work +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Work. +# +# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS +# IN THE WORK. +#---------------------------------------------------------------------- + +#---------------------------------------------------------------------- +# +# Set the expiration column to the certificate expiration. +# +#---------------------------------------------------------------------- + +import datetime +import logging +import optparse +import psycopg2 +import psycopg2.extras +import sys +import time +import OpenSSL + +tables = dict(inside='ma_inside_key', outside='ma_outside_cert') + +def init_logging(options): + level = logging.INFO + if options.debug: + level = logging.DEBUG + logging.basicConfig(level=level) + +def parse_args(argv): + usage = '%s [options] --table TABLE' % (argv[0]) + parser = optparse.OptionParser(usage=usage) + parser.add_option("--debug", action="store_true", default=False, + help="enable debugging output") + parser.add_option("-d", "--database", default='portal', + help="database name") + parser.add_option("--host", default='localhost', + help="database host") + parser.add_option("-u", "--user", default='portal', + help="database user") + parser.add_option("-t", "--table", + type='choice', + choices=tables.keys(), + help='database certificate table') + parser.add_option("--delay", default=5, type=int, + help='delay between updates') + options,args = parser.parse_args() + if not options.table: + print 'Table must be one of %r' % (tables.keys()) + parser.print_usage() + raise Exception("Missing some required arguments") + return options,args + + +def main(argv=None): + if argv is None: + argv = sys.argv + try: + options,args = parse_args(argv) + init_logging(options) + except Exception as e: + sys.stderr.write(str(e) + "\n") + return 1 + logger = logging.getLogger() + if options.table in tables: + table = tables[options.table] + else: + msg = 'Unknown table %s: must be one of %r\n' % (options.table, + tables.keys()) + sys.stderr.write(msg) + return 1 + + conn = psycopg2.connect(database=options.database, + user=options.user, + host=options.host) + cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) + select_sql = ('SELECT id, certificate FROM ' + table + + ' WHERE expiration IS NULL LIMIT 1') + update_sql = 'UPDATE ' + table + ' SET expiration = %s WHERE id = %s' + try: + while True: + cur.execute(select_sql) + #logger.debug("Ran: %s" % (cur.query)) + row = cur.fetchone() + if row is None: + return 0 + row_id = row['id'] + chain = row['certificate'] + logger.debug("Processing row %d" % (row_id)) + cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, + chain) + not_after = cert.get_notAfter() + expires = datetime.datetime.strptime(not_after, '%Y%m%d%H%M%SZ') + cur.execute(update_sql, (expires, row['id'])) + logger.info("Updated row %d with expiration %s" + % (row_id, expires)) + conn.commit() + logger.debug('Sleeping for %d seconds' % (options.delay)) + time.sleep(options.delay) + except psycopg2.Error, e: + print cur.query + print "Doing %s: Got %s: %s" % (select_sql, e.pgcode, e.pgerror) + return 1 + finally: + cur.close() + conn.close() + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bin/gmoc_list_contacts b/bin/gmoc_list_contacts new file mode 100755 index 0000000..e0e50bc --- /dev/null +++ b/bin/gmoc_list_contacts @@ -0,0 +1,16 @@ +#!/bin/bash +# +# List all slices at SA +# +# Usage: list_contacts.sh + +if [ $# -ne 0 ]; then + echo "Usage: list_contacts.sh" + exit +else + if [ "$PGPASSFILE" = "" ]; then + export PGPASSFILE="/etc/geni-ch/.pgpass" + fi + echo "COPY (select distinct m1.value as contact_urn, m2.value as contact_email, m3.value as contact_lastname, m4.value as contact_givenname, m1.member_id as owner_id from ma_member_attribute m1, ma_member_attribute m2, ma_member_attribute m3, ma_member_attribute m4 where (m1.name='urn' and m2.name='email_address' and m3.name='last_name' and m4.name='first_name')and m1.member_id=m2.member_id and m1.member_id=m3.member_id and m1.member_id=m4.member_id) TO STDOUT with CSV HEADER;" | psql -U portal -h localhost portal +fi + diff --git a/bin/gmoc_list_slices b/bin/gmoc_list_slices new file mode 100755 index 0000000..2e7d351 --- /dev/null +++ b/bin/gmoc_list_slices @@ -0,0 +1,15 @@ +#!/bin/bash +# +# List all slices at SA +# +# Usage: list_slices.sh + +if [ $# -ne 0 ]; then + echo "Usage: list_slices.sh" + exit +else + if [ "$PGPASSFILE" = "" ]; then + export PGPASSFILE="/etc/geni-ch/.pgpass" + fi + echo "COPY (select slice_urn, slice_email, value as slice_creator, creation as slice_created, expiration as slice_expires, slice_id as slice_uuid from ma_member_attribute, sa_slice where (name='urn') and owner_id=member_id and expired='f') TO STDOUT with CSV HEADER;" | psql -U portal -h localhost portal +fi diff --git a/bin/init-ca b/bin/init-ca new file mode 100755 index 0000000..891ecca --- /dev/null +++ b/bin/init-ca @@ -0,0 +1,38 @@ +#!/bin/sh + +#------------------------------------------------------------ +# Initialize the Clearinghouse Certificate Authority +#------------------------------------------------------------ + +CATOP=/usr/share/geni-ch/CA +CACONF="${CATOP}"/openssl.cnf +CAKEY="${CATOP}"/private/cakey.pem +CACERT="${CATOP}"/cacert.pem +FQDN=`hostname -f` + +if [ -f "${CAKEY}" -o -f "${CACERT}" ]; then + echo "Cowardly refusing to overwrite ${CAKEY} and ${CACERT}." + exit 1 +fi + +SHORT_HOST=`/bin/hostname -s` + +URN="URI:urn:publicid:IDN+${SHORT_HOST}+authority+ca" + +EXT_FILE=`/bin/mktemp` +cp "${CACONF}" "${EXT_FILE}" +EXT_NAME='v3_genica' +cat >> "${EXT_FILE}" < " 1>&2 + exit 1 +fi + +KEYFILE=$1 +REQFILE=$2 +AUTHORITY=$3 +EMAIL=$4 + +SHORT_HOST=`/bin/hostname -s` +OPENSSL=/usr/bin/openssl +UUID=`/usr/bin/uuidgen -t` +SUBJECT="/O=${SHORT_HOST}/OU=authority/OU=${AUTHORITY}/CN=${UUID}/emailAddress=${EMAIL}" + +"${OPENSSL}" req -new -newkey rsa:1024 -nodes \ + -subj "${SUBJECT}" \ + -keyout "${KEYFILE}" \ + -out "${REQFILE}" + +if [ $? ]; then + echo "Generated ${REQFILE} and ${KEYFILE}" +else + echo "Certificate request generation failed." 1>&2 +fi diff --git a/bin/populate_am_type_attribute.sql b/bin/populate_am_type_attribute.sql new file mode 100644 index 0000000..c829b9c --- /dev/null +++ b/bin/populate_am_type_attribute.sql @@ -0,0 +1,89 @@ +delete from service_registry_attribute; +alter sequence service_registry_attribute_id_seq restart with 1; +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.geni.it.cornell.edu:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://instageni.sox.net:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_TYPE', 'ui_instageni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.sox.net:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://instageni.ku.gpeni.net:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_TYPE', 'ui_instageni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.ku.gpeni.net:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://instageni.stanford.edu:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_TYPE', 'ui_instageni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.stanford.edu:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://sl-geni.northwestern.edu:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.cenic.net:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://max-myplc.dragon.maxgigapop.net:12346'), 'UI_AM_TYPE', 'ui_other_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://128.89.91.170:5002'), 'UI_AM_TYPE', 'ui_other_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://128.89.118.100:5002'), 'UI_AM_TYPE', 'ui_other_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://uh-hn.exogeni.net:11443/orca/xmlrpc'), 'UI_AM_TYPE', 'ui_exogeni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://www.instageni.gpolab.bbn.com:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_TYPE', 'ui_instageni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://www.uky.emulab.net:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_TYPE', 'ui_instageni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://www.emulab.net:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_TYPE', 'ui_instageni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://boss.geni.kettering.edu:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_TYPE', 'ui_instageni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://www.utah.geniracks.net:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_TYPE', 'ui_instageni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.utahddc.geniracks.net:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://boss.instageni.northwestern.edu:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_TYPE', 'ui_instageni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.nysernet.org:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://fiu-hn.exogeni.net:11443/orca/xmlrpc'), 'UI_AM_TYPE', 'ui_exogeni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://boss.utahddc.geniracks.net:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_TYPE', 'ui_instageni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://instageni.nysernet.org:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_TYPE', 'ui_instageni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.gpolab.bbn.com:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.geni.kettering.edu:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.utah.geniracks.net:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.northwestern.edu:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.nysernet.org:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.sox.net:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://instageni.maxgigapop.net:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_TYPE', 'ui_instageni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.maxgigapop.net:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://instageni.clemson.edu:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_TYPE', 'ui_instageni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.clemson.edu:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://genirack.nyu.edu:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_TYPE', 'ui_instageni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.genirack.nyu.edu:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://instageni.rnet.missouri.edu:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_TYPE', 'ui_instageni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.rnet.missouri.edu:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://instageni.illinois.edu:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_TYPE', 'ui_instageni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.illinois.edu:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://instageni.wisc.edu:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_TYPE', 'ui_instageni_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.wisc.edu:3626/foam/gapi/2'), 'UI_AM_TYPE', 'ui_foam_am'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://geni.it.cornell.edu:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_TYPE', 'ui_instageni_am'); + +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.geni.it.cornell.edu:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://instageni.sox.net:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.sox.net:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://instageni.ku.gpeni.net:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat ui_stitchable_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.ku.gpeni.net:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://instageni.stanford.edu:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat ui_stitchable_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.stanford.edu:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://sl-geni.northwestern.edu:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.cenic.net:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://max-myplc.dragon.maxgigapop.net:12346'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://128.89.91.170:5002'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://128.89.118.100:5002'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://uh-hn.exogeni.net:11443/orca/xmlrpc'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://www.instageni.gpolab.bbn.com:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_CAT', 'ui_dev_cat ui_compute_cat ui_stitchable_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://www.uky.emulab.net:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat ui_stitchable_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://www.emulab.net:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat ui_stitchable_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://boss.geni.kettering.edu:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://www.utah.geniracks.net:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_CAT', 'ui_dev_cat ui_compute_cat ui_stitchable_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.utahddc.geniracks.net:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://boss.instageni.northwestern.edu:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.nysernet.org:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://fiu-hn.exogeni.net:11443/orca/xmlrpc'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://boss.utahddc.geniracks.net:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat ui_stitchable_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://instageni.nysernet.org:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat ui_stitchable_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.gpolab.bbn.com:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_dev_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.geni.kettering.edu:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.utah.geniracks.net:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_dev_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.northwestern.edu:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.nysernet.org:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.sox.net:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://instageni.maxgigapop.net:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat ui_stitchable_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.maxgigapop.net:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://instageni.clemson.edu:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.clemson.edu:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://genirack.nyu.edu:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.genirack.nyu.edu:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://instageni.rnet.missouri.edu:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat ui_stitchable_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.rnet.missouri.edu:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://instageni.illinois.edu:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat ui_stitchable_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.illinois.edu:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://instageni.wisc.edu:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat ui_stitchable_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://foam.instageni.wisc.edu:3626/foam/gapi/2'), 'UI_AM_CAT', 'ui_prod_cat ui_network_cat'); +insert into service_registry_attribute (service_id, name, value) values ((select id from service_registry where service_url='https://geni.it.cornell.edu:12369/protogeni/xmlrpc/am/2.0'), 'UI_AM_CAT', 'ui_prod_cat ui_compute_cat'); diff --git a/bin/report_genich_relations b/bin/report_genich_relations new file mode 100755 index 0000000..1031b63 --- /dev/null +++ b/bin/report_genich_relations @@ -0,0 +1,255 @@ +#!/usr/bin/python +# +#---------------------------------------------------------------------- +# Copyright (c) 2012-2015 Raytheon BBN Technologies +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and/or hardware specification (the "Work") to +# deal in the Work without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Work, and to permit persons to whom the Work +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Work. +# +# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS +# IN THE WORK. +#---------------------------------------------------------------------- + +# The output of SLICE_GATHER_SCRIPT contains dates of the form +# YYYY-mm-dd HH:MM:SS +# which are in UTC. +# Set process timezone to UTC before doing anything else so python naive +# date tools can parse these correctly. +import os +os.putenv('TZ', 'UTC') + +import csv +import datetime +import dateutil.parser +import re +import subprocess +import sys +import traceback + +PASSWD_FILE = '/usr/local/etc/monitoring_passwd' +VERSION_FILE = '/etc/geni-ch/geni-ch-githash' +CONFIG_FILE = '/usr/local/etc/gmoc_monitoring.conf' +CONTACT_GATHER_SCRIPT_ARGS = ('/usr/local/bin/geni-ops-report -u portal ' + \ + '-P /usr/sysadmin/etc/portal_password -g') +SLICE_GATHER_SCRIPT = '/usr/local/bin/gmoc_list_slices' + +MANDATORY_CONFIG_VARS = ['GMOC_REL_URL', 'SITENAME', 'ORGNAME', 'POPNAME', 'SA_URN', 'SA_TYPE'] + +# Variables for things we expect to find in data gathering script output +MANDATORY_KEYS = ['name', 'urn', 'version', 'type'] +MANDATORY_CONTACT_KEYS = ['contact_email', 'contact_lastname', 'contact_givenname'] +MANDATORY_SLICE_KEYS = ['slice_email', 'slice_creator', 'slice_created', 'slice_expires', 'slice_uuid'] + +# Include local libraries, to submit monitoring data +sys.path.append('/usr/local/lib') +import gmoc + +## The caller may pass the relational URL on the command-line. Take it +## as a default if so. + +config = {} +verbose_output = True +debug_output = False +while len(sys.argv) > 1: + nextarg = sys.argv.pop(1) + if nextarg == '-h': + print """Report GENI CH SA data to GMOC +usage: {0} [-h] + {0} [-q] [-d] [GMOC_URL] + + -h: print this help output + -q: be quiet - only syslog failed submissions, don't print them on STDERR + -d: debug - show full GMOC data structures and submission details + GMOC_URL: set a default GMOC URL to use if none is found in + {1} +""".format(sys.argv[0], CONFIG_FILE) + sys.exit(0) + elif nextarg == '-q': + verbose_output = False + elif nextarg == '-d': + debug_output = True + else: + config['GMOC_REL_URL'] = nextarg + +# Utility function for failing on errors. +# Note: don't use this for configuration errors; those should always be +# printed on STDERR +def fail_on_error(message): + import syslog + syslog.openlog("report_to_gmoc", syslog.LOG_PID, syslog.LOG_USER) + syslog.syslog(syslog.LOG_ERR, message) + if verbose_output: + sys.stderr.write("%s\n" % message) + sys.exit(1) + +if len(sys.argv) > 1: + config['GMOC_REL_URL'] = sys.argv[1] + +## Read the config file for monitoring data submission + +config_line_re = re.compile('^(\S+)\s*=\s*(.*)$') +config_file = open(CONFIG_FILE) +for line in config_file.readlines(): + mobj = config_line_re.match(line) + if mobj: + config[mobj.group(1)] = mobj.group(2) +config_file.close() + +for configvar in MANDATORY_CONFIG_VARS: + if not config.has_key(configvar): + print "Mandatory config variable %s not defined in %s" % \ + (configvar, CONFIG_FILE) + sys.exit(1) + +## Read the password for monitoring data submission + +f = open(PASSWD_FILE) +gmoc_passwd = f.readline().strip() +f.close() + +## Read the git hash for version info +f = open(VERSION_FILE) +geni_ch_version = f.readline().strip() +f.close() + +# Collect all information into a dict called %info + +info = { + 'contacts': {}, + 'slices': {}, +} + + + + + + +## create stub objects for site metadata +organization = gmoc.Organization( + config['ORGNAME'], + ) + +pop = gmoc.POP( + config['POPNAME'], + ) + +## create fully-populated objects for aggregate data + +sa = gmoc.SliceAuthority( + config['SA_URN'], + type = config['SA_TYPE'], + version = geni_ch_version, + pop = pop, + operator = organization, + ) + +## Information is collected from GENI CH two dedicated scripts +# Run the script to get CONTACT info +p = subprocess.Popen(CONTACT_GATHER_SCRIPT_ARGS.split(), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) +[contact_info, errout] = p.communicate() +if p.returncode != 0: + print "%s failed: %s" % (CONTACT_GATHER_SCRIPT_ARGS, errout) + sys.exit(1) + +# Parse the script output line-by-line +contact = None +slice = None + +contact_info_list = contact_info.split("\n") +contact_list = csv.DictReader(contact_info_list, delimiter=',') +users = {} + +for contact in contact_list: + for contact_key in MANDATORY_CONTACT_KEYS: + if not contact.has_key(contact_key): + print "%s output for contact %s missing mandatory key: %s" % \ + (CONTACT_GATHER_SCRIPT_ARGS, contact['contact_urn'], contact_key) + user = contact['contact_urn'] + users[user] = gmoc.Contact( + user, + givenName = contact['contact_givenname'], + lastName = contact['contact_lastname'], + email = contact['contact_email'], + ) +sa.users = users.values() + + +# Run the script to get SLICE info +p = subprocess.Popen([SLICE_GATHER_SCRIPT], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) +[output, errout] = p.communicate() +if p.returncode != 0: + print "%s failed: %s" % (SLICE_GATHER_SCRIPT, errout) + sys.exit(1) +output2 = output.split("\n") +slice_list = csv.DictReader(output2, delimiter=',') + + +# for key in MANDATORY_KEYS: +# if not info.has_key(key): +# print "output missing mandatory key: %s" % (key) +# sys.exit(1) + + +slices = {} +for slice in slice_list: + for slice_key in MANDATORY_SLICE_KEYS: + if not slice.has_key(slice_key): + print "%s output for slice %s missing mandatory key: %s" % \ + (SLICE_GATHER_SCRIPT, slice['slice_urn'], slice_key) + slice_urn = slice['slice_urn'] + if not users.has_key(slice['slice_creator']): + fail_on_error("Slice %s has creator %s who was not found in contact list" \ + % (slice_urn, slice['slice_creator'])) + slices[slice_urn] = gmoc.Slice( + slice_urn, + uuid = slice['slice_uuid'], +# created = datetime.datetime.fromtimestamp(int(slice['slice_created'])), + created = dateutil.parser.parse(slice['slice_created']), +# expires = datetime.datetime.fromtimestamp(int(slice['slice_expires'])), + expires = dateutil.parser.parse(slice['slice_expires']), + creator = users[slice['slice_creator']], + sa = sa, + operator = organization, + ) + +## Repackage information into GMOC format +# create brand new data object +data = gmoc.GMOCClient( + serviceURL = config['GMOC_REL_URL'], + username = config['SITENAME'], + password = gmoc_passwd, + ) +if debug_output: + data.debugLevel = gmoc.GMOC_DEBUG_VERBOSE + +# Now register everything at this POP +try: + result = data.store(pop) +except Exception, e: + fail_on_error( + "Received exception (%s) while storing relations to GMOC: %s" % \ + (str(e), traceback.format_exc()) + ) +if result != 0: + fail_on_error( + "Attempted to submit relational data, but received: %s" % result + ) + +sys.exit(0) diff --git a/bin/sign-auth-req b/bin/sign-auth-req new file mode 100755 index 0000000..42bb8ce --- /dev/null +++ b/bin/sign-auth-req @@ -0,0 +1,59 @@ +#!/bin/sh +# -*- mode:sh -*- + +if [ -z "$1" -o -z "$2" ]; then + echo "Usage: sign-auth-req [CA]" 1>&2 + exit 1 +fi + +REQ=$1 +OUT=$2 +URN=$3 + + +FQDN=`/bin/hostname -f` + + +# Use a non-existent hostname for the authority URL. We were getting a +# lot of "slice is busy errors from pgeni3.gpolab.bbn.com when using +# the FQDN of internal lab VMs. My theory is that those connections +# had to time out. With a non-existent hostname, the connection should +# error immediately so we'll get fewer "busy" errors. It remains to be +# seen whether this works in practices. + +#AUTH_URL=https://${FQDN}/ca.html +AUTH_URL=https://example.geni.net/info.html + + +UUID=`openssl req -in ${REQ} -text -noout | grep 'Subject:' | sed -e 's/.*CN=\([^/$]*\).*/\1/g'` +echo "first uuid = ${UUID}" +UUID="URI:uuid:${UUID}" +echo "second uuid = ${UUID}" +EXT_FILE=`/bin/mktemp` +EXT_NAME='v3_auth' +cat > "${EXT_FILE}" <> "${EXT_FILE}" +else + echo 'basicConstraints = CA:true' >> "${EXT_FILE}" +fi + +OPENSSL=/usr/bin/openssl +CONF=/usr/share/geni-ch/CA/openssl.cnf + +# This is the policy in the $CONF file that checks fields for validity. +# Policy anything is not restrictive in any way. +POLICYARG="-policy policy_anything" + +"${OPENSSL}" ca -config "${CONF}" ${POLICYARG} -batch -notext \ + -extfile "${EXT_FILE}" -extensions "${EXT_NAME}" \ + -in "${REQ}" -out "${OUT}" + +/bin/rm "${EXT_FILE}" diff --git a/bin/ssh-key-daemon b/bin/ssh-key-daemon new file mode 100755 index 0000000..3d5a421 --- /dev/null +++ b/bin/ssh-key-daemon @@ -0,0 +1,16 @@ +#!/bin/sh +# -*- Mode: sh -*- + +KEYFILE=~/Downloads/id_geni_ssh_rsa + +# Infinite while loop to chmod the private key file +while : +do + sleep 10 + if [ -f "${KEYFILE}" ] + then + chmod 600 "${KEYFILE}" + echo chmodded "${KEYFILE}" + fi + echo looping +done diff --git a/bin/sync-gram-racks b/bin/sync-gram-racks new file mode 100755 index 0000000..974d269 --- /dev/null +++ b/bin/sync-gram-racks @@ -0,0 +1,52 @@ +#!/bin/sh + +#To run sync-gram-rack IPADDR_of_GRAM_Rack +#A script that copies all the root trusted certificates to a GRAM rack specified by IP addr +#But even then - you still have to login to the machine to copy the certs into the right directory +#and then restart the gram-am service, also need to check that for Flack to run we need +#the flash security policy installed + +if [ "$#" -ne 1 ];then + echo "Usage: $0 IPADDR_of_GRAM_RACK" >&2 + exit 1 +fi + +HOSTADDR=$1 + +echo "gram rack address is $HOSTADDR" +TMPDIR=${HOME}/tmp/gram-rack-root-certs + +if [ -e $TMPDIR ]; then + echo "Temporary directory $TMPDIR already exists - bailing" + exit 1 +fi + +mkdir -p $TMPDIR +if [ ! -d $TMPDIR ]; then + echo "Failed to create temporary directory $TMPDIR - bailing" + exit 1 +fi +cd $TMPDIR + +#------------------------------------------------------------ +# ch1 = cascade +# ch5 = tau-ceti +# ch-bu = bigslide +#------------------------------------------------------------ + +for h in dagoola illyrica marilac sergyar ch1 ch5 ch-bu ch-dm ch-ph; do + echo "Pulling from ${h}" + scp "${h}.gpolab.bbn.com:/usr/share/geni-ch/CA/cacert.pem" ${h}-cacert.pem +done + +#cat *.pem > extracerts.bundle +scp *.pem gram@${HOSTADDR}:~/certs + +echo "Log in to $HOSTADDR" +echo "Move files from ~ to /etc/gram/certs/trusted_roots" +echo "Restart gram aggregate manager - sudo service gram-am restart" +echo "Check that the flash security policy is installed on the gram rack - telnet $HOSTADDR 843" +#ssh gram@128.89.72.107 sudo service gram-am restart + +cd +rm -rf $TMPDIR