|
| 1 | +## Overview |
| 2 | + |
| 3 | +`adminapi` is a Python library for interacting with Serveradmin, a system for querying and managing server attributes. It provides both a Python API and a CLI tool for server management operations. |
| 4 | + |
| 5 | +Documentation: https://serveradmin.readthedocs.io/en/latest/python-api.html |
| 6 | + |
| 7 | +## Development Commands |
| 8 | + |
| 9 | +### Setup |
| 10 | +```bash |
| 11 | +# Install from parent directory, like |
| 12 | +cd ~/projects/serveradmin |
| 13 | +pip install -e . |
| 14 | +``` |
| 15 | + |
| 16 | +### Testing |
| 17 | +```bash |
| 18 | +# Run all tests |
| 19 | +python -m unittest discover adminapi/tests |
| 20 | + |
| 21 | +# Run specific test file |
| 22 | +python -m unittest adminapi.tests.test_cli |
| 23 | +python -m unittest adminapi.tests.test_dataset |
| 24 | + |
| 25 | +# Run single test |
| 26 | +python -m unittest adminapi.tests.test_cli.TestCommandlineInterface.test_one_argument |
| 27 | +``` |
| 28 | + |
| 29 | +### CLI Usage |
| 30 | +```bash |
| 31 | +# Query servers |
| 32 | +adminapi 'hostname=web01' |
| 33 | +adminapi 'project=adminapi' --attr hostname --attr state |
| 34 | + |
| 35 | +# Update servers |
| 36 | +adminapi 'hostname=web01' --update state=maintenance |
| 37 | +``` |
| 38 | + |
| 39 | +### Python API Examples |
| 40 | + |
| 41 | +**Query and modify servers** |
| 42 | +```python |
| 43 | +from adminapi.dataset import Query |
| 44 | + |
| 45 | +# Query servers matching criteria |
| 46 | +servers = Query({ |
| 47 | + 'servertype': 'vm', |
| 48 | + 'project': 'test_project', |
| 49 | + 'state': 'offline', |
| 50 | +}) |
| 51 | + |
| 52 | +# Modify server attributes |
| 53 | +for server in servers: |
| 54 | + print(f"Bringing {server['hostname']} online") |
| 55 | + server['state'] = 'online' |
| 56 | + |
| 57 | +# Commit changes to persist them |
| 58 | +servers.commit() |
| 59 | +``` |
| 60 | + |
| 61 | +**Query with restricted attributes** |
| 62 | +```python |
| 63 | +from adminapi.dataset import Query |
| 64 | + |
| 65 | +# Only fetch specific attributes to reduce payload |
| 66 | +servers = Query( |
| 67 | + {'project': 'test_project', 'state': 'maintenance'}, |
| 68 | + restrict=['hostname', 'state'] |
| 69 | +) |
| 70 | + |
| 71 | +for server in servers: |
| 72 | + print(f"{server['hostname']}: {server['state']}") |
| 73 | +``` |
| 74 | + |
| 75 | +**Get single server** |
| 76 | +```python |
| 77 | +from adminapi.dataset import Query |
| 78 | + |
| 79 | +# Use get() when expecting exactly one result |
| 80 | +server = Query({'hostname': 'web01'}).get() |
| 81 | +server['state'] = 'maintenance' |
| 82 | +server.commit() |
| 83 | +``` |
| 84 | + |
| 85 | +## Architecture |
| 86 | + |
| 87 | +### Core Components |
| 88 | + |
| 89 | +**Query System** (`dataset.py`) |
| 90 | +- `Query`: Main class for filtering and fetching server objects from Serveradmin |
| 91 | +- `BaseQuery`: Base class with common query operations (filtering, ordering, committing) |
| 92 | +- Queries are lazy-loaded and cached until explicitly fetched |
| 93 | +- Always ensures `object_id` is included in restrict lists for correlation during commits |
| 94 | + |
| 95 | +**Data Objects** (`dataset.py`) |
| 96 | +- `DatasetObject`: Dict-like wrapper for server data with change tracking |
| 97 | +- `MultiAttr`: Set-like wrapper for multi-value attributes with automatic change propagation |
| 98 | +- Track state: `created`, `changed`, `deleted`, or `consistent` |
| 99 | +- Old values stored in `old_values` dict for rollback/commit operations |
| 100 | + |
| 101 | +**Filters** (`filters.py`) |
| 102 | +- `BaseFilter`: Exact match filter (default) |
| 103 | +- Comparison: `GreaterThan`, `LessThan`, `GreaterThanOrEquals`, `LessThanOrEquals` |
| 104 | +- Pattern: `Regexp`, `StartsWith`, `Contains`, `ContainedBy`, `Overlaps` |
| 105 | +- Logical: `Any` (OR), `All` (AND), `Not` |
| 106 | +- Special: `Empty` (null check), `ContainedOnlyBy` (IP network containment) |
| 107 | + |
| 108 | +**Request Layer** (`request.py`) |
| 109 | +- Handles HTTP communication with Serveradmin server |
| 110 | +- Authentication via SSH keys (paramiko), SSH agent, or auth tokens (HMAC-SHA1) |
| 111 | +- Automatic retry logic (3 attempts, 5s interval) for network failures |
| 112 | +- Gzip compression support for responses |
| 113 | +- Environment variables: `SERVERADMIN_BASE_URL`, `SERVERADMIN_KEY_PATH`, `SERVERADMIN_TOKEN` |
| 114 | + |
| 115 | +**Query Parser** (`parse.py`) |
| 116 | +- Converts string queries to filter dictionaries |
| 117 | +- Supports function syntax: `attribute=Function(value)` |
| 118 | +- Regex detection: triggers `Regexp` filter for patterns with `.*`, `.+`, `[`, `]`, etc. |
| 119 | +- Hostname shorthand: first token without `=` treated as hostname filter |
| 120 | + |
| 121 | +**API Calls** (`api.py`) |
| 122 | +- `FunctionGroup`: Dynamic proxy for calling Serveradmin API functions |
| 123 | +- Pattern: `FunctionGroup('group_name').function_name(*args, **kwargs)` |
| 124 | +- Example: `api.get('nagios').commit('push', 'user', project='foo')` |
| 125 | + |
| 126 | +### Data Flow |
| 127 | + |
| 128 | +1. **Query Construction**: Create `Query` with filters dict, restrict list, order_by |
| 129 | +2. **Lazy Fetch**: Results fetched on first iteration/access via `_get_results()` |
| 130 | +3. **Modification**: Change `DatasetObject` attributes, tracked in `old_values` |
| 131 | +4. **Commit**: Build commit object with created/changed/deleted arrays, send to server |
| 132 | +5. **Confirm**: Clear `old_values`, update object state after successful commit |
| 133 | + |
| 134 | +### Change Tracking |
| 135 | + |
| 136 | +- **Single attributes**: Save original value on first modification to `old_values` |
| 137 | +- **Multi attributes**: `MultiAttr` operations create new sets, trigger parent `__setitem__` |
| 138 | +- **Validation**: Type checking against existing attribute types (bool, multi, datatype) |
| 139 | +- **Serialization**: Changed objects serialize with action (`update` or `multi`) and old/new values |
| 140 | + |
| 141 | +### Authentication Flow |
| 142 | + |
| 143 | +1. Check for `SERVERADMIN_KEY_PATH` → load private key file |
| 144 | +2. Else check for `SERVERADMIN_TOKEN` → use HMAC authentication |
| 145 | +3. Else try SSH agent (`paramiko.agent.Agent`) |
| 146 | +4. Sign timestamp + request body, include signature in `X-Signatures` header |
| 147 | +5. Server validates signature using stored public key |
| 148 | + |
| 149 | +## Important Patterns |
| 150 | + |
| 151 | +- Always use `commit()` after modifying objects to persist changes |
| 152 | +- Use `restrict` parameter to limit fetched attributes (reduces payload size) |
| 153 | +- `get()` expects exactly one result; use for single-server queries |
| 154 | +- Multi-value attributes must be modified via `MultiAttr` methods or reassignment |
| 155 | +- Query filters are immutable; changes create new filter instances |
| 156 | +- The API is marked as draft and may change in future versions |
0 commit comments