Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ This repository contains Python-based NI SystemLink examples.
### Jupyter examples

The **jupyter** folder contains Jupyter notebook examples that exercise the different SystemLink Services APIs (Tag, File, TDMReader and Test Montor). To be able to run these examples, you need to have the "NI SystemLink Server - JupyterHub Module" installed. From the Jupyter interface in SystemLink, create an examples folder and upload all the content of the jupyter folder. For additional documentation on how to upload/download files to a Jupyter server, check https://jupyterlab.readthedocs.io/en/stable/user/files.html. Once the notebooks are uploaded, follow the instructions on each notebook.

### Asset examples

The **asset** folder contains examples that exercise the Asset Management APIs. The README from the **asset** folder contains instructions on how to run each example.
22 changes: 22 additions & 0 deletions asset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Asset examples

The **asset** folder contains examples that exercise the Asset Management APIs.

## Scripts

### **cpu_triggered_asset_utilization.py**

#### Description

Script used to automatically mark the system as utilized (or a subset of assets from the system) as long as the CPU utilization perecentage goes over a specified limit.

#### Requirements
This script only supports 64bit Windows systems. The script requires the "NI SystemLink Client" to be installed as it interacts with some components of it. For the same reason, the script should be run with the distribution of Python that comes with the "NI SystemLink Client".

#### How to run it

From a command line with administrator privileges run:

`"path-to-Program-Files-directory\National Instruments\Shared\salt-minion\bin\python.exe" cpu_triggered_asset_utilization.py`

Use the *-h* argument to see all possible script configurations.
116 changes: 116 additions & 0 deletions asset/cpu_triggered_asset_utilization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import argparse
import getpass
import sys
import time
import winreg

with winreg.OpenKey(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic should be put into a private function but called globally (since you need the path for an import).

winreg.HKEY_LOCAL_MACHINE,
'SOFTWARE\\National Instruments\\Salt\\Minion\\CurrentVersion',
0,
winreg.KEY_READ) as hkey:
(SALT_COMMON_PATH, _) = winreg.QueryValueEx(hkey, 'CommonAppDataPath')
# This looks like: C:\ProgramData\National Instruments\salt\var\cache\salt\minion\extmods
BEACON_DIR = SALT_COMMON_PATH + 'var\\cache\\salt\\minion\\extmods'

# A helper used by the system monitoring beacon is used by this script.
# It has to be acquired from the beacon installation path.
sys.path.insert(0, BEACON_DIR)

from beacons import _nisysmgmt_health
from systemlink.assetmgmtutilclient import AssetManagementUtilization

# The parsed arguments. Set in :func:`main`.
PARSED_ARGUMENTS = None
# Whether a utilization is currently in progress.
UTILIZATION_IN_PROGRESS = False
# An instance of :class:`AssetManagementUtilization` representing the current utilization, if any.
ONGOING_UTILIZATION = None


def _stop_ongoing_utilization():
global UTILIZATION_IN_PROGRESS
global ONGOING_UTILIZATION
if UTILIZATION_IN_PROGRESS:
ONGOING_UTILIZATION.close()
ONGOING_UTILIZATION = None
UTILIZATION_IN_PROGRESS = False


def _start_or_update_ongoing_utilization():
global UTILIZATION_IN_PROGRESS
global ONGOING_UTILIZATION
assets = PARSED_ARGUMENTS.asset_names
if assets: # checks both None and empty string
assets = [x.strip() for x in assets.split(',')]
if not UTILIZATION_IN_PROGRESS:
ONGOING_UTILIZATION = AssetManagementUtilization(
assets,
PARSED_ARGUMENTS.utilization_category,
PARSED_ARGUMENTS.user_name,
PARSED_ARGUMENTS.task_name)
UTILIZATION_IN_PROGRESS = True
else:
ONGOING_UTILIZATION.update()


def _mark_as_utilized_by_cpu_percentage(cpu_value):
if cpu_value >= PARSED_ARGUMENTS.threshold:
_start_or_update_ongoing_utilization()
else:
_stop_ongoing_utilization()


def _get_mean_cpu_usage():
tag_info = {}
_nisysmgmt_health.setup_tags(tag_info, '')
_nisysmgmt_health.calc_cpu(tag_info)
return tag_info['cpu_mean_perc']['value']


def _check_cpu_triggered_utilization():
cpu_value = _get_mean_cpu_usage()
if PARSED_ARGUMENTS.print_recorded_cpu_values:
print("Mean CPU utilization for the past {} minutes is {}".format(PARSED_ARGUMENTS.interval, cpu_value))
_mark_as_utilized_by_cpu_percentage(cpu_value)


def _minutes_to_seconds(minutes):
return minutes * 60


def loop():

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Private function (prefix with underscore).

'''
This function indefinitely snapshots CPU usage and handles the ongoing utilization.
It uses a try-finally structure to make sure that the ongoing utilization is stopped
when the user ends the script execution.
'''
try:
while True:
_nisysmgmt_health.cpu_usage_snapshot()
time.sleep(_minutes_to_seconds(PARSED_ARGUMENTS.interval))
_check_cpu_triggered_utilization()
finally:
_stop_ongoing_utilization()


def main(arguments=None):
global PARSED_ARGUMENTS
logged_user_name = getpass.getuser()
parser = argparse.ArgumentParser(description = 'CPU usage triggered asset utilization.', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('filename', nargs ='+', action = 'store')
parser.add_argument('--threshold', '-th', type = int, default = 15, help = 'The threshold percentage that triggers the utilization start.')
parser.add_argument('--interval', '-i', type = int, default = 5, help = 'Interval in minutes used to check the CPU usage.')
parser.add_argument('--asset_names', '-a', default = None, help = 'The asset names that will be used in the utilization entries separated by commas.')
parser.add_argument('--utilization_category', '-c', default = 'Test', help = 'The utilization category that will be used in the utilization entries.')
parser.add_argument('--user_name', '-u', default = logged_user_name, help = 'The user name that will be used in the utilization entries. Defaults to the logged user.')
parser.add_argument('--task_name', '-t', default = 'cpu_usage_triggered_utilization', help = 'The task name that will be used in the utilization entries.')
parser.add_argument('--print_recorded_cpu_values', '-p', action = 'store_true', help = 'Prints the CPU usage once for every interval.')
PARSED_ARGUMENTS = parser.parse_args(sys.argv if arguments is None else arguments)
loop()


if __name__ == '__main__':
main()