diff --git a/holidays/bin/holidays.py b/holidays/bin/holidays.py index db51631c..33487f08 100755 --- a/holidays/bin/holidays.py +++ b/holidays/bin/holidays.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# -*- encoding: utf-8; py-indent-offset: 4 -*- # # (C) 2023 Heinlein Support GmbH - License: GNU General Public License v2 @@ -19,7 +20,8 @@ # # defaults # -country = "de" +countries = ["de"] +default_country = "de" states = { 'de': { 'bb': 'Brandenburg', @@ -42,26 +44,12 @@ } always = [{'day': 'all', 'time_ranges': [{'start': '00:00', 'end': '24:00'}]}] -def exclude_in(name, exclude_in_tp): - etp, etag = cmk.get_timeperiod(exclude_in_tp) - - if args.debug: - pprint(etp) - pprint(etag) - - exclude = etp['extensions']['exclude'] - exclude.append(name) - - if args.debug: - pprint(exclude) - - cmk.edit_timeperiod(exclude_in_tp, etag, exclude=exclude) parser = argparse.ArgumentParser() parser.add_argument('-s', '--url', help='URL to Check_MK site') parser.add_argument('-u', '--username', help='name of the automation user') parser.add_argument('-p', '--password', help='secret of the automation user') -parser.add_argument('-c', '--config-file', help='config file', +parser.add_argument('-c', '--config-file', help='config file (JSON)', default=os.path.join(os.environ.get('OMD_ROOT'), 'etc', 'holidays')) parser.add_argument('-D', '--debug', action='store_true') subparsers = parser.add_subparsers(title='available commands', help='call "subcommand --help" for more information') @@ -72,109 +60,71 @@ def exclude_in(name, exclude_in_tp): delete_old.set_defaults(func='delete_old') dump_timeperiods = subparsers.add_parser('dump', help='dump timeperiods') dump_timeperiods.set_defaults(func='dump_timeperiods') -create_timeperiod = subparsers.add_parser('create', help='create timeperiod from api-feiertage.de') -create_timeperiod.set_defaults(func='create_timeperiod') -create_timeperiod.add_argument('-a', '--all-states', action='store_true', help='Nur bundesweite Feiertage') -create_timeperiod.add_argument('-l', '--country', default=country, help='Land') -create_timeperiod.add_argument('-s', '--state', choices=states['de'].keys(), help='Bundesland') -create_timeperiod.add_argument('-y', '--year') -create_timeperiod.add_argument('-c', '--current-year', action='store_true') -create_timeperiod.add_argument('-e', '--exclude-in-default', action='store_true', help='Exclude in passender Standard-Timeperiod') -create_timeperiod.add_argument('-E', '--exclude-in', help='Exclude in anderer Timeperiod') -add_region = subparsers.add_parser('addregion', help='add a new region to the configuration (base timeperiods and tag)') +add_holidays = subparsers.add_parser('add_holidays', help='add timeperiod from api-feiertage.de') +add_holidays.set_defaults(func='add_holidays') +add_holidays.add_argument('-a', '--all-states', action='store_true', help='Nur bundesweite Feiertage') +add_holidays.add_argument('-l', '--country', choices=countries, default=default_country, help='Land (default=de)') +add_holidays.add_argument('-s', '--state', choices=states[default_country].keys(), help='Bundesland') +add_holidays.add_argument('-y', '--year') +add_holidays.add_argument('-Y', '--current-year', action='store_true') +add_holidays.add_argument('-e', '--exclude-in-default', action='store_true', help='Exclude in passender Standard-Timeperiod') +add_holidays.add_argument('-E', '--exclude-in', help='Exclude in anderer Timeperiod') +add_region = subparsers.add_parser('add_region', help='add a new region to the configuration (base timeperiods and tag)') add_region.set_defaults(func='add_region') -add_region.add_argument('-l', '--country', default=country, help='Land') +add_region.add_argument('-l', '--country', choices=countries, default=default_country, help='Land') add_region.add_argument('-s', '--state', choices=states['de'].keys(), help='Bundesland', required=True) +add_auto_holidays = subparsers.add_parser('add_auto_holidays', help='add timeperiods from api-feiertage.de for all regions') +add_auto_holidays.set_defaults(func='add_auto_holidays') +add_auto_holidays.add_argument('-y', '--year') +add_auto_holidays.add_argument('-Y', '--current-year', action='store_true') cleanup = subparsers.add_parser('cleanup', help='remove timeperiods and tag group. Use with caution!') cleanup.set_defaults(func='cleanup') -args = parser.parse_args() -if 'func' not in args: - parser.print_help() - sys.exit(1) -if args.debug: - pprint(args) - -config = json.load(open(args.config_file)) - -if args.debug: - pprint(config) - -cmk = checkmkapi.CMKRESTAPI(args.url, args.username, args.password) - -if args.func == 'dump_timeperiods': - pprint(cmk.get_timeperiods()[0]) - -if args.func == 'delete_timeperiod': - cmk.delete_timeperiod(args.name, '*') - cmk.activate() - -if args.func == 'delete_old': - tps, etag = cmk.get_timeperiods() - - to_delete = [] - thisyear = str(date.today().year) +def exclude_in_timeperiod(name, exclude_in_tp): + etp, etag = cmk.get_timeperiod(exclude_in_tp) if args.debug: - print(f"Removing each timeperiod starting with \"{config['timeperiods']['holidays']['name']}_\" up to but not including {thisyear}") + pprint(etp) + pprint(etag) - tps, etag = cmk.get_timeperiods() + exclude = etp['extensions']['exclude'] + exclude.append(name) - for tp in tps['value']: - if tp['id'].startswith(config['timeperiods']['holidays']['name'] + '_'): - year = tp['id'].split('_')[1] - if year < thisyear: - to_delete.append(tp['id']) + if args.debug: + pprint(exclude) - for tp in tps['value']: - excludes = tp['extensions'].get('exclude', []) - changes = False - for td in to_delete: - if td in excludes: - if args.debug: - print(f"Removing {td} from {tp['id']}") - excludes.remove(td) - changes = True - if changes: - te, etag = cmk.get_timeperiod(tp['id']) - cmk.edit_timeperiod(tp['id'], etag, exclude=excludes) - for td in to_delete: - if args.debug: - print(f"Removing {td}") - cmk.delete_timeperiod(td, '*') - if to_delete: - cmk.activate() + cmk.edit_timeperiod(exclude_in_tp, etag, exclude=exclude) -if args.func == 'create_timeperiod': +def add_holiday_timeperiod(country=default_country, state=None, all_states=False, current_year=False, set_year=None, exclude_in_default=True, exclude_in=None): params = {} name = config['timeperiods']['holidays']['name'] + '_' alias = config['timeperiods']['holidays']['title'] + ' ' year = None - if args.current_year: + if current_year: year = str(date.today().year) - elif args.year: - year = args.year + elif set_year: + year = set_year if year: params['years'] = year name += year alias += year - name += "_" + args.country - alias += " " + args.country.upper() - if args.all_states: + name += "_" + country + alias += " " + country.upper() + if all_states: params['all_states'] = "true" name += '_bundeseinheitlich' alias += ' bundeseinheitlich' - elif args.state: - params['states'] = args.state - name += '_%s' % args.state - alias += ' %s' % states[args.country][args.state] + elif state: + params['states'] = state + name += '_%s' % state + alias += ' %s' % states[country][state] if args.debug: pprint(params) if not params: print('Please give at least a year or a state.\n') - create_timeperiod.print_help() + add_holidays.print_help() sys.exit(1) resp = requests.get(apifeiertage, params=params) @@ -188,10 +138,7 @@ def exclude_in(name, exclude_in_tp): if resp.status_code >= 400: sys.stderr.write("%r\n" % data) - if not data.get('feiertage'): - print('Error: %s' % data.get('additional_note')) - sys.exit(1) - else: + if data.get('feiertage'): exceptions = [] for feiertag in data['feiertage']: exceptions.append({ @@ -210,13 +157,75 @@ def exclude_in(name, exclude_in_tp): if args.debug: pprint(tp) - if args.exclude_in_default: - exclude_in(name, config['timeperiods']['workhours']['name'] + "_" + args.country + "_" + args.state) + if exclude_in_default: + exclude_in_timeperiod(name, config['timeperiods']['workhours']['name'] + "_" + country + "_" + state) - if args.exclude_in: - exclude_in(name, args.exclude_in) + if exclude_in: + exclude_in_timeperiod(name, exclude_in) + else: + print('Error: %s' % data.get('additional_note')) + sys.exit(1) + +args = parser.parse_args() +if 'func' not in args: + parser.print_help() + sys.exit(1) +if args.debug: + pprint(args) + +config = json.load(open(args.config_file)) + +if args.debug: + pprint(config) + +cmk = checkmkapi.CMKRESTAPI(args.url, args.username, args.password) + +if args.func == 'dump_timeperiods': + pprint(cmk.get_timeperiods()[0]) + +if args.func == 'delete_timeperiod': + cmk.delete_timeperiod(args.name, '*') + cmk.activate() + +if args.func == 'delete_old': + tps, etag = cmk.get_timeperiods() + + to_delete = [] + thisyear = str(date.today().year) + + if args.debug: + print(f"Removing each timeperiod starting with \"{config['timeperiods']['holidays']['name']}_\" up to but not including {thisyear}") + + tps, etag = cmk.get_timeperiods() + + for tp in tps['value']: + if tp['id'].startswith(config['timeperiods']['holidays']['name'] + '_'): + year = tp['id'].split('_')[1] + if year < thisyear: + to_delete.append(tp['id']) + + for tp in tps['value']: + excludes = tp['extensions'].get('exclude', []) + changes = False + for td in to_delete: + if td in excludes: + if args.debug: + print(f"Removing {td} from {tp['id']}") + excludes.remove(td) + changes = True + if changes: + te, etag = cmk.get_timeperiod(tp['id']) + cmk.edit_timeperiod(tp['id'], etag, exclude=excludes) + for td in to_delete: + if args.debug: + print(f"Removing {td}") + cmk.delete_timeperiod(td, '*') + if to_delete: + cmk.activate() - cmk.activate() +if args.func == 'add_holidays': + add_holiday_timeperiod(args.country, args.state, args.all_states, args.current_year, args.year, args.exclude_in_default, args.exclude_in) + cmk.activate() if args.func == "add_region": workhoursname = '%s_%s_%s' % ( config['timeperiods']['workhours']['name'], @@ -241,6 +250,8 @@ def exclude_in(name, exclude_in_tp): if args.debug: pprint(tp) + add_holiday_timeperiod(country=args.country, state=args.state, current_year=True) + tg = None try: tg, etag = cmk.get_host_tag_group(config['taggroup']['name']) @@ -275,6 +286,19 @@ def exclude_in(name, exclude_in_tp): ) cmk.activate() +if args.func == 'add_auto_holidays': + tps, etag = cmk.get_timeperiods() + changes = False + for tp in tps['value']: + if tp['id'].startswith(config['timeperiods']['workhours']['name']): + if args.debug: + print(f"found {tp['id']}") + _, country, state = tp['id'].split('_') + add_holiday_timeperiod(country=country, state=state, current_year=args.current_year, set_year=args.year) + changes = True + if changes: + cmk.activate() + if args.func == "cleanup": tps, etag = cmk.get_timeperiods() @@ -294,7 +318,8 @@ def exclude_in(name, exclude_in_tp): try: cmk.delete_host_tag_group(config['taggroup']['name']) - print(f"remove host tag group {config['taggroup']['name']}") + if args.debug: + print(f"remove host tag group {config['taggroup']['name']}") changes = True except: pass diff --git a/holidays/etc/cron.d/holidays b/holidays/etc/cron.d/holidays new file mode 100644 index 00000000..87be723d --- /dev/null +++ b/holidays/etc/cron.d/holidays @@ -0,0 +1,8 @@ +# +# Remove old holiday timeperiods in February +# +1 1 12 2 * $OMD_ROOT/local/bin/holidays.py delete_old +# +# Automatically add new holiday timeperiods in November +# +1 1 12 11 * $OMD_ROOT/local/bin/holidays.py add_auto_holidays diff --git a/holidays/etc/holidays b/holidays/etc/holidays new file mode 100644 index 00000000..2784a0cf --- /dev/null +++ b/holidays/etc/holidays @@ -0,0 +1,33 @@ +{ + "comment": "Configuration for ~/local/bin/holidays.py", + + "workdays": [ + {"day": "monday", "time_ranges": [{"start": "09:00", "end": "18:00"}]}, + {"day": "tuesday", "time_ranges": [{"start": "09:00", "end": "18:00"}]}, + {"day": "wednesday", "time_ranges": [{"start": "09:00", "end": "18:00"}]}, + {"day": "thursday", "time_ranges": [{"start": "09:00", "end": "18:00"}]}, + {"day": "friday", "time_ranges": [{"start": "09:00", "end": "15:00"}]} + ], + + "taggroup": { + "name": "holidays", + "title": "Feiertage", + "empty_title": "Keine Feiertage", + "topic": "Notifications" + }, + + "timeperiods": { + "oncall": { + "name": "oncall", + "title": "Bereitschaft" + }, + "workhours": { + "name": "workhours", + "title": "Arbeitszeit" + }, + "holidays": { + "name": "holidays", + "title": "Feiertage" + } + } +} diff --git a/holidays/holidays-2.0.0.mkp b/holidays/holidays-2.0.0.mkp deleted file mode 100644 index 2ca76d42..00000000 Binary files a/holidays/holidays-2.0.0.mkp and /dev/null differ diff --git a/holidays/holidays-2.1.0.mkp b/holidays/holidays-2.1.0.mkp new file mode 100644 index 00000000..c236c40f Binary files /dev/null and b/holidays/holidays-2.1.0.mkp differ