Controlling scheduler for endpoint?

Hey guys, is it currently possible to control the schedule for a UD endpoint? after the dashboard has started?

Hi,

I can’t answer your specific question, but maybe you could try to explain what you are trying to achieve?

@dtnyc9005
Unfortunately you cannot edit the endpoint schedule after starting the dashboard.

@PorreKaj, so I’m looking to give the user the option to possibly control the schedule for endpoints, for instance on a standard endpoint as such, would be interesting if we can pass an integer to the New-UDEndpointSchedule -every (parameter), again not even sure if this is possible.

New-UDEndpoint -Endpoint {

 #Do Something....

} -Schedule (New-UDEndpointSchedule -Every $Cache:VARIABLE -Minute)

Im thinking of a $cache:varable because UD under the hood will need to know the latest value, maybe a question for @adam , if this could be changed at runtime, how often is the endpoint/schedule evaluated?

@dtnyc9005
you can have the endpoint schedule for 1 every day and create a button to call the endpoint at any time and re run the script block to refresh the value of the cache variable.
look for Invoke-UDEvent -Id “someid” -scheduled.

1 Like

So the Beaty/horrors of Powershell is that there is never only one way of solving things.

I’m this case I would experiment with having the endpoint run on the lowest amount of time possible (the amount of time the script will take to execute), then inside the endpoint have code that checks wether or not X amount has
passed since last run. With X being the $cache:variable that the user can define.

1 Like

Yup I definitely need to test this out, I’ve been looking for this for sometime didn’t even know it was there :wink:

I saw that you had opened a git issue, seeing this come to life would be awesome I’m sure others with cache datasets would love to have that kind of control outside the endpoint.

I’ll try this and post my findings… thanks.

Yes, I was thinking the same, love that posh is this way, you just have to be creative with it ;),

I’m in the process of writing all this up so I will post how this goes.

thanks again.

@wsl2001 So, this feature is insanley cool to have, but I have noticed an issue, where it will only fire once, let me explain:

  • I have table that gets populated with $cache:variable (that comes from the schedule endpoint, this is fine comes through with data and buttons and modals - no biggy here…)
  • One of those buttons has the invoke-UDEvent code, this fires but ONLY once, thereafter when trying to execute it, I get: “an unexpected error occured invoking ‘client event’ on the server. exception”
  • no matter what I do, refresh, click other buttons with modals, I get same “clientEvent” error

I’m adding somecode to put things in perspective:

####### ENDPOINT1.PS1 #########

New-UDEndpoint -id "testthisep" -Endpoint {

#Execute some code and build cache variable below

$cache:data = @(
    
    [PSCustomObject]@{
        Application       = (New-UDLink -Text "$AppName" -Url "/Apps/$AppName" -OpenInNewWindow)
        Results           = $Result
        Uri               = (New-UDLink -Text "$Uri" -Url "$Uri" -OpenInNewWindow)
        Details           = New-UDButton -Text "Details" -OnClick (
            New-UDEndpoint -Endpoint {
                Show-UDModal -Width auto -Height auto -Content {
                }
            }
        )
        ReExecuteEndpoint = New-UDButton -Text "ReFire" -OnClick (
            New-UDEndpoint -Endpoint {
                Invoke-UDEvent -Id "testthisep" -scheduled
            }
        )
        LasRefreshed      = (get-date -Format t)
    }
)

} -Schedule (New-UDEndpointSchedule -Every 30 -Minute)


####### HOMEPAGE.PS1 #########


New-UDPage -Name "HomePage" -Content {


New-UDTable -Title "AppList" -Headers @("Application", "Results", "Uri", "Details", "ReExecuteEndpoint", "LastRefreshed")   -AutoRefresh -Endpoint {
    $cache:data | Out-UDTableData -Property @("Application", "Results", "Uri", "Details", "ReExecuteEndpoint", "LastRefreshed") 
}
}

Anythoughts on this behavior?

@dtnyc9005 thats correct because the id of the button changed, this is the same issue i have when i tried invoke-udevent and i have opened a ticket for it.
try to give your button an id and see if that helps in your example.

@wsl2001, yeah this was exciting while it lasted, but still no dice, tried to set ids where ever possible but same clientevent on server error. throwing this @adam way to see if there any plans for enterprise folks?

