Archives
Trending
Support
Login
clear text
XML
Django
JavaScript
MATLAB
C
C++
C#
Python
SQL
Shell
Bash
Markdown
YAML
JSON
HTML
CSS
PHP
Java
Ruby
Go
Rust
Swift
Kotlin
Arduino
TypeScript
Perl
Autohotkey
Lua
SQF
R
Scala
Haskell
Groovy
Dart
Clojure
VB.NET
Objective-C
PowerShell
Bash
CoffeeScript
Verilog
# Resource Manager Tutorial This tutorial covers how to use the Resource Manager for allocating and managing resources like VLANs, ports, IP addresses, and hostnames. ## Table of Contents 1. [Template Definition (YAML)](#1-template-definition-yaml) 2. [Integer Resource Types](#2-integer-resource-types) 3. [String Resource Types & DSL](#3-string-resource-types--dsl) 4. [Workflows for Allocation/Deallocation](#4-workflows-for-allocationdeallocation) 5. [Resource Manager External API](#5-resource-manager-external-api) --- ## 1. Template Definition (YAML) Resource templates define how resources should be allocated. Templates use YAML/JSON format: **⚠️ Note: Template management will eventually move to Git-based versioning. Currently, templates are managed via gRPC APIs.** **⚠️ Note: The template's format will be changed in the CURRENT version. We'll update all relevant people after this change is made, and/or fix existing templates to the new format.** ### Basic Template Structure ```yaml name: "vlan-pool" scope: "datacenter-east" description: "VLAN ID allocation for network interfaces" resources: - name: "vlan_id" base_type: "vlan" resource_type: "int" external: false # Resource Manager provides candidates and stores values constraints: start: 100 end: 4000 workflows: allocation: "allocate-vlan-workflow" deallocation: "deallocate-vlan-workflow" ``` ### Template Fields **User-Provided Fields:** - **name**: Unique template identifier - **scope**: Allocation scope (e.g., datacenter, environment) - **description**: Human-readable description - **version**: Initial version (user provides at creation, backend manages thereafter) - **status**: Initial status (user provides at creation, backend manages thereafter) - **resources**: Array of resource definitions ### Resource Definition Fields **User-Provided Fields:** - **name**: Resource field name - **base_type**: Resource identifier (e.g., "vlan", "port", "hostname") - **resource_type**: Pool type ("int" or "string") - **external**: Boolean indicating resource ownership (OPTIONAL, default: `false`) - `true`: Resource Manager provides candidates - `false`: External system provides values - **constraints**: Type-specific constraints (start/end/step for int, pattern for string) - **workflows**: Allocation and deallocation workflow names (OPTIONAL) - `allocation`: Workflow for allocation (default: built-in allocator selects first available value) - `deallocation`: Workflow for cleanup (no default, must be specified if needed) **Backend-Managed Fields:** - **id**: Unique template ID (auto-generated) - **version**: Template version (user provides initial, backend manages updates) - **status**: Template status (user provides initial, backend manages lifecycle: `active`, `inactive`, `deprecated`) - **created_at**: Creation timestamp (auto-generated) - **updated_at**: Last modification timestamp (auto-generated) ### Default Values Summary | Field | Default Value | Description | |-------|---------------|-------------| | `external` | `false` | External system provides candidates | | `step` (integer) | `1` | Increment by 1 | | `start` (integer) | `0` | Start from 0 | | `exclude` (integer) | `[]` | No values excluded | | `exclude` (string) | `[]` | No values excluded | | `allocation` workflow | Built-in allocator | Selects first available value | | `deallocation` workflow | None | Must specify if cleanup needed | ### Custom Scope Templates Templates can use `scope="custom"` for runtime scoping, allowing a single template to support multiple isolated resource pools. **Use Case**: Define one template that can be used for multiple instances without creating separate templates. **Example**: Router interfaces where each router instance needs its own isolated interface pool. ```yaml name: "router-interfaces" scope: "custom" # Enables runtime scoping description: "Interface allocation for router instances" resources: - name: "interface_name" base_type: "string" resource_type: "interface" constraints: pattern: "eth{seq:start=0:end=47:reusable=true}" ``` **Allocation Request with Custom Scope:** ```json { "template_name": "router-interfaces", "template_scope": "custom", "allocation_key": "router-clli-12345", // Required for scope="custom" "request_id": "interface-req-1" } ``` **How Custom Scoping Works:** - Each `allocation_key` creates an isolated resource pool - Resources are unique within each allocation key scope - Different allocation keys can reuse the same resource values - Example: Router "clli-12345" gets `eth0`, Router "clli-67890" also gets `eth0` **Requirements:** - Templates with `scope="custom"` require `allocation_key` in allocation requests - Templates with fixed scope cannot use `allocation_key` --- ## 2. Integer Resource Types Integer resources are used for numeric allocations like VLANs, ports, or IDs. ### Integer Constraints ```yaml resources: - name: "vlan_id" base_type: "vlan" resource_type: "int" external: false # Resource ownership (default: true) constraints: start: 100 # Minimum value (default: 0) end: 4000 # Maximum value (default: MaxInt64) step: 1 # Increment step (default: 1) exclude: [150, 200] # Values to exclude from allocation (optional) ``` ### Integer Examples **VLAN Pool:** ```yaml name: "vlan-pool" resources: - name: "vlan_id" base_type: "vlan" resource_type: "int" # external: false (default) constraints: start: 100 end: 4000 # workflows: uses default allocation workflow (selects first available) ``` **Simple Example (All Defaults):** ```yaml name: "simple-ids" resources: - name: "id" base_type: "vlan" resource_type: "id" constraints: start: 1 end: 1000 # Uses all defaults: # - external: false (RM provides candidates) # - step: 1 (increment by 1) # - allocation workflow: built-in (selects first available) # - no deallocation workflow ``` **Custom Step Size:** ```yaml name: "even-numbers" resources: - name: "even_id" base_type: "vlan" resource_type: "even_id" constraints: start: 1000 end: 2 step: -2 # Only even numbers ``` --- ## 3. String Resource Types & DSL String resources use a powerful DSL for generating complex identifiers. ### String Constraints ```yaml resources: - name: "hostname" base_type: "string" resource_type: "hostname" external: false # Resource ownership (default: true) constraints: pattern: "srv-{choice:options=web,api,db}-{seq:start=1:end=999:reusable=false}" exclude: ["srv-web-1", "srv-api-5"] # Values to exclude from allocation (optional) ``` **String Constraint Fields:** - **pattern**: DSL pattern specification (REQUIRED) - **exclude**: Array of values to exclude from allocation (OPTIONAL) **Exclude Constraint Behavior:** - **Template excludes**: Permanently excluded values defined in template - **Filter excludes**: Runtime excludes passed via workflow filters (when supported) - **Reusable sequences**: Work well with excludes (multiple candidates available) - **Non-reusable sequences**: Problematic with excludes (single candidate may be excluded) **⚠️ Exclude Limitations:** - **Non-reusable sequences + excludes**: May cause allocation failures when sequence hits excluded value - **Recommendation**: Avoid excludes with `reusable=false` sequences - **Filter support**: Available via workflow filters (requires custom workflow implementation) **Pattern Field:** The `pattern` field uses the Resource Manager's Domain Specific Language (DSL) to define how string values should be generated. The DSL supports multiple pattern types that can be combined to create complex identifiers. ### DSL Pattern Types The DSL uses explicit named parameters for all pattern types. **Positional parameters are NOT supported.** ### DSL Escape Sequences The DSL uses special characters for pattern syntax. When you need these characters as literals in your output, you must escape them with a backslash (`\`). #### **Special Characters** | Character | Purpose | Must Escape When | |-----------|---------|------------------| | **`{`** | Pattern start | You want literal `{` in static text | | **`}`** | Pattern end | You want literal `}` in static text | | **`:`** | Parameter separator | You want literal `:` in static text | | **`\`** | Escape character | You want literal `\` anywhere | #### **Escape Sequences** | Escape | Result | Usage | |--------|--------|-------| | **`\{`** | `{` | Literal opening brace | | **`\}`** | `}` | Literal closing brace | | **`\:`** | `:` | Literal colon | | **`\\`** | `\` | Literal backslash | #### **Where Escaping Applies** **In Static Text (outside patterns):** - **`:`** must be escaped → `\:` - **`{`** and **`}`** must be escaped → `\{` and `\}` - **`\`** must be escaped → `\\` **Inside Pattern Parameters:** - Same escaping rules apply when defining custom character sets or parameter values - Example: `{str:charset=custom:chars=A\:B\{C\}:length=3}` includes `:`, `{`, `}` in the character set #### **Validation** The parser will reject patterns with unescaped special characters and provide clear error messages indicating the position and required escape sequence. #### **1. Number Patterns** - `{num:...}` Generates random numeric values within specified ranges with optional zero-padding. **Parameters:** | Parameter | Required | Default | Description | |-----------|----------|---------|-------------| | `start=N` | Yes | None | Minimum value (inclusive) | | `end=N` | Yes | None | Maximum value (inclusive) | | `length=N` | No | No padding | Zero-padding length | **Parameter Details:** **start=N:** - **Purpose**: Lower bound of the numeric range (inclusive) - **Required**: YES - no default value - **Examples**: `start=1`, `start=0`, `start=-10` - **Validation**: Can be negative, must be ≤ end value **end=N:** - **Purpose**: Upper bound of the numeric range (inclusive) - **Required**: YES - no default value - **Examples**: `end=100`, `end=4094`, `end=65535` - **Validation**: Must be ≥ start value **length=N:** - **Purpose**: Zero-pad numbers to specified length - **Required**: NO - **Default**: No padding (numbers as-is) - **Examples**: `length=3` → "042", `length=5` → "00123" - **Validation**: Must be > 0 if specified **Examples:** ```yaml {num:start=1:end=100} # Random 1-100: "42", "87", "3" {num:start=1:end=999:length=5} # Zero-padded: "00042", "00123", "00999" {num:start=-10:end=10} # Negative range: "-5", "0", "7" {num:start=1:end=100:length=3} # Padded range: "042", "087", "003" {num:start=8000:end=9999} # Port range: "8000", "8001", "8002" ``` **Common Use Cases:** | Use Case | Configuration | Result Examples | |----------|---------------|-----------------| | **Port Numbers** | `{num:start=8000:end=9999}` | "8000", "8001", "8002" | | **VLAN IDs** | `{num:start=100:end=4000}` | "100", "101", "102" | | **Zero-Padded IDs** | `{num:start=1:end=999:length=4}` | "0001", "0042", "0999" | | **Small Ranges** | `{num:start=1:end=10}` | "1", "2", "3" | #### **2. String Patterns** - `{str:...}` Generates random strings from predefined or custom character sets. **Parameters:** | Parameter | Required | Default | Description | |-----------|----------|---------|-------------| | `charset=NAME` | Yes | None | Character set type | | `length=N` | Yes | None | String length in characters | | `chars=CUSTOM` | No | None | Custom character set (sets charset=custom) | **Parameter Details:** **charset=NAME:** - **Purpose**: Specifies which character set to use for generation - **Required**: YES - no default charset - **Options**: `alpha`, `numeric`, `alphanumeric`, `lowercase`, `uppercase`, `hex`, `custom` - **Validation**: Must be a supported charset name **length=N:** - **Purpose**: Length of generated string in characters - **Required**: YES - no default length - **Examples**: `length=5`, `length=8`, `length=12` - **Validation**: Must be > 0 **chars=CUSTOM:** - **Purpose**: Define custom character set for random selection - **Required**: NO (only when using custom charset) - **Auto-behavior**: Automatically sets `charset=custom` - **Examples**: `chars=abc123`, `chars=ABCDEF0123456789` - **Validation**: Cannot be empty if specified **Supported Character Sets:** | Charset | Characters | Use Cases | |---------|------------|-----------| | **alpha** | `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ` | General identifiers | | **numeric** | `0123456789` | Numeric codes | | **alphanumeric** | Letters + digits (62 chars) | Mixed identifiers | | **lowercase** | `abcdefghijklmnopqrstuvwxyz` | Lowercase identifiers | | **uppercase** | `ABCDEFGHIJKLMNOPQRSTUVWXYZ` | Uppercase identifiers | | **hex** | `0123456789abcdef` | Hexadecimal values | | **custom** | User-defined via `chars=` | Specific character requirements | **Examples:** ```yaml {str:charset=alpha:length=5} # "AbCdE", "xyZaB" {str:charset=numeric:length=6} # "123456", "987654" {str:charset=alphanumeric:length=10} # "aB3xY7zQ9m" {str:charset=custom:chars=abc123:length=4} # "abc", "cab", "1a2", "3cb" {str:charset=hex:length=8} # "a1b2c3d4", "f9e8d7c6" {str:charset=lowercase:length=6} # "abcdef", "ghijkl" {str:charset=uppercase:length=4} # "ABCD", "EFGH" ``` **Common Use Cases:** | Use Case | Configuration | Result Examples | |----------|---------------|-----------------| | **Server Names** | `{str:charset=lowercase:length=6}` | "websvr", "apisvr", "dbsvr" | | **Hex Tokens** | `{str:charset=hex:length=8}` | "a1b2c3d4", "f9e8d7c6" | | **Mixed IDs** | `{str:charset=alphanumeric:length=10}` | "aB3xY7zQ9m", "X2nP8vK4sR" | | **Custom Codes** | `{str:charset=custom:chars=ABCDEF123:length=5}` | "AB1C2", "3DEFF", "1A2B3" | #### **3. Choice Patterns** - `{choice:...}` Selects values from predefined lists with equal or weighted probabilities. **Parameters:** | Parameter | Required | Default | Description | |-----------|----------|---------|-------------| | `options=LIST` | Yes | None | Comma-separated list of choices | | `type=TYPE` | No | `simple` | Choice type: `simple` or `weighted` | **Parameter Details:** **options=LIST:** - **Purpose**: Define the list of possible values to choose from - **Required**: YES - no default options - **Format**: Comma-separated values (no spaces around commas) - **Examples**: `options=dev,test,prod`, `options=active,inactive` - **Validation**: Cannot be empty, must have at least one option **type=TYPE:** - **Purpose**: Controls selection probability distribution - **Required**: NO - **Default**: `simple` (equal probability for all options) - **Options**: `simple` or `weighted` - **Validation**: Must be exactly `simple` or `weighted` **Choice Types:** **Simple Choice (`type=simple` or default):** - **Behavior**: Equal probability for all options - **Format**: `options=option1,option2,option3` - **Probability**: Each option gets `1/N` chance (where N = number of options) - **Examples**: - `{choice:options=dev,test,prod}` → 33.3% chance each - `{choice:options=active,inactive}` → 50% chance each **Weighted Choice (`type=weighted`):** - **Behavior**: Custom probability distribution - **Format**: `options=option1-weight1,option2-weight2,option3-weight3` - **Weights**: Numbers after dash (`-`) specify relative probability - **Examples**: - `{choice:type=weighted:options=dev-70,prod-30}` → 70% dev, 30% prod - `{choice:type=weighted:options=standard-60,premium-30,enterprise-10}` → 60%, 30%, 10% **Examples:** ```yaml # Simple choice (equal probability) {choice:options=dev,test,prod} # 33.3% each: "dev", "test", "prod" {choice:options=active,inactive} # 50% each: "active", "inactive" {choice:type=simple:options=enabled,disabled} # Explicit simple type # Weighted choice (custom probability) {choice:type=weighted:options=dev-70,prod-30} # 70% dev, 30% prod {choice:type=weighted:options=standard-60,premium-30,enterprise-10} # Custom distribution {choice:type=weighted:options=web-80,api-15,db-5} # Server type distribution ``` **Common Use Cases:** | Use Case | Configuration | Result Examples | |----------|---------------|-----------------| | **Environment Types** | `{choice:options=dev,test,prod}` | "dev", "test", "prod" | | **Server Roles** | `{choice:type=weighted:options=web-60,api-30,db-10}` | "web" (60%), "api" (30%), "db" (10%) | | **Status Values** | `{choice:options=active,inactive,pending}` | "active", "inactive", "pending" | | **Service Tiers** | `{choice:type=weighted:options=standard-80,premium-20}` | "standard" (80%), "premium" (20%) | #### **4. Sequence Patterns** - `{seq:...}` Generates sequential numeric values with gap-filling or monotonic behavior. **Supports multi-dimensional sequences** for complex combinations. **Parameters:** | Parameter | Required | Default | Description | |-----------|----------|---------|-------------| | `start=N` | No | `1` | Starting value for the sequence | | `end=N` | No | Auto-calculated | Ending value (max 10M values total) | | `step=N` | No | `1` | Increment step between values | | `reusable=BOOL` | No | `true` | Whether values can be reused after deallocation | **Understanding Reusability:** **Reusable (`reusable=true`) - Gap-Filling Behavior:** - **What it means**: Values can be **reused after deallocation** - **Behavior**: Fills gaps in the sequence when values are freed - **Perfect for**: Interface names, VLAN IDs, port numbers, temporary resources - **Example**: - Allocate: `lo0`, `lo1`, `lo2` - Deallocate: `lo1` - Next allocation: `lo1` (reuses the freed value) - **Implementation**: Uses `generate_series` with availability checking **Non-Reusable (`reusable=false`) - Monotonic Behavior:** - **What it means**: Values **NEVER reused, even after deallocation** - **Behavior**: Sequence continues monotonically, always increments - **Perfect for**: Transaction IDs, audit trails, user IDs, permanent identifiers - **Example**: - Allocate: `tx_1`, `tx_2`, `tx_3` - Deallocate: `tx_2` - Next allocation: `tx_4` (tx_2 never used again) - **Implementation**: Uses PostgreSQL sequences for atomic increments **Parameter Details:** **start=N:** - **Purpose**: Where the sequence begins counting - **Default**: `1` - **Examples**: `start=0` (lo0, lo1...), `start=100` (vlan100, vlan101...) **end=N:** - **Purpose**: Maximum value in the sequence range - **Default**: Auto-calculated to ensure max 10,000,000 values - **Examples**: `end=4094` (VLAN range), `end=65535` (port range) - **Validation**: Must be ≥ start value **step=N:** - **Purpose**: Increment between consecutive values - **Default**: `1` - **Examples**: `step=2` (even numbers: 2,4,6...), `step=10` (10,20,30...) - **Validation**: Must be > 0 **reusable=BOOL:** - **Purpose**: Controls gap-filling vs monotonic behavior - **Default**: `true` (gap-filling) - **Values**: `true` (reusable) or `false` (monotonic) **Examples:** ```yaml {seq} # Default: start=1, step=1, reusable=true, end=auto {seq:start=0:end=4094:reusable=true} # Interface names: lo0, lo1, lo2... (fills gaps) {seq:start=1:end=10000:reusable=false} # Transaction IDs: tx_1, tx_2, tx_3... (never reuses) {seq:step=2:reusable=false} # Even numbers: 2, 4, 6, 8... (monotonic) {seq:start=100:step=5:reusable=true} # Custom increment: 100, 105, 110... (gap-filling) {seq:start=1000:end=9999:step=1:reusable=false} # User IDs: 1000, 1001, 1002... (permanent) ``` **Multi-Dimensional Sequences:** Multiple reusable sequences in a single pattern create **multi-dimensional combinations**: ```yaml # 2D sequences - generates all combinations "ge{seq:start=0:end=2:reusable=true}/{seq:start=0:end=2:reusable=true}" # Results: ge0/0, ge0/1, ge0/2, ge1/0, ge1/1, ge1/2, ge2/0, ge2/1, ge2/2 # 3D sequences - generates 3×3×3 = 27 combinations "ge100-{seq:start=0:end=2:reusable=true}/{seq:start=0:end=2:reusable=true}/{seq:start=0:end=2:reusable=true}" # Results: ge100-0/0/0, ge100-0/0/1, ge100-0/0/2, ..., ge100-2/2/2 # Descending sequences with negative steps "item_{seq:start=5:end=1:step=-1:reusable=true}_{seq:start=10:end=5:step=-1:reusable=true}" # Results: item_5_10, item_5_9, item_5_8, ..., item_1_5 ``` **⚠️ Sequence Limitations:** - **✅ Multiple reusable sequences**: Supported (generates all combinations) - **❌ Multiple non-reusable sequences**: NOT supported (would waste sequence values) - **❌ Mixed sequence types**: Cannot mix `reusable=true` and `reusable=false` in same pattern - **❌ Excludes with non-reusable sequences**: Problematic (may cause allocation failures) **Common Use Cases:** | Use Case | Configuration | Behavior | |----------|---------------|----------| | **Interface Names** | `{seq:start=0:end=4094:reusable=true}` | lo0, lo1, lo2... fills gaps when freed | | **VLAN IDs** | `{seq:start=100:end=4000:reusable=true}` | vlan100, vlan101... reuses freed VLANs | | **Transaction IDs** | `{seq:start=1:reusable=false}` | tx_1, tx_2, tx_3... never reuses | | **User IDs** | `{seq:start=1000:reusable=false}` | user_1000, user_1001... permanent IDs | | **Even Port Numbers** | `{seq:start=8000:step=2:reusable=true}` | 8000, 8002, 8004... even ports only | | **Multi-Dimensional GE Interfaces** | `ge{seq:0:2:reusable=true}/{seq:0:2:reusable=true}` | ge0/0, ge0/1, ge0/2, ge1/0... all combinations | | **3D Network Topology** | `rack{seq:1:3:reusable=true}-slot{seq:1:4:reusable=true}-port{seq:1:8:reusable=true}` | rack1-slot1-port1, rack1-slot1-port2... 96 combinations | #### **5. UUID Patterns** - `{uuid:...}` Generates UUID-based identifiers with configurable formats. **Parameters:** | Parameter | Required | Default | Description | |-----------|----------|---------|-------------| | No parameters | No | `full` | Full UUID format (36 characters) | | `length=N` | No | None | Custom length (1-32 characters, no dashes) | **Parameter Details:** **No Parameters (`{uuid}`):** - **Purpose**: Generate full UUID with dashes - **Required**: NO - this is the default behavior - **Format**: "550e8400-e29b-41d4-a716-446655440000" (36 characters) - **Use case**: When you need full UUID uniqueness **length=N:** - **Purpose**: Generate custom-length UUID substring (no dashes) - **Required**: NO - **Range**: 1-32 characters - **Format**: First N characters of UUID without dashes - **Examples**: `length=8` → "550e8400", `length=12` → "550e8400e29b" - **Validation**: Must be between 1 and 32 **Predefined Formats:** | Format | Configuration | Length | Example Output | |--------|---------------|--------|----------------| | **Full UUID** | `{uuid}` or `{uuid:full}` | 36 chars | "550e8400-e29b-41d4-a716-446655440000" | | **Short UUID** | `{uuid:short}` | 8 chars | "550e8400" | | **Custom Length** | `{uuid:length=12}` | 12 chars | "550e8400e29b" | | **Minimal** | `{uuid:length=4}` | 4 chars | "550e" | **Examples:** ```yaml {uuid} # Full: "550e8400-e29b-41d4-a716-446655440000" {uuid:full} # Explicit full (same as no params) {uuid:short} # Short: "550e8400" (8 characters) {uuid:length=12} # Custom: "550e8400e29b" (12 characters) {uuid:length=16} # Medium: "550e8400e29b41d4" (16 characters) {uuid:length=4} # Minimal: "550e" (4 characters) ``` **Common Use Cases:** | Use Case | Configuration | Result Examples | |----------|---------------|-----------------| | **Unique Resource IDs** | `{uuid}` | "550e8400-e29b-41d4-a716-446655440000" | | **Short Identifiers** | `{uuid:short}` | "550e8400", "a1b2c3d4" | | **Database Keys** | `{uuid:length=16}` | "550e8400e29b41d4", "a1b2c3d4e5f6g7h8" | | **Compact IDs** | `{uuid:length=8}` | "550e8400", "a1b2c3d4" | #### **6. Static Text** Static text appears literally in generated strings. **Examples:** ```yaml "srv-production-001" # Fixed string "server_{num:start=1:max=100}_suffix" # Combined with patterns ``` #### **7. Time Patterns** - `{time:...}` Generates current timestamp values in various formats when resources are allocated. **Parameters:** | Parameter | Required | Default | Description | |-----------|----------|---------|-------------| | Format type | Yes | None | Timestamp format specification | | `timezone=TZ` | No | `UTC` | Timezone for formatting | **Parameter Details:** **Format Type (First Parameter):** - **Purpose**: Specifies how to format the current timestamp - **Required**: YES - no default format - **Options**: See supported formats table below - **Examples**: `unix`, `iso8601`, `date`, `yyyymmdd` - **Validation**: Must be a supported format type **timezone=TZ:** - **Purpose**: Timezone for timestamp formatting - **Required**: NO - **Default**: `UTC` - **Examples**: `timezone=EST`, `timezone=PST`, `timezone=UTC` - **Note**: Applies to ISO/date formats, not unix timestamps **Supported Formats:** | Format | Description | Example Output | Use Cases | |--------|-------------|----------------|-----------| | **unix** | Unix timestamp (seconds) | "1705327800" | System logs, epoch-based systems | | **unix_ms** | Unix timestamp (milliseconds) | "1705327800000" | High-precision tracking | | **iso8601** | ISO 8601 format | "2024-01-15T14:30:00Z" | Standard timestamps | | **rfc3339** | RFC 3339 format (same as ISO) | "2024-01-15T14:30:00Z" | Internet standards | | **date** | Date only | "2024-01-15" | Daily operations | | **time** | Time only | "14:30:00" | Time-specific resources | | **yyyymmdd** | Compact date | "20240115" | File naming | | **yyyymmddhhmmss** | Compact datetime | "20240115143000" | Detailed file naming | | **yyyy_mm_dd** | Underscore date | "2024_01_15" | Path-friendly dates | **Examples:** ```yaml {time:unix} # Current unix timestamp: "1705327800" {time:unix_ms} # Current timestamp in ms: "1705327800000" {time:iso8601} # Current ISO time: "2024-01-15T14:30:00Z" {time:date} # Current date: "2024-01-15" {time:time} # Current time: "14:30:00" {time:yyyymmdd} # Compact date: "20240115" {time:yyyymmddhhmmss} # Compact datetime: "20240115143000" {time:yyyy_mm_dd} # Underscore date: "2024_01_15" {time:iso8601:timezone=EST} # With timezone: "2024-01-15T09:30:00Z" ``` **Common Use Cases:** | Use Case | Configuration | Result Examples | |----------|---------------|-----------------| | **System Logs** | `{time:unix}` | "1705327800", "1705327801" | | **Backup Files** | `{time:yyyymmdd}` | "20240115", "20240116" | | **Session Tracking** | `{time:unix_ms}` | "1705327800000", "1705327800050" | | **ISO Timestamps** | `{time:iso8601}` | "2024-01-15T14:30:00Z" | | **File Naming** | `{time:yyyymmddhhmmss}` | "20240115143000" | **Real-World Pattern Examples:** ```yaml # System log files with timestamps "log_{time:unix}_{choice:options=error,warn,info}" # Result: "log_1705327800_error" # Daily backup file names "backup_{time:yyyymmdd}_{seq:start=1:end=999:reusable=false}_{uuid:short}" # Result: "backup_20240115_1_a1b2c3d4" # Session tracking with millisecond precision "session_{time:unix_ms}_user_{num:start=1000:max=9999}" # Result: "session_1705327800000_user_5432" # Certificate management with date stamps "cert_{time:date}_{str:charset=hex:length=8}" # Result: "cert_2024-01-15_a1b2c3d4" ``` #### **8. Dynamic Patterns** - `{dynamic:...}` Runtime placeholders for client substitution (e.g., Python `.format()`). **Parameters:** | Parameter | Required | Default | Description | |-----------|----------|---------|-------------| | Parameter name | Yes | None | Placeholder name for runtime substitution | **Parameter Details:** **Parameter Name (First Parameter):** - **Purpose**: Name of the placeholder that will be substituted at runtime - **Required**: YES - no default parameter name - **Format**: Simple identifier (letters, numbers, underscores) - **Examples**: `env`, `region`, `customer_id`, `datacenter` - **Validation**: Must be valid identifier, cannot be empty **How Dynamic Patterns Work:** 1. **Template Definition**: Use `{dynamic:param_name}` in pattern 2. **Resource Generation**: System generates template with `{param_name}` placeholder 3. **Client Substitution**: Python/client uses `.format()` to fill placeholder 4. **Final Result**: Completed string with actual values **Examples:** ```yaml {dynamic:env} # Generates: "{env}" placeholder {dynamic:region} # Generates: "{region}" placeholder {dynamic:customer_id} # Generates: "{customer_id}" placeholder {dynamic:datacenter} # Generates: "{datacenter}" placeholder ``` **Complete Workflow Example:** ```yaml # Template pattern: "app_{dynamic:env}_{dynamic:region}_{seq:start=1:end=100:reusable=false}" # Step 1: Resource Manager generates template # Result: "app_{env}_{region}_42" # Step 2: Python workflow fills placeholders result = "app_{env}_{region}_42" final_result = result.format(env="production", region="us-west") # Final result: "app_production_us-west_42" ``` **Common Use Cases:** | Use Case | Configuration | Template Generated | After Client Substitution | |----------|---------------|-------------------|---------------------------| | **Environment-Specific** | `{dynamic:env}` | "server_{env}_001" | "server_production_001" | | **Regional Resources** | `{dynamic:region}` | "lb_{region}_web" | "lb_us-west_web" | | **Customer Resources** | `{dynamic:customer}` | "vm_{customer}_db" | "vm_acme-corp_db" | | **Multi-Tenant** | `{dynamic:tenant}_{dynamic:env}` | "app_{tenant}_{env}" | "app_customer1_prod" | **Pattern Examples:** ```yaml # Environment-aware applications "app_{dynamic:env}_{dynamic:region}_{uuid:short}" # Template: "app_{env}_{region}_a1b2c3d4" # Result: "app_production_us-west_a1b2c3d4" # Customer-specific resources "customer_{dynamic:customer_id}_vm_{seq:start=1:end=999:reusable=false}" # Template: "customer_{customer_id}_vm_42" # Result: "customer_acme-corp_vm_42" # Multi-datacenter nastartg "dc_{dynamic:datacenter}_rack_{num:start=1:max=50:length=2}" # Template: "dc_{datacenter}_rack_05" # Result: "dc_east_rack_05" ``` ### Complex String Examples **Network Interfaces (Gap-Filling):** ```yaml name: "interface-names" resources: - name: "interface" base_type: "string" resource_type: "interface" constraints: pattern: "lo{seq:start=0:end=4094:reusable=true}" # Result: "lo0", "lo1", "lo2", ..., fills gaps when interfaces freed ``` **Transaction IDs (Monotonic):** ```yaml name: "transaction-ids" resources: - name: "transaction_id" base_type: "string" resource_type: "transaction_id" constraints: pattern: "tx_{seq:start=1:end=999999:reusable=false}_{uuid:short}" # Result: "tx_1_a1b2c3d4", "tx_2_e5f6g7h8", never reuses numbers ``` **Server Hostnames (Complex):** ```yaml name: "server-hostnames" resources: - name: "hostname" base_type: "string" resource_type: "hostname" constraints: pattern: "{choice:type=weighted:options=web-60,api-30,db-10}_{str:charset=lowercase:length=4}_{num:start=1:max=999:length=3}" # Result: "web_abcd_042", "api_efgh_127", "db_ijkl_891" ``` **Timestamped System Resources:** ```yaml name: "log-files" resources: - name: "log_filename" base_type: "string" resource_type: "logfile" constraints: pattern: "log_{time:unix}_{choice:options=error,warn,info,debug}" # Result: "log_1705327800_error", "log_1705327801_warn" # Each allocation gets current timestamp when allocated ``` **Daily Backup Files:** ```yaml name: "backup-files" resources: - name: "backup_filename" base_type: "string" resource_type: "backup_file" constraints: pattern: "backup_{time:yyyymmdd}_{seq:start=1:end=999:reusable=false}_{uuid:short}" # Result: "backup_20240115_1_a1b2c3d4", "backup_20240115_2_e5f6g7h8" # Date changes daily, sequence continues within each day ``` **Session Identifiers:** ```yaml name: "user-sessions" resources: - name: "session_id" base_type: "string" resource_type: "session" constraints: pattern: "session_{time:unix_ms}_user_{num:start=1000:max=9999}" # Result: "session_1705327800000_user_5432", "session_1705327800050_user_7891" # Millisecond precision for unique session tracking ``` ### DSL Limitations & Validation #### **Parameter Requirements:** | Pattern Type | Required Parameters | Optional Parameters | |--------------|-------------------|-------------------| | **Number** | `start=N`, `end=N` | `length=N` | | **String** | `charset=NAME`, `length=N` | `chars=CUSTOM` | | **Choice** | `options=LIST` | `type=simple/weighted` | | **Sequence** | None | `reusable=true/false`, `start=N`, `end=N`, `step=N` | | **UUID** | None | `length=N` | | **Time** | Format type | `timezone=TZ` | | **Static** | None | None | | **Dynamic** | Parameter name | None | #### **Validation Rules:** - **All parameters must be explicitly named** - no positional parameters - **Required parameters must be provided** - no silent defaults - **Empty values are rejected** - `start=`, `start=`, `charset=` all fail - **Duplicate parameters are rejected** - `start=1:start=2` fails - **Unknown parameters are rejected** - unrecognized parameters fail - **Maximum 10,000,000 values** per sequence (step-aware validation) #### **Common Limitations:** - **✅ Multiple reusable sequences** - generates all combinations - **❌ Multiple non-reusable sequences** - NOT supported (would waste sequence values unnecessarily) - **❌ Mixed sequence types** - Cannot mix `reusable=true` and `reusable=false` in same pattern - **⚠️ Excludes with non-reusable sequences** - Problematic (may cause allocation failures) - **✅ String pool exclude filters** - **NOW SUPPORTED** (template + runtime excludes) - **Unicode characters** - Limited to ASCII character sets - **Custom charset** - Cannot be empty #### **Common Error Examples:** ``` Error: "positional parameters not supported: 1 (use explicit: start=N, end=N, step=N, reusable=true/false)" Fix: Use {seq:start=1:end=100:reusable=false} instead of {seq:1:100:false} Error: "start parameter is required (use start=N)" Fix: Use {num:start=1:end=100} instead of {num:end=100} Error: "charset parameter is required (use charset=alpha, charset=numeric, etc.)" Fix: Use {str:charset=alpha:length=5} instead of {str:length=5} Error: "sequence will generate 50000000 values, exceeds maximum allowed 10000000" Fix: Reduce range or increase step: {seq:start=1:end=1000000:step=5} Error: "multiple non-reusable sequences (reusable=false) are not supported - this would waste sequence values unnecessarily" Fix: Use only one non-reusable sequence per pattern Error: "mixing reusable and non-reusable sequences is not supported - use either all reusable=true or all reusable=false" Fix: Make all sequences in pattern have same reusable setting Error: "start parameter cannot be empty (use start=N)" Fix: Provide actual values for all parameters ``` --- ## 4. Workflows for Allocation/Deallocation Workflows handle the allocation process and integration with external systems. ### Workflow Configuration ```yaml resources: - name: "vlan_id" base_type: "vlan" resource_type: "int" external: false # Default: true constraints: { ... } workflows: allocation: "allocate-vlan-workflow" # Custom workflow (optional) deallocation: "deallocate-vlan-workflow" # Cleanup workflow (optional) ``` ### Workflow Defaults **Allocation Workflow:** - **Default behavior**: Built-in allocator selects first available value - **Custom workflow**: Specify workflow name for custom selection logic - **When to use custom**: - Custom candidate selection (odd/even values, priority-based, pattern matching) - External system integration and validation - Approval processes and business rules - Multi-step allocation with rollback capability **Deallocation Workflow:** - **No default**: Must specify workflow if cleanup is needed - **When required**: External system cleanup, resource release notifications - **Self-managed resources**: Often don't need deallocation workflows ### Workflow API The Resource Manager exposes two APIs for workflows: #### **GetCandidates API** Called by workflows to get candidate values for allocation: ```protobuf message GetCandidatesRequest { string correlation_id = 1; // Unique allocation session ID int32 limit = 2; // Number of candidates requested string filters = 3; // JSON filters (optional) } message GetCandidatesResponse { google.protobuf.Value candidates = 1; // Array of candidate values } ``` **Example Workflow Call:** ```python # Python workflow activity - Integer pool filters response = resource_manager.GetCandidates( correlation_id="alloc-12345", limit=10, filters=json.dumps({"start": 200, "end": 300}) ) candidates = json.loads(response.candidates) # [201, 203, 205, ...] # Python workflow activity - String pool exclude filter response = resource_manager.GetCandidates( correlation_id="alloc-67890", limit=5, filters=json.dumps({"exclude": ["lo1", "lo5", "lo10"]}) ) candidates = json.loads(response.candidates) # ["lo0", "lo2", "lo3", "lo4", "lo6"] ``` #### **SelectCandidate API** Called by workflows to select a candidate or report failure: ```protobuf message SelectCandidateRequest { string correlation_id = 1; // Same ID from GetCandidates bool success = 2; // true for success, false for failure google.protobuf.Value selected_value = 3; // Structured selected value (when success=true) string error_message = 4; // Error description (if success=false) } ``` **Success Example:** ```python # Workflow successfully allocated value resource_manager.SelectCandidate( correlation_id="alloc-12345", success=True, selected_value=json.dumps(205), error_message="" ) ``` **Failure Example:** ```python # Workflow failed to allocate resource_manager.SelectCandidate( correlation_id="alloc-12345", success=False, selected_value="", error_message="External system rejected allocation" ) ``` ### Resource Management Patterns The `external` field determines who controls the resource allocation: #### **Three Main Allocation Patterns:** 1. **Default Allocation**: Built-in allocator selects first candidate (no custom workflow) 2. **Custom Selection**: Custom workflow chooses specific candidate from RM-provided options 3. **External Integration**: External system provides values, RM tracks for bookkeeping #### **Self-Managed Resources (`external: false`)** Resource Manager provides candidates and stores values: ```yaml name: "simple-vlan" resources: - name: "vlan_id" base_type: "vlan" resource_type: "int" external: false # RM provides candidates and stores values constraints: { start: 100, end: 4000 } workflows: allocation: "simple-allocate-workflow" ``` **How it Works:** - **Resource Manager**: Generates candidate values using constraints/patterns - **Workflow**: Calls `GetCandidates()` to get available values from RM - **Resource Manager**: Tracks allocated values in internal tables - **Use Cases**: VLANs, internal IDs, generated hostnames **Workflow Patterns:** **Simple Allocation (Default Behavior):** 1. Call `GetCandidates()` to get RM-generated candidates 2. Select first candidate (no custom logic needed) 3. Call `SelectCandidate(success=true, selected_value=candidate)` **Custom Selection Logic:** 1. Call `GetCandidates()` to get RM-generated candidates 2. Apply custom business logic to select preferred candidate 3. Call `SelectCandidate(success=true, selected_value=selected_candidate)` **Example - Regional VLAN Selection:** ```python # Workflow: vlan-east-allocation candidates = resource_manager.GetCandidates(correlation_id) # [100, 101, 102, 103, 104, ...] # Custom logic: Choose odd VLANs for east datacenter selected_vlan = None for candidate in candidates: if candidate % 2 == 1: # Odd VLAN selected_vlan = candidate break if selected_vlan: resource_manager.SelectCandidate( correlation_id=correlation_id, success=True, selected_value=json.dumps(selected_vlan) # e.g., 101 ) else: resource_manager.SelectCandidate( correlation_id=correlation_id, success=False, error_message="No odd VLANs available in current batch" ) ``` ```python # Workflow: vlan-west-allocation # Custom logic: Choose even VLANs for west datacenter for candidate in candidates: if candidate % 2 == 0: # Even VLAN selected_vlan = candidate break ``` #### **Externally-Managed Resources (`external: true`)** External system provides values, Resource Manager does bookkeeping: ```yaml name: "cloud-instances" resources: - name: "instance_id" base_type: "string" resource_type: "instance_id" external: true # External system provides values, RM tracks only constraints: {} # No constraints needed for external values workflows: allocation: "provision-cloud-instance-workflow" deallocation: "cleanup-cloud-instance-workflow" ``` **How it Works:** - **External System**: Generates/provides the actual resource values - **Workflow**: Calls external system directly to get values - **Resource Manager**: Only tracks allocated values for bookkeeping - **Use Cases**: Cloud resource IDs, external service tokens, third-party allocations **Allocation Workflow Pattern:** 1. Workflow calls external system directly (no `GetCandidates()` to RM) 2. External system provides value (e.g., cloud instance ID) 3. Call `SelectCandidate(success=true, selected_value=external_value)` 4. If failed: Call `SelectCandidate(success=false, error_message=reason)` **Deallocation Workflow Pattern:** 1. Workflow triggered when resource is released 2. Clean up external system (deprovision instance) 3. Resource Manager automatically removes from allocation table #### **Hybrid Example** Some templates may have both self-managed and externally-managed resources: ```yaml name: "server-deployment" resources: - name: "hostname" base_type: "string" resource_type: "hostname" external: false # RM generates hostname constraints: pattern: "srv_{seq:start=1:end=999:reusable=false}" - name: "cloud_instance_id" base_type: "string" resource_type: "instance_id" external: true # Cloud provider generates instance ID constraints: {} workflows: allocation: "deploy-server-workflow" deallocation: "cleanup-server-workflow" ``` --- ## 5. Resource Manager External API The Resource Manager provides gRPC APIs for managing templates and allocations. ### Template Management API **⚠️ Note: Template gRPC APIs will eventually be replaced with Git-based versioning. Until that migration is complete, we use the gRPC APIs below.** #### **Create Template** ```protobuf // gRPC call rpc CreateTemplate(CreateTemplateRequest) returns (TemplateResponse); message CreateTemplateRequest { string name = 1; string version = 2; string scope = 3; string description = 4; string status = 5; repeated ResourceDefinition resources = 6; } message ResourceDefinition { string name = 1; string base_type = 2; // Resource identifier: "vlan", "port", "hostname", etc. string resource_type = 3; // Pool type: "int" or "string" google.protobuf.Struct constraints = 4; LifecycleWorkflows workflows = 5; bool external = 6; // true: RM provides candidates, false: external system provides values } ``` **Example gRPC Call:** ```python request = resource_manager_pb2.CreateTemplateRequest( name="vlan-pool", version="1.0.0", # User-provided initial version scope="datacenter-east", description="VLAN allocation pool", status="active", # User-provided initial status resources=[ resource_manager_pb2.ResourceDefinition( name="vlan_id", base_type="vlan", resource_type="int", external=False, # default: false constraints=Struct(fields={ "start": Value(number_value=100), "end": Value(number_value=4000), }), workflows=resource_manager_pb2.LifecycleWorkflows( allocation="allocate-vlan-workflow", deallocation="deallocate-vlan-workflow" ) ) ] ) response = client.CreateTemplate(request) # Note: Subsequent version management will be handled by backend ``` #### **Get Template** ```protobuf rpc GetTemplate(GetTemplateRequest) returns (TemplateResponse); message GetTemplateRequest { string name = 1; string scope = 2; string version = 3; } ``` #### **List Templates** ```protobuf rpc ListTemplates(ListTemplatesRequest) returns (ListTemplatesResponse); message ListTemplatesRequest { // No parameters } message ListTemplatesResponse { repeated ResourceTemplate templates = 1; } ``` #### **Update Template** ```protobuf rpc UpdateTemplate(UpdateTemplateRequest) returns (google.protobuf.Empty); message UpdateTemplateRequest { string name = 1; string scope = 2; string version = 3; // Current version to update string newVersion = 4; // New version (backend-managed after creation) string newDescription = 5; // Updated description string newStatus = 6; // New status (backend-managed after creation) } ``` #### **Delete Template** ```protobuf rpc DeleteTemplate(DeleteTemplateRequest) returns (google.protobuf.Empty); message DeleteTemplateRequest { string name = 1; string scope = 2; string version = 3; } ``` ### Allocation Management API #### **Create Allocation** ```protobuf rpc Allocate(AllocationRequest) returns (Allocation); message AllocationRequest { string request_id = 1; string template_name = 2; string template_scope = 3; string template_version = 4; google.protobuf.Struct parameters = 5; string allocation_key = 6; // Custom scope identifier for templates with scope='custom' } message Allocation { int64 id = 1; int64 template_id = 2; string template_name = 3; string template_scope = 4; string template_version = 5; string request_id = 6; string status = 7; google.protobuf.Struct parameters = 8; repeated AllocatedResource resources = 9; google.protobuf.Timestamp created_at = 10; google.protobuf.Timestamp updated_at = 11; } message AllocatedResource { string name = 1; string base_type = 2; // Resource identifier: "vlan", "port", "hostname", etc. string resource_type = 3; // Pool type: "int" or "string" google.protobuf.Value value = 4; } ``` **Example gRPC Call (Fixed Scope):** ```python request = resource_manager_pb2.AllocationRequest( request_id="req-12345", template_name="vlan-pool", template_scope="datacenter-east", template_version="1.0.0", # Optional - defaults to latest active parameters=Struct(fields={ "datacenter": Value(string_value="east"), "environment": Value(string_value="production") }) ) response = client.Allocate(request) ``` **Example gRPC Call (Custom Scope):** ```python request = resource_manager_pb2.AllocationRequest( request_id="interface-req-1", template_name="router-interfaces", template_scope="custom", allocation_key="router-clli-12345", # Required for scope='custom' template_version="1.0.0" ) response = client.Allocate(request) ``` #### **Get Allocation** ```protobuf rpc GetAllocation(GetAllocationRequest) returns (Allocation); message GetAllocationRequest { int64 allocation_id = 1; } ``` #### **List Allocations** ```protobuf rpc ListAllocations(google.protobuf.Empty) returns (ListAllocationsResponse); message ListAllocationsResponse { repeated Allocation allocations = 1; } ``` #### **Delete Allocation** ```protobuf rpc DeleteAllocation(DeleteAllocationRequest) returns (google.protobuf.Empty); message DeleteAllocationRequest { int64 allocation_id = 1; } ``` ### Workflow Callback API (Internal) These APIs are used internally by workflows during allocation: #### **GetCandidates (Internal)** ```protobuf rpc GetCandidates(GetCandidatesRequest) returns (GetCandidatesResponse); message GetCandidatesRequest { string correlation_id = 1; // Unique allocation session ID int32 limit = 2; // Number of candidates requested string filters = 3; // JSON filters (optional) } message GetCandidatesResponse { google.protobuf.Value candidates = 1; // Array of candidate values } ``` **Example gRPC Call:** ```python request = resource_manager_pb2.GetCandidatesRequest( correlation_id="alloc-12345", limit=10, filters='{"start": 200, "end": 300}' ) response = workflow_callback_client.GetCandidates(request) candidates = json.loads(response.candidates) ``` #### **SelectCandidate (Internal)** ```protobuf rpc SelectCandidate(SelectCandidateRequest) returns (google.protobuf.Empty); message SelectCandidateRequest { string correlation_id = 1; // Same ID from GetCandidates bool success = 2; // true for success, false for failure google.protobuf.Value selected_value = 3; // Structured selected value (if success=true) string error_message = 4; // Error description (if success=false) } ``` **Example gRPC Call:** ```python request = resource_manager_pb2.SelectCandidateRequest( correlation_id="alloc-12345", success=True, selected_value=json.dumps(205), error_message="" ) response = workflow_callback_client.SelectCandidate(request) ``` ### Workflow Engine Tasks **All Resource Manager APIs are exposed as workflow tasks!** This is how customers should use the Resource Manager in practice. #### **Two Levels of Workflow Integration** **Level 1: Pre-built Allocation Tasks (Standard Use Case)** Use these for standard resource allocation without custom logic: - **ResourceManagerAllocateResources** - Complete allocation (built-in selection logic) - **ResourceManagerDeallocateResources** - Complete deallocation with cleanup - **ResourceManagerCreateTemplate** - Template management **Level 2: Workflow Callback Tasks (Custom Logic)** Use these when you need custom allocation/deallocation workflows: - **GetCandidates** - Get candidates from Resource Manager - **SelectCandidate** - Select specific candidate with custom logic #### **When to Use Each Level** **Use Pre-built Tasks when:** - Standard allocation (first available value is fine) - No custom selection logic needed - Simple deallocation without complex cleanup **Use Workflow Callback Tasks when:** - Custom candidate selection (odd/even values, priority rules) - Complex validation with external systems - Multi-step allocation with rollback - Custom deallocation workflows #### **Available Workflow Tasks** **Pre-built Allocation Tasks:** - `ResourceManagerAllocateResources` - Complete end-to-end allocation - `ResourceManagerDeallocateResources` - Complete deallocation with cleanup - `ResourceManagerCreateTemplate` - Template creation and management **Workflow Callback Tasks:** - `GetCandidates` - Retrieve candidate values from Resource Manager - `SelectCandidate` - Select specific candidate or report failure #### **Usage Recommendations** **Use Pre-built Tasks for:** - Standard allocation workflows where first available value is acceptable - Simple resource management without custom logic - Quick allocation/deallocation without business rules **Use Callback Tasks for:** - Custom candidate selection logic (regional preferences, priority rules) - Multi-step allocation workflows with validation - Complex integration with external systems - Custom deallocation workflows with specific cleanup requirements All tasks are available in the workflow builder UI and handle gRPC communication automatically. #### **Why Use Workflow Tasks?** **Simplified Integration:** - ✅ **Drag & drop** in workflow builder UI - ✅ **No coding** required for basic resource allocation - ✅ **Built-in error handling** and retry logic - ✅ **Automatic logging** and monitoring **vs. Direct gRPC:** - ❌ **Complex setup** - gRPC client configuration, proto imports - ❌ **Manual error handling** - implement retry logic yourself - ❌ **More coding** - serialize/deserialize data manually **When to Use Each:** - **Workflow Tasks**: Most common use case - visual workflows, standard patterns - **Direct gRPC**: Advanced integrations, custom applications, external systems --- ## Complete Examples ### Example 1: VLAN Pool Template ```yaml name: "datacenter-vlans" scope: "datacenter-east" description: "VLAN allocation for east datacenter" # version and status are managed by backend resources: - name: "vlan_id" base_type: "vlan" resource_type: "int" constraints: start: 100 end: 4000 step: 1 workflows: allocation: "vlan-allocation-workflow" deallocation: "vlan-cleanup-workflow" ``` ### Example 2: Server Hostname Template ```yaml name: "server-hostnames" scope: "production" description: "Production server hostname generation" # version and status are managed by backend resources: - name: "hostname" base_type: "string" resource_type: "hostname" constraints: pattern: "srv_{choice:options=web,api,db}_{seq:start=100:end=999:reusable=false}" workflows: allocation: "provision-server-workflow" deallocation: "deprovision-server-workflow" ``` ### Example 3: Network Interface Template (Self-Managed) ```yaml name: "interface-names" scope: "network-config" description: "Network interface naming" # version and status are managed by backend resources: - name: "interface_name" base_type: "string" resource_type: "interface" external: false # RM generates interface names constraints: pattern: "lo{seq:start=0:end=128:reusable=true}" exclude: ["lo0", "lo1"] # Reserve management interfaces workflows: allocation: "configure-interface-workflow" deallocation: "cleanup-interface-workflow" ``` ### Example 3b: Multi-Dimensional GE Interface Template ```yaml name: "ge-interfaces-multidim" scope: "network-topology" description: "Multi-dimensional GE interface allocation" # version and status are managed by backend resources: - name: "ge_interface" base_type: "string" resource_type: "interface" external: false # RM generates all combinations constraints: pattern: "ge{seq:start=0:end=7:reusable=true}/{seq:start=0:end=3:reusable=true}/{seq:start=0:end=47:reusable=true}" exclude: ["ge0/0/0", "ge0/0/1"] # Reserve management ports workflows: allocation: "configure-ge-interface-workflow" deallocation: "cleanup-ge-interface-workflow" # Results: ge0/0/2, ge0/0/3, ..., ge0/1/0, ge0/1/1, ..., ge7/3/47 # Total combinations: 8×4×48 = 1,536 interfaces (minus excludes) ``` ### Example 4: Timestamped Log Files Template ```yaml name: "system-logs" scope: "production" description: "System log file naming with timestamps" # version and status are managed by backend resources: - name: "log_filename" base_type: "string" resource_type: "logfile" external: false # RM generates timestamped filenames constraints: pattern: "log_{time:yyyymmdd}_{time:unix}_{choice:options=error,warn,info,debug}" workflows: allocation: "create-log-file-workflow" deallocation: "archive-log-file-workflow" # Result: "log_20240115_1705327800_error", "log_20240115_1705327850_warn" # Each allocation gets current date and timestamp ``` ### Example 5: Cloud Instance Template (Externally-Managed) ```yaml name: "cloud-instances" scope: "aws-us-east" description: "AWS EC2 instance allocation" # version and status are managed by backend resources: - name: "instance_id" base_type: "string" resource_type: "ec2_instance" external: true # AWS provides instance IDs, RM tracks only constraints: {} # No constraints - external system controls values workflows: allocation: "provision-ec2-instance-workflow" deallocation: "terminate-ec2-instance-workflow" ``` --- ## Workflow Integration ### Allocation Flow 1. **Client** creates allocation request via gRPC API 2. **Resource Manager** starts allocation workflow 3. **Workflow** calls `GetCandidates()` to get available values (for self-managed resources) 4. **Workflow** applies custom selection logic to choose preferred candidate 5. **Workflow** attempts allocation (may involve external systems) 6. **Workflow** calls `SelectCandidate()` with success/failure result 7. **Resource Manager** completes allocation and returns result ### Custom Selection Examples Workflows can implement business logic to select specific candidates from the `GetCandidates()` response: **Regional VLAN Selection:** ```python # Workflow: vlan-east-allocation (choose odd VLANs) candidates_response = resource_manager.GetCandidates(correlation_id) candidates = json.loads(candidates_response.candidates) # [100, 101, 102, 103, ...] selected_vlan = None for candidate in candidates: if candidate % 2 == 1: # Select odd VLAN for east datacenter selected_vlan = candidate break # Workflow: vlan-west-allocation (choose even VLANs) for candidate in candidates: if candidate % 2 == 0: # Select even VLAN for west datacenter selected_vlan = candidate break ``` **Priority-Based Selection:** ```python # Select lowest available value selected = min(candidates) # Select highest available value selected = max(candidates) # Select value within preferred range preferred_candidates = [c for c in candidates if 200 <= c <= 300] selected = preferred_candidates[0] if preferred_candidates else candidates[0] ``` **Filter-Based Candidate Retrieval:** ```python # Integer pool filters - restrict range at query time candidates_response = resource_manager.GetCandidates( correlation_id="alloc-12345", limit=10, filters=json.dumps({"start": 200, "end": 300, "exclude": [250, 275]}) ) candidates = json.loads(candidates_response.candidates) # [201, 202, ..., 249, 251, ..., 299] (excluding 250, 275) # String pool exclude filters - exclude specific values candidates_response = resource_manager.GetCandidates( correlation_id="alloc-67890", limit=10, filters=json.dumps({"exclude": ["lo1", "lo5", "lo10"]}) # Only exclude filter supported ) candidates = json.loads(candidates_response.candidates) # ["lo0", "lo2", "lo3", "lo4", "lo6", "lo7", "lo8", "lo9", "lo11", "lo12"] ``` **Pattern-Based Selection:** ```python # Select hostname with specific pattern candidates = ["web_srv_001", "api_srv_002", "db_srv_003"] selected = next(c for c in candidates if c.startswith("web_")) # Select interface with specific number candidates = ["eth0", "eth1", "eth2", "eth3"] selected = next(c for c in candidates if int(c[3:]) % 2 == 0) # Even interfaces ``` **Time-Based Resource Selection:** ```python # Template: "backup_{time:yyyymmdd}_{seq:start=1:end=999:reusable=false}_{choice:options=full,incremental}" # Workflow: backup-allocation-workflow candidates_response = resource_manager.GetCandidates(correlation_id) candidates = json.loads(candidates_response.candidates) # candidates = ["backup_20240115_1_full", "backup_20240115_2_incremental", ...] # Custom logic: Prefer full backups on weekends import datetime is_weekend = datetime.datetime.now().weekday() >= 5 # Saturday/Sunday selected = None for candidate in candidates: if is_weekend and "full" in candidate: selected = candidate # Prefer full backups on weekends break elif not is_weekend and "incremental" in candidate: selected = candidate # Prefer incremental on weekdays break if not selected: selected = candidates[0] # Fallback to first available resource_manager.SelectCandidate( correlation_id=correlation_id, success=True, selected_value=json.dumps(selected) ) # Result: "backup_20240115_1_full" (if weekend) ``` ### Error Handling Workflows should handle failures gracefully: ```python # Workflow pseudocode candidates_response = resource_manager.GetCandidates(correlation_id) candidates = json.loads(candidates_response.candidates) for candidate in candidates: try: # Attempt allocation in external system external_system.allocate(candidate) # Success - select this candidate resource_manager.SelectCandidate( correlation_id=correlation_id, success=True, selected_value=json.dumps(candidate) ) break except ExternalSystemError as e: # Try next candidate continue else: # All candidates failed resource_manager.SelectCandidate( correlation_id=correlation_id, success=False, error_message="All candidates rejected by external system" ) ``` This tutorial provides the foundation for using the Resource Manager effectively for various resource allocation scenarios.
Mark as private (unlisted)
for 30 minutes
for 6 hours
for 1 day
for 1 week
for 1 month
for 1 year
everlasting (like CCCP)