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

<h4 align="center">
Python package for interacting with the Veeam Backup & Replication REST API
</h4>

<!-- Summary -->
This project is an independent, open source Python client for the Veeam Backup & Replication <a href="https://helpcenter.veeam.com/references/vbr/13/rest/1.3-rev1/tag/SectionAbout">REST API</a>. It is not affiliated with, endorsed by, or sponsored by Veeam Software.
<!-- Summary -->

## Supported Versions

<table>
  <thead>
    <tr>
      <th>VBR Version</th>
      <th>API Version</th>
      <th>Supported</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>13.0.1.180</td>
      <td>1.3-rev1</td>
      <td style="text-align:center;">&#9989;</td>
    </tr>
    <tr>
      <td>13.0.0.4967</td>
      <td>1.3-rev0</td>
      <td style="text-align:center;">&#9989;</td>
    </tr>
    <tr>
      <td>12.3.1.1139</td>
      <td>1.2-rev1</td>
      <td style="text-align:center;">&#9989;</td>
    </tr>
    <tr>
      <td>&lt; 12.3.1.1139</td>
      <td>&lt; 1.2-rev1</td>
      <td style="text-align:center;">&#10060;</td>
    </tr>
  </tbody>
</table>

## How to support new API versions
1. Download the OpenAPI schema into openapi_schemas
2. Install the openapi-python-client package
3. Run `python fix_openapi_yaml.py .\openapi_schemas\vbr_rest_{version}.yaml .\openapi_schemas\vbr_rest_{version}_fixed.yaml` 
4. Run `openapi-python-client generate --path ".\openapi_schemas\vbr_rest_{version}_fixed.json" --output-path ".\veeam_br" --overwrite`
5. Fix any warnings/errors
6. Rename the folder to match the API version (i.e., `v1.3-rev1`)
7. Add the version mapping to versions.py
8. Write pytest tests
9. If an older API has been deprecated, delete its folder, json, and version.py entry, then update the supported versions section of the readme

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


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

## Usage
### Recommended Usage (Smart Client)
The `VeeamClient` handles:
- API version routing
- Authentication
- Token refresh
- `x-api-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 <strong>recommended way</strong>  to use this library.

#### Create a client and connect
```python
import asyncio
from veeam_br.client import VeeamClient

async def main():
    vc = VeeamClient(
        host="https://vbr.example.com:9419",
        username="administrator",
        password="SuperSecretPassword",
        api_version="1.3-rev1",
        verify_ssl=False,
    )

    await vc.connect()

    # use the client...

    await vc.close()

asyncio.run(main())
```

#### Call an API endpoint (async)
```python
repos = await vc.call(
    vc.api("repositories").get_all_repositories
)
```

#### Filter certain object types
Some objects, such as SmartObjectS3, use polymorphic subtypes with circular inheritance. These tend to not play well with package creation tools, so as part of the import process into this project, those relationships are broken. Where a repository would normally have a `bucket` object with immutability information inside, this breakage instead causes `bucket` to always be `UNSET` and instead populates the data into `additional_properties`. For example:
```python
from veeam_br.v1_3_rev1.models.e_repository_type import ERepositoryType

smart_s3 = [
    r for r in repos.data
    if r.type_ == ERepositoryType.SMARTOBJECTS3
]
```

So to access bucket & immutability data:
```python
for repo in smart_s3:
    bucket = repo.additional_properties.get("bucket", {})
    immutability = bucket.get("immutability", {})

    print({
        "name": repo.name,
        "bucket": bucket.get("bucketName"),
        "days": immutability.get("daysCount"),
        "enabled": immutability.get("isEnabled"),
    })
```

Until `openapi-python-client` figures out and implements a way around this or Veeam rewrites their OpenAPI schemas to not use circular references/inheritance, this is a known limitation.

#### Call any endpoint
Operations map directly to the OpenAPI layout:
```markdown
api/
└── repositories/
    └── get_all_repositories.py
```

Call it like this:
```python
await vc.call(
    vc.api("repositories").get_all_repositories
)
```

Or explicity:
```python
await vc.call(
    vc.api("repositories.get_all_repositories")
)
```

#### Pagination example
```python
result = await vc.call(
    vc.api("repositories").get_all_repositories,
    limit=50,
    skip=0,
)
```

#### Close the client
```python
await vc.close()
```