hoping I can give the other solution a try (@PorreKaj) tonight, were we set and control the schedule through a $cache:endpoint

Hey everyone,

Seems like controlling schedules after the dashboard has started is really important to you so I took some this morning and implemented it.

I’ve added Remove-UDEndpoint. You’ll be able to remove endpoints (even non-scheduled ones but be careful with that).

        $Schedule = New-UDEndpointSchedule -Every 1 -Second 

        $EverySecond = New-UDEndpoint -Schedule $Schedule -Endpoint {
            $Cache:Value = "FirstSchedule"
        } -Id 'schedule'

        Remove-UDEndpoint -Id 'schedule'

You’ll also be able to create new schedules on the fly. If you call New-UDEndpoint in a script block, like an OnClick for example, it will automatically register the new endpoint with the UD scheduler.

For example, in my tests, I’m using the UD REST API to remove the existing schedule and then recreate a new schedule on the fly.

        $Endpoint = New-UDEndpoint -Url "changeSchedule" -Endpoint {
            Remove-UDEndpoint -Id 'schedule'
            $Schedule = New-UDEndpointSchedule -Every 1 -Second 
            New-UDEndpoint -Schedule $Schedule -Endpoint {
                $Cache:Value = "SecondSchedule"
            } -Id 'schedule'
        }

Hope that’s what you’re after. This will be in tonight’s nightly build.

5 Likes

This is awesome. Thanks :slight_smile:

1 Like

@adam
is it possible to call the scheduled endpoint manually as well like onclick?

That wasn’t part of this PR.

I have a question about that though. What’s the benefit of calling a scheduled endpoint manually vs just calling a function in a module? For example, let’s say you have a scheduled endpoint that sets some value in the cache.

        $Schedule = New-UDEndpointSchedule -Every 1 -Second 

        $EverySecond = New-UDEndpoint -Schedule $Schedule -Endpoint {
            $Cache:Value = "FirstSchedule"
        } -Id 'schedule'

I understand that you’d like to call that scheduled endpoint with something like:

New-UDButton -OnClick {
      Invoke-UDEndpoint -Id 'schedule'
}

But couldn’t you just define a function in a module like Set-Cache?

function Set-Cache { 
      $Cache:Value = "FirstSchedule"
}


$Schedule = New-UDEndpointSchedule -Every 1 -Second 

$EverySecond = New-UDEndpoint -Schedule $Schedule -Endpoint {
        Set-Cache
} -Id 'schedule'

New-UDButton -OnClick {
       Set-Cache
}

Or is it that you want the Invoke-UDEndpoint to run asynchronously?

@adam
so the benefit of that is then you dont have to to run a scheduled endpoints more often and have the memory builds up fast.
so for example if i have an endpoint scheduled every 5 sec to pull services from a remote machine and assign a button foreach to control start/stop the service, i have noticed that in matter of couple hours the memory consumption is high and UD becomes slow.
but with the same scenario if the schedule is every 1 day then when someone click the button to start / stop a service i can add a call of the endpoint manually and this way i can get the service status updated right away and will save the memory from building up very quickly.
the issue right now with invoke-udelement is if you have the button as a part of the scheduled endpoint once you initiated the invoke-udelement it will change the button id and it wont work for a second click.

I can understand the memory issue. But couldn’t you just update the server list when the user clicked the button? Why does it need to call the scheduled endpoint when the user clicks the button and not just a function or something?

The issue with a changing button ID will not be solved by allowing you to invoke a scheduled endpoint. If the ID changes and the user doesn’t refresh the page or if the control containing that button ID isn’t updated some how, the button won’t work because it will try to invoke the old ID.

You could cache the server data and create the button when the page is loaded rather than creating the button and storing it in the cache.

Maybe this is all best described in code. I will put an example of what I am thinking and then we can discuss it based on the code. :blush: I need to take my dog for a walk but I will write something up when I get back

1 Like

@adam
Thank you for the upcoming testing code i have tried in the past your idea but i have found that creating the button in the cache was way faster than having it created when the page is loaded am going to post my code as an example which shows the fastest way based on my testing but with a downside of memory build up fast if i scheduled the endpoint in seconds.

$Schedule1 = New-UDEndpointSchedule -Every 5 -Second

