diff --git a/bin/pd-queue b/bin/pd-queue index edabe8d..d3cfc20 100755 --- a/bin/pd-queue +++ b/bin/pd-queue @@ -1,150 +1,9 @@ -#!/usr/bin/env python -# -# Python script to work on the local queue maintained by PagerDuty Agent. -# -# Copyright (c) 2013-2014, PagerDuty, Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the copyright holder nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - - -def build_queue_arg_parser(description): - from pdagent.thirdparty.argparse import ArgumentParser - - parser = ArgumentParser(description=description) - subparsers = parser.add_subparsers(title="sub-commands") - retry_parser_desc = "set up 'dead' PagerDuty events for retry." - status_parser_desc = "print out status of local event queue." - - retry_parser = subparsers.add_parser( - "retry", - description=retry_parser_desc.capitalize(), # printed as title - help=retry_parser_desc # printed in 'options' part of main cmd - ) - retry_parser.add_argument( - "-k", "--service-key", dest="service_key", - help="retry events in given Service API Key (not to be used with -a)" - ) - retry_parser.add_argument( - "-a", "--all-keys", action="store_true", - dest="all_keys", - help="retry events in all Service API Keys (not to be used with -k)" - ) - retry_parser.set_defaults(func=_retry) - - status_parser = subparsers.add_parser( - "status", - description=status_parser_desc.capitalize(), # printed as title - help=status_parser_desc # printed in 'options' part of main cmd - ) - status_parser.add_argument( - "-k", "--service-key", dest="service_key", - help="print status of events in given Service API Key" - ) - status_parser.set_defaults(func=_status) - - return parser - - -def _retry(agent_config, parser, args): - from pdagent.pdagentutil import resurrect_events - - # We explicitly require either a specific service key, or an indication that - # events of all service keys can be resurrected. We don't want to assume - # anything here. - if not args.service_key and not args.all_keys: - parser.error("A specific service key or a flag for all keys required") - elif args.service_key and args.all_keys: - parser.error( - "Only one of specific service key or flag for all keys required") - else: - count = resurrect_events( - agent_config.get_queue(), - args.service_key # 'None' for all keys. - ) - print("%d event(s) set up for retry." % count) - - -def _status(agent_config, _, args): - # prints queue snapshot stats in this format: - # Service Key Pending Success In Error - # ================================================================= - # key1 1 0 0 - # key2 1 0 1 - - from pdagent.thirdparty.six.moves import zip_longest - from pdagent.pdagentutil import get_stats - - status = get_stats( - agent_config.get_queue(), - service_key=args.service_key # 'None' for all-keys. - ) - - snapshot = status.get("snapshot") - if not snapshot: - print("Nothing to report.") - else: - # left-aligned service key, right-aligned counts. - flags = ["-", "", "", ""] - widths = [35, 10, 10, 10] - types = ["s", "s", "s", "s"] - column_fmts = [ - "%" + "".join(e) - for e in zip_longest(flags, map(str, widths), types) - ] - fmt = "".join(column_fmts) - print(fmt % ("Service Key", "Pending", "Success", "In Error")) - print("=" * sum(widths)) - empty_dict = dict() - for (svc_key, state) in sorted(snapshot.iteritems()): - print(fmt % ( - svc_key, - state.get("pending_events", empty_dict).get("count", 0), - state.get("succeeded_events", empty_dict).get("count", 0), - state.get("failed_events", empty_dict).get("count", 0) - )) - - -def main(): - from pdagent.config import load_agent_config - - description = "Access local queue of PagerDuty Agent." - parser = build_queue_arg_parser(description) - args = parser.parse_args() - args.func(load_agent_config(), parser, args) - - -if __name__ == "__main__": - try: - import pdagent.config - except ImportError: - # Fix up for dev layout - import sys - from os.path import realpath, dirname - - sys.path.append(dirname(dirname(realpath(__file__)))) - import pdagent.config - main() +#!/bin/bash +#!/bin/bash + +if [ -x "$(command -v python3)" ]; +then + python3 ./bin/pd-queue.py "$@" +else + python ./bin/pd-queue.py "$@" +fi diff --git a/bin/pd-queue.py b/bin/pd-queue.py new file mode 100644 index 0000000..27bfa71 --- /dev/null +++ b/bin/pd-queue.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python +# +# Python script to work on the local queue maintained by PagerDuty Agent. +# +# Copyright (c) 2013-2014, PagerDuty, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + + +def build_queue_arg_parser(description): + from pdagent.thirdparty.argparse import ArgumentParser + + parser = ArgumentParser(description=description) + subparsers = parser.add_subparsers(title="sub-commands") + retry_parser_desc = "set up 'dead' PagerDuty events for retry." + status_parser_desc = "print out status of local event queue." + + retry_parser = subparsers.add_parser( + "retry", + description=retry_parser_desc.capitalize(), # printed as title + help=retry_parser_desc # printed in 'options' part of main cmd + ) + retry_parser.add_argument( + "-k", "--service-key", dest="service_key", + help="retry events in given Service API Key (not to be used with -a)" + ) + retry_parser.add_argument( + "-a", "--all-keys", action="store_true", + dest="all_keys", + help="retry events in all Service API Keys (not to be used with -k)" + ) + retry_parser.set_defaults(func=_retry) + + status_parser = subparsers.add_parser( + "status", + description=status_parser_desc.capitalize(), # printed as title + help=status_parser_desc # printed in 'options' part of main cmd + ) + status_parser.add_argument( + "-k", "--service-key", dest="service_key", + help="print status of events in given Service API Key" + ) + status_parser.set_defaults(func=_status) + + return parser + + +def _retry(agent_config, parser, args): + from pdagent.pdagentutil import resurrect_events + + # We explicitly require either a specific service key, or an indication that + # events of all service keys can be resurrected. We don't want to assume + # anything here. + if not args.service_key and not args.all_keys: + parser.error("A specific service key or a flag for all keys required") + elif args.service_key and args.all_keys: + parser.error( + "Only one of specific service key or flag for all keys required") + else: + count = resurrect_events( + agent_config.get_queue(), + args.service_key # 'None' for all keys. + ) + print("%d event(s) set up for retry." % count) + + +def _status(agent_config, _, args): + # prints queue snapshot stats in this format: + # Service Key Pending Success In Error + # ================================================================= + # key1 1 0 0 + # key2 1 0 1 + + from pdagent.thirdparty.six import iteritems + from pdagent.thirdparty.six.moves import zip_longest + from pdagent.pdagentutil import get_stats + + status = get_stats( + agent_config.get_queue(), + service_key=args.service_key # 'None' for all-keys. + ) + + snapshot = status.get("snapshot") + if not snapshot: + print("Nothing to report.") + else: + # left-aligned service key, right-aligned counts. + flags = ["-", "", "", ""] + widths = [35, 10, 10, 10] + types = ["s", "s", "s", "s"] + column_fmts = [ + "%" + "".join(e) + for e in zip_longest(flags, map(str, widths), types) + ] + fmt = "".join(column_fmts) + print(fmt % ("Service Key", "Pending", "Success", "In Error")) + print("=" * sum(widths)) + empty_dict = dict() + for (svc_key, state) in sorted(iteritems(snapshot)): + print(fmt % ( + svc_key, + state.get("pending_events", empty_dict).get("count", 0), + state.get("succeeded_events", empty_dict).get("count", 0), + state.get("failed_events", empty_dict).get("count", 0) + )) + + +def main(): + from pdagent.config import load_agent_config + + description = "Access local queue of PagerDuty Agent." + parser = build_queue_arg_parser(description) + args = parser.parse_args() + args.func(load_agent_config(), parser, args) + + +if __name__ == "__main__": + try: + import pdagent.config + except ImportError: + # Fix up for dev layout + import sys + from os.path import realpath, dirname + + sys.path.append(dirname(dirname(realpath(__file__)))) + import pdagent.config + main() diff --git a/bin/pd-send b/bin/pd-send index efece25..2eef992 100755 --- a/bin/pd-send +++ b/bin/pd-send @@ -1,129 +1,8 @@ -#!/usr/bin/env python -# -# Python script to queue an incident event for delayed send to PagerDuty. -# -# Copyright (c) 2013-2014, PagerDuty, Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the copyright holder nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - - -def build_queue_arg_parser(description): - from pdagent.thirdparty.argparse import ArgumentParser - - parser = ArgumentParser(description=description) - parser.add_argument( - "-k", "--service-key", dest="service_key", required=True, - help="Service API Key" - ) - parser.add_argument( - "-t", "--event-type", dest="event_type", required=True, - choices=["trigger", "acknowledge", "resolve"], - help="Event type" - ) - parser.add_argument( - "-d", "--description", dest="description", - help="Short description of the problem" - ) - parser.add_argument( - "-i", "--incident-key", dest="incident_key", - help="Incident Key" - ) - parser.add_argument( - "-c", "--client", dest="client", - help="Client" - ) - parser.add_argument( - "-u", "--client-url", dest="client_url", - help="Client URL" - ) - parser.add_argument( - "-f", "--field", action="append", dest="fields", - help="Add given KEY=VALUE pair to the event details" - ) - parser.add_argument( - "-q", "--quiet", action="store_true", dest="quiet", - help="Operate quietly (no output)" - ) - return parser - - -def parse_fields(fields): - if fields is None: - return {} - return dict(f.split("=", 1) for f in fields) - - -def main(): - from pdagent.pdagentutil import queue_event - from pdagent.config import load_agent_config - from pdagent.constants import EnqueueWarnings - - description = "Queue up a trigger, acknowledge, or resolve event to PagerDuty." - parser = build_queue_arg_parser(description) - args = parser.parse_args() - details = parse_fields(args.fields) - - if args.event_type == "trigger": - if (not args.description) or (not args.description.strip()): - parser.error("Event type '%s' requires description" % args.event_type) - else: - if not args.incident_key: - parser.error("Event type '%s' requires incident key" % args.event_type) - - agent_config = load_agent_config() - - enqueuer = agent_config.get_enqueuer() - incident_key, problems = queue_event( - enqueuer, - args.event_type, args.service_key, args.incident_key, args.description, - args.client, args.client_url, details, - agent_config.get_agent_id(), "pd-send", - ) - if not args.quiet: - for problem in problems: - if problem == EnqueueWarnings.UMASK_TOO_RESTRICTIVE: - print( - "WARNING: Current umask too restrictive. " + - "Using default umask (%03o)." % enqueuer.default_umask - ) - print( - "(For umask requirements, please refer: %s)" % - "https://www.pagerduty.com/docs/guides/agent-install-guide/" - ) - print("Event processed. Incident Key:", incident_key) - - -if __name__ == "__main__": - try: - import pdagent.config - except ImportError: - # Fix up for dev layout - import sys - from os.path import realpath, dirname - sys.path.append(dirname(dirname(realpath(__file__)))) - import pdagent.config - main() +#!/bin/bash + +if [ -x "$(command -v python3)" ]; +then + python3 ./bin/pd-send.py "$@" +else + python ./bin/pd-send.py "$@" +fi diff --git a/bin/pd-send.py b/bin/pd-send.py new file mode 100644 index 0000000..efece25 --- /dev/null +++ b/bin/pd-send.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# +# Python script to queue an incident event for delayed send to PagerDuty. +# +# Copyright (c) 2013-2014, PagerDuty, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + + +def build_queue_arg_parser(description): + from pdagent.thirdparty.argparse import ArgumentParser + + parser = ArgumentParser(description=description) + parser.add_argument( + "-k", "--service-key", dest="service_key", required=True, + help="Service API Key" + ) + parser.add_argument( + "-t", "--event-type", dest="event_type", required=True, + choices=["trigger", "acknowledge", "resolve"], + help="Event type" + ) + parser.add_argument( + "-d", "--description", dest="description", + help="Short description of the problem" + ) + parser.add_argument( + "-i", "--incident-key", dest="incident_key", + help="Incident Key" + ) + parser.add_argument( + "-c", "--client", dest="client", + help="Client" + ) + parser.add_argument( + "-u", "--client-url", dest="client_url", + help="Client URL" + ) + parser.add_argument( + "-f", "--field", action="append", dest="fields", + help="Add given KEY=VALUE pair to the event details" + ) + parser.add_argument( + "-q", "--quiet", action="store_true", dest="quiet", + help="Operate quietly (no output)" + ) + return parser + + +def parse_fields(fields): + if fields is None: + return {} + return dict(f.split("=", 1) for f in fields) + + +def main(): + from pdagent.pdagentutil import queue_event + from pdagent.config import load_agent_config + from pdagent.constants import EnqueueWarnings + + description = "Queue up a trigger, acknowledge, or resolve event to PagerDuty." + parser = build_queue_arg_parser(description) + args = parser.parse_args() + details = parse_fields(args.fields) + + if args.event_type == "trigger": + if (not args.description) or (not args.description.strip()): + parser.error("Event type '%s' requires description" % args.event_type) + else: + if not args.incident_key: + parser.error("Event type '%s' requires incident key" % args.event_type) + + agent_config = load_agent_config() + + enqueuer = agent_config.get_enqueuer() + incident_key, problems = queue_event( + enqueuer, + args.event_type, args.service_key, args.incident_key, args.description, + args.client, args.client_url, details, + agent_config.get_agent_id(), "pd-send", + ) + if not args.quiet: + for problem in problems: + if problem == EnqueueWarnings.UMASK_TOO_RESTRICTIVE: + print( + "WARNING: Current umask too restrictive. " + + "Using default umask (%03o)." % enqueuer.default_umask + ) + print( + "(For umask requirements, please refer: %s)" % + "https://www.pagerduty.com/docs/guides/agent-install-guide/" + ) + print("Event processed. Incident Key:", incident_key) + + +if __name__ == "__main__": + try: + import pdagent.config + except ImportError: + # Fix up for dev layout + import sys + from os.path import realpath, dirname + sys.path.append(dirname(dirname(realpath(__file__)))) + import pdagent.config + main() diff --git a/build-linux/deb/postinst b/build-linux/deb/postinst index 3546cb6..27375d8 100755 --- a/build-linux/deb/postinst +++ b/build-linux/deb/postinst @@ -38,7 +38,13 @@ install_init () { } install_systemd () { - cp /var/lib/pdagent/scripts/pdagent.service /lib/systemd/system + if [ -d "/lib/systemd/system" ] + then + cp /var/lib/pdagent/scripts/pdagent.service /lib/systemd/system + else + cp /var/lib/pdagent/scripts/pdagent.service /etc/systemd/system + fi + systemctl enable pdagent systemctl start pdagent } @@ -58,7 +64,13 @@ if [ "$1" = "configure" ]; then chmod 2750 /var/lib/pdagent/outqueue/err /var/lib/pdagent/outqueue/suc chmod 2753 /var/lib/pdagent/outqueue/tmp /var/lib/pdagent/outqueue/pdq - pycompile -p pdagent + if [ -x "$(command -v pycompile)" ]; then + pycompile -p pdagent + fi + + if [ -x "$(command -v py3compile)" ]; then + py3compile -p pdagent + fi if which systemctl >/dev/null; then install_systemd diff --git a/build-linux/howto.md b/build-linux/howto.md index cfae23f..067c140 100644 --- a/build-linux/howto.md +++ b/build-linux/howto.md @@ -10,9 +10,14 @@ To build Linux packages, you will need GPG v1 keys to sign the packages. Do the ``` brew install gpg1 -mkdir build-linux/gnupg -chmod 700 build-linux/gnupg -gpg1 --homedir=build-linux/gnupg --gen-key + +mkdir build-linux/gpg-deb +chmod 700 build-linux/gpg-deb +gpg1 --homedir=build-linux/gpg-deb --gen-key + +mkdir build-linux/gpg-rpm +chmod 700 build-linux/gpg-rpm +gpg1 --homedir=build-linux/gpg-rpm --gen-key ``` For key generation use the suggested defaults and *no passphrase*. (when diff --git a/build-linux/rpm/postinst b/build-linux/rpm/postinst index 99888ca..1ae3fec 100755 --- a/build-linux/rpm/postinst +++ b/build-linux/rpm/postinst @@ -38,7 +38,13 @@ install_init () { } install_systemd () { - cp /var/lib/pdagent/scripts/pdagent.service /lib/systemd/system + if [ -d "/lib/systemd/system" ] + then + cp /var/lib/pdagent/scripts/pdagent.service /lib/systemd/system + else + cp /var/lib/pdagent/scripts/pdagent.service /etc/systemd/system + fi + systemctl enable pdagent systemctl start pdagent }