(20250211) MikroTik VLAN set up and rollback also ready.
This commit is contained in:
@@ -361,7 +361,7 @@ class MikroTikController(CoreSoftwareController, ABC):
|
|||||||
password: str,
|
password: str,
|
||||||
port_no: int | str = None,
|
port_no: int | str = None,
|
||||||
use_https: bool = True
|
use_https: bool = True
|
||||||
):
|
) -> ApiResponse:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
To enlist all the physical connectivity interfaces available on the MikroTik device.
|
To enlist all the physical connectivity interfaces available on the MikroTik device.
|
||||||
@@ -395,42 +395,29 @@ class MikroTikController(CoreSoftwareController, ABC):
|
|||||||
mikrotik_ip: str,
|
mikrotik_ip: str,
|
||||||
username: str,
|
username: str,
|
||||||
password: str,
|
password: str,
|
||||||
interface_id: str,
|
dot_id: str,
|
||||||
|
json_payload: dict,
|
||||||
port_no: int | str = None,
|
port_no: int | str = None,
|
||||||
use_https: bool = True,
|
use_https: bool = True
|
||||||
name: str = None,
|
) -> ApiResponse:
|
||||||
disabled: bool = False,
|
|
||||||
comment: str = None
|
|
||||||
):
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
To update an interface's configuration.
|
To update an interface's configuration.
|
||||||
:param mikrotik_ip: The IP address of the MikroTik device.
|
:param mikrotik_ip: The IP address of the MikroTik device.
|
||||||
:param username: The username to get access to the MikroTik device.
|
:param username: The username to get access to the MikroTik device.
|
||||||
:param password: The password to get access to the MikroTik device.
|
:param password: The password to get access to the MikroTik device.
|
||||||
:param interface_id: The id of the interface that you'd like to modify.
|
:param dot_id: The value of the '.id' field in the list.
|
||||||
:param port_no: The port no. to hit the MikroTik device on.
|
:param port_no: The port no. to hit the MikroTik device on.
|
||||||
:param use_https: Whether to use HTTPS, or HTTP.
|
:param use_https: Whether to use HTTPS, or HTTP.
|
||||||
:param name: The new name for the interface.
|
:param json_payload: The JSON to send in the body pf the request.
|
||||||
:param disabled: Whether, or not, you'd like to disable the interface.
|
|
||||||
:param comment: A note that you'd like to attach to the entry. It'll then be available in the listing API.
|
|
||||||
:return: A structured API response.
|
:return: A structured API response.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Prepare the JSON to give to the API:
|
|
||||||
input_json = {
|
|
||||||
k: v for k, v in {
|
|
||||||
"name": name,
|
|
||||||
"disabled": disabled,
|
|
||||||
"comment": comment
|
|
||||||
}.items() if v is not None
|
|
||||||
}
|
|
||||||
|
|
||||||
# Make the API call:
|
# Make the API call:
|
||||||
api_response = await self._rest.patch(
|
api_response = await self._rest.patch(
|
||||||
url = self.get_mikrotik_url(
|
url = self.get_mikrotik_url(
|
||||||
mikrotik_ip = mikrotik_ip,
|
mikrotik_ip = mikrotik_ip,
|
||||||
path = f"/interface/{interface_id}",
|
path = f"/interface/{dot_id}",
|
||||||
port_no = port_no,
|
port_no = port_no,
|
||||||
use_https = use_https
|
use_https = use_https
|
||||||
),
|
),
|
||||||
@@ -438,7 +425,165 @@ class MikroTikController(CoreSoftwareController, ABC):
|
|||||||
username = username,
|
username = username,
|
||||||
password = password
|
password = password
|
||||||
),
|
),
|
||||||
json = input_json
|
json = json_payload
|
||||||
|
)
|
||||||
|
|
||||||
|
# Done here:
|
||||||
|
return api_response
|
||||||
|
|
||||||
|
# ┓┏┓ ┏┓┳┓
|
||||||
|
# ┃┃┃ ┣┫┃┃┏
|
||||||
|
# ┗┛┗┛┛┗┛┗┛
|
||||||
|
|
||||||
|
async def list_vlans(
|
||||||
|
self,
|
||||||
|
mikrotik_ip: str,
|
||||||
|
username: str,
|
||||||
|
password: str,
|
||||||
|
port_no: int | str = None,
|
||||||
|
use_https: bool = True
|
||||||
|
) -> ApiResponse:
|
||||||
|
|
||||||
|
"""
|
||||||
|
To enlist all the physical connectivity interfaces available on the MikroTik device.
|
||||||
|
:param mikrotik_ip: The IP address of the MikroTik device.
|
||||||
|
:param username: The username to get access to the MikroTik device.
|
||||||
|
:param password: The password to get access to the MikroTik device.
|
||||||
|
:param port_no: The port no. to hit the MikroTik device on.
|
||||||
|
:param use_https: Whether to use HTTPS, or HTTP.
|
||||||
|
:return: A structured API response.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Make the API call:
|
||||||
|
api_response = await self._rest.get(
|
||||||
|
url = self.get_mikrotik_url(
|
||||||
|
mikrotik_ip = mikrotik_ip,
|
||||||
|
path = r"/interface/vlan",
|
||||||
|
port_no = port_no,
|
||||||
|
use_https = use_https
|
||||||
|
),
|
||||||
|
auth = httpx.BasicAuth(
|
||||||
|
username = username,
|
||||||
|
password = password
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Done here:
|
||||||
|
return api_response
|
||||||
|
|
||||||
|
async def add_vlan(
|
||||||
|
self,
|
||||||
|
mikrotik_ip: str,
|
||||||
|
username: str,
|
||||||
|
password: str,
|
||||||
|
json_payload: dict,
|
||||||
|
port_no: int | str = None,
|
||||||
|
use_https: bool = True
|
||||||
|
) -> ApiResponse:
|
||||||
|
|
||||||
|
"""
|
||||||
|
To add a new VLAN to the MikroTik's configuration.
|
||||||
|
:param mikrotik_ip: The IP address of the MikroTik device.
|
||||||
|
:param username: The username to get access to the MikroTik device.
|
||||||
|
:param password: The password to get access to the MikroTik device.
|
||||||
|
:param json_payload: The JSON to send in the body pf the request.
|
||||||
|
:param port_no: The port no. to hit the MikroTik device on.
|
||||||
|
:param use_https: Whether to use HTTPS, or HTTP.
|
||||||
|
:return: A structured API response.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Make the API call:
|
||||||
|
api_response = await self._rest.put(
|
||||||
|
url = self.get_mikrotik_url(
|
||||||
|
mikrotik_ip = mikrotik_ip,
|
||||||
|
path = r"/interface/vlan",
|
||||||
|
port_no = port_no,
|
||||||
|
use_https = use_https
|
||||||
|
),
|
||||||
|
auth = httpx.BasicAuth(
|
||||||
|
username = username,
|
||||||
|
password = password
|
||||||
|
),
|
||||||
|
json = json_payload
|
||||||
|
)
|
||||||
|
|
||||||
|
# Done here:
|
||||||
|
return api_response
|
||||||
|
|
||||||
|
async def update_vlan(
|
||||||
|
self,
|
||||||
|
mikrotik_ip: str,
|
||||||
|
username: str,
|
||||||
|
password: str,
|
||||||
|
dot_id: str,
|
||||||
|
json_payload: dict,
|
||||||
|
port_no: int | str = None,
|
||||||
|
use_https: bool = True,
|
||||||
|
) -> ApiResponse:
|
||||||
|
|
||||||
|
"""
|
||||||
|
To update an existing VLAN in the MikroTik's configuration.
|
||||||
|
:param mikrotik_ip: The IP address of the MikroTik device.
|
||||||
|
:param username: The username to get access to the MikroTik device.
|
||||||
|
:param password: The password to get access to the MikroTik device.
|
||||||
|
:param dot_id: The value of the '.id' field in the list.
|
||||||
|
:param json_payload: The JSON to send in the body pf the request.
|
||||||
|
:param port_no: The port no. to hit the MikroTik device on.
|
||||||
|
:param use_https: Whether to use HTTPS, or HTTP.
|
||||||
|
:return: A structured API response.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Make the API call:
|
||||||
|
api_response = await self._rest.patch(
|
||||||
|
url = self.get_mikrotik_url(
|
||||||
|
mikrotik_ip = mikrotik_ip,
|
||||||
|
path = f"/interface/vlan/{dot_id}",
|
||||||
|
port_no = port_no,
|
||||||
|
use_https = use_https
|
||||||
|
),
|
||||||
|
auth = httpx.BasicAuth(
|
||||||
|
username = username,
|
||||||
|
password = password
|
||||||
|
),
|
||||||
|
json = json_payload
|
||||||
|
)
|
||||||
|
|
||||||
|
# Done here:
|
||||||
|
return api_response
|
||||||
|
|
||||||
|
async def remove_vlan(
|
||||||
|
self,
|
||||||
|
mikrotik_ip: str,
|
||||||
|
username: str,
|
||||||
|
password: str,
|
||||||
|
dot_id: str,
|
||||||
|
port_no: int | str = None,
|
||||||
|
use_https: bool = True,
|
||||||
|
):
|
||||||
|
|
||||||
|
"""
|
||||||
|
To delete an existing VLAN from the MikroTik's configuration.
|
||||||
|
:param mikrotik_ip: The IP address of the MikroTik device.
|
||||||
|
:param username: The username to get access to the MikroTik device.
|
||||||
|
:param password: The password to get access to the MikroTik device.
|
||||||
|
:param dot_id: The value of the '.id' field in the list.
|
||||||
|
:param port_no: The port no. to hit the MikroTik device on.
|
||||||
|
:param use_https: Whether to use HTTPS, or HTTP.
|
||||||
|
:return: A structured API response.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Make the API call:
|
||||||
|
api_response = await self._rest.delete(
|
||||||
|
url = self.get_mikrotik_url(
|
||||||
|
mikrotik_ip = mikrotik_ip,
|
||||||
|
path = f"/interface/vlan/{dot_id}",
|
||||||
|
port_no = port_no,
|
||||||
|
use_https = use_https
|
||||||
|
),
|
||||||
|
auth = httpx.BasicAuth(
|
||||||
|
username = username,
|
||||||
|
password = password
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Done here:
|
# Done here:
|
||||||
|
|||||||
@@ -162,18 +162,45 @@ class MikroTikPPPoE1000Controller(MikroTikController):
|
|||||||
# Init a variable in a parent:
|
# Init a variable in a parent:
|
||||||
self._client = self.CLIENT_NAME
|
self._client = self.CLIENT_NAME
|
||||||
|
|
||||||
|
# ┓┏ ┓
|
||||||
|
# ┣┫┏┓┃┏┓┏┓┏┓┏
|
||||||
|
# ┛┗┗ ┗┣┛┗ ┛ ┛
|
||||||
|
# ┛
|
||||||
|
|
||||||
|
def created_by_easyfi(
|
||||||
|
self,
|
||||||
|
resource_json: dict = None
|
||||||
|
) -> bool:
|
||||||
|
|
||||||
|
"""
|
||||||
|
Checks if the resource was created/modified by this class.
|
||||||
|
:param resource_json; The JSON of the resource that needs to be checked.
|
||||||
|
:return: True if created by this class, else False.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Extract values:
|
||||||
|
resource_name = resource_json.get("name") or ""
|
||||||
|
comment_json = self.parse_comment_json(resource_json.get("comment")) or {}
|
||||||
|
|
||||||
|
# Perform checks:
|
||||||
|
if (
|
||||||
|
comment_json.get("createdBy", "???") == self.CREATED_BY_NAME or
|
||||||
|
resource_name.lower().find("easyfi") >= 0
|
||||||
|
): return True
|
||||||
|
else: return False
|
||||||
|
|
||||||
# ┳ ┏
|
# ┳ ┏
|
||||||
# ┃┏┓╋┏┓┏┓╋┏┓┏┏┓┏
|
# ┃┏┓╋┏┓┏┓╋┏┓┏┏┓┏
|
||||||
# ┻┛┗┗┗ ┛ ┛┗┻┗┗ ┛
|
# ┻┛┗┗┗ ┛ ┛┗┻┗┗ ┛
|
||||||
|
|
||||||
async def interface_setup(
|
async def set_up_interface(
|
||||||
self,
|
self,
|
||||||
mikrotik_auth: MikroTikPPPoE1000Auth,
|
mikrotik_auth: MikroTikPPPoE1000Auth,
|
||||||
use_https: bool = True
|
use_https: bool = True
|
||||||
) -> MikroTikConfigAttemptResponse:
|
) -> MikroTikConfigAttemptResponse:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
To find the first available interface.
|
To find the first available interface and set it up for use.
|
||||||
:param mikrotik_auth: The set of credentials as received from the UI/API.
|
:param mikrotik_auth: The set of credentials as received from the UI/API.
|
||||||
:param use_https: Whether to use HTTPS, or HTTP.
|
:param use_https: Whether to use HTTPS, or HTTP.
|
||||||
:return: A structured response to indicate what happened during the process.
|
:return: A structured response to indicate what happened during the process.
|
||||||
@@ -217,16 +244,18 @@ class MikroTikPPPoE1000Controller(MikroTikController):
|
|||||||
mikrotik_ip = mikrotik_auth.nasIp,
|
mikrotik_ip = mikrotik_auth.nasIp,
|
||||||
username = mikrotik_auth.username,
|
username = mikrotik_auth.username,
|
||||||
password = mikrotik_auth.password,
|
password = mikrotik_auth.password,
|
||||||
interface_id = unused_if_json.get(".id"),
|
dot_id = unused_if_json.get(".id"),
|
||||||
port_no = mikrotik_auth.nasPort,
|
port_no = mikrotik_auth.nasPort,
|
||||||
use_https = use_https,
|
use_https = use_https,
|
||||||
name = self.HW_INTERFACE_NAME,
|
json_payload = {
|
||||||
disabled = False,
|
"name": self.HW_INTERFACE_NAME,
|
||||||
comment = self.create_comment_json(
|
"disabled": "false",
|
||||||
created_by = self.CREATED_BY_NAME,
|
"comment": self.create_comment_json(
|
||||||
created_ts = date_time.get_current_utc_date_time(as_string = False),
|
created_by = self.CREATED_BY_NAME,
|
||||||
roll_back_config = unused_if_json
|
created_ts = date_time.get_current_utc_date_time(as_string = False),
|
||||||
)
|
roll_back_config = unused_if_json
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check if the attempt was successful:
|
# Check if the attempt was successful:
|
||||||
@@ -242,7 +271,7 @@ class MikroTikPPPoE1000Controller(MikroTikController):
|
|||||||
# Done here:
|
# Done here:
|
||||||
return step_response
|
return step_response
|
||||||
|
|
||||||
async def interface_roll_back(
|
async def roll_back_interface(
|
||||||
self,
|
self,
|
||||||
mikrotik_auth: MikroTikPPPoE1000Auth,
|
mikrotik_auth: MikroTikPPPoE1000Auth,
|
||||||
use_https: bool = True
|
use_https: bool = True
|
||||||
@@ -296,12 +325,14 @@ class MikroTikPPPoE1000Controller(MikroTikController):
|
|||||||
mikrotik_ip = mikrotik_auth.nasIp,
|
mikrotik_ip = mikrotik_auth.nasIp,
|
||||||
username = mikrotik_auth.username,
|
username = mikrotik_auth.username,
|
||||||
password = mikrotik_auth.password,
|
password = mikrotik_auth.password,
|
||||||
interface_id = if_json.get(".id"),
|
dot_id = if_json.get(".id"),
|
||||||
port_no = mikrotik_auth.nasPort,
|
port_no = mikrotik_auth.nasPort,
|
||||||
use_https = use_https,
|
use_https = use_https,
|
||||||
name = original_config.get("name", self.HW_INTERFACE_NAME),
|
json_payload={
|
||||||
disabled = False,
|
"name": original_config.get("name", self.HW_INTERFACE_NAME),
|
||||||
comment = original_config.get("comment", "")
|
"disabled": "false",
|
||||||
|
"comment": original_config.get("comment", "")
|
||||||
|
}
|
||||||
)
|
)
|
||||||
total_count += 1
|
total_count += 1
|
||||||
if api_response.success: rolled_back_count += 1
|
if api_response.success: rolled_back_count += 1
|
||||||
@@ -314,6 +345,275 @@ class MikroTikPPPoE1000Controller(MikroTikController):
|
|||||||
# Done here:
|
# Done here:
|
||||||
return step_response
|
return step_response
|
||||||
|
|
||||||
|
# ┓┏┓ ┏┓┳┓
|
||||||
|
# ┃┃┃ ┣┫┃┃┏
|
||||||
|
# ┗┛┗┛┛┗┛┗┛
|
||||||
|
|
||||||
|
async def set_up_one_vlan(
|
||||||
|
self,
|
||||||
|
mikrotik_auth: MikroTikPPPoE1000Auth,
|
||||||
|
vlan_id: int,
|
||||||
|
vlan_list: List[dict],
|
||||||
|
use_https: bool = True
|
||||||
|
):
|
||||||
|
|
||||||
|
"""
|
||||||
|
To set up just one of the needed VLANs. If the VLAN id is not taken, a new VLAN will be created, otherwise the
|
||||||
|
existing one will be updated.
|
||||||
|
:param mikrotik_auth: The set of credentials as received from the UI/API.
|
||||||
|
:param vlan_id: The id of the VLAN you want to create.
|
||||||
|
:param vlan_list: The list of existing VLANs already configured in the MikroTik device. Helps us decide between
|
||||||
|
the use of PUT and PATCH methods.
|
||||||
|
:param use_https: Whether to use HTTPS, or HTTP.
|
||||||
|
:return: A structured response to indicate what happened during the process.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Start by assuming failure:
|
||||||
|
step_response = MikroTikConfigAttemptResponse()
|
||||||
|
|
||||||
|
# Check if you can find the VLAN id in the existing VLANs list:
|
||||||
|
existing_dot_id = None
|
||||||
|
existing_vlan_id = None
|
||||||
|
existing_vlan_json = None
|
||||||
|
for vlan_json in vlan_list:
|
||||||
|
if vlan_json.get("vlan-id") == str(vlan_id):
|
||||||
|
existing_dot_id = vlan_json.get(".id")
|
||||||
|
existing_vlan_id = vlan_json.get("vlan-id")
|
||||||
|
existing_vlan_json = vlan_json
|
||||||
|
break
|
||||||
|
|
||||||
|
# If the VLAN id is unused:
|
||||||
|
if existing_dot_id is None:
|
||||||
|
|
||||||
|
# We add the VLAN:
|
||||||
|
api_response = await self.add_vlan(
|
||||||
|
mikrotik_ip = mikrotik_auth.nasIp,
|
||||||
|
username = mikrotik_auth.username,
|
||||||
|
password = mikrotik_auth.password,
|
||||||
|
json_payload = {
|
||||||
|
"name": self.VLAN_NAME.format(vlan_id),
|
||||||
|
"vlan-id": str(vlan_id),
|
||||||
|
"interface": self.HW_INTERFACE_NAME,
|
||||||
|
"disabled": "false",
|
||||||
|
"comment": self.create_comment_json(
|
||||||
|
created_by = self.CREATED_BY_NAME,
|
||||||
|
created_ts = date_time.get_current_utc_date_time(as_string = False),
|
||||||
|
roll_back_config = existing_vlan_json
|
||||||
|
)
|
||||||
|
},
|
||||||
|
port_no = mikrotik_auth.nasPort,
|
||||||
|
use_https = use_https
|
||||||
|
)
|
||||||
|
|
||||||
|
# And assess the result:
|
||||||
|
if api_response.success:
|
||||||
|
step_response.success = True
|
||||||
|
step_response.message = f"Added new VLAN with id {vlan_id}."
|
||||||
|
step_response.exception = None
|
||||||
|
else:
|
||||||
|
step_response.success = False
|
||||||
|
step_response.message = f"Failed to add new VLAN with id {vlan_id}."
|
||||||
|
step_response.exception = api_response.exception
|
||||||
|
|
||||||
|
# If the VLAN id is already used:
|
||||||
|
else:
|
||||||
|
|
||||||
|
# We update it:
|
||||||
|
api_response = await self.update_vlan(
|
||||||
|
mikrotik_ip = mikrotik_auth.nasIp,
|
||||||
|
username = mikrotik_auth.username,
|
||||||
|
password = mikrotik_auth.password,
|
||||||
|
dot_id = existing_dot_id,
|
||||||
|
json_payload = {
|
||||||
|
"name": self.VLAN_NAME.format(vlan_id),
|
||||||
|
"interface": self.HW_INTERFACE_NAME,
|
||||||
|
"disabled": "false",
|
||||||
|
"comment": self.create_comment_json(
|
||||||
|
created_by = self.CREATED_BY_NAME,
|
||||||
|
created_ts = date_time.get_current_utc_date_time(as_string = False),
|
||||||
|
roll_back_config = existing_vlan_json
|
||||||
|
)
|
||||||
|
},
|
||||||
|
port_no = mikrotik_auth.nasPort,
|
||||||
|
use_https = use_https
|
||||||
|
)
|
||||||
|
|
||||||
|
# And assess the result:
|
||||||
|
if api_response.success:
|
||||||
|
step_response.success = True
|
||||||
|
step_response.message = f"Updated existing VLAN ({existing_vlan_id}) to id {vlan_id}."
|
||||||
|
step_response.exception = None
|
||||||
|
else:
|
||||||
|
step_response.success = False
|
||||||
|
step_response.message = f"Failed to update existing VLAN ({existing_vlan_id}) to id {vlan_id}."
|
||||||
|
step_response.exception = api_response.exception
|
||||||
|
|
||||||
|
# Done here:
|
||||||
|
return step_response
|
||||||
|
|
||||||
|
async def set_up_vlans(
|
||||||
|
self,
|
||||||
|
mikrotik_auth: MikroTikPPPoE1000Auth,
|
||||||
|
use_https: bool = True
|
||||||
|
) -> ApiResponse:
|
||||||
|
|
||||||
|
"""
|
||||||
|
To set up all the needed VLANs:
|
||||||
|
:param mikrotik_auth: The set of credentials as received from the UI/API.
|
||||||
|
:param use_https: Whether to use HTTPS, or HTTP.
|
||||||
|
:return: A structured response to indicate what happened during the process.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Start by assuming failure:
|
||||||
|
step_response = MikroTikConfigAttemptResponse()
|
||||||
|
|
||||||
|
# Enlist all the existing VLANs:
|
||||||
|
api_response = await self.list_vlans(
|
||||||
|
mikrotik_ip = mikrotik_auth.nasIp,
|
||||||
|
port_no = mikrotik_auth.nasPort,
|
||||||
|
username = mikrotik_auth.username,
|
||||||
|
password = mikrotik_auth.password,
|
||||||
|
use_https = use_https
|
||||||
|
)
|
||||||
|
|
||||||
|
# If the listing failed:
|
||||||
|
if not api_response.success:
|
||||||
|
step_response.success = False
|
||||||
|
step_response.message = "Failed to enlist existing VLANs during configuration."
|
||||||
|
step_response.exception = step_response.exception
|
||||||
|
return step_response
|
||||||
|
|
||||||
|
# Extract the list here:
|
||||||
|
vlan_list = await api_response.get_json()
|
||||||
|
|
||||||
|
# Create a task for each VLAN that you need to set up,
|
||||||
|
# then fire them all asynchronously:
|
||||||
|
tasks = [
|
||||||
|
self.set_up_one_vlan(
|
||||||
|
mikrotik_auth = mikrotik_auth,
|
||||||
|
vlan_id = vlan_id,
|
||||||
|
vlan_list = vlan_list,
|
||||||
|
use_https = use_https
|
||||||
|
) for vlan_id in mikrotik_auth.vlanRange
|
||||||
|
]
|
||||||
|
results = await asyncio.gather(*tasks)
|
||||||
|
|
||||||
|
# Assess the results:
|
||||||
|
total_count = len(results)
|
||||||
|
success_count = 0
|
||||||
|
all_messages = []
|
||||||
|
for result in results:
|
||||||
|
if result.success: success_count += 1
|
||||||
|
all_messages.append(result.message)
|
||||||
|
step_response.message = " -> ".join(all_messages)
|
||||||
|
step_response.success = True if success_count == total_count else False
|
||||||
|
|
||||||
|
# Done here:
|
||||||
|
return step_response
|
||||||
|
|
||||||
|
async def roll_back_vlans(
|
||||||
|
self,
|
||||||
|
mikrotik_auth: MikroTikPPPoE1000Auth,
|
||||||
|
use_https: bool = True
|
||||||
|
) -> MikroTikRollBackAttemptResponse:
|
||||||
|
|
||||||
|
"""
|
||||||
|
To reset VLANs to their original state (if they already existed) or remove them if this class added them.
|
||||||
|
:param mikrotik_auth: The set of credentials as received from the UI/API.
|
||||||
|
:param use_https: Whether to use HTTPS, or HTTP.
|
||||||
|
:return: A structured response to indicate what happened during the process.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Start by assuming failure:
|
||||||
|
total_count = 0
|
||||||
|
success_count = 0
|
||||||
|
all_messages = []
|
||||||
|
step_response = MikroTikConfigAttemptResponse()
|
||||||
|
|
||||||
|
# Enlist all the existing VLANs:
|
||||||
|
api_response = await self.list_vlans(
|
||||||
|
mikrotik_ip = mikrotik_auth.nasIp,
|
||||||
|
port_no = mikrotik_auth.nasPort,
|
||||||
|
username = mikrotik_auth.username,
|
||||||
|
password = mikrotik_auth.password,
|
||||||
|
use_https = use_https
|
||||||
|
)
|
||||||
|
|
||||||
|
# If the listing failed:
|
||||||
|
if not api_response.success:
|
||||||
|
step_response.success = False
|
||||||
|
step_response.message = "Failed to enlist existing VLANs during rollback."
|
||||||
|
step_response.exception = step_response.exception
|
||||||
|
return step_response
|
||||||
|
|
||||||
|
# Loop through all the VLANs,
|
||||||
|
# and either reset them to their original state (if they already existed),
|
||||||
|
# or remove them entirely if they didn't exist before we came:
|
||||||
|
for vlan_json in await api_response.get_json():
|
||||||
|
|
||||||
|
# Check if the VLAN was created by this class:
|
||||||
|
if not self.created_by_easyfi(vlan_json): continue
|
||||||
|
|
||||||
|
# Extract the rollback information:
|
||||||
|
comment = self.parse_comment_json(vlan_json.get("comment")) or {}
|
||||||
|
roll_back_json = comment.get("rollbackConfig")
|
||||||
|
vlan_id = vlan_json.get("vlan-id")
|
||||||
|
dot_id = vlan_json.get(".id")
|
||||||
|
|
||||||
|
# If the resource has no rollback information:
|
||||||
|
if roll_back_json is None:
|
||||||
|
|
||||||
|
# We delete the resource:
|
||||||
|
await self.remove_vlan(
|
||||||
|
mikrotik_ip = mikrotik_auth.nasIp,
|
||||||
|
username = mikrotik_auth.username,
|
||||||
|
password = mikrotik_auth.password,
|
||||||
|
dot_id = dot_id,
|
||||||
|
port_no = mikrotik_auth.nasPort,
|
||||||
|
use_https = use_https
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assess the result:
|
||||||
|
total_count += 1
|
||||||
|
if api_response.success:
|
||||||
|
all_messages.append(f"Removed VLAN with id {vlan_id}")
|
||||||
|
success_count += 1
|
||||||
|
else: all_messages.append(f"Failed to remove VLAN with id {vlan_id}")
|
||||||
|
|
||||||
|
# If the resource has rollback information:
|
||||||
|
else:
|
||||||
|
|
||||||
|
# We roll back to the previous configuration:
|
||||||
|
await self.update_vlan(
|
||||||
|
mikrotik_ip = mikrotik_auth.nasIp,
|
||||||
|
username = mikrotik_auth.username,
|
||||||
|
password = mikrotik_auth.password,
|
||||||
|
dot_id = dot_id,
|
||||||
|
json_payload = {
|
||||||
|
"name": roll_back_json.get("name"),
|
||||||
|
"interface": roll_back_json.get("interface"),
|
||||||
|
"disabled": roll_back_json.get("disabled"),
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
|
port_no = mikrotik_auth.nasPort,
|
||||||
|
use_https = use_https
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assess the result:
|
||||||
|
total_count += 1
|
||||||
|
if api_response.success:
|
||||||
|
all_messages.append(f"Rolled back VLAN with id {vlan_id}")
|
||||||
|
success_count += 1
|
||||||
|
else: all_messages.append(f"Failed to roll back VLAN with id {vlan_id}")
|
||||||
|
|
||||||
|
# Assess the overall results:
|
||||||
|
all_messages.append(f"Removed/rolled-back {success_count}/{total_count} VLANs.")
|
||||||
|
if success_count == total_count: step_response.success = True
|
||||||
|
step_response.message = " -> ".join(all_messages)
|
||||||
|
|
||||||
|
# Done here:
|
||||||
|
return step_response
|
||||||
|
|
||||||
# ┏┓ ┓
|
# ┏┓ ┓
|
||||||
# ┣┫┓┏╋┣┓
|
# ┣┫┓┏╋┣┓
|
||||||
# ┛┗┗┻┗┛┗
|
# ┛┗┗┻┗┛┗
|
||||||
@@ -334,43 +634,25 @@ class MikroTikPPPoE1000Controller(MikroTikController):
|
|||||||
:return: A structured response to indicate what happened during authorization.
|
:return: A structured response to indicate what happened during authorization.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# print("IN-AUTH:", json.to_string(mikrotik_auth.model_dump(), default = str))
|
# # print("IN-AUTH:", json.to_string(mikrotik_auth.model_dump(), default = str))
|
||||||
print("VLAN SUBNETS:", json.to_string(
|
# print("VLAN SUBNETS:", json.to_string(
|
||||||
self.split_ipv4_range_among_targets(
|
# self.split_ipv4_range_among_targets(
|
||||||
start_ip = mikrotik_auth.firstPrivateIp,
|
# start_ip = mikrotik_auth.firstPrivateIp,
|
||||||
end_ip = mikrotik_auth.lastPrivateIp,
|
# end_ip = mikrotik_auth.lastPrivateIp,
|
||||||
targets = mikrotik_auth.vlanRange
|
# targets = mikrotik_auth.vlanRange
|
||||||
),
|
# ),
|
||||||
default = str
|
# default = str
|
||||||
))
|
# ))
|
||||||
|
|
||||||
# Start with a blank response:
|
# Start with a blank response:
|
||||||
auth_response = MikroTikAuthResponse()
|
auth_response = MikroTikAuthResponse()
|
||||||
|
|
||||||
# # Try connecting to the server to check if the credentials are valid, or not:
|
# Try to configure the system:
|
||||||
# api_response = await self.get_system_resource(
|
config_response = await self.configure(mikrotik_auth)
|
||||||
# mikrotik_ip = mikrotik_auth.nasIp,
|
print("CONFIG RESPONSE:", config_response)
|
||||||
# port_no = mikrotik_auth.nasPort,
|
auth_response = config_response
|
||||||
# username = mikrotik_auth.username,
|
|
||||||
# password = mikrotik_auth.password,
|
|
||||||
# use_https = False
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# # If the connection attempt failed:
|
|
||||||
# if not api_response.success:
|
|
||||||
# auth_response.exception = api_response.exception
|
|
||||||
# if api_response.httpCode is None:
|
|
||||||
# auth_response.message = "Exception: " + api_response.exception.__class__.__name__
|
|
||||||
# if exception_str := str(auth_response.exception): auth_response.message += f" ({exception_str})"
|
|
||||||
# elif api_response.httpCode in [401]: auth_response.message = "Invalid credentials passed."
|
|
||||||
# elif api_response.httpCode in [502]: auth_response.message = "Could not ."
|
|
||||||
# else: auth_response.message = "Unknown error."
|
|
||||||
|
|
||||||
# # Try to configure the system:
|
await asyncio.sleep(10.0)
|
||||||
# config_response = await self.configure(mikrotik_auth)
|
|
||||||
# print("CONFIG RESPONSE:", config_response)
|
|
||||||
#
|
|
||||||
# await asyncio.sleep(5.0)
|
|
||||||
|
|
||||||
# Try to roll all configuration back:
|
# Try to roll all configuration back:
|
||||||
roll_back_response = await self.roll_back(mikrotik_auth)
|
roll_back_response = await self.roll_back(mikrotik_auth)
|
||||||
@@ -401,9 +683,15 @@ class MikroTikPPPoE1000Controller(MikroTikController):
|
|||||||
all_messages = []
|
all_messages = []
|
||||||
roll_back_response = MikroTikConfigAttemptResponse()
|
roll_back_response = MikroTikConfigAttemptResponse()
|
||||||
|
|
||||||
# First, we arrange an interface:
|
# Next we remove all the VLANs:
|
||||||
if keep_going:
|
if keep_going:
|
||||||
step_response = await self.interface_roll_back(mikrotik_auth, use_https = False)
|
step_response = await self.roll_back_vlans(mikrotik_auth, use_https = False)
|
||||||
|
all_messages.append(step_response.message)
|
||||||
|
keep_going = step_response.success
|
||||||
|
|
||||||
|
# Next, we free-up the interface:
|
||||||
|
if keep_going:
|
||||||
|
step_response = await self.roll_back_interface(mikrotik_auth, use_https = False)
|
||||||
all_messages.append(step_response.message)
|
all_messages.append(step_response.message)
|
||||||
keep_going = step_response.success
|
keep_going = step_response.success
|
||||||
|
|
||||||
@@ -430,7 +718,14 @@ class MikroTikPPPoE1000Controller(MikroTikController):
|
|||||||
|
|
||||||
# First, we arrange an interface:
|
# First, we arrange an interface:
|
||||||
if keep_going:
|
if keep_going:
|
||||||
step_response = await self.interface_setup(mikrotik_auth, use_https = False)
|
step_response = await self.set_up_interface(mikrotik_auth, use_https = False)
|
||||||
|
all_messages.append(step_response.message)
|
||||||
|
keep_going = step_response.success
|
||||||
|
|
||||||
|
# Next, we create the needed VLANs:
|
||||||
|
if keep_going:
|
||||||
|
await asyncio.sleep(0.25)
|
||||||
|
step_response = await self.set_up_vlans(mikrotik_auth, use_https = False)
|
||||||
all_messages.append(step_response.message)
|
all_messages.append(step_response.message)
|
||||||
keep_going = step_response.success
|
keep_going = step_response.success
|
||||||
|
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ Use `PUT` or `PATCH` method on the path `/interface/pppoe-server/server`. Consid
|
|||||||
**ROLL-BACK:** Save the original configuration as a JSON string in the `comment` field. If there was no original
|
**ROLL-BACK:** Save the original configuration as a JSON string in the `comment` field. If there was no original
|
||||||
configuration, simply delete your record.
|
configuration, simply delete your record.
|
||||||
|
|
||||||
### 6. Set-up AAA to use RADIUS
|
### 6. Set up AAA to use RADIUS
|
||||||
|
|
||||||
Now we need to tell the MikroTik device to use a RADIUS server for AAA.
|
Now we need to tell the MikroTik device to use a RADIUS server for AAA.
|
||||||
|
|
||||||
|
|||||||
@@ -338,7 +338,7 @@ class AsyncREST:
|
|||||||
api_response = ApiResponse(
|
api_response = ApiResponse(
|
||||||
action = inspect.stack()[1].function,
|
action = inspect.stack()[1].function,
|
||||||
url = url,
|
url = url,
|
||||||
method = "POST"
|
method = "PATCH"
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user