<h1 align="center">
<br>
<img src="https://raw.githubusercontent.com/Cenvora/veeam-spc/main/media/Veeam_logo_2024_RGB_main_20.png"
     alt="Veeam Logo"
     height="100">
<br>
<br>
Veeam Service Provider Console Python API Wrapper
</h1>

<h4 align="center">
Python package for interacting with the Veeam Service Provider Console REST API
</h4>

<!-- Summary -->
This project is an independent, open source Python client for the Veeam Service Provider Console <a href="https://helpcenter.veeam.com/docs/vac/rest/reference/vspc-rest.html">REST API</a>. It is not affiliated with, endorsed by, or sponsored by Veeam Software.
<!-- Summary -->

## Supported Versions

<table>
  <thead>
    <tr>
      <th>VSPC Version</th>
      <th>API Version</th>
      <th>Supported</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>9.1</td>
      <td>3.6.1</td>
      <td style="text-align:center;">&#9989;</td>
    </tr>
    <tr>
      <td>9</td>
      <td>3.6</td>
      <td style="text-align:center;">&#9989;</td>
    </tr>
    <tr>
      <td>8.1</td>
      <td>3.5.1</td>
      <td style="text-align:center;">&#9989;</td>
    </tr>
    <tr>
      <td>&lt; 8.1</td>
      <td>&lt; 3.5.1</td>
      <td style="text-align:center;">&#10060;</td>
    </tr>
  </tbody>
</table>

## How to support new API versions
1. Download the OpenAPI yaml into openapi_schemas
2. Install the openapi-python-client package
2. Fix the OpenAPI yaml to conform to proper standards: `python fix_openapi_yaml.py .\openapi_schemas\vspc_rest_{vspc_version}.yaml .\openapi_schemas\vspc_rest_{vspc_version}_fixed.yaml`
3. Run `openapi-python-client generate --path ".\openapi_schemas\vspc_rest_{vspc_version}_fixed.yaml" --output-path ".\veeam_spc" --overwrite`
4. Fix any warnings/errors (application/binary+base64 can be ignored)
5. Rename the folder to match the API version (i.e., `v3_5_1`)
6. Write pytest tests
7. If an older API has been deprecated, delete its folder and yaml, then update the supported versions section of the readme

## Install
### From PyPi
`pip install veeam-spc`


### From Source
Clone the repository and install dependencies:
```sh
git clone https://github.com/Cenvora/veeam-spc.git
cd veeam-spc
pip install -e .
```

## Usage Tips
- For endpoints requiring authentication, use `AuthenticatedClient` instead of `Client`.
- To support multiple API versions, import the client and models from the desired versioned subpackage (e.g., `veeam_spc.v3_5_1`).
- For file downloads (PDF, CSV, etc.), access the raw response content: `response.content`.
- Refer to the generated `api` and `models` modules for available endpoints and request/response schemas.


## Usage

### Recommended Usage (Smart Client)

The `VeeamClient` handles:

- API version routing
- Authentication
- Token refresh
- `X-Client-Version` header injection so package API version matches header values
- Async calls
- Operation discovery

Each packaged version can be called independently through separate imports, but this is the **recommended way** to use this library.

#### Create a client and connect

You can authenticate using either **username/password** or a **pre-existing token**.

**Option 1: Username/Password Authentication**

```python
import asyncio
from veeam_spc.client import VeeamClient

async def main():
    vc = VeeamClient(
        host="https://vspc.example.com:1280",
        username="administrator",
        password="SuperSecretPassword",
        api_version="3.6",
        verify_ssl=False,
    )

    await vc.connect()

    # use the client...

    await vc.close()

asyncio.run(main())
```

**Option 2: Token Authentication** (recommended for long-lived access)

VSPC supports permanent tokens that don't expire, making them ideal for service accounts and CI/CD pipelines.

```python
import asyncio
from veeam_spc.client import VeeamClient

async def main():
    vc = VeeamClient(
        host="https://vspc.example.com:1280",
        token="your-permanent-token-here",
        api_version="3.6",
        verify_ssl=False,
    )

    await vc.connect()

    # use the client...

    await vc.close()

asyncio.run(main())
```

