Metadata-Version: 2.4
Name: hfortix-fortiztp
Version: 0.5.161
Summary: FortiZTP Cloud API Client - Device provisioning and management
Author-email: FortiX Development Team <herman.w.jacobsen@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/hermanwjacobsen/hfortix-fortiztp
Project-URL: Documentation, https://hfortix.readthedocs.io
Project-URL: Repository, https://github.com/hermanwjacobsen/hfortix-fortiztp
Project-URL: Issues, https://github.com/hermanwjacobsen/hfortix-fortiztp/issues
Keywords: fortinet,fortiztp,api,client,provisioning,zero-touch,fortigate,fortiap,fortiswitch
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Systems Administration
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: hfortix-core>=0.5.161
Requires-Dist: httpx>=0.24.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: black>=23.0.0; extra == "dev"
Requires-Dist: isort>=5.12.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Requires-Dist: pyright>=1.1.0; extra == "dev"

# FortiZTP Package

Python SDK for FortiZTP Cloud API v2.0 - Device provisioning and management for FortiGate, FortiAP, FortiSwitch, and FortiExtender.

## Status

**✅ FUNCTIONAL - BETA RELEASE**

**Schema Completion:** 100% (18/18 endpoints)
- ✅ Device endpoints: 5/5 complete
- ✅ Script endpoints: 7/7 complete
- ✅ FortiManager endpoints: 5/5 complete
- ✅ System endpoint: 1/1 complete

**Code Generation:** ✅ Complete - All 18 endpoints auto-generated from schema

**Testing:** ✅ Live tests passing - GET /v2/devices validated with real API

See `dev/SCHEMA_GAPS_ANALYSIS.md` for details on missing information.

---

## Overview

This package will provide a fully typed Python SDK for FortiZTP Cloud API with:

- 🔒 **OAuth 2.0 Authentication** - Automatic token management
- 📝 **Full Type Safety** - Type hints with Literal types for autocomplete
- 🎯 **Specialized Responses** - Property access on response objects
- 🔄 **Smart Defaults** - Sensible defaults for optional parameters
- ⚡ **HTTP Metadata** - Status codes, response times, raw access
- 🛡️ **Error Handling** - Comprehensive exception handling

---

## Quick Start

### Installation

```bash
pip install hfortix-fortiztp
```

### Basic Usage

```python
from hfortix_fortiztp import FortiZTP

# Option 1: Auto-login with credentials
client = FortiZTP(
    api_id="your_api_id",
    password="your_password"
)

# Option 2: CloudSession (recommended for multi-service)
from hfortix_core.session import CloudSession

with CloudSession(api_id="...", password="...") as session:
    client = FortiZTP(session=session)  # Auto-uses "fortiztp" client_id
    devices = client.api.devices.list()
    # Token managed automatically

# Option 3: Pre-obtained OAuth token
client = FortiZTP(oauth_token="your_token")

# List all devices
devices = client.api.devices.list()
print(f"Found {len(devices.devices)} devices")

# List provisioned devices only
provisioned = client.api.devices.list(provision_status="provisioned")

# Get specific device details
device = client.api.devices.get(device_sn="FGT60FTK19000001")
print(f"Device: {device.deviceType} - Status: {device.provisionStatus}")

# Get system status
status = client.api.system.get()
print(f"System status: {status.serviceStatus}")
```

### Rate Limit Tracking

FortiZTP API has a limit of **2000 calls per hour**. Track your usage:

```python
from hfortix_fortiztp import FortiZTP

# Configure custom limits (optional, defaults to None)
client = FortiZTP(
    api_id="...",
    password="...",
    rate_limit_calls_per_hour=2000  # FortiZTP: 2000/hour
)

# Make API calls
devices = client.api.devices.list()

# Check rate limit status
status = client.get_rate_limit_status()
print(f"Calls last min: {status['calls_last_min']}")
print(f"Calls last 5min: {status['calls_last_5min']}")
print(f"Calls last hour: {status['calls_last_hour']}/{status['limits']['calls_per_hour']}")
print(f"Within limits: {status['within_limits']}")
```

**Note**: Rate limiting counters are **informational only** and do not enforce limits.

---

## API Coverage

### Devices API (5 endpoints)
- ✅ `list()` - List devices with filters (status, type, SN)
- ✅ `bulk_provision()` - Provision/unprovision multiple devices
- ✅ `get()` - Get single device details
- ✅ `update()` - Provision/unprovision single device
- ✅ `firmware_profiles()` - Get firmware profiles by region

### Scripts API (7 endpoints)
- ✅ `scripts_list()` - List all scripts
- ✅ `scripts_post()` - Create new script
- ✅ `scripts_get()` - Get script metadata
- ✅ `scripts_put()` - Update script metadata
- ✅ `scripts_delete()` - Delete script
- ✅ `scripts_content_get()` - Download script content
- ✅ `scripts_content_put()` - Upload script content

