💻 Bash Scripts, RESTCONF/NETCONF & YANG

Script Interpretation & Model-Driven Programmability

💻 5.9 Identify Bash Script Workflows

Overview

Bash scripts automate repetitive system administration tasks. By reading a bash script, you can identify what workflow is being automated such as file management, application installation, user management, or directory navigation.

graph TD A["Bash Script Workflows"] --> B["File Management"] A --> C["App Installation"] A --> D["User Management"] A --> E["Directory Navigation"] B --> B1["Create/Copy/Move"] B --> B2["Delete/Archive"] B --> B3["Permissions"] C --> C1["apt/yum/dnf"] C --> C2["wget/curl"] C --> C3["tar/unzip"] D --> D1["useradd/usermod"] D --> D2["passwd/groups"] D --> D3["SSH keys"] E --> E1["cd/pwd"] E --> E2["find/locate"] E --> E3["mkdir -p"] style A fill:#667eea,stroke:#764ba2,stroke-width:3px,color:#fff style B fill:#66bb6a,stroke:#2e7d32,stroke-width:2px,color:#fff style C fill:#42a5f5,stroke:#1976d2,stroke-width:2px,color:#fff style D fill:#ffa726,stroke:#e65100,stroke-width:2px,color:#fff style E fill:#ab47bc,stroke:#6a1b9a,stroke-width:2px,color:#fff

1. File Management Workflow

📁 Creating, Copying, Moving, and Deleting Files

Workflow Identification: Bash scripts that manage files and directories

#!/bin/bash
# File Management Script

# Create directory structure
echo "Creating directory structure..."
mkdir -p /var/backups/daily
mkdir -p /var/backups/weekly
mkdir -p /var/backups/monthly

# Copy configuration files
echo "Backing up configuration files..."
cp /etc/nginx/nginx.conf /var/backups/daily/nginx.conf.$(date +%Y%m%d)
cp /etc/mysql/my.cnf /var/backups/daily/mysql.conf.$(date +%Y%m%d)

# Move old logs to archive
echo "Archiving old logs..."
find /var/log/app -name "*.log" -mtime +7 -exec mv {} /var/backups/weekly/ \;

# Delete files older than 30 days
echo "Cleaning up old backups..."
find /var/backups/daily -mtime +30 -delete

