Skip to content

Commit

Permalink
1.2.4 (2023-05-02)
Browse files Browse the repository at this point in the history
------------------
* [fix] Fortigate._valid_url()
  • Loading branch information
vladimirs-git committed May 2, 2023
1 parent b19f7bd commit ae5031c
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 42 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
CHANGELOG
=========

1.2.4 (2023-05-02)
------------------
* [fix] Fortigate._valid_url()


1.2.3 (2023-04-27)
------------------
* [feat] poetry
Expand Down
7 changes: 5 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ or install the package from github.com release

.. code:: bash
pip install https://github.com/vladimirs-git/fortigate-api/archive/refs/tags/1.2.3.tar.gz
pip install https://github.com/vladimirs-git/fortigate-api/archive/refs/tags/1.2.4.tar.gz
or install the package from github.com repository

Expand Down Expand Up @@ -866,6 +866,8 @@ Python examples `./examples/fortigate.py`_

Python examples `./examples/fortigate_token.py`_

Python examples `./examples/monitor.py`_

.. code:: python

from fortigate_api import Fortigate
Expand Down Expand Up @@ -1084,14 +1086,15 @@ Return
.. _`./examples/yml/zone.yml`: ./examples/yml/zone.yml

.. _`./examples/address.py`: ./examples/address.py
.. _`./examples/address_token.py`: ./examples/address_token.py
.. _`./examples/address_group.py`: ./examples/address_group.py
.. _`./examples/address_token.py`: ./examples/address_token.py
.. _`./examples/dhcp_server.py`: ./examples/dhcp_server.py
.. _`./examples/external_resource.py`: ./examples/external_resource.py
.. _`./examples/fortigate.py`: ./examples/fortigate.py
.. _`./examples/fortigate_token.py`: ./examples/fortigate_token.py
.. _`./examples/interface.py`: ./examples/interface.py
.. _`./examples/ip_pool.py`: ./examples/ip_pool.py
.. _`./examples/monitor.py`: ./examples/monitor.py
.. _`./examples/policy.py`: ./examples/policy.py
.. _`./examples/policy_extended_filter.py`: ./examples/policy_extended_filter.py
.. _`./examples/snmp_community.py`: ./examples/snmp_community.py
Expand Down
10 changes: 6 additions & 4 deletions examples/address.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@
fgt.login()

# Create Address
data = {"name": "ADDRESS",
"obj-type": "ip",
"subnet": "127.0.0.100 255.255.255.252",
"type": "ipmask"}
data = {
"name": "ADDRESS",
"obj-type": "ip",
"subnet": "127.0.0.100 255.255.255.252",
"type": "ipmask",
}
response = fgt.address.create(data=data)
print("address.create", response) # address.create <Response [200]>

