Python Forum

Full Version: Python script that calls jamfHelper binary
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2
Hi I have a python script I am using to update my Macbooks at work that calls the jamfBinary to display a hud that allows the user to defer updates.

At this time it only presents the hud once with 5 options,
  • "Start Now",
  • "5 minutes",
  • "2 hours",
  • "4 hours",
  • "8 hours".
If the user chooses "5 minutes" then the install starts in five minutes.

What I want is it to display the prompt again minus the options that preceded it.
So if the input is 5 mins, then in roughly five minutes the hud reappears with
  • "Start Now",
  • "2 hours",
  • "4 hours",
  • "8 hours".
If the hud comes up and user decides I will do it in "4 hours" then I want the prompt to reappear with only the 8 hour option.

I want them to be able to defer multiple times until the max deferral time period has been reached.
I have attached the script.
I was thinking I could just re-use this section of code however many times I need it to prompt but I am not sure if I am thinking about this correctly as I am not a Python Pro but I have some experience programming in C++.
My apologies. Any guidance is much appreciated.

***This is the code I believe I can reuse to accomplish the hud being prompted, I am just not sure if I am logically approaching this the right way.

def display_prompt():
    """Displays prompt to allow user to schedule update installation

    Args:
        None

    Returns:
        (int) defer_seconds: Number of seconds user wishes to defer policy
        OR
        None if an error occurs
    """
    cmd = [JAMFHELPER,
           '-windowType', 'hud',
           '-title', GUI_WINDOW_TITLE,
           '-heading', GUI_HEADING,
           '-icon', GUI_ICON,
           '-description', GUI_MESSAGE,
           '-button1', GUI_BUTTON,
           '-showDelayOptions',
           ' '.join(GUI_DEFER_OPTIONS),
           '-lockHUD']
    error_values = ['2', '3', '239', '243', '250', '255']
    # Instead of returning an error code to stderr, jamfHelper always returns 0
    # and possibly returns an 'error value' to stdout. This makes it somewhat
    # spotty to check for some deferrment values including 0 for 'Start Now'.
    # The return value is an integer, so leading zeroes are dropped. Selecting
    # 'Start Now' should technically return '01'; instead, only '1' is returned
    # which matches the 'error value' for 'The Jamf Helper was unable to launch'
    # All we can do is make sure the subprocess doesn't raise an error, then
    # assume (yikes!) a return value of '1' equates to 'Start Now'
    try:
        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
        out, err = proc.communicate()
        # Check that the return value does not represent an 'error value'
        if not out in error_values:
            # Special case for 'Start Now' which returns '1'
            if out == '1':
                return 0
            else:
                return int(out[:-1])
        else:
            return None
    except:
        # Catch possible CalledProcessError and OSError
        print "An error occurred when displaying the user prompt."
        return None
I'm not entirely sure if this is what your looking for and it can probably be improved upon.
class ChoiceError(Exception):
    pass

class RestartChoices():

    def __init__(self):
        self.reset_choices()

    def reset_choices(self):
        self.choices = ["Start Now", "5 minutes", "2 hours", "4 hours",
                        "8 hours"]
        self.last_choice = ''

    def make_choice(self, choice):
        if choice not in self.choices:
            raise ChoiceError('invalid choice')
        self.last_choice = choice
        if choice == self.choices[-1] or choice == self.choices[0]:
            self.choices = []
            return

        choice_index = self.choices.index(choice) + 1
        self.choices = self.choices[:1] + self.choices[choice_index:]

restart_choices = RestartChoices()
restart_choices.make_choice('5 minutes')
print(f'choice: {restart_choices.last_choice}')
print(f'Remaining: {restart_choices.choices}')

restart_choices.make_choice('4 hours')
print(f'choice: {restart_choices.last_choice}')
print(f'Remaining: {restart_choices.choices}')

restart_choices.make_choice('8 hours')
print(f'choice: {restart_choices.last_choice}')
print(f'Remaining: {restart_choices.choices}')

restart_choices.reset_choices()
restart_choices.make_choice('2 hours')
print(f'choice: {restart_choices.last_choice}')
print(f'Remaining: {restart_choices.choices}')

restart_choices.reset_choices()
restart_choices.make_choice('Start Now')
print(f'choice: {restart_choices.last_choice}')
print(f'Remaining: {restart_choices.choices}')
Output:
choice: 5 minutes Remaining: ['Start Now', '2 hours', '4 hours', '8 hours'] choice: 4 hours Remaining: ['Start Now', '8 hours'] choice: 8 hours Remaining: [] choice: 2 hours Remaining: ['Start Now', '4 hours', '8 hours'] choice: Start Now Remaining: []
Hi Yoriz,

Ty for your response.

So I assume I would somehow pass the output from
def display_prompt():
to
 class ChoiceError(Exception): ??? 
I hope I tagged the code correctly Yoriz!!! Sorry for my first post.
ChoiceError is just an error to raise if a choice is made that is not a valid choice
restart_choices = RestartChoices()
restart_choices.make_choice('15 minutes')
Output:
Traceback (most recent call last): File "C:\Users\Dave\Documents\Eclipse Workspace\Test\forum\forum_post.py", line 29, in <module> restart_choices.make_choice('15 minutes') File "C:\Users\Dave\Documents\Eclipse Workspace\Test\forum\forum_post.py", line 17, in make_choice raise ChoiceError('invalid choice') __main__.ChoiceError: invalid choice
Sorry I copied and pasted the wrong function... I meant:

How do I pass the value of:
def display_prompt(): 
to
class RestartChoices():
You could make a self in RestartChoices that takes the value of a parameter passed through and then update it with the value of display_prompt
(May-02-2019, 10:13 PM)SheeppOSU Wrote: [ -> ]You could make a self in RestartChoices that takes the value of a parameter passed through and then update it with the value of display_prompt
Ty SheeppOSU I will give it a shot!!!
(May-02-2019, 10:13 PM)SheeppOSU Wrote: [ -> ]You could make a self in RestartChoices that takes the value of a parameter passed through and then update it with the value of display_prompt

Should I create that outside of my current better-jamf-policy-deferral.py script? Or create it in the same .py script... are classes in python like classes in C#? You create them as a different file and then bundle them all up together? My apologies I am script kiddy that took some courses at my local community college, I am def not a Computer Science major lol
I've done a litte C++ which I've heard is similar to C#. In C++ classes are usually made in different files like you said. In Python in depends. When making a game, to shorten code, sometimes functions and classes are put into a separate files. In this case since it is a small amount of code it can go into the same file

Also create what outside of the current file
(May-03-2019, 10:18 PM)SheeppOSU Wrote: [ -> ]I've done a litte C++ which I've heard is similar to C#. In C++ classes are usually made in different files like you said. In Python in depends. When making a game, to shorten code, sometimes functions and classes are put into a separate files. In this case since it is a small amount of code it can go into the same file
Also create what outside of the current file

Ok thanks SheeppOSU!!! I will toy with this all weekend :)
Pages: 1 2