### FortiManagers API (5 endpoints)
- ✅ `fortimanagers_list()` - List all FortiManager configs
- ✅ `fortimanagers_post()` - Create FortiManager config
- ✅ `fortimanagers_get()` - Get FortiManager details
- ✅ `fortimanagers_put()` - Update FortiManager config
- ✅ `fortimanagers_delete()` - Delete FortiManager config

### System API (1 endpoint)
- ✅ `get()` - Get system status
- Script association

### System (1 endpoint)
- Get system status

---

## Planned Usage

> **Note:** This is the intended API design. Code is not yet generated.

### Basic Usage

```python
from hfortix import FortiZTP

# Initialize client with OAuth credentials
client = FortiZTP(
    client_id="your_client_id",
    api_key="your_api_key",
    password="your_password"
)

# List all provisioned devices
response = client.v2.devices.list.get(provision_status="provisioned")

# Access response properties
print(f"Total devices: {response.total}")
print(f"Has cache: {response.hasCache}")

for device in response.data:
    print(f"{device['deviceSN']}: {device['provisionStatus']}")

# Get specific device
device = client.v2.devices.get.get(device_sn="FGT60D4615067214")
print(f"Device type: {device.deviceType}")
print(f"Status: {device.provisionStatus}")

# Provision a device to FortiManager
client.v2.devices.put.put(
    device_sn="FGT60D4615067214",
    request_body={
        "provisionStatus": "provisioned",
        "provisionTarget": "FortiManager",
        "provisionTargetOid": 123
    }
)
```

### Type Safety with Literals

```python
from typing import Literal

# IDE will autocomplete these values:
DeviceType = Literal["FortiGate", "FortiAP", "FortiSwitch", "FortiExtender"]
ProvisionStatus = Literal["provisioned", "unprovisioned", "hidden", "incomplete"]
ProvisionTarget = Literal["FortiManager", "FortiGateCloud", "FortiEdgeCloud", "ExternalController"]

# Strongly typed parameters
response = client.v2.devices.list.get(
    provision_status="provisioned",  # Autocomplete!
    device_type="FortiGate"           # Autocomplete!
)
```

### Script Management

```python
# List all scripts
scripts = client.v2.settings.scripts.list.get()
print(f"Total scripts: {scripts.total}")

# Create new script
new_script = client.v2.settings.scripts.post.post(
    request_body={
        "name": "Initial Configuration",
    }
)
print(f"Created script OID: {new_script.oid}")

# Upload script content
client.v2.settings.scripts.put_content.put(
    oid=new_script.oid,
    file_path="./scripts/initial_config.conf"
)

# Download script content
content = client.v2.settings.scripts.get_content.get(oid=new_script.oid)
print(content.raw)  # Plain text content
```

### FortiManager Configuration

```python
# Add FortiManager
fmg = client.v2.settings.fortimanagers.post.post(
    request_body={
        "sn": "FMG-VMTM23010656",
        "ip": "192.168.223.20",
        "scriptOid": 123  # Optional pre-run script
    }
)

# HA Setup (comma-separated values)
fmg_ha = client.v2.settings.fortimanagers.post.post(
    request_body={
        "sn": "FMG-VMTM23010656,FMG-VMTM23010657",
        "ip": "192.168.223.20,192.168.223.21",
        "scriptOid": 123
    }
)

# List all FortiManagers
all_fmgs = client.v2.settings.fortimanagers.list.get()
for fmg in all_fmgs.data:
    print(f"{fmg['sn']} at {fmg['ip']}")
```

### HTTP Metadata Access

```python
response = client.v2.devices.get.get(device_sn="FGT60D4615067214")

# HTTP metadata
print(f"Status code: {response.http_status_code}")
print(f"Response time: {response.response_time}s")

# Raw access
raw_dict = response.raw
copy_dict = response.dict()
```

### Error Handling

```python
from hfortix_core.exceptions import (
    AuthenticationError,
    PermissionDeniedError,
    ResourceNotFoundError,
    RateLimitError
)

try:
    device = client.v2.devices.get.get(device_sn="INVALID_SN")
except ResourceNotFoundError as e:
    print(f"Device not found: {e}")
except PermissionDeniedError as e:
    print(f"Access denied: {e}")
except RateLimitError as e:
    print(f"Rate limit exceeded: {e}")
    print(f"Retry after: {e.retry_after}s")
```

---

## Development Status

### Completed ✅
- [x] Generator architecture designed
- [x] Schema file structure created
- [x] Type definitions (5 Literal types)
- [x] Common data structures (5 structures)
- [x] All 18 endpoints documented
- [x] 6 endpoints with complete responses

