# PyPanRestV2

**PyPanRestV2** is a Python library designed to simplify interactions with Palo Alto Networks firewalls and Panorama via the PAN-OS / Panorama **REST API**.

It provides a higher level of abstraction (Python classes for common config areas) while still exposing the lower-level request helpers you need when you want to call an endpoint that isn’t wrapped yet.

---

## Features

- **High-Level Abstraction**: Simplifies interaction with the Palo Alto Networks API.
- **Support for Firewalls and Panorama**: Manage both individual firewalls and Panorama devices.
- **REST API Integration**: Allows seamless communication with devices using REST API.
- **XML API Support**: Handles XML API calls for configurations not yet available in REST API.
- **Convenient Pythonic Objects**: Intuitive Python objects for interacting with specific sections of Palo Alto firewall configurations.
- **Error Handling**: Custom exceptions for better error management and troubleshooting.

---

## Documentation

- **Base (core primitives)**: `docs/base.md`
- **Panorama**: `docs/panorama.md`
- **Policies**: `docs/policies.md`
- **Objects**: `docs/objects.md`
- **Network**: `docs/network.md`
- **Device**: `docs/device.md`

---

## Installation

You can install `PyPanRestV2` using pip:

```bash
pip install pypanrestv2
```

Alternatively, you can clone the repository and install it as a package for development:

```bash
# Clone the repository
git clone https://github.com/mrzepa/pypanrestv2.git

# Navigate to the project directory
cd pypanrestv2

# Install the package in development mode
pip install -e .
```

This will install the package and all required dependencies automatically. The `-e` flag installs the package in "editable" mode, which is useful if you plan to modify the code or contribute to the project.

---

## Basic Usage

### Import the Required Classes

Start by importing the necessary classes from the library:

```python
from pypanrestv2 import Firewall, Panorama
```

### Connect to a Firewall or Panorama Device

Create a `Firewall` or `Panorama` object by providing the required connection details:

For a **Firewall**:

```python
firewall = Firewall(base_url="192.168.1.1", api_key="12345")
```

For **Panorama**:

```python
panorama = Panorama(base_url="192.168.2.1", username="admin", password="my_password")
```

### Authentication

Most examples below use an API key:

```python
from pypanrestv2 import Firewall

fw = Firewall(base_url="https://fw.example.local", api_key="YOUR_API_KEY")
```

If you prefer, you can also initialize with `username` / `password` (the library will handle key retrieval internally where supported).

### Common Use Cases

#### 1. Managing Security Rules

```python
from pypanrestv2.Policies import SecurityRules

# Create a new security rule
security_rule = SecurityRules(firewall, name='allow_web')
security_rule.from_ = {'member': ['trust']}
security_rule.to = {'member': ['untrust']}
security_rule.source = {'member': ['any']}
security_rule.destination = {'member': ['any']}
security_rule.application = {'member': ['web-browsing']}
security_rule.service = {'member': ['application-default']}
security_rule.action = 'allow'
security_rule.create()

# Modify an existing rule
existing_rule = SecurityRules(firewall, name='existing_rule')
existing_rule.refresh()  # Load current configuration
existing_rule.action = 'deny'
existing_rule.update()
```

#### 2. Managing Address Objects

```python
from pypanrestv2.Objects import Addresses

# Create a new address object
address = Addresses(firewall, name='web_server')
address.value = '192.168.1.100'
address.type = 'ip-netmask'
address.create()

# Get all address objects
all_addresses = Addresses.get_all(firewall)
```

#### 3. Working with Panorama Policies and Rulebase

```python
from pypanrestv2 import Panorama
from pypanrestv2.Policies import SecurityRules

# Initialize Panorama connection
panorama = Panorama(base_url='panorama.example.com', api_key='YOUR_API_KEY')

# Create a security rule in the pre-rulebase of a device group
security_rule = SecurityRules(panorama, name='allow_internal', rulebase='Pre')
security_rule.from_ = {'member': ['trust']}
security_rule.to = {'member': ['untrust']}
security_rule.source = {'member': ['any']}
security_rule.destination = {'member': ['any']}
security_rule.application = {'member': ['web-browsing']}
security_rule.service = {'member': ['application-default']}
security_rule.action = 'allow'
security_rule.create()
```

---

## Base Module Cookbook (Core Request Helpers)

The `pypanrestv2.Base` module underpins the whole SDK.

Use these helpers when:

- You need raw REST or XML API access.
- You want higher-level “convenience” workflows like log retrieval or Panorama device-group queries.

### 1. Raw REST requests

All objects ultimately use `PAN.rest_request()`.