Expand Down
38 changes: 24 additions & 14 deletions examples/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
- Filter interface by multiple conditions
- Update interface data in the Fortigate
- Check for presence of interface in the Fortigate
- Get all interfaces in vdom "VDOM"
- Change virtual domain to VDOM and get all interfaces of this virtual domain
- Change virtual domain to root and get all interfaces of this virtual domain
- Get all interfaces in all virtual domains (root and VDOM)
"""

import logging
Expand All @@ -25,53 +27,61 @@
fgt = FortigateAPI(host=HOST, username=USERNAME, password=PASSWORD)
fgt.login()

print("\nGets all interfaces in vdom \"root\" from the Fortigate")
print("\nGet all interfaces in vdom \"root\" from the Fortigate")
interfaces = fgt.interface.get()
print(f"interfaces count={len(interfaces)}") # interfaces count=21

print("\nGets filtered interface by name (unique identifier)")
print("\nGet filtered interface by name (unique identifier)")
interfaces = fgt.interface.get(uid="dmz")
pprint(interfaces)
# [{"name": "dmz",
# "ip": "0.0.0.0 0.0.0.0",
# ...
# }]

print("\nFilters interface by operator equals \"==\"")
print("\nFilter interface by operator equals \"==\"")
interfaces = fgt.interface.get(filter="name==dmz")
print(f"interfaces count={len(interfaces)}") # interfaces count=1

print("\nFilters interface by operator contains \"=@\"")
print("\nFilter interface by operator contains \"=@\"")
interfaces = fgt.interface.get(filter="name=@wan")
print(f"interfaces count={len(interfaces)}") # interfaces count=2

print("\nFilters interface by operator not equals \"!=\"")
print("\nFilter interface by operator not equals \"!=\"")
interfaces = fgt.interface.get(filter="name!=dmz")
print(f"interfaces count={len(interfaces)}") # interfaces count=20

print("\nFilters interface by multiple conditions")
print("\nFilter interface by multiple conditions")
interfaces = fgt.interface.get(filter=["allowaccess=@ping", "detectprotocol==ping"])
print(f"interfaces count={len(interfaces)}") # interfaces count=8

print("\nUpdates interface data in the Fortigate")
print("\nUpdate interface data in the Fortigate")
data = dict(name="dmz", description="dmz")
response = fgt.interface.update(uid="dmz", data=data)
print("interface.update", response) # interface.update <Response [200]>

print("\nChecks for presence of interface in the Fortigate")
print("\nCheck for presence of interface in the Fortigate")
response = fgt.interface.is_exist(uid="dmz")
print("interface.is_exist", response) # interface.is_exist True

print("\nChanges virtual domain to \"VDOM\" and gets all interfaces inside this vdom")
# Interfaces in virtual domains

print("\nChange virtual domain to VDOM and get all interfaces of this virtual domain")
fgt.rest.vdom = "VDOM"
print(f"{fgt!r}") # Fortigate(host='host', username='username', password='********', vdom='VDOM')
print(f"{fgt!r}") # Fortigate(host='host', username='username', vdom='VDOM')
print(fgt.vdom) # VDOM
interfaces = fgt.interface.get()
print(f"interfaces count={len(interfaces)}") # interfaces count=0
print(f"interfaces count={len(interfaces)}") # interfaces count=12

print("\nChanges virtual domain to \"root\"")
print("\nChange virtual domain to root and get all interfaces of this virtual domain")
fgt.vdom = "root"
print(f"{fgt!r}") # Fortigate(host='host', username='username', password='********')
print(f"{fgt!r}") # Fortigate(host='host', username='username')
print(fgt.vdom) # root
interfaces = fgt.interface.get()
print(f"interfaces count={len(interfaces)}") # interfaces count=31

print("\nGet all interfaces in all virtual domains (root and VDOM)")
interfaces = fgt.interface.get(all=True)
print(f"interfaces count={len(interfaces)}") # interfaces count=43

fgt.logout()
73 changes: 73 additions & 0 deletions examples/monitor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""Monitor examples.
- Get directory of monitor options (schema)
- Get all ipv4 routes
- Get static ipv4 routes
- Get route to interested ip address
"""
import logging
from pprint import pprint

from fortigate_api import Fortigate

logging.getLogger().setLevel(logging.DEBUG)

HOST = "host"
USERNAME = "username"
PASSWORD = "password"

fgt = Fortigate(host=HOST, username=USERNAME, password=PASSWORD)

# Get directory of monitor options (schema)
directory = fgt.directory("api/v2/monitor")
pprint(directory)
# [{"access_group": "sysgrp.cfg",
# "action": "select",
# "name": "health",
# "path": "firewall",
# "request": {"http_method": "GET"},
# "response": {"type": "array"},
# "summary": "List configured load balance server health monitors.",
# "supported": True},
# ...

