graphiant-sdk 26.1.1
Python SDK for Graphiant NaaS
License: MIT
Keywords: SDK, Graphiant, NaaS

# Graphiant SDK Python

[![PyPI version](https://badge.fury.io/py/graphiant-sdk.svg)](https://badge.fury.io/py/graphiant-sdk)
[![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Documentation](https://img.shields.io/badge/docs-latest-brightgreen.svg)](https://docs.graphiant.com/docs/graphiant-sdk-python)
[![CI/CD](https://github.com/Graphiant-Inc/graphiant-sdk-python/actions/workflows/test.yml/badge.svg)](https://github.com/Graphiant-Inc/graphiant-sdk-python/actions)

A comprehensive Python SDK for [Graphiant Network-as-a-Service (NaaS)](https://www.graphiant.com) offerings, providing seamless integration with Graphiant's network automation platform.

Refer [Graphiant Docs](https://docs.graphiant.com) to get started with [Graphiant Network-as-a-Service (NaaS)](https://www.graphiant.com) offerings.

## 📚 Documentation

- **Official Documentation**: [Graphiant SDK Python Guide](https://docs.graphiant.com/docs/graphiant-sdk-python) <-> [Graphiant Automation Docs](https://docs.graphiant.com/docs/automation)
- **API Reference**: [Graphiant SDK Python API Docs](docs/DefaultApi.md) <-> [Graphiant Portal REST API Guide](https://docs.graphiant.com/docs/graphiant-portal-rest-api)
- **Package**: [PyPI package - graphiant-sdk](https://pypi.org/project/graphiant-sdk)
- **Changelog**: [CHANGELOG.md](CHANGELOG.md) - Detailed release notes and version history

## ✨ Features

- **Complete API Coverage**: Full access to all Graphiant REST API endpoints
- **Authentication**: Built-in bearer token authentication
- **Device Management**: Comprehensive device configuration and monitoring
- **Network Operations**: Circuit management, interface configuration, and routing
- **Error Handling**: Robust exception handling with detailed error messages
- **Type Safety**: Full type hints and validation using Pydantic models
- **CLI Support**: Command-line interface for quick operations

## 🚀 Quick Start

### Installation

Install the package from PyPI:

```bash
pip install graphiant-sdk
```

### Basic Usage

```python
import graphiant_sdk
from graphiant_sdk.exceptions import (
    ApiException, BadRequestException, UnauthorizedException, 
    ForbiddenException, NotFoundException, ServiceException
)

# Create client configuration
config = graphiant_sdk.Configuration(
    host="https://api.graphiant.com",
    username="your_username",
    password="your_password"
)

# Initialize API client
api_client = graphiant_sdk.ApiClient(config)
api = graphiant_sdk.DefaultApi(api_client)

# Authenticate and get bearer token
auth_request = graphiant_sdk.V1AuthLoginPostRequest(
    username=config.username,
    password=config.password
)

try:
    auth_response = api.v1_auth_login_post(v1_auth_login_post_request=auth_request)
    bearer_token = f'Bearer {auth_response.token}'
    print(f"Authentication successful")
except Exception as e:
    print(f"Authentication failed: {e}")
    exit(1)

# Get device summary
try:
    edges_summary = api.v1_edges_summary_get(authorization=bearer_token)
    print(f"Found {len(edges_summary.edges_summary)} devices")
    
    for device in edges_summary.edges_summary:
        print(f"Device: {device.hostname}, Status: {device.status}")
        
except Exception as e:
    print(f"Failed to get device summary: {e}")
```

## 🔄 Migration Guide: Upgrading from 25.10.2 to 25.11.1+

The 25.11.1+ API is optimized to reuse redundant schemas, which results in changes to inner class types. You may need to update your existing scripts to use the newer class names.

### Benefits of Upgrading

The new API specification (25.11.1+) brings significant improvements:

- **Reduced Specification Size**: The API specification file size has been reduced from **9.8M to 1.5M** (~85% reduction) through schema optimization and reuse
- **Enhanced Documentation**: The new spec includes more comprehensive documentation for better developer experience
- **Cleaner Class Names**: Response classes no longer include HTTP status codes, making imports and type references more intuitive
- **Reusable Schemas**: Inner classes now use reusable schema names, meaning **common schemas share the same inner class names across different endpoints**. This reduces code duplication, improves maintainability, and allows you to reuse the same imports and type references for similar data structures

### Important Changes

#### 1. Remove Status Code from Class Names

Response class names no longer include HTTP status codes. Update your imports and type references:

**Before (25.11.1):**
```python
from graphiant_sdk.models.v1_edges_summary_get200_response import V1EdgesSummaryGet200Response
from graphiant_sdk.models.v1_global_summary_post200_response import V1GlobalSummaryPost200Response
from graphiant_sdk.models.v1_devices_device_id_config_put202_response import V1DevicesDeviceIdConfigPut202Response
```

**After (25.11.1):**
```python
from graphiant_sdk.models.v1_edges_summary_get_response import V1EdgesSummaryGetResponse
from graphiant_sdk.models.v1_global_summary_post_response import V1GlobalSummaryPostResponse
from graphiant_sdk.models.v1_devices_device_id_config_put_response import V1DevicesDeviceIdConfigPutResponse
```

**Common patterns to update:**
- `Post200Response` → `PostResponse`
- `Get200Response` → `GetResponse`
- `Put202Response` → `PutResponse`
- `Put204Response` → `PutResponse`
- `Post201Response` → `PostResponse`

> **Note**: The vast majority of response classes have been updated. A few exceptions may remain (e.g., `V1AuthRefreshGet200Response`), but these are rare edge cases. When in doubt, check the current API file (`graphiant_sdk/api/default_api.py`) or the model documentation.

#### 2. Find and Rename Inner Property Class Names

Inner classes have been renamed to use reusable schema names. **Because schemas are now reused, common schemas will share the same inner class names across different endpoints.** This means you can reuse the same import and type references for similar data structures.

To find the new class name:

1. **Step 1**: Find the top-level class name by removing the status code (if it exists) and trimming to `Response`:
   - `V1GlobalSummaryPost200Response` → `V1GlobalSummaryPostResponse`

2. **Step 2**: Check the documentation for the inner property's new class name:
   - Open `docs/V1GlobalSummaryPostResponse.md`
   - Find the property (e.g., `summaries`)
   - Note the new class name (e.g., `ManaV2GlobalObjectSummary`)

**Key Benefit**: If multiple endpoints use the same schema structure, they will now share the same inner class name. For example, if both `V1GlobalSummaryPostResponse` and `V1EdgesSummaryGetResponse` use the same summary schema, they will both use `ManaV2GlobalObjectSummary` as the inner class type.

**Example Migration:**

**Before (25.11.1):**
```python
from graphiant_sdk.models.v1_global_summary_post200_response import V1GlobalSummaryPost200Response
from graphiant_sdk.models.v1_global_summary_post200_response_summaries_inner import V1GlobalSummaryPost200ResponseSummariesInner

response = api.v1_global_summary_post(...)
for summary in response.summaries:
    # summary is V1GlobalSummaryPost200ResponseSummariesInner
    print(summary.name)
```

**After (25.11.1):**
```python
from graphiant_sdk.models.v1_global_summary_post_response import V1GlobalSummaryPostResponse
from graphiant_sdk.models.mana_v2_global_object_summary import ManaV2GlobalObjectSummary
# or
from graphiant_sdk.models import ManaV2GlobalObjectSummary

response = api.v1_global_summary_post(...)
for summary in response.summaries:
    # summary is ManaV2GlobalObjectSummary
    print(summary.name)
```

#### 3. Finding Endpoint Request/Response Models

To find all endpoints and their request/response models:

- **API Reference**: See `graphiant_sdk/api/default_api.py` or `docs/DefaultApi.md`
- **Model Documentation**: Check individual model files in `docs/` directory (e.g., `docs/V1GlobalSummaryPostResponse.md`)

### Migration Checklist

- [ ] Search and replace all `200Response`, `202Response`, `201Response`, `204Response` patterns
- [ ] Update imports for response classes
- [ ] Find and update inner class references (check documentation files)
- [ ] Test all API calls with new class names
- [ ] Update type hints and annotations

### Need Help?

- Check the [API Reference](docs/DefaultApi.md) for endpoint details
- Review model documentation in the `docs/` directory
- See [Support](#-support) section for additional resources

## 🔧 Advanced Usage

### Device Configuration Management

```python
# Verify device portal status before configuration
def verify_device_portal_status(api, bearer_token, device_id):
    """Verify device is ready for configuration updates"""
    edges_summary = api.v1_edges_summary_get(authorization=bearer_token)
    
    for edge in edges_summary.edges_summary:
        if edge.device_id == device_id:
            if edge.portal_status == "Ready" and edge.tt_conn_count == 2:
                return True
            else:
                raise Exception(f"Device {device_id} not ready. "
                              f"Status: {edge.portal_status}, "
                              f"TT Connections: {edge.tt_conn_count}")
    return False

# Configure device interfaces
def configure_device_interfaces(api, bearer_token, device_id):
    """Configure device interfaces with circuits and subinterfaces"""
    
    # Define circuits
    circuits = {
        "c-gigabitethernet5-0-0": {
            "name": "c-gigabitethernet5-0-0",
            "description": "c-gigabitethernet5-0-0",
            "linkUpSpeedMbps": 50,
            "linkDownSpeedMbps": 100,
            "connectionType": "internet_dia",
            "label": "internet_dia_4",
            "qosProfile": "gold25",
            "qosProfileType": "balanced",
            "diaEnabled": False,
            "lastResort": False,
            "patAddresses": {},
            "staticRoutes": {}
        }
    }
    
    # Define interfaces
    interfaces = {
        "GigabitEthernet5/0/0": {
            "interface": {
                "adminStatus": True,
                "maxTransmissionUnit": 1500,
                "circuit": "c-gigabitethernet5-0-0",
                "description": "wan_1",
                "alias": "primary_wan",
                "ipv4": {"dhcp": {"dhcpClient": True}},
                "ipv6": {"dhcp": {"dhcpClient": True}}
            }
        },
        "GigabitEthernet8/0/0": {
            "interface": {
                "subinterfaces": {
                    "18": {
                        "interface": {
                            "lan": "lan-7-test",
                            "vlan": 18,
                            "description": "lan-7",
                            "alias": "non_production",
                            "adminStatus": True,
                            "ipv4": {"address": {"address": "10.2.7.1/24"}},
                            "ipv6": {"address": {"address": "2001:10:2:7::1/64"}}
                        }
                    }
                }
            }
        }
    }
    
    # Create configuration request
    edge_config = graphiant_sdk.ManaV2EdgeDeviceConfig(
        circuits=circuits,
        interfaces=interfaces
    )
    
    config_request = graphiant_sdk.V1DevicesDeviceIdConfigPutRequest(
        edge=edge_config
    )
    
    try:
        # Verify device is ready
        verify_device_portal_status(api, bearer_token, device_id)
        
        # Push configuration
        response = api.v1_devices_device_id_config_put(
            authorization=bearer_token,
            device_id=device_id,
            v1_devices_device_id_config_put_request=config_request
        )
        
        print(f"Configuration job submitted. Job ID: {response.job_id}")
        return response
        
    except ForbiddenException as e:
        print(f"Permission denied: {e}")
    except Exception as e:
        print(f"Configuration failed: {e}")
```

### Error Handling

```python
def handle_api_errors(func):
    """Decorator for consistent error handling"""
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except BadRequestException as e:
            print(f"Bad Request: {e}")
        except UnauthorizedException as e:
            print(f"Unauthorized: {e}")
        except ForbiddenException as e:
            print(f"Forbidden: {e}")
        except NotFoundException as e:
            print(f"Not Found: {e}")
        except ServiceException as e:
            print(f"Service Error: {e}")
        except ApiException as e:
            print(f"API Error: {e}")
    return wrapper

@handle_api_errors
def get_device_info(api, bearer_token, device_id):
    """Get detailed device information"""
    return api.v1_devices_device_id_get(
        authorization=bearer_token,
        device_id=device_id
    )
```

## 🛠️ Development

### Prerequisites

- Python 3.9+ (3.12+ recommended)
- Git
- OpenAPI Generator (for code generation)

### CI/CD Workflows

This repository uses GitHub Actions for continuous integration and deployment:

- **Linting** (`lint.yml`): Runs Flake8 and MyPy type checking on pull requests and pushes
- **Testing** (`test.yml`): Runs pytest with coverage across Python 3.9, 3.10, 3.11, 3.12, and 3.13
- **Building** (`build.yml`): Builds wheel and source distributions
- **Releasing** (`release.yml`): Publishes to PyPI (manual trigger, admin-only)

See [`.github/workflows/README.md`](.github/workflows/README.md) for detailed workflow documentation.

### Building from Source

```bash
# Clone repository
git clone git@github.com:Graphiant-Inc/graphiant-sdk-python.git
cd graphiant-sdk-python

# Create virtual environment
python3 -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install dependencies
pip install --upgrade pip setuptools wheel
pip install -r requirements.txt

# Build distribution
python setup.py sdist bdist_wheel

# Install locally
pip install dist/*.tar.gz
```

### Code Generation

To regenerate the SDK from the latest API specification:

```bash
# Install OpenAPI Generator
brew install openapi-generator  # macOS
# or download from: https://github.com/OpenAPITools/openapi-generator

# Generate SDK
openapi-generator generate \
    -i graphiant_api_docs_v26.1.1.json \
    -g python \
    --git-user-id Graphiant-Inc \
    --git-repo-id graphiant-sdk-python \
    --package-name graphiant_sdk \
    --additional-properties=packageVersion=26.1.1
```

> **Note**: Latest API documentation can be downloaded from the Graphiant portal under "Support Hub" > "Developer Tools".

### Testing

```bash
# Run tests
python -m pytest tests/

# Run with coverage
python -m pytest tests/ --cov=graphiant_sdk --cov-report=html
```

## 📖 API Reference

### Core Classes

- **`Configuration`**: Client configuration with authentication
- **`ApiClient`**: HTTP client for API requests
- **`DefaultApi`**: Main API interface with all endpoints

### Key Models

- **`V1AuthLoginPostRequest`**: Authentication request
- **`V1AuthLoginPostResponse`**: Authentication response
- **`V1EdgesSummaryGetResponse`**: Device summary response
- **`V1DevicesDeviceIdConfigPutRequest`**: Device configuration request
- **`V1DevicesDeviceIdConfigPutResponse`**: Device configuration response
- **`V1GlobalSummaryPostResponse`**: Global summary response (uses `ManaV2GlobalObjectSummary` for inner items)

### Common Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/v1/auth/login` | POST | Authenticate and get bearer token |
| `/v1/edges/summary` | GET | Get all device summaries |
| `/v1/devices/{device_id}` | GET | Get device details |
| `/v1/devices/{device_id}/config` | PUT | Update device configuration |
| `/v1/circuits` | GET | List circuits |
| `/v1/alarms` | GET | Get system alarms |

## 🔐 Security

- **Authentication**: Bearer token-based authentication
- **HTTPS**: All API communications use HTTPS
- **Credentials**: Store credentials securely using environment variables
- **Token Management**: Bearer tokens expire and should be refreshed as needed

### Environment Variables

```bash
export GRAPHIANT_HOST="https://api.graphiant.com"
export GRAPHIANT_USERNAME="your_username"
export GRAPHIANT_PASSWORD="your_password"
```

```python
import os

username = os.getenv("GRAPHIANT_USERNAME")
password = os.getenv("GRAPHIANT_PASSWORD")
host = os.getenv("GRAPHIANT_HOST", "https://api.graphiant.com")
```

**Note**: For detailed security policies, vulnerability reporting, and security best practices, see [SECURITY.md](SECURITY.md).

## 🤝 Contributing

We welcome contributions! Please follow these steps:

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes and ensure they pass local tests:
   ```bash
   # Run linting
   flake8 graphiant_sdk/
   mypy graphiant_sdk/
   
   # Run tests
   pytest --cov=graphiant_sdk
   ```
4. Commit your changes with a clear message (`git commit -m 'Add amazing feature'`)
5. Push to the branch (`git push origin feature/amazing-feature`)
6. Open a Pull Request

**Note**: All pull requests automatically run CI/CD checks (linting, testing across multiple Python versions). Ensure all checks pass before requesting review.

See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed contribution guidelines.

## 📄 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## 🆘 Support

- **Official Documentation**: [Graphiant SDK Python Guide](https://docs.graphiant.com/docs/graphiant-sdk-python) <-> [Graphiant Automation Docs](https://docs.graphiant.com/docs/automation)
- **API Reference**: [Graphiant SDK Python API Docs](docs/DefaultApi.md) <-> [Graphiant Portal REST API Guide](https://docs.graphiant.com/docs/graphiant-portal-rest-api)
- **Changelog**: [CHANGELOG.md](CHANGELOG.md) - Detailed release notes and version history
- **Issues**: [GitHub Issues](https://github.com/Graphiant-Inc/graphiant-sdk-python/issues)
- **Email**: support@graphiant.com

## 🔗 Related Projects

- [Graphiant SDK Go](https://github.com/Graphiant-Inc/graphiant-sdk-go)
- [Graphiant Playbooks](https://github.com/Graphiant-Inc/graphiant-playbooks)

---

**Made with ❤️ by the Graphiant Team**