#### Call an API endpoint (async)

```python
provider = await vc.call(
    vc.api("provider").get_provider
)

# provider is a Provider model
print(provider.name)
```

#### Call any endpoint

Operations map directly to the OpenAPI layout:

```
api/
└── provider/
    └── get_provider.py
```

Call it like this:

```python
await vc.call(
    vc.api("provider").get_provider
)
```

Or explicitly:

```python
await vc.call(
    vc.api("provider.get_provider")
)
```

#### Pagination example

```python
result = await vc.call(
    vc.api("companies").get_companies,
    limit=50,
    offset=0,
)
```

#### Close the client

```python
await vc.close()
```

### Basic Usage (Direct API Access)

If you prefer to use the versioned packages directly without the SmartClient:

First, create a client from the appropriate API version:

```python
from veeam_spc.v3_5_1 import Client

client = Client(base_url="https://server:1280/api/v3")
```

If the endpoints you're going to hit require authentication, use `AuthenticatedClient`:

```python
from veeam_spc.v3_5_1 import AuthenticatedClient

client = AuthenticatedClient(base_url="https://server:1280/api/v3", token="SuperSecretToken")
```

Now call your endpoint and use your models:

```python
from veeam_spc.v3_5_1.models import About
from veeam_spc.v3_5_1.api.about import get_about_information
from veeam_spc.v3_5_1.types import Response

with client:
    about_info: About = get_about_information.sync(client=client, x_client_version="3.5.1")
    # or if you need more info (e.g. status_code)
    response: Response[About] = get_about_information.sync_detailed(client=client, x_client_version="3.5.1")
```

#### Async Usage
Or do the same thing with an async version:

```python
from veeam_spc.v3_5_1.models import About
from veeam_spc.v3_5_1.api.about import get_about_information
from veeam_spc.v3_5_1.types import Response

client = AuthenticatedClient(base_url="https://server:1280/api/v3", token="SuperSecretToken")

async with client:
    about_info = await get_about_information.asyncio(client=client, x_client_version="3.5.1")
    response: Response[About] = await get_about_information.asyncio_detailed(client=client, x_client_version="3.5.1")
```

### SSL Verification
By default, HTTPS APIs will verify SSL certificates. You can pass a custom certificate bundle or disable verification (not recommended):

```python
from veeam_spc.v3_5_1 import AuthenticatedClient

client = AuthenticatedClient(
    base_url="https://internal_api.example.com/api/v3",
    token="SuperSecretToken",
    verify_ssl="/path/to/certificate_bundle.pem",
)

# Disable SSL verification (security risk)
client = AuthenticatedClient(
    base_url="https://internal_api.example.com/api/v3",
    token="SuperSecretToken",
    verify_ssl=False
)
```

### Advanced Customizations
You can customize the underlying `httpx.Client` or `httpx.AsyncClient`:

```python
from veeam_spc.v3_5_1 import Client

def log_request(request):
    print(f"Request event hook: {request.method} {request.url} - Waiting for response")

def log_response(response):
    request = response.request
    print(f"Response event hook: {request.method} {request.url} - Status {response.status_code}")

client = Client(
    base_url="https://server:1280/api/v3",
    httpx_args={"event_hooks": {"request": [log_request], "response": [log_response]}},
)
# Or get the underlying httpx client to modify directly with client.get_httpx_client() or client.get_async_httpx_client()
```

## Contributing
Contributions are welcome! To contribute:
- Fork the repository
- Create a feature branch
- Make your changes and add tests
- Submit a pull request with a clear description

Please follow PEP8 style and include docstrings for new functions/classes.

## 🤝 Core Contributors
This project is made possible thanks to the efforts of our core contributors:

- [Jonah May](https://github.com/JonahMMay)  
- [Maurice Kevenaar](https://github.com/mkevenaar)  

We’re grateful for their continued support and contributions.
