def test_get_vos(self): """ Test get_vos function """ vo_file = get_test_config('test_files/sample-vos.txt') self.assertEqual(utilities.get_vos(vo_file), ['osg', 'LIGO', 'cdf'], "Correct vos not found")
def _ensure_valid_user_vo_file(self): using_gums = self.authorization_method == 'xacml' if not (validation.valid_user_vo_file(USER_VO_MAP_LOCATION) and utilities.get_vos(USER_VO_MAP_LOCATION)): self.log("Trying to create user-vo-map file", level=logging.INFO) result = False if using_gums: sys.stdout.write( "Querying GUMS server. This may take some time\n") sys.stdout.flush() result = utilities.run_script(['/usr/bin/gums-host-cron']) if not result: # gums-host-cron failed, let's try the json interface try: sys.stdout.write( "Querying GUMS server via JSON interface. This may take some time\n" ) sys.stdout.flush() user_vo_file_text = gums_supported_vos.gums_json_user_vo_map_file( self.gums_host) open(USER_VO_MAP_LOCATION, "w").write(user_vo_file_text) return True except exceptions.Error as e: self.log( "Could not query GUMS server via JSON interface: %s" % e, level=logging.WARNING) else: sys.stdout.write( "Running edg-mkgridmap, this process may take some time to query vo servers\n" ) sys.stdout.flush() result = utilities.run_script(['/usr/sbin/edg-mkgridmap']) temp, invalid_lines = validation.valid_user_vo_file( USER_VO_MAP_LOCATION, True) result = result and temp if not result: if not invalid_lines: self.log( "Empty %s generated, please check the GUMS configuration (if using GUMS), " "the edg-mkgridmap configuration (if not using GUMS), and/or log messages for the above" % USER_VO_MAP_LOCATION, level=logging.WARNING) else: self.log("Invalid lines in user-vo-map file:", level=logging.WARNING) self.log("\n".join(invalid_lines), level=logging.WARNING) self.log("Error creating user-vo-map file", level=logging.WARNING) return False else: return True
def configure(self, attributes): """Configure installation using attributes""" self.log("InfoServicesConfiguration.configure started") if self.ignored: self.log("%s configuration ignored" % self.config_section, level=logging.WARNING) self.log('InfoServicesConfiguration.configure completed') return True if not self.enabled: self.log("Not enabled") self.log("InfoServicesConfiguration.configure completed") return True if self.copy_host_cert_for_service_cert: if not self.create_missing_service_cert_key( SERVICECERT_PATH, SERVICEKEY_PATH, 'tomcat'): self.log("Could not create service cert/key", level=logging.ERROR) return False if self.ce_collector_required_rpms_installed and self.htcondor_gateway_enabled: if classad is None: self.log( "Cannot configure HTCondor CE info services: unable to import HTCondor Python bindings." "\nEnsure the 'classad' Python module is installed and accessible to Python scripts." "\nIf using HTCondor from RPMs, install the 'condor-python' RPM." "\nIf not, you may need to add the directory containing the Python bindings to PYTHONPATH." "\nHTCondor version must be at least 8.2.0.", level=logging.WARNING) else: using_gums = self.authorization_method == 'xacml' default_allowed_vos = None try: misc.ensure_valid_user_vo_file(using_gums, logger=self.logger) default_allowed_vos = utilities.get_vos( misc.USER_VO_MAP_LOCATION) except exceptions.ConfigureError, err: self.log("Could not determine allowed VOs: %s" % str(err), level=logging.WARNING) self.resource_catalog = subcluster.resource_catalog_from_config( self.subcluster_sections, logger=self.logger, default_allowed_vos=default_allowed_vos) self._configure_ce_collector()
def configure(self, attributes): """Configure installation using attributes""" self.log("InfoServicesConfiguration.configure started") if self.ignored: self.log("%s configuration ignored" % self.config_section, level=logging.WARNING) self.log('InfoServicesConfiguration.configure completed') return True if not self.enabled: self.log("Not enabled") self.log("InfoServicesConfiguration.configure completed") return True if self.copy_host_cert_for_service_cert: if not self.create_missing_service_cert_key(SERVICECERT_PATH, SERVICEKEY_PATH, 'tomcat'): self.log("Could not create service cert/key", level=logging.ERROR) return False if self.ce_collector_required_rpms_installed and self.htcondor_gateway_enabled: if classad is None: self.log("Cannot configure HTCondor CE info services: unable to import HTCondor Python bindings." "\nEnsure the 'classad' Python module is installed and accessible to Python scripts." "\nIf using HTCondor from RPMs, install the 'condor-python' RPM." "\nIf not, you may need to add the directory containing the Python bindings to PYTHONPATH." "\nHTCondor version must be at least 8.2.0.", level=logging.WARNING) else: using_gums = self.authorization_method == 'xacml' default_allowed_vos = None try: misc.ensure_valid_user_vo_file(using_gums, logger=self.logger) default_allowed_vos = utilities.get_vos(misc.USER_VO_MAP_LOCATION) except exceptions.ConfigureError, err: self.log("Could not determine allowed VOs: %s" % str(err), level=logging.WARNING) self.resource_catalog = subcluster.resource_catalog_from_config(self.subcluster_sections, logger=self.logger, default_allowed_vos=default_allowed_vos) self._configure_ce_collector()
def configure(self, attributes): """Configure installation using attributes""" self.log("InfoServicesConfiguration.configure started") if self.ignored: self.log("%s configuration ignored" % self.config_section, level=logging.WARNING) self.log('InfoServicesConfiguration.configure completed') return True if not self.enabled: self.log("Not enabled") self.log("InfoServicesConfiguration.configure completed") return True if self.ce_collector_required_rpms_installed and self.htcondor_gateway_enabled: if classad is None: self.log( "Cannot configure HTCondor CE info services: unable to import HTCondor Python bindings." "\nEnsure the 'classad' Python module is installed and accessible to Python scripts." "\nIf using HTCondor from RPMs, install the 'condor-python' RPM." "\nIf not, you may need to add the directory containing the Python bindings to PYTHONPATH." "\nHTCondor version must be at least 8.2.0.", level=logging.WARNING) else: if self.authorization_method == 'vomsmap': error = False for requiredfile in [BAN_MAPFILE, BAN_VOMS_MAPFILE]: if not os.path.exists(requiredfile): self.log( "%s authorization requested but %s was not found." "\nThis will cause all mappings to fail." "\nPlease reinstall lcmaps >= 1.6.6-1.3 or create a blank %s yourself." % (self.authorization_method, requiredfile, requiredfile), level=logging.ERROR) error = True if error: return False default_allowed_vos = reversevomap.get_allowed_vos() else: using_gums = self.authorization_method == 'xacml' # HACK for SOFTWARE-2792 if using_gums: self.misc_module.update_gums_client_location() self._ensure_valid_user_vo_file() default_allowed_vos = utilities.get_vos( USER_VO_MAP_LOCATION) if not default_allowed_vos: # UGLY: only issue the warning if the admin has requested autodetection for some of their SCs/REs raise_warning = False for section in self.subcluster_sections.sections(): if utilities.config_safe_get(self.subcluster_sections, section, 'allowed_vos', '').strip() == "*": raise_warning = True if raise_warning: self.log( "Could not determine default allowed VOs for subclusters/resource entries.", level=logging.WARNING) if self.authorization_method == 'vomsmap': self.log( "Install vo-client-lcmaps-voms to obtain default mappings for VOs, and/or create" " your own mapfile at /etc/grid-security/voms-mapfile.", level=logging.WARNING) else: self.log( "Ensure %s exists and is non-empty, or fill out allowed_vos in all your" " Subcluster and Resource Entry sections." % USER_VO_MAP_LOCATION, level=logging.WARNING) try: self.resource_catalog = subcluster.resource_catalog_from_config( self.subcluster_sections, default_allowed_vos=default_allowed_vos) except exceptions.SettingError as err: self.log("Error in info services configuration: %s" % str(err), level=logging.ERROR) return False self._configure_ce_collector() self.log("InfoServicesConfiguration.configure completed") return True
def check_se(self, config, section): """ Check attributes currently stored and make sure that they are consistent """ self.log('GipConfiguration.check_se started') attributes_ok = True enabled = True try: if config.has_option(section, 'enabled'): enabled = config.getboolean(section, 'enabled') # pylint: disable-msg=W0703 except Exception: enabled = False if not enabled: # if section is disabled, we can exit return attributes_ok if section.lower().find('changeme') >= 0: msg = "You have a section named 'SE CHANGEME', but it is not turned off.\n" msg += "'SE CHANGEME' is an example; you must change it if it is enabled." raise exceptions.SettingError(msg) for option, value in SE_ENTRIES.items(): status, kind = value entry = self._check_entry(config, section, option, status, kind) if option in SE_BANNED_ENTRIES and entry == SE_BANNED_ENTRIES[option]: raise exceptions.SettingError("Value for %s in section %s is " \ "a default or banned entry (%s); " \ "you must change this value." % \ (option, section, SE_BANNED_ENTRIES[option])) if entry is None: continue # Validate the mount point information if option == 'mount_point': regex = re.compile(r"/(/*[A-Za-z0-9_\-]/*)*$") err_info = {'input': value} if len(entry) != 2: err_info['reason'] = "Only one path was specified!" msg = MOUNT_POINT_ERROR % err_info raise exceptions.SettingError(msg) if not regex.match(entry[0]): err_info['reason'] = "First path does not pass validation" msg = MOUNT_POINT_ERROR % err_info raise exceptions.SettingError(msg) if not regex.match(entry[1]): err_info['reason'] = "Second path does not pass validation" msg = MOUNT_POINT_ERROR % err_info raise exceptions.SettingError(msg) if option == 'srm_endpoint': regex = re.compile(r'([A-Za-z]+)://([A-Za-z0-9_\-.]+):([0-9]+)/(.+)') match = regex.match(entry) if not match or match.groups()[3].find('?SFN=') >= 0: msg = "Given SRM endpoint is not valid! It must be of the form " + \ "srm://<hostname>:<port>/<path>. The hostname, port, and path " + \ "must be present. The path should not contain the string '?SFN='" raise exceptions.SettingError(msg) elif option == 'allowed_vos': user_vo_map = None if config.has_option('Install Locations', 'user_vo_map'): user_vo_map = config.get('Install Locations', 'user_vo_map') vo_list = utilities.get_vos(user_vo_map) for vo in entry: if vo not in vo_list: msg = "The vo %s is explicitly listed in the allowed_vos list in " % vo msg += "section %s, but is not in the list of allowed VOs." % section if vo_list: msg += " The list of allowed VOs are: %s." % ', '.join(vo_list) else: msg += " There are no allowed VOs detected; contact the experts!" raise exceptions.SettingError(msg) self.log('GipConfiguration.check_se completed') return attributes_ok
def check_attributes(self, attributes): """Check attributes currently stored and make sure that they are consistent""" self.log("SiteAttributes.check_attributes started") attributes_ok = True if not self.enabled: self.log("Not enabled, returning True") self.log("SiteAttributes.check_attributes completed") return attributes_ok # OSG_GROUP must be either OSG or OSG-ITB if self.options["group"].value not in ("OSG", "OSG-ITB"): self.log( "The group setting must be either OSG or OSG-ITB, got: %s" % self.options["group"].value, option="group", section=self.config_section, level=logging.ERROR, ) attributes_ok = False # host_name must be different from the default setting if self.options["host_name"].value == "my.domain.name": self.log( "Setting left at default value: my.domain.name", option="host_name", section=self.config_section, level=logging.ERROR, ) attributes_ok = False # host_name must be a valid dns name, check this by getting it's ip adddress if not validation.valid_domain(self.options["host_name"].value, True): self.log( "hostname %s can't be resolved" % self.options["host_name"].value, option="host_name", section=self.config_section, level=logging.ERROR, ) attributes_ok = False # site_name or resource/resource_group must be specified not both if not utilities.blank(self.options["site_name"].value) and ( not utilities.blank(self.options["resource"].value) or not utilities.blank(self.options["resource_group"].value) ): self.log( "In section '%s', site_name and " % self.config_section + "resource or resource_group given at the same time, " + "you should use just the resource and resource_group settings.", level=logging.WARNING, ) if self.options["latitude"].value > 90 or self.options["latitude"].value < -90: self.log( "Latitude must be between -90 and 90, got %s" % self.options["latitude"].value, section=self.config_section, option="latitude", level=logging.ERROR, ) attributes_ok = False if self.options["longitude"].value > 180 or self.options["longitude"].value < -180: self.log( "Longitude must be between -180 and 180, got %s" % self.options["longitude"].value, section=self.config_section, option="longitude", level=logging.ERROR, ) attributes_ok = False # make sure that the email address is different from the default value if self.options["email"] == "*****@*****.**": self.log( "The email setting must be changed from the default", section=self.config_section, option="email", level=logging.ERROR, ) attributes_ok = False # make sure the email address has the correct format if not validation.valid_email(self.options["email"].value): self.log( "Invalid email address in site information: %s" % self.options["email"].value, section=self.config_section, option="email", level=logging.ERROR, ) attributes_ok = False vo_list = self.options["sponsor"].value percentage = 0 vo_names = utilities.get_vos(None) if vo_names == []: map_file_present = False else: map_file_present = True vo_names.append("usatlas") # usatlas is a valid vo name vo_names.append("uscms") # uscms is a valid vo name vo_names.append("local") # local is a valid vo name cap_vo_names = [vo.upper() for vo in vo_names] for vo in re.split(r"\s*,?\s*", vo_list): vo_name = vo.split(":")[0] if vo_name not in vo_names: if vo_name.upper() in cap_vo_names: self.log( "VO name %s has the wrong capitialization" % vo_name, section=self.config_section, option="sponsor", level=logging.WARNING, ) vo_mesg = "Valid VO names are as follows:\n" for name in vo_names: vo_mesg += name + "\n" self.log(vo_mesg, level=logging.WARNING) else: if map_file_present: self.log("In %s section, problem with sponsor setting" % self.config_section) self.log( "VO name %s not found" % vo_name, section=self.config_section, option="sponsor", level=logging.ERROR, ) vo_mesg = "Valid VO names are as follows:\n" for name in vo_names: vo_mesg += name + "\n" self.log(vo_mesg, level=logging.ERROR) attributes_ok = False else: self.log( "Can't currently check VOs in sponsor setting because " + "the /var/lib/osg/user-vo-map is empty. If you are " + "configuring osg components, this may be resolved when " + "osg-configure runs the appropriate script to generate " + "this file later in the configuration process", section=self.config_section, option="sponsor", level=logging.WARNING, ) if len(vo.split(":")) == 1: percentage += 100 elif len(vo.split(":")) == 2: vo_percentage = vo.split(":")[1] try: percentage += int(vo_percentage) except ValueError: self.log( "VO percentage (%s) in sponsor field (%s) not an integer" % (vo_percentage, vo), section=self.config_section, option="sponsor", level=logging.ERROR, exception=True, ) attributes_ok = False else: self.log( "VO sponsor field is not formated correctly: %s" % vo, section=self.config_section, option="sponsor", level=logging.ERROR, ) self.log("Sponsors should be given as sponsor:percentage " "separated by a space or comma") if percentage != 100: self.log( "VO percentages in sponsor field do not add up to 100, got %s" % percentage, section=self.config_section, option="sponsor", level=logging.ERROR, ) attributes_ok = False self.log("SiteAttributes.check_attributes completed") return attributes_ok
def check_attributes(self, attributes): """Check attributes currently stored and make sure that they are consistent""" self.log('SiteAttributes.check_attributes started') attributes_ok = True if not self.enabled: self.log('Not enabled, returning True') self.log('SiteAttributes.check_attributes completed') return attributes_ok # OSG_GROUP must be either OSG or OSG-ITB if self.options['group'].value not in ('OSG', 'OSG-ITB'): self.log("The group setting must be either OSG or OSG-ITB, got: %s" % self.options['group'].value, option='group', section=self.config_section, level=logging.ERROR) attributes_ok = False # host_name must be different from the default setting if self.options['host_name'].value == 'my.domain.name': self.log("Setting left at default value: my.domain.name", option='host_name', section=self.config_section, level=logging.ERROR) attributes_ok = False # host_name must be a valid dns name, check this by getting it's ip adddress if not validation.valid_domain(self.options['host_name'].value, True): self.log("hostname %s can't be resolved" % self.options['host_name'].value, option='host_name', section=self.config_section, level=logging.ERROR) attributes_ok = False # site_name or resource/resource_group must be specified not both if (not utilities.blank(self.options['site_name'].value) and (not utilities.blank(self.options['resource'].value) or not utilities.blank(self.options['resource_group'].value))): self.log("In section '%s', site_name and " % self.config_section + "resource or resource_group given at the same time, " + "you should use just the resource and resource_group settings.", level=logging.WARNING) if self.options['latitude'].value > 90 or self.options['latitude'].value < -90: self.log("Latitude must be between -90 and 90, got %s" % self.options['latitude'].value, section=self.config_section, option='latitude', level=logging.ERROR) attributes_ok = False if self.options['longitude'].value > 180 or self.options['longitude'].value < -180: self.log("Longitude must be between -180 and 180, got %s" % self.options['longitude'].value, section=self.config_section, option='longitude', level=logging.ERROR) attributes_ok = False # make sure that the email address is different from the default value if self.options['email'] == '*****@*****.**': self.log("The email setting must be changed from the default", section=self.config_section, option='email', level=logging.ERROR) attributes_ok = False # make sure the email address has the correct format and that the domain portion is # resolvable if not validation.valid_email(self.options['email'].value): self.log("Invalid email address in site information: %s" % self.options['email'].value, section=self.config_section, option='email', level=logging.ERROR) attributes_ok = False else: match = re.match(r'(?:[a-zA-Z\-_+0-9.]+)@([a-zA-Z0-9_\-]+(?:\.[a-zA-Z0-9_\-]+)+)', self.options['email'].value) try: socket.gethostbyname(match.group(1)) except socket.herror: self.log("Can't resolve domain in email: %s" % self.options['email'].value, section=self.config_section, option='email', level=logging.WARNING, exception=True) except socket.gaierror: self.log("Can't resolve domain in email: %s" % self.options['email'].value, section=self.config_section, option='email', level=logging.WARNING, exception=True) vo_list = self.options['sponsor'].value percentage = 0 vo_names = utilities.get_vos(None) if vo_names == []: map_file_present = False else: map_file_present = True vo_names.append('usatlas') # usatlas is a valid vo name vo_names.append('uscms') # uscms is a valid vo name vo_names.append('local') # local is a valid vo name cap_vo_names = [vo.upper() for vo in vo_names] for vo in re.split(r'\s*,?\s*', vo_list): vo_name = vo.split(':')[0] if vo_name not in vo_names: if vo_name.upper() in cap_vo_names: self.log("VO name %s has the wrong capitialization" % vo_name, section=self.config_section, option='sponsor', level=logging.WARNING) vo_mesg = "Valid VO names are as follows:\n" for name in vo_names: vo_mesg += name + "\n" self.log(vo_mesg, level=logging.WARNING) else: if map_file_present: self.log("In %s section, problem with sponsor setting" % \ self.config_section) self.log("VO name %s not found" % vo_name, section=self.config_section, option='sponsor', level=logging.ERROR) vo_mesg = "Valid VO names are as follows:\n" for name in vo_names: vo_mesg += name + "\n" self.log(vo_mesg, level=logging.ERROR) attributes_ok = False else: self.log("Can't currently check VOs in sponsor setting because " + "the /var/lib/osg/user-vo-map is empty. If you are " + "configuring osg components, this may be resolved when " + "osg-configure runs the appropriate script to generate " + "this file later in the configuration process", section=self.config_section, option='sponsor', level=logging.WARNING) if len(vo.split(':')) == 1: percentage += 100 elif len(vo.split(':')) == 2: vo_percentage = vo.split(':')[1] try: percentage += int(vo_percentage) except ValueError: self.log("VO percentage (%s) in sponsor field (%s) not an integer" \ % (vo_percentage, vo), section=self.config_section, option='sponsor', level=logging.ERROR, exception=True) attributes_ok = False else: self.log("VO sponsor field is not formated correctly: %s" % vo, section=self.config_section, option='sponsor', level=logging.ERROR) self.log("Sponsors should be given as sponsor:percentage " "separated by a space or comma") if percentage != 100: self.log("VO percentages in sponsor field do not add up to 100, got %s" \ % percentage, section=self.config_section, option='sponsor', level=logging.ERROR) attributes_ok = False self.log('SiteAttributes.check_attributes completed') return attributes_ok
def check_se(self, config, section): """ Check attributes currently stored and make sure that they are consistent """ self.log('GipConfiguration.check_se started') attributes_ok = True enabled = True try: if config.has_option(section, 'enabled'): enabled = config.getboolean(section, 'enabled') # pylint: disable-msg=W0703 except Exception: enabled = False if not enabled: # if section is disabled, we can exit return attributes_ok if section.lower().find('changeme') >= 0: msg = "You have a section named 'SE CHANGEME', but it is not turned off.\n" msg += "'SE CHANGEME' is an example; you must change it if it is enabled." raise exceptions.SettingError(msg) for option, value in SE_ENTRIES.items(): status, kind = value entry = self._check_entry(config, section, option, status, kind) if option in SE_BANNED_ENTRIES and entry == SE_BANNED_ENTRIES[ option]: raise exceptions.SettingError("Value for %s in section %s is " \ "a default or banned entry (%s); " \ "you must change this value." % \ (option, section, SE_BANNED_ENTRIES[option])) if entry is None: continue # Validate the mount point information if option == 'mount_point': regex = re.compile(r"/(/*[A-Za-z0-9_\-]/*)*$") err_info = {'input': value} if len(entry) != 2: err_info['reason'] = "Only one path was specified!" msg = MOUNT_POINT_ERROR % err_info raise exceptions.SettingError(msg) if not regex.match(entry[0]): err_info['reason'] = "First path does not pass validation" msg = MOUNT_POINT_ERROR % err_info raise exceptions.SettingError(msg) if not regex.match(entry[1]): err_info['reason'] = "Second path does not pass validation" msg = MOUNT_POINT_ERROR % err_info raise exceptions.SettingError(msg) if option == 'srm_endpoint': regex = re.compile( r'([A-Za-z]+)://([A-Za-z0-9_\-.]+):([0-9]+)/(.+)') match = regex.match(entry) if not match or match.groups()[3].find('?SFN=') >= 0: msg = "Given SRM endpoint is not valid! It must be of the form " + \ "srm://<hostname>:<port>/<path>. The hostname, port, and path " + \ "must be present. The path should not contain the string '?SFN='" raise exceptions.SettingError(msg) elif option == 'allowed_vos': user_vo_map = None if config.has_option('Install Locations', 'user_vo_map'): user_vo_map = config.get('Install Locations', 'user_vo_map') vo_list = utilities.get_vos(user_vo_map) for vo in entry: if vo not in vo_list: msg = "The vo %s is explicitly listed in the allowed_vos list in " % vo msg += "section %s, but is not in the list of allowed VOs." % section if vo_list: msg += " The list of allowed VOs are: %s." % ', '.join( vo_list) else: msg += " There are no allowed VOs detected; contact the experts!" raise exceptions.SettingError(msg) self.log('GipConfiguration.check_se completed') return attributes_ok
def check_sponsor(self, sponsor): attributes_ok = True percentage = 0 vo_names = utilities.get_vos(None) if vo_names == []: map_file_present = False else: map_file_present = True vo_names.append('usatlas') # usatlas is a valid vo name vo_names.append('uscms') # uscms is a valid vo name vo_names.append('local') # local is a valid vo name cap_vo_names = [vo.upper() for vo in vo_names] for vo in re.split(r'\s*,?\s*', sponsor): vo_name = vo.split(':')[0] if vo_name not in vo_names: if vo_name.upper() in cap_vo_names: self.log("VO name %s has the wrong capitialization" % vo_name, section=self.config_section, option='sponsor', level=logging.WARNING) vo_mesg = "Valid VO names are as follows:\n" for name in vo_names: vo_mesg += name + "\n" self.log(vo_mesg, level=logging.WARNING) else: if map_file_present: self.log("In %s section, problem with sponsor setting" % \ self.config_section) self.log("VO name %s not found" % vo_name, section=self.config_section, option='sponsor', level=logging.ERROR) vo_mesg = "Valid VO names are as follows:\n" for name in vo_names: vo_mesg += name + "\n" self.log(vo_mesg, level=logging.ERROR) attributes_ok = False else: self.log("Can't currently check VOs in sponsor setting because " + "the /var/lib/osg/user-vo-map is empty. If you are " + "configuring osg components, this may be resolved when " + "osg-configure runs the appropriate script to generate " + "this file later in the configuration process", section=self.config_section, option='sponsor', level=logging.WARNING) if len(vo.split(':')) == 1: percentage += 100 elif len(vo.split(':')) == 2: vo_percentage = vo.split(':')[1] try: percentage += int(vo_percentage) except ValueError: self.log("VO percentage (%s) in sponsor field (%s) not an integer" \ % (vo_percentage, vo), section=self.config_section, option='sponsor', level=logging.ERROR, exception=True) attributes_ok = False else: self.log("VO sponsor field is not formated correctly: %s" % vo, section=self.config_section, option='sponsor', level=logging.ERROR) self.log("Sponsors should be given as sponsor:percentage " "separated by a space or comma") if percentage != 100: self.log("VO percentages in sponsor field do not add up to 100, got %s" \ % percentage, section=self.config_section, option='sponsor', level=logging.ERROR) attributes_ok = False return attributes_ok