### In Progress ⏳
- [ ] Complete schema (12 endpoints missing responses)
- [ ] Verify data structures
- [ ] Get firmware_profiles structure

### Not Started 📋
- [ ] Schema parser
- [ ] Endpoint generator adaptation
- [ ] Template creation
- [ ] Code generation
- [ ] Testing
- [ ] Documentation
- [ ] PyPI release

---

## Project Structure

```
packages/fortiztp/
├── README.md                          # This file
├── dev/
│   ├── GENERATOR_PLAN.md              # Implementation roadmap
│   ├── API_DOCUMENTATION.md           # API reference
│   ├── SCHEMA_GAPS_ANALYSIS.md        # Missing information checklist
│   ├── generator/
│   │   ├── generate.py                # Main generator script (to be created)
│   │   ├── schema/
│   │   │   └── fortiztp_schema.py     # ✅ Manual API schema
│   │   ├── parsers/
│   │   │   └── fortiztp_schema_parser.py  # Schema parser (to be created)
│   │   ├── generators/
│   │   │   └── endpoint_generator.py  # Code generator (to be adapted)
│   │   ├── templates/
│   │   │   ├── endpoint_template.py.tmpl   # (to be created)
│   │   │   └── endpoint_template.pyi.tmpl  # (to be created)
│   │   └── endpoint_config.py         # FortiZTP overrides (to be created)
│   └── tests/                         # Generator tests (to be created)
└── src/
    └── hfortix_fortiztp/              # Generated SDK code (not yet created)
        ├── __init__.py
        ├── models.py                  # Response classes
        ├── types.py                   # Literal type aliases
        ├── api/
        │   └── v2/
        │       ├── __init__.py
        │       ├── devices/
        │       ├── settings/
        │       │   ├── scripts/
        │       │   └── fortimanagers/
        │       └── system/
        └── py.typed
```

---

## Schema Status

### Type Definitions (5/5) ✅
- DeviceType
- ProvisionStatus
- ProvisionSubStatus
- ProvisionTarget
- ServiceStatus

### Data Structures (5/5) ✅
- DEVICE_V2_DATA (16 properties) - needs verification
- SCRIPT_META_DATA (3 properties) - needs verification
- FORTIMANAGER_META_DATA (5 properties)
- SYSTEM_DATA (4 properties)
- ERROR_RESPONSE (2 properties)

### Endpoints (6/18) ⚠️

**Complete:**
- ✅ GET /v2/setting/fortimanagers
- ✅ POST /v2/setting/fortimanagers
- ✅ GET /v2/setting/fortimanagers/{oid}
- ✅ PUT /v2/setting/fortimanagers/{oid}
- ✅ DELETE /v2/setting/fortimanagers/{oid}
- ✅ GET /v2/system

**Missing Response Definitions (12):**
- ❌ GET /v2/devices
- ❌ PUT /v2/devices
- ❌ GET /v2/devices/{deviceSN}
- ❌ PUT /v2/devices/{deviceSN}
- ❌ GET /v2/devices/{deviceSN}/regions/{region}/firmwareprofiles
- ❌ GET /v2/setting/scripts
- ❌ POST /v2/setting/scripts
- ❌ GET /v2/setting/scripts/{oid}
- ❌ PUT /v2/setting/scripts/{oid}
- ❌ DELETE /v2/setting/scripts/{oid}
- ❌ GET /v2/setting/scripts/{oid}/content
- ❌ PUT /v2/setting/scripts/{oid}/content

---

## Next Steps

### Critical (Blocking Code Generation):
1. Get response body examples for 12 missing endpoints
2. Verify DEVICE_V2_DATA structure (all 16 fields correct?)
3. Get firmware_profiles actual response structure
4. Confirm HTTP status codes (200, 201, 204)

### Implementation:
5. Create schema parser
6. Adapt endpoint generator from FortiCare
7. Create templates
8. Generate code
9. Test and validate

---

## Resources

- **API Base URL**: https://fortiztp.forticloud.com/public/api
- **Auth URL**: https://customerapiauth.fortinet.com
- **API Version**: v2.0
- **Rate Limit**: 2000 calls/hour
- **Authentication**: OAuth 2.0 (IAM API Users - Local type only)

---

## Contributing

This package is part of the hfortix SDK family:
- hfortix-core: Core HTTP client and utilities
- hfortix-fortios: FortiOS REST API
- hfortix-forticare: FortiCare Asset Management API
- hfortix-fortiztp: FortiZTP Cloud API (this package)
- hfortix: Meta package

See main repository for contribution guidelines.

---

## License

[Your License Here]

---

## Authors

[Your Name/Organization]

---

**Last Updated:** February 6, 2026  
**Schema Version:** 2.0 (33% complete)  
**Status:** Development (blocked - awaiting API documentation)
