Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Printer rotation command #174

Merged
merged 9 commits into from
Feb 26, 2022
131 changes: 131 additions & 0 deletions staff/lab/mod-printer
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#!/usr/bin/env python3
"""Remove or add printers to/from CUPS classes."""
import argparse
import re
import subprocess
import sys


def modify_printer(args):
"""Perform internal call to CUPS CLI to modify class(es)."""
# Determine which CUPS classes are available
try:
lpstat_out = subprocess.run(["lpstat", "-p"], check=True, \
stdout=subprocess.PIPE, stdin=subprocess.PIPE).strip().split("\n")
except subprocess.CalledProcessError as e:
Boomaa23 marked this conversation as resolved.
Show resolved Hide resolved
print("ERROR: lpstat process exited with exit code %i" % e.returncode)
return e.returncode

# Parse lpstat output for printer class names
# Output is of the form: printer <printername> is idle. <etc>
cups_classes = [re.search(r"printer (\S*) is", classname).group(1) for classname in lpstat_out]

if args.verbose:
print("Found CUPS classes: %s" % cups_classes)

# Override with specified classname if provided and valid
if args.classname:
if args.classname in cups_classes:
cups_classes = [args.classname]
if args.verbose:
print("Overriding CUPS classes with parameter: %s" % args.classname)
else:
print("ERROR: Specified class %s could not be found in available classes %s" % (args.classname, cups_classes))
return 1

# Perform pre-call logging and input processing
printer = args.printer.lower()
if args.verbose:
print("Printer to perform action on: %s" % printer)
action_flag = "-r" if args.action == "remove" else "-c"
if args.verbose:
print("Action to be performed (and flag): %s (%s)" % (args.action, action_flag))

# Call lpadmin command to perform addition/removal
for classname in cups_classes:
action_cmd = ["lpadmin", "-p", printer + "-" + classname, action_flag, classname]
try:
subprocess.run(action_cmd, check=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
except subprocess.CalledProcessError as e:
print("ERROR: lpstat process exited with exit code %i" % e.returncode)
return e.returncode
else:
action_out = "added to" if args.action == "add" else "removed from"
print("Printer %s was successfully %s %s" % (printer, action_out, classname))


def list_printers(args):
"""Perform internal call to CUPS CLI to list printer status(es) (and potentially jobs)."""
# Initialize base lpstat command
lpstat_cmd = ["lpstat", "-c"]

# Add on to lpstat command with arguments
if args.classname:
if args.verbose:
print("Limiting results to only class %s" % args.classname)
lpstat_cmd.append(args.classname)
if args.jobs:
if args.verbose:
print("Including jobs in stdout")
Boomaa23 marked this conversation as resolved.
Show resolved Hide resolved
lpstat_cmd.append("-o")

if args.verbose:
print("Calling %s to list printer status" % lpstat_cmd)

try:
# Call lpstat - prints members of the class and can
# be configured to include jobs as well. No way to
# check for printers not in classes.
lpstat_out = subprocess.run(lpstat_cmd, check=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
except subprocess.CalledProcessError as e:
print("ERROR: lpstat process exited with exit code %i" % e.returncode)
return e.returncode

# Parse output to fit printer arg, or send directly to stdout
status_msgs = lpstat_out.stdout.decode("utf-8")
if args.printer:
status_lines = status_msgs.split("\n")
for line in status_lines[:]:
if args.printer not in line and "members of class" not in line:
status_lines.remove(line)
Boomaa23 marked this conversation as resolved.
Show resolved Hide resolved
if args.verbose:
print("Removed \"%s\" from output to fit printer parameter of \"%s\"" % (line, args.printer))
print("\n".join(status_lines))
else:
print(status_msgs, end="")



def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("-v", "--verbose", "--debug",
action="store_true",
help="output more verbose logging to stdout"
)
parser.add_argument("-c", "--classname", help="modify/list only a specific class")

subparsers = parser.add_subparsers(dest="action", help="action to perform", required=True)

add_parser = subparsers.add_parser("add", help="add a printer")
add_parser.add_argument("printer", help="name of printer to add")

remove_parser = subparsers.add_parser("remove", help="remove a printer")
remove_parser.add_argument("printer", help="name of printer to remove")

list_parser = subparsers.add_parser("list", help="list printer statuses")
list_parser.add_argument("-p", "--printer", help="list only this named printer")
list_parser.add_argument("-j", "--jobs",
action="store_true", help="include print jobs")

args = parser.parse_args()
if args.action == "add" or args.action == "remove":
return modify_printer(args)
elif args.action == "list":
return list_printers(args)
else:
print("ERROR: Invalid action passed %s" % args.action)
return 1


if __name__ == "__main__":
sys.exit(main())