$Services = New-UDEndpoint -Schedule $Schedule1 -Endpoint {
   
              
    $Cache:Services = (Get-Service -ComputerName "server1" -Name "*Certification*", "*Notification*", "*Distribution*") | Select-Object Name, Status | Sort-Object Name | ForEach-Object { 
                    
                    
        $FontColor = 'Green'

        if ($_.Status -ne 'Running') {
                        
            $FontColor = 'Red'
        }
                    
                    
                    
        [PSCustomObject]@{
            Name   = $_.Name
            Status = New-UDElement -Tag 'div' -Attributes @{ style = @{ color = $FontColor } } -Content { $_.Status.ToString() }
            Select = if ($_.Status -eq 'Stopped') {

                New-UDTooltip -Type info -Effect float -Place top -TooltipContent { "Start service!" } -Content {
                    New-UDButtonLoader -BackgroundColor "Red" -Text "Start" -OnClick {
                        Get-Service -ComputerName "server1" -Name $_.Name | Start-Service
                        Show-UDToast -Message "Service Started!" -MessageColor Green -Title $_.Name -Position topCenter -Duration 2500
                    }
                }

            }
            else {

                if ($_.Status -eq 'Running') {

                    New-UDTooltip -Type info -Effect float -Place top -TooltipContent { "Stop service!" } -Content {
                        New-UDButtonLoader -BackgroundColor "#26a69a" -Text "Stop" -Icon stop_circle -OnClick {
                            Get-Service -ComputerName "server1" -Name $_.Name | Stop-Service
                            Show-UDToast -Message "Service Stopped!" -MessageColor Red -Title $_.Name -Position topCenter -Duration 2500
                        
                        }
                    }
                }
            }
        }
    }
}