```python
from pypanrestv2 import Firewall

fw = Firewall(base_url="https://fw.example.local", api_key="YOUR_API_KEY")

# Example: raw GET request against a REST API class endpoint
fw.endpoint = "Device"
fw.ver = "v11.2"  # optional, defaults to detected PAN-OS major.minor

resp = fw.rest_request("GET", params={"location": "shared"})
print(resp.get("@status"))
```

### 2. XML API operational commands

Some PAN-OS features are only available via XML API. Use `op()` for operational commands.

```python
from pypanrestv2 import Firewall

fw = Firewall(base_url="https://fw.example.local", api_key="YOUR_API_KEY")
clock = fw.op("show clock")
print(clock)
```

### 3. Log retrieval (`PAN.get_logs`)

`get_logs()` retrieves log entries using the XML API log job flow (start job, poll, page results) and can:

- Query relative windows like `15m`, `12h`, `2d`
- Page with `skip`/`nlogs`
- Show progress via `tqdm`
- Optionally save output as CSV or JSON (with metadata wrapper)

```python
from pypanrestv2 import Firewall

fw = Firewall(base_url="https://fw.example.local", api_key="YOUR_API_KEY")

result = fw.get_logs(
    log_type="traffic",
    since="15m",
    query="( app eq dns )",
    output_format="json",
    show_progress=True,
    max_logs=5000,
)

if result.get("status") == "success":
    print(result["meta"]["retrieved_count"])
else:
    print(result.get("error"))
```

### 4. Panorama device-group membership (serial -> DG)

```python
from pypanrestv2 import Panorama

pano = Panorama(base_url="https://panorama.example.local", api_key="YOUR_API_KEY")

dg_map = pano.get_devicegroups_devices()
print(dg_map.get("Manitoba Clinic"))
```

### 5. Find the nearest device-group that defines a rule locally

Given a device serial and rule name, this searches:

- All policy types (dynamically enumerated from `pypanrestv2.Policies`)
- Both `pre` and `post` rulebases
- From the leaf DG upward to `shared`

It returns the *nearest* DG where the rule is defined **locally** (not inherited).

```python
from pypanrestv2 import Panorama

pano = Panorama(base_url="https://panorama.example.local", api_key="YOUR_API_KEY")

result = pano.find_rule_device_group_for_serial(
    serial="021201125996",
    rule_name="Allow DNS from phones",
)

if result.get("status") == "success":
    for hit in result["data"]:
        print(hit)
else:
    print(result.get("error"))
```

---

## Repository

Visit the project's GitHub repository for source code, documentation, enhancements, and contributions:

[PyPanRestV2 Repository on GitHub](https://github.com/mrzepa/pypanrestv2.git)

---

## Requirements

- **Python 3.11+**
- **Palo Alto Networks Devices** running PAN-OS 9.0+ or Panorama
- Python dependencies:
  - dnspython
  - icecream
  - pycountry
  - python-dotenv
  - requests
  - tqdm
  - validators

---

## API Documentation

The SDK provides access to the following main components:

### Core Modules

- `Firewall/Panorama`: Base connection and authentication
- `Policies`: Security rules, NAT rules, and policy management
- `Objects`: Address objects, service objects, and security profiles
- `Network`: Interfaces, zones, and routing configuration
- `Device`: System settings and device management

### Error Handling

The SDK uses custom exceptions for better error handling:

```python
from pypanrestv2.Exceptions import PANConnectionError, PANConfigError

try:
    firewall = Firewall(base_url='192.168.1.1', api_key='invalid_key')
    firewall.test_connection()
except PANConnectionError as e:
    print(f'Connection failed: {e}')
except PANConfigError as e:
    print(f'Configuration error: {e}')
```

Common errors and solutions:

- `PANConnectionError`: Check network connectivity and API credentials
- `PANConfigError`: Verify object names and configuration values
- `PANNotFoundError`: Ensure referenced objects exist

## Status and Updates

This SDK is actively maintained and regularly updated to support new PAN-OS versions. While not all API endpoints are implemented, core functionality is stable and production-ready. Check the GitHub repository for the latest updates and supported features.

## Contributing

Contributions are welcome! If you want to report issues, request features, or contribute to the library:

1. Fork the repository.
2. Create a feature branch: `git checkout -b my-feature`.
3. Commit your changes: `git commit -m "Add detailed description of changes"`.
4. Push to the branch: `git push origin my-feature`.
5. Submit a pull request.

Be sure to check the documentation, if provided, before starting contributions.

---

## License

This project is licensed under the MIT License. See the [LICENSE](https://opensource.org/license/mit) file for details.

---

## Author

Mark Rzepa
[mark@rzepa.com](mailto:mark@rzepa.com)

---