# Get all ipv4 routes
routes = fgt.get(url="api/v2/monitor/router/ipv4")
pprint(routes)
# [{"distance": 20,
# "gateway": "10.0.0.1",
# "install_date": 1681965487,
# "interface": "tunnel1",
# "ip_mask": "10.0.0.0/8",
# "ip_version": 4,
# "is_tunnel_route": True,
# "metric": 100,
# "priority": 1,
# "tunnel_parent": "tunnel1",
# "type": "bgp",
# "vrf": 0},
# ...
# ]

# Get static ipv4 routes
routes = fgt.get(url="api/v2/monitor/router/ipv4?type=static")
pprint(routes)
# [{"distance": 10,
# "gateway": "10.0.1.1",
# "interface": "wan1",
# "ip_mask": "0.0.0.0/0",
# "ip_version": 4,
# "metric": 0,
# "priority": 1,
# "type": "static",
# "vrf": 0},
# ...
# ]

# Get route to interested ip address
routes = fgt.get(url="api/v2/monitor/router/lookup?destination=10.1.1.1")
pprint(routes)
# {"gateway": "10.0.0.1",
# "interface": "tunnel1",
# "network": "10.0.0.0/10",
# "success": True}
13 changes: 8 additions & 5 deletions fortigate_api/fortigate.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import logging
import re
from typing import Callable, Iterable, Optional
from urllib.parse import urlencode
from urllib.parse import urlencode, urljoin

import requests
from requests import Session, Response
Expand Down Expand Up @@ -423,8 +423,11 @@ def _response(self, method: Method, url: str, data: ODAny = None) -> Response:
return response

def _valid_url(self, url: str) -> str:
"""Add "https://" to `url` if absent."""
"""Return a valid URL string.
Add "https://" to `url` if it is absent and remove any trailing "/" character.
"""
if re.match("http(s)?://", url):
return url
url = url.strip("/")
return f"{self.url}/{url}/"
return url.rstrip("/")
path = url.strip("/")
return urljoin(self.url, path)
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "fortigate_api"
version = "1.2.3"
version = "1.2.4"
description = "Python package to configure Fortigate (Fortios) devices using REST API and SSH"
authors = ["Vladimir Prusakov <[email protected]>"]
license = "MIT"
Expand Down Expand Up @@ -44,4 +44,4 @@ test = ["pytest"]

[tool.poetry.urls]
"Bug Tracker" = "https://github.com/vladimirs-git/fortigate-api/issues"
"Download URL" = "https://github.com/vladimirs-git/fortigate-api/archive/refs/tags/1.2.3.tar.gz"
"Download URL" = "https://github.com/vladimirs-git/fortigate-api/archive/refs/tags/1.2.4.tar.gz"
10 changes: 5 additions & 5 deletions tests/helper__tst.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,13 @@ class MockResponse(Response):
f"api/v2/cmdb/antivirus/profile/{NAME1}": [D_NAME1],
f"api/v2/cmdb/application/list/{NAME1}": [D_NAME1],

"api/v2/cmdb/firewall/address/": [D_ADDR1, D_ADDR2, D_ADDR3, D_ADDR4, D_ADDR5],
"api/v2/cmdb/firewall/address": [D_ADDR1, D_ADDR2, D_ADDR3, D_ADDR4, D_ADDR5],
f"api/v2/cmdb/firewall/address/?filter=name{EQ}{ADDR1}": [D_ADDR1],
f"api/v2/cmdb/firewall/address/{ADDR1}": [D_ADDR1],
f"api/v2/cmdb/firewall/address/{ADDR1}?filter=name{EQ}{ADDR1}": [D_ADDR1],
f"api/v2/cmdb/firewall/address/{SLASH_}": [D_SLASH],

"api/v2/cmdb/firewall/addrgrp/": [D_ADDGR1],
"api/v2/cmdb/firewall/addrgrp": [D_ADDGR1],
f"api/v2/cmdb/firewall/addrgrp/?filter=name{EQ}{ADDGR1}": [D_ADDGR1],
f"api/v2/cmdb/firewall/addrgrp/{ADDGR1}": [D_ADDGR1],
f"api/v2/cmdb/firewall/addrgrp/{ADDGR1}?filter=name{EQ}{ADDGR1}": [D_ADDGR1],
Expand All @@ -126,7 +126,7 @@ class MockResponse(Response):
f"api/v2/cmdb/firewall/ippool/{NAME1}": [D_NAME1],
f"api/v2/cmdb/firewall/vip/{NAME1}": [D_NAME1],