# Set permissions
chmod 644 /var/backups/daily/*
chown root:root /var/backups/daily/*

# Create compressed archive
tar -czf /var/backups/monthly/config-$(date +%Y%m).tar.gz /etc/nginx /etc/mysql

echo "File management complete!"

# WORKFLOW IDENTIFIED:
# 1. Create directory structure with mkdir -p
# 2. Copy files with timestamp
# 3. Move old files to archive using find
# 4. Delete files older than 30 days
# 5. Set file permissions (chmod/chown)
# 6. Create compressed archives

Common File Management Commands

Command Purpose Example
cp Copy files cp file.txt backup/
mv Move/rename files mv old.txt new.txt
rm Remove files rm -rf /tmp/old/
mkdir Create directories mkdir -p /path/to/dir
chmod Change permissions chmod 755 script.sh
chown Change ownership chown user:group file

2. Application Installation Workflow

📥 Installing and Configuring Applications

Workflow Identification: Scripts that download, install, and configure software

#!/bin/bash
# Application Installation Script

# Update package lists
echo "Updating package lists..."
apt-get update -y

# Install prerequisites
echo "Installing prerequisites..."
apt-get install -y curl wget git build-essential

# Download application
echo "Downloading application..."
cd /tmp
wget https://example.com/app/releases/app-v1.2.3.tar.gz

# Extract archive
echo "Extracting application..."
tar -xzf app-v1.2.3.tar.gz
cd app-v1.2.3

# Compile and install
echo "Compiling application..."
./configure --prefix=/usr/local
make
make install

# Install Python dependencies
echo "Installing Python dependencies..."
pip3 install -r requirements.txt

# Install Node.js application
echo "Installing Node.js app..."
npm install -g pm2
npm install --production

# Create systemd service
cat > /etc/systemd/system/myapp.service << EOF
[Unit]
Description=My Application
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/node /opt/myapp/server.js
Restart=always

[Install]
WantedBy=multi-user.target
EOF

# Enable and start service
systemctl daemon-reload
systemctl enable myapp
systemctl start myapp

echo "Application installed successfully!"

# WORKFLOW IDENTIFIED:
# 1. Update system packages (apt-get update)
# 2. Install prerequisites
# 3. Download application (wget/curl)
# 4. Extract archive (tar)
# 5. Compile from source (./configure, make)
# 6. Install dependencies (pip, npm)
# 7. Create systemd service
# 8. Enable and start service

Installation Commands

Command Purpose Package Manager
apt-get install Install packages Debian/Ubuntu
yum install Install packages RHEL/CentOS
dnf install Install packages Fedora/RHEL 8+
wget/curl Download files HTTP/FTP
tar Extract archives Compression
pip/npm Language packages Python/Node.js

3. User Management Workflow

👥 Creating and Managing Users

Workflow Identification: Scripts that create, modify, or delete user accounts

#!/bin/bash
# User Management Script

# Create new user
echo "Creating user..."
useradd -m -s /bin/bash -c "Application User" appuser

# Set password
echo "appuser:SecureP@ss123" | chpasswd

# Create additional users from list
USERS=("devuser1" "devuser2" "devuser3")
for user in "${USERS[@]}"; do
    useradd -m -s /bin/bash "$user"
    echo "Created user: $user"
done

# Add user to groups
usermod -aG sudo,docker appuser

# Create SSH directory and set permissions
mkdir -p /home/appuser/.ssh
chmod 700 /home/appuser/.ssh

# Add SSH public key
cat > /home/appuser/.ssh/authorized_keys << EOF
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABg... user@hostname
EOF

chmod 600 /home/appuser/.ssh/authorized_keys
chown -R appuser:appuser /home/appuser/.ssh

# Modify existing user
usermod -s /bin/zsh devuser1
usermod -d /opt/devuser1 -m devuser1

# Lock/unlock user accounts
usermod -L tempuser    # Lock account
usermod -U tempuser    # Unlock account

# Delete user
userdel -r olduser     # Remove user and home directory

# Set password expiration
chage -M 90 appuser    # Password expires in 90 days
chage -l appuser       # List password aging information

echo "User management complete!"

# WORKFLOW IDENTIFIED:
# 1. Create users with useradd
# 2. Set passwords with chpasswd
# 3. Add users to groups with usermod -aG
# 4. Configure SSH keys
# 5. Modify user properties (shell, home directory)
# 6. Lock/unlock accounts
# 7. Delete users with userdel

User Management Commands

Command Purpose Example
useradd Create user useradd -m username
usermod Modify user usermod -aG group user
userdel Delete user userdel -r username
passwd Set password passwd username
chage Password aging chage -M 90 user
groups Show user groups groups username

4. Directory Navigation Workflow

🧭 Navigating and Searching Directories

Workflow Identification: Scripts that navigate filesystem and find files

#!/bin/bash
# Directory Navigation Script

# Store current directory
CURRENT_DIR=$(pwd)
echo "Starting from: $CURRENT_DIR"

# Change to home directory
cd ~
echo "Home directory: $(pwd)"

# Change to specific directory
cd /var/log || exit 1

# Go up one directory
cd ..
echo "Parent directory: $(pwd)"

# Go back to previous directory
cd -

# Find files by name
echo "Finding log files..."
find /var/log -name "*.log" -type f

# Find files modified in last 7 days
find /var/log -mtime -7 -type f

# Find and execute command on files
find /tmp -name "*.tmp" -mtime +30 -exec rm -f {} \;

# Find large files (>100MB)
find /home -size +100M -type f

# Locate command (faster search)
locate nginx.conf

# Update locate database
updatedb

# Search in multiple directories
for dir in /etc /var /opt; do
    if [ -d "$dir" ]; then
        echo "Searching in $dir..."
        find "$dir" -name "config.*" -type f 2>/dev/null
    fi
done

# Create directory path if it doesn't exist
mkdir -p /opt/myapp/config/production
mkdir -p /opt/myapp/logs
mkdir -p /opt/myapp/data

# Navigate and create project structure
cd /opt/myapp
mkdir -p {bin,lib,config,logs,data,tmp}

# Return to original directory
cd "$CURRENT_DIR"

echo "Directory navigation complete!"

# WORKFLOW IDENTIFIED:
# 1. Navigate directories (cd, pwd)
# 2. Find files by criteria (find command)
# 3. Search for files (locate)
# 4. Create directory structures (mkdir -p)
# 5. Execute commands on found files
# 6. Store and return to directories

Navigation Commands

Command Purpose Example
cd Change directory cd /var/log
pwd Print working directory pwd
find Search for files find / -name "*.conf"
locate Fast file search locate filename
ls List directory contents ls -lah
tree Display directory tree tree -L 2 /etc

🔍 5.10 Interpret RESTCONF/NETCONF Query Results

Overview

RESTCONF and NETCONF queries return structured data (JSON/XML) that represents device configuration and operational state. Interpreting these results requires understanding the data format, hierarchy, and meaning of each field.

graph LR A["Query"] --> B["RESTCONF
HTTP GET"] A --> C["NETCONF
RPC Request"] B --> D["JSON/XML Response"] C --> D D --> E["Parse Data"] E --> F["Interpret Structure"] F --> G["Extract Values"] style A fill:#667eea,stroke:#764ba2,stroke-width:3px,color:#fff style B fill:#66bb6a,stroke:#2e7d32,stroke-width:2px,color:#fff style C fill:#42a5f5,stroke:#1976d2,stroke-width:2px,color:#fff style G fill:#ffa726,stroke:#e65100,stroke-width:2px,color:#fff

1. RESTCONF JSON Response - Interfaces

📄 Interpreting Interface Configuration (JSON)

Query: GET /restconf/data/ietf-interfaces:interfaces

{
  "ietf-interfaces:interfaces": {
    "interface": [
      {
        "name": "GigabitEthernet1",
        "description": "WAN Interface to ISP",
        "type": "iana-if-type:ethernetCsmacd",
        "enabled": true,
        "ietf-ip:ipv4": {
          "address": [
            {
              "ip": "203.0.113.1",
              "netmask": "255.255.255.252"
            }
          ]
        },
        "ietf-ip:ipv6": {
          "enabled": false
        }
      },
      {
        "name": "GigabitEthernet2",
        "description": "LAN Interface",
        "type": "iana-if-type:ethernetCsmacd",
        "enabled": true,
        "ietf-ip:ipv4": {
          "address": [
            {
              "ip": "192.168.1.1",
              "netmask": "255.255.255.0"
            }
          ]
        }
      }
    ]
  }
}

# INTERPRETATION:
# - Root: ietf-interfaces:interfaces container
# - Two interfaces configured: GigabitEthernet1 and GigabitEthernet2
# - GigabitEthernet1:
#   * Public IP: 203.0.113.1/30 (WAN connection)
#   * Description indicates ISP connection
#   * Interface is enabled (operational)
#   * IPv6 is disabled
# - GigabitEthernet2:
#   * Private IP: 192.168.1.1/24 (LAN connection)
#   * Interface is enabled
#   * Likely serves as default gateway for internal network

Key Fields Explained

Field Meaning
name Interface identifier (e.g., GigabitEthernet1)
description Human-readable interface purpose
type Interface type (Ethernet, Serial, Loopback, etc.)
enabled Administrative status (true=up, false=down)
ipv4/ipv6 IP address configuration and settings

2. NETCONF XML Response - Configuration

📜 Interpreting Device Configuration (XML)

Query: NETCONF <get-config> operation

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
           message-id="101">
  <data>
    <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
      <interface>
        <name>GigabitEthernet1</name>
        <description>Management Interface</description>
        <type>ianaift:ethernetCsmacd</type>
        <enabled>true</enabled>
        <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
          <address>
            <ip>10.10.10.1</ip>
            <netmask>255.255.255.0</netmask>
          </address>
        </ipv4>
      </interface>
    </interfaces>
    <routing xmlns="urn:ietf:params:xml:ns:yang:ietf-routing">
      <routing-instance>
        <name>default</name>
        <type>rt:default-routing-instance</type>
        <router-id>10.10.10.1</router-id>
        <routing-protocols>
          <routing-protocol>
            <type>rt:ospf</type>
            <name>ospf-1</name>
          </routing-protocol>
        </routing-protocols>
      </routing-instance>
    </routing>
  </data>
</rpc-reply>

# INTERPRETATION:
# - RPC Reply to message-id 101 (successful response)
# - Interface Configuration:
#   * GigabitEthernet1 configured as Management Interface
#   * IP address: 10.10.10.1/24
#   * Interface is administratively up (enabled: true)
#   * Type: Ethernet interface
# - Routing Configuration:
#   * Default routing instance active
#   * Router ID: 10.10.10.1
#   * OSPF protocol enabled (ospf-1)
#   * Using default VRF

XML Structure Elements

Element Purpose
<rpc-reply> Root element for NETCONF response
<data> Contains configuration/operational data
xmlns XML namespace (identifies YANG model)
message-id Matches request with response
Nested elements Hierarchical data structure

3. Operational State Data (RESTCONF)

📊 Interpreting Live Device Statistics

Query: GET /restconf/data/ietf-interfaces:interfaces-state

{
  "ietf-interfaces:interfaces-state": {
    "interface": [
      {
        "name": "GigabitEthernet1",
        "type": "iana-if-type:ethernetCsmacd",
        "admin-status": "up",
        "oper-status": "up",
        "if-index": 1,
        "phys-address": "00:1a:2b:3c:4d:5e",
        "speed": 1000000000,
        "statistics": {
          "in-octets": 1234567890,
          "in-unicast-pkts": 9876543,
          "in-errors": 0,
          "in-discards": 0,
          "out-octets": 9876543210,
          "out-unicast-pkts": 8765432,
          "out-errors": 0,
          "out-discards": 2
        }
      }
    ]
  }
}

# INTERPRETATION:
# Interface Status:
#   * Administrative status: UP (enabled by administrator)
#   * Operational status: UP (physically connected and working)
#   * Speed: 1 Gbps (1000000000 bits/second)
#   * MAC Address: 00:1a:2b:3c:4d:5e
#
# Traffic Statistics:
#   * Received: ~1.23 GB (1,234,567,890 octets)
#   * Received packets: 9,876,543 unicast packets
#   * No input errors or discards (healthy)
#   * Transmitted: ~9.87 GB (9,876,543,210 octets)
#   * Transmitted packets: 8,765,432
#   * No output errors, 2 discarded packets (minimal)
#
# Health Assessment: Interface is healthy and operational

Status Values

Field Possible Values Meaning
admin-status up, down, testing Administratively configured state
oper-status up, down, testing, unknown Current operational state
in-errors integer Count of inbound errors (CRC, etc.)
in-discards integer Packets discarded (buffer full, etc.)

4. Error Response Interpretation

⚠️ Understanding Error Messages

Example 1: NETCONF Error Response

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
           message-id="102">
  <rpc-error>
    <error-type>protocol</error-type>
    <error-tag>invalid-value</error-tag>
    <error-severity>error</error-severity>
    <error-message>
      Invalid IP address format: 192.168.1.256
    </error-message>
    <error-info>
      <bad-element>ip</bad-element>
    </error-info>
  </rpc-error>
</rpc-reply>

# INTERPRETATION:
# - Error Type: protocol (validation error)
# - Error Tag: invalid-value (data doesn't meet constraints)
# - Severity: error (operation failed)
# - Message: IP address 192.168.1.256 is invalid (octet > 255)
# - Bad Element: "ip" field contains the error
#
# Action Required: Correct IP address to valid format (0-255 per octet)

Example 2: RESTCONF HTTP Error

HTTP/1.1 404 Not Found
Content-Type: application/yang-data+json

{
  "ietf-restconf:errors": {
    "error": [
      {
        "error-type": "application",
        "error-tag": "invalid-value",
        "error-path": "/ietf-interfaces:interfaces/interface[name='GigabitEthernet99']",
        "error-message": "Interface GigabitEthernet99 does not exist"
      }
    ]
  }
}

# INTERPRETATION:
# - HTTP Status: 404 Not Found
# - Error Type: application (resource doesn't exist)
# - Error Tag: invalid-value
# - Error Path: Shows exact location in YANG tree
# - Message: Interface GigabitEthernet99 doesn't exist on device
#
# Action Required: Verify interface name or check available interfaces first

Common Error Types

Error Type Meaning Common Causes
protocol Protocol layer error Invalid XML/JSON, malformed request
application Application layer error Resource not found, access denied
rpc RPC layer error Operation not supported
transport Transport layer error Connection issues, timeouts

📐 5.11 Interpret Basic YANG Models

Overview

YANG (Yet Another Next Generation) is a data modeling language used to model configuration and state data. Reading YANG models helps understand the structure, data types, and constraints of network device configurations accessible via NETCONF/RESTCONF.

graph TD A["YANG Model"] --> B["Containers"] A --> C["Lists"] A --> D["Leafs"] A --> E["Leaf-Lists"] B --> B1["Grouping Elements"] C --> C1["Repeating Structures"] D --> D1["Single Values"] E --> E1["Multiple Values"] style A fill:#667eea,stroke:#764ba2,stroke-width:3px,color:#fff style B fill:#66bb6a,stroke:#2e7d32,stroke-width:2px,color:#fff style C fill:#42a5f5,stroke:#1976d2,stroke-width:2px,color:#fff style D fill:#ffa726,stroke:#e65100,stroke-width:2px,color:#fff style E fill:#ab47bc,stroke:#6a1b9a,stroke-width:2px,color:#fff

1. Basic YANG Module Structure

📄 Understanding YANG Module Components

Example: Basic interface YANG model

---
- name: Package Management Workflow
  hosts: webservers
  become: yes
  
  tasks:
    # Install multiple packages
    - name: Install Apache and PHP
      apt:
        name:
          - apache2
          - php
          - php-mysql
        state: present
        update_cache: yes
    
    # Update specific package
    - name: Ensure Git is latest version
      apt:
        name: git
        state: latest
    
    # Remove unwanted package
    - name: Remove telnet
      apt:
        name: telnet
        state: absent

# WORKFLOW IDENTIFIED:
# 1. Install multiple packages (apache2, php, php-mysql)
# 2. Update existing package to latest version (git)
# 3. Remove security risk package (telnet)
# 4. Update package cache before installing

Key Package Modules

  • apt: Debian/Ubuntu package management
  • yum/dnf: RHEL/CentOS package management
  • package: Generic package manager
  • pip: Python package installation

2. User Management Workflow

👥 Creating, Modifying, and Managing Users

Workflow Identification: User account lifecycle management and access control

---
- name: User Management Workflow
  hosts: all
  become: yes
  
  tasks:
    # Create new user with specific settings
    - name: Create application user
      user:
        name: appuser
        comment: "Application Service Account"
        uid: 5000
        group: appgroup
        groups: docker,sudo
        shell: /bin/bash
        create_home: yes
        state: present
    
    # Add SSH key for user
    - name: Add SSH key for appuser
      authorized_key:
        user: appuser
        key: "{{ lookup('file', 'files/appuser_key.pub') }}"
        state: present
    
    # Modify existing user
    - name: Update user shell
      user:
        name: devuser
        shell: /bin/zsh
    
    # Remove user
    - name: Remove temporary user
      user:
        name: tempuser
        state: absent
        remove: yes

# WORKFLOW IDENTIFIED:
# 1. Create new user with UID, groups, and home directory
# 2. Configure SSH key authentication
# 3. Modify existing user properties
# 4. Remove user and associated files

User Management Modules

  • user: Create, modify, delete user accounts
  • group: Manage user groups
  • authorized_key: Manage SSH authorized keys
  • lineinfile: Modify configuration files (sudoers, etc.)

3. Service Configuration Workflow

⚙️ Deploying and Configuring Services

Workflow Identification: Service deployment with configuration file management

---
- name: Service Configuration Workflow
  hosts: webservers
  become: yes
  
  tasks:
    # Copy configuration file
    - name: Deploy Nginx configuration
      copy:
        src: files/nginx.conf
        dest: /etc/nginx/nginx.conf
        owner: root
        group: root
        mode: '0644'
        backup: yes
      notify: Restart Nginx
    
    # Use template for dynamic config
    - name: Deploy Apache vhost from template
      template:
        src: templates/vhost.conf.j2
        dest: /etc/apache2/sites-available/{{ domain }}.conf
        owner: root
        group: root
        mode: '0644'
      vars:
        domain: example.com
        doc_root: /var/www/example
    
    # Modify configuration file
    - name: Set max_connections in MySQL
      lineinfile:
        path: /etc/mysql/my.cnf
        regexp: '^max_connections'
        line: 'max_connections = 500'
        backup: yes
      notify: Restart MySQL
  
  handlers:
    - name: Restart Nginx
      service:
        name: nginx
        state: restarted
    
    - name: Restart MySQL
      service:
        name: mysql
        state: restarted

# WORKFLOW IDENTIFIED:
# 1. Deploy static configuration files
# 2. Generate configuration from templates
# 3. Modify existing configuration values
# 4. Trigger service restart when config changes

Configuration Modules

  • copy: Copy files from controller to hosts
  • template: Deploy Jinja2 templates with variables
  • lineinfile: Modify single lines in files
  • blockinfile: Insert/update/remove text blocks

4. Service Start/Stop Workflow

🔄 Service Lifecycle Management

Workflow Identification: Starting, stopping, restarting, and enabling services

---
- name: Service Lifecycle Workflow
  hosts: all
  become: yes
  
  tasks:
    # Start service
    - name: Start Apache service
      service:
        name: apache2
        state: started
    
    # Stop service
    - name: Stop old application service
      service:
        name: legacy-app
        state: stopped
    
    # Restart service
    - name: Restart Nginx after config change
      service:
        name: nginx
        state: restarted
    
    # Reload service (graceful restart)
    - name: Reload Apache configuration
      service:
        name: apache2
        state: reloaded
    
    # Enable service at boot
    - name: Enable Docker service
      service:
        name: docker
        enabled: yes
        state: started
    
    # Disable service at boot
    - name: Disable unnecessary service
      service:
        name: bluetooth
        enabled: no
        state: stopped

# WORKFLOW IDENTIFIED:
# 1. Start services that should be running
# 2. Stop services that should not be running
# 3. Restart services after configuration changes
# 4. Enable/disable services at system boot
# 5. Use reload for graceful restarts

Service States

  • started: Ensure service is running
  • stopped: Ensure service is not running
  • restarted: Stop and start service
  • reloaded: Reload configuration without stopping
  • enabled: Start service at boot
  • disabled: Don't start service at boot

Complete Ansible Workflow Example

🎯 Combined Workflow: Web Server Deployment

Workflow Identification: Complete server setup combining all four workflow types

---
- name: Complete Web Server Deployment
  hosts: webservers
  become: yes
  
  tasks:
    # 1. PACKAGE MANAGEMENT
    - name: Install required packages
      apt:
        name:
          - nginx
          - php-fpm
          - git
        state: present
        update_cache: yes
    
    # 2. USER MANAGEMENT
    - name: Create web application user
      user:
        name: webapp
        comment: "Web Application User"
        shell: /bin/bash
        create_home: yes
    
    - name: Add webapp user to www-data group
      user:
        name: webapp
        groups: www-data
        append: yes
    
    # 3. SERVICE CONFIGURATION
    - name: Deploy Nginx configuration
      template:
        src: templates/nginx-site.conf.j2
        dest: /etc/nginx/sites-available/webapp.conf
        owner: root
        group: root
        mode: '0644'
      notify: Reload Nginx
    
    - name: Enable Nginx site
      file:
        src: /etc/nginx/sites-available/webapp.conf
        dest: /etc/nginx/sites-enabled/webapp.conf
        state: link
      notify: Reload Nginx
    
    # 4. SERVICE START/STOP
    - name: Ensure Nginx is running and enabled
      service:
        name: nginx
        state: started
        enabled: yes
    
    - name: Ensure PHP-FPM is running
      service:
        name: php7.4-fpm
        state: started
        enabled: yes
  
  handlers:
    - name: Reload Nginx
      service:
        name: nginx
        state: reloaded

# COMPLETE WORKFLOW IDENTIFIED:
# 1. Install web server packages (nginx, php-fpm, git)
# 2. Create dedicated application user and add to groups
# 3. Deploy configuration files from templates
# 4. Enable site and create symlinks
# 5. Start and enable services at boot
# 6. Define handlers for graceful service reloads

🐍 Interactive Python Interpreter

💡 How to use:
  • Write Python code in the editor
  • Click "▶ Run Code" to execute
  • Try sample code buttons
📝 Python Code Editor
⚡ Console Output
Ready to execute Python code...