Creation of new functionality for developers¶
When developing new functionality on the part of thin client administrators, it is necessary to:
- Create state/script with the required functionality;
- Test the work of state/script at the stand;
- Place files similarly to the stand in tkcontrol-configure/tkcontrol_configure/config/saltmaster;
- Update production server configuration.
When developing new functionality from the side of the external interface of the web application, you must:
- Create form;
- Send POST request to /hosts or /groups.
Example of development cycle for new functionality¶
We create the ability to send users a notification with a request to turn off the computer.
Go to the salt-master test bench;
Add script /srv/salt/states/files/notify-shutdown.sh which will display notification:
#!/bin/bash # Get xauthority location AUTH=$(ps aux | grep "\-auth " | head -n 1) AUTH=${AUTH/*\-auth /} AUTH=${AUTH/ */} # Declare zenity variables declare -x XAUTHORITY=$AUTH declare -x LC_ALL="ru_RU.utf8" declare -x DISPLAY=":0" title="Notification" text="Dear user, the administrator asks you to turn off your computer!" zenity \ --info \ --title "$title" \ --text "$text" \ --width 500
Check the script work:
sudo salt 'skupov-stagin-tkcontrol-client' cmd.script salt://files/notify-shutdown.sh
Add button to the front-end with call to this script:
<v-btn @click="sendShutdownNotify(hostname)"> Send shutdown notify </v-btn> ... async sendShutdownNotify(hostname) { await axios.post(`/api/${hostname}`, { salt_client: 'local_async', fun: 'cmd.script', arg: ['salt://files/notify-shutdown.sh'], }) }
If everything went well, move the script from salt-master /srv/salt/states/files/notify-shutdown.sh to tkcontrol-configure/tkcontrol_configure/config/saltmaster;
After assembling the packages, when configuring salt-master, our script will be installed:
sudo tkcontrol-configure salt-master
Example of cycle for receiving and storing data in database¶
Add the schema of the new collection to tkcontrol-modules/tkcontrol_modules/eve/schemes. Create file hosts_statuses.py with the following code (you can learn about all variations of the circuit at https://docs.python-eve.org/en/stable/config.html):
schema = { '_id': { 'type': 'string', 'unique': True, 'required': True, }, 'status': { 'type': 'string', 'required': True, 'allowed': ['up', 'down'], }, } hosts_statuses = { # By default, the pattern is used for ObjectId, replace it with any сharacters other than spaces 'item_url': 'regex("[\S]+")', 'schema': schema, }
Import the collection schema in schemes/__init__:
from .hosts_statuses import hosts_statuses
Register the scheme in dbadapter/tkcontrol_dbadapter/config/mongo_settings.py and in backend/tkcontrol_backend/config/mongo_settings.py:
from tkcontrol_modules.eve.schemes import hosts_statuses ... DOMAIN = { ... 'hosts_statuses': hosts_statuses }
At this stage, we have the ability to read data from the backend and read/create/modify data from dbadapter.
Add command call to dbsync_service services/tkcontrol_services/services/dbsync_services.py:
# We send command every 600 seconds. if self._timer.time_to_refresh(600): self._salt_request(self._ping_request, request_name='ping')
Create a filter services/tkcontrol_services/senders/filters/ping_filter.py to extract data from salt events bus:
from .filter import Filter class PingFilter(Filter): def __init__(self): super().__init__() # Command runs from runnser_async self._allowed_types = ['run'] # Name of the command we are running self._allowed_functions = ['runner.manage.status'] # We only care about the return value self._allowed_statuses = ['ret'] # Meta information for updating/creating documents self._db_tag = { 'method': 'patch', # Initially trying to update data 'collection': 'hosts_statuses', # Collection name 'force': True, # If the record cannot be updated (it does not exist), then we create record } # Before calling this function, Sender sets the value of the message being processed to self._ev_msg def process(self): # Retrie data from salt event up_hostnames = self._ev_msg.data['return']['up'] down_hostnames = self._ev_msg.data['return']['down'] # We generate useful data for uploading to the database. Best practice is outputing processing logic into a separate serializer, see key_filter for example payloads = [] for hostname in up_hostnames: payloads.append({ 'data': {'_id': hostname, 'status': 'up'}, 'lookup': hostname, }) for hostname in up_hostnames: payloads.append({ 'data': {'_id': hostname, 'status': 'down'}, 'lookup': hostname, }) return self._db_tag, payloads
We cover the filter with tests. We look at analogs in services/test/senders/filters/test_key_filter;
Add filter to the application assembly services/tkcontrol_services/app:
def create_events_bus_service(config, salt_api) -> Service: ... ping_filter = PingFilter() ... db_sender.add_filter(ping_filter)
After restarting dbadapter, backend, services services will start collecting and saving data in dbadapter, and backend will be able to display this data.