"api/v2/cmdb/firewall/policy/": [D_POL1, D_POL3],
"api/v2/cmdb/firewall/policy": [D_POL1, D_POL3],
"api/v2/cmdb/firewall/policy/1": [D_POL1],
"api/v2/cmdb/firewall/policy/3": [D_POL3],
f"api/v2/cmdb/firewall/policy/1?filter=name{EQ}{POL1}": [D_POL1],
Expand All @@ -141,7 +141,7 @@ class MockResponse(Response):
f"api/v2/cmdb/firewall.service/custom/{NAME1}": [D_NAME1],
f"api/v2/cmdb/firewall.service/group/{NAME1}": [D_NAME1],

"api/v2/cmdb/system.snmp/community/": [D_SNMP1, D_SNMP3],
"api/v2/cmdb/system.snmp/community": [D_SNMP1, D_SNMP3],
"api/v2/cmdb/system.snmp/community/1": [D_SNMP1],
"api/v2/cmdb/system.snmp/community/3": [D_SNMP3],
f"api/v2/cmdb/system.snmp/community/1?filter=name{EQ}{NAME1}": [D_SNMP1],
Expand All @@ -154,7 +154,7 @@ class MockResponse(Response):
f"api/v2/cmdb/system/external-resource/{NAME1}": [D_NAME1],
f"api/v2/cmdb/system/external-resource/?filter=name{EQ}{NAME1}": [D_NAME1],

"api/v2/cmdb/system/interface/": [D_INTF1, D_INTF3],
"api/v2/cmdb/system/interface": [D_INTF1, D_INTF3],
f"api/v2/cmdb/system/interface/?filter=name{EQ}{NAME1}": [D_INTF1],
f"api/v2/cmdb/system/interface/?filter=name{EQ}{NAME3}": [D_INTF3],
f"api/v2/cmdb/system/interface/{NAME1}": [D_INTF1],
Expand Down
22 changes: 12 additions & 10 deletions tests/test__fortigate.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,17 +231,19 @@ def test_valid__hide_secret(self):
def test_valid__valid_url(self):
"""Fortigate._valid_url()"""
default = dict(host="host", username="username", password="", port=443)
query = "api/v2/cmdb/firewall/address/"
url_ = f"https://host/{query}"
query = "api/v2/cmdb/firewall/address"
full_url = f"https://host/{query}"
https_80 = "https://host:80/"
http_80 = "http://host:80/"
for kwargs, url, req in [
({}, query, url_),
({}, f"/{query}", url_),
({}, "api/v2/cmdb/firewall/address", url_),
({}, url_, url_),
(dict(port=80), query, f"https://host:80/{query}"),
(dict(port=80), f"https://host:80/{query}", f"https://host:80/{query}"),
(dict(scheme="http", port=80), query, f"http://host:80/{query}"),
(dict(scheme="http", port=80), f"http://host:80/{query}", f"http://host:80/{query}"),
({}, query, full_url),
({}, f"/{query}/", full_url),
({}, full_url, full_url),
({}, f"{full_url}/", full_url),
(dict(port=80), query, f"{https_80}{query}"),
(dict(port=80), f"{https_80}{query}", f"{https_80}{query}"),
(dict(scheme="http", port=80), query, f"{http_80}{query}"),
(dict(scheme="http", port=80), f"{http_80}{query}", f"{http_80}{query}"),
]:
kwargs_ = {**default, **kwargs}
fgt = Fortigate(**kwargs_)
Expand Down

0 comments on commit ae5031c

Please sign in to comment.