$HomePage = New-UDPage -Name "Home" -Icon home -Content {

    New-UDScrollUp

    New-UDCard -Title "Welcome to Tasks Manager Dashboard! You can begin by clicking hamburger menu icon above"

    New-UdRow {
        New-UdColumn -Size 6 -Content {
            New-UdRow {
                New-UdColumn -Size 12 -Content {
                    New-UdTable -Title "Server Information" -Headers @(" ", " ") -BackgroundColor "#050f7f" -FontColor "#FFFFFF" -Endpoint {
                        @{
                            'Computer Name'         = $env:COMPUTERNAME
                            'Operating System'      = (Get-CimInstance -ClassName Win32_OperatingSystem).Caption
                            'Total Disk Space (C:)' = (Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DeviceID='C:'").Size / 1GB | ForEach-Object { "$([Math]::Round($_, 2)) GBs " }
                            'Free Disk Space (C:)'  = (Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DeviceID='C:'").FreeSpace / 1GB | ForEach-Object { "$([Math]::Round($_, 2)) GBs " }
                            'Product Version'       = "2.8.3"
                        }.GetEnumerator() | Out-UDTableData -Property @("Name", "Value")
                    }
                }
            }
            New-UdRow {
                New-UdColumn -Size 3 -Content {
                    New-UdChart -Title "Memory by Process" -Type Doughnut -RefreshInterval 5 -Endpoint {
                        Get-Process | ForEach-Object { [PSCustomObject]@{ Name = $_.Name; WorkingSet = [Math]::Round($_.WorkingSet / 1MB, 2) } } | Out-UDChartData -DataProperty "WorkingSet" -LabelProperty Name
                    } -Options @{
                        legend = @{
                            display = $false
                        }
                    }
                }
                New-UdColumn -Size 3 -Content {
                    New-UdChart -Title "CPU by Process" -Type Doughnut -RefreshInterval 5 -Endpoint {
                        Get-Process | ForEach-Object { [PSCustomObject]@{ Name = $_.Name; CPU = $_.CPU } } | Out-UDChartData -DataProperty "CPU" -LabelProperty Name
                    } -Options @{
                        legend = @{
                            display = $false
                        }
                    }
                }
                New-UdColumn -Size 3 -Content {
                    New-UdChart -Title "Handle by Process" -Type Doughnut -RefreshInterval 5 -Endpoint {
                        Get-Process | Out-UDChartData -DataProperty "HandleCount" -LabelProperty Name
                    } -Options @{
                        legend = @{
                            display = $false
                        }
                    }
                }
                New-UdColumn -Size 3 -Content {
                    New-UdChart -Title "Threads by Process" -Type Doughnut -RefreshInterval 5 -Endpoint {
                        Get-Process | ForEach-Object { [PSCustomObject]@{ Name = $_.Name; Threads = $_.Threads.Count } } | Out-UDChartData -DataProperty "Threads" -LabelProperty Name
                    } -Options @{
                        legend = @{
                            display = $false
                        }
                    }
                }
            }
            New-UdRow {
                New-UdColumn -Size 12 -Content {
                    New-UdChart -Title "Disk Space by Drive" -Type Bar -AutoRefresh -Endpoint {
                        Get-CimInstance -ClassName Win32_LogicalDisk | ForEach-Object {
                            [PSCustomObject]@{ DeviceId = $_.DeviceID;
                                Size                    = [Math]::Round($_.Size / 1GB, 2);
                                FreeSpace               = [Math]::Round($_.FreeSpace / 1GB, 2); 
                            } } | Out-UDChartData -LabelProperty "DeviceID" -Dataset @(
                            New-UdChartDataset -DataProperty "Size" -Label "Size" -BackgroundColor "#80962F23" -HoverBackgroundColor "#80962F23"
                            New-UdChartDataset -DataProperty "FreeSpace" -Label "Free Space" -BackgroundColor "#8014558C" -HoverBackgroundColor "#8014558C"
                        )
                    } -Options @{
                        scales = @{
                            yAxes = @(@{
                                    min = 0
                                })
                        }
                    }
                }
            }
        }
        New-UdColumn -Size 6 -Content {
            New-UdRow {
                New-UdColumn -Size 6 -Content {
                    New-UdMonitor -Title "CPU (% processor time)" -Type Line -DataPointHistory 20 -RefreshInterval 5 -ChartBackgroundColor '#80FF6B63' -ChartBorderColor '#FFFF6B63'  -Endpoint {
                        try {
                            Get-Counter '\Processor(_Total)\% Processor Time' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue | Out-UDMonitorData
                        }
                        catch {
                            0 | Out-UDMonitorData
                        }
                    }
                }
                New-UdColumn -Size 6 -Content {
                    New-UdMonitor -Title "SQL CPU (% processor time)" -Type Line -DataPointHistory 20 -RefreshInterval 5 -ChartBackgroundColor '#80FF6B63' -ChartBorderColor '#FFFF6B63'  -Endpoint {
                        try {
                            Get-Counter '\Processor(_Total)\% Processor Time' -ComputerName sqlserver -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue | Out-UDMonitorData
                        }
                        catch {
                            0 | Out-UDMonitorData
                        }
                    }
                }
                New-UdColumn -Size 6 -Content {
                    New-UdMonitor -Title "Memory (% in use)" -Type Line -DataPointHistory 20 -RefreshInterval 5 -ChartBackgroundColor '#8028E842' -ChartBorderColor '#FF28E842'  -Endpoint {
                        try {
                            Get-Counter '\memory\% committed bytes in use' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue | Out-UDMonitorData
                        }
                        catch {
                            0 | Out-UDMonitorData
                        }
                    }
                }
                New-UdColumn -Size 6 -Content {
                    New-UdMonitor -Title "SQL Memory (% in use)" -Type Line -DataPointHistory 20 -RefreshInterval 5 -ChartBackgroundColor '#8028E842' -ChartBorderColor '#FF28E842'  -Endpoint {
                        try {
                            Get-Counter '\memory\% committed bytes in use' -ComputerName sqlserver -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue | Out-UDMonitorData
                        }
                        catch {
                            0 | Out-UDMonitorData
                        }
                    }
                }
            }
            New-UdRow {
                New-UdColumn -Size 6 -Content {
                    New-UdMonitor -Title "Network (IO Read Bytes/sec)" -Type Line -DataPointHistory 20 -RefreshInterval 5 -ChartBackgroundColor '#80E8611D' -ChartBorderColor '#FFE8611D'  -Endpoint {
                        try {
                            Get-Counter '\Process(_Total)\IO Read Bytes/sec' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue | Out-UDMonitorData
                        }
                        catch {
                            0 | Out-UDMonitorData
                        }
                    }
                }
                New-UdColumn -Size 6 -Content {
                    New-UdMonitor -Title "SQL Network (IO Read Bytes/sec)" -Type Line -DataPointHistory 20 -RefreshInterval 5 -ChartBackgroundColor '#80E8611D' -ChartBorderColor '#FFE8611D'  -Endpoint {
                        try {
                            Get-Counter '\Process(_Total)\IO Read Bytes/sec' -ComputerName sqlserver -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue | Out-UDMonitorData
                        }
                        catch {
                            0 | Out-UDMonitorData
                        }
                    }
                }
                New-UdColumn -Size 6 -Content {
                    New-UdMonitor -Title "Disk (% disk time)" -Type Line -DataPointHistory 20 -RefreshInterval 5 -ChartBackgroundColor '#80E8611D' -ChartBorderColor '#FFE8611D'  -Endpoint {
                        try {
                            Get-Counter '\physicaldisk(_total)\% disk time' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue | Out-UDMonitorData
                        }
                        catch {
                            0 | Out-UDMonitorData
                        }
                    }
                }
                New-UdColumn -Size 6 -Content {
                    New-UdMonitor -Title "SQL Disk (% disk time)" -Type Line -DataPointHistory 20 -RefreshInterval 5 -ChartBackgroundColor '#80E8611D' -ChartBorderColor '#FFE8611D'  -Endpoint {
                        try {
                            Get-Counter '\physicaldisk(_total)\% disk time' -ComputerName sqlserver -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue | Out-UDMonitorData
                        }
                        catch {
                            0 | Out-UDMonitorData
                        }
                    }
                }
            }
            New-UdRow {
                New-UdColumn -Size 12 {
                    New-UdGrid -Title "Processes" -Headers @("Name", "ID", "Working Set", "CPU") -Properties @("Name", "Id", "WorkingSet", "CPU") -AutoRefresh -RefreshInterval 60 -Endpoint {
                        Get-Process | Select-Object Name, ID, WorkingSet, CPU | Out-UDGridData
                    }
                }
            }
        }
    }
}



$Page1 = New-UDPage -Name "Services" -Icon server -Content { 

    New-UDScrollUp

    New-UDElement -Id "Services" -Tag div4 -EndPoint {
    
        New-UDGrid -Id "Grid" -Title "Services" -Headers @("Name", "Status", "Select") -Properties @("Name", "Status", "Select") -Endpoint {
        
            $Cache:Services | Out-UDGridData
               
        }
                                 
    }  
                        
    New-UDButtonParticle -Color "#26a69a" -BackgroundColor "#26a69a" -Text "Refresh" -Duration "800" -OnClick {
        Start-Sleep -Seconds 2
        Sync-UDElement -Id "Grid" -Broadcast 
        Start-Sleep -Seconds 2      
    } 
} 
           
    

Start-UDDashboard -Wait -Endpoint @($Services) -Content {  
    New-UDDashboard -Pages @($HomePage, $Page1) -Title "Tasks Manager" -Color '#FF050F7F'
} -Port 80 -Name Tasks

as you can see from the code above i have added a refresh button in the page so when a user click the button to start a service he can click the refresh button which is in return refresh the grid and he can see the status of the service has been changed based on his action and the button changed as well to stop. since the schedule is set for 5 seconds the results will be fast since by the time the user click the refresh button the cache will be updated.
but the downside of this process a memory consumption is high.
if the scheduled time increased for example to every 1 minute, after the user click the button to start a service he has to wait longer until the cache updated before he can see the new status and that might cause confusing.
the ultimate solution will be a schedule for every 1 -day , when user click the button recall the schedule to run 1 time so the cache updated right away and hitting the refresh button will show the new status.

1 Like

I understand what you are describing and that’s definitely a solution but in my opinion it makes sense to ha something like a Invoke-UDScheduledEndpoint in order to have a cleaner code. For it makes more sense to but all the logic in the scheduled endpoint and invoke it whenever I want, rather than to have to create an “external” function … and for many people it is more self explained to manually execute a scheduled endpoint :slight_smile:

2 Likes

And one last addition in order to explain why it would result in bad code (only my opinion):

The scheduled endpoint is having for example a punch of logic (code) - caching variable get/set and in some conditions execute other functions (e.g. Active Directory) and/or update some UD components …

If I would have to create an external function for that I would name it “Invoke-…ScheduledEndpointLogic” and put in the exact same logic maybe used and needed only for this one use case.

And that makes my code much harder to read.