refactor: use f-strings in controller modules

This commit is contained in:
Mark Paronyan 2026-09-30 20:58:11 +03:00
parent 552c0ca3e0
commit 7a9c600f54
No known key found for this signature in database
GPG Key ID: 8DC7338A87FE22F8
9 changed files with 47 additions and 86 deletions

View File

@ -362,7 +362,7 @@ class Controller:
# install the built-in appliances if needed # install the built-in appliances if needed
if Config.instance().settings.Server.install_builtin_appliances: if Config.instance().settings.Server.install_builtin_appliances:
previous_version = controller_vars.get("version") previous_version = controller_vars.get("version")
log.info("Comparing controller version {} with config version {}".format(__version__, previous_version)) log.info(f"Comparing controller version {__version__} with config version {previous_version}")
builtin_appliances_path = self._appliance_manager.builtin_appliances_path() builtin_appliances_path = self._appliance_manager.builtin_appliances_path()
if not previous_version or parse_version(__version__.split("+")[0]) > parse_version( if not previous_version or parse_version(__version__.split("+")[0]) > parse_version(
previous_version.split("+")[0] previous_version.split("+")[0]

View File

@ -72,9 +72,7 @@ class Drawing:
return data.decode() return data.decode()
except UnicodeError: except UnicodeError:
width, height, filetype = get_size(data) width, height, filetype = get_size(data)
return '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="{height}" width="{width}">\n<image height="{height}" width="{width}" xlink:href="data:image/{filetype};base64,{b64}" />\n</svg>'.format( return f'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="{height}" width="{width}">\n<image height="{height}" width="{width}" xlink:href="data:image/{filetype};base64,{base64.b64encode(data).decode()}" />\n</svg>'
b64=base64.b64encode(data).decode(), filetype=filetype, width=width, height=height
)
except OSError: except OSError:
log.warning("Image file %s missing", filename) log.warning("Image file %s missing", filename)
return "<svg></svg>" return "<svg></svg>"

View File

@ -175,7 +175,7 @@ def _is_exportable(path, include_snapshots=False):
return False return False
# do not export directories of snapshots # do not export directories of snapshots
if include_snapshots is False and "{sep}snapshots{sep}".format(sep=os.path.sep) in path: if include_snapshots is False and f"{os.path.sep}snapshots{os.path.sep}" in path:
return False return False
try: try:

View File

@ -60,9 +60,7 @@ class GNS3VM:
:returns: Return list of engines supported by GNS3 for the GNS3VM :returns: Return list of engines supported by GNS3 for the GNS3VM
""" """
download_url = "https://github.com/GNS3/gns3-gui/releases/download/v{version}/GNS3.VM.VMware.Workstation.{version}.zip".format( download_url = f"https://github.com/GNS3/gns3-gui/releases/download/v{__version__}/GNS3.VM.VMware.Workstation.{__version__}.zip"
version=__version__
)
vmware_info = { vmware_info = {
"engine_id": "vmware", "engine_id": "vmware",
"description": f'VMware is the recommended choice for best performances.<br>The GNS3 VM can be <a href="{download_url}">downloaded here</a>.', "description": f'VMware is the recommended choice for best performances.<br>The GNS3 VM can be <a href="{download_url}">downloaded here</a>.',
@ -76,9 +74,7 @@ class GNS3VM:
vmware_info["name"] = "VMware Workstation / Player (recommended)" vmware_info["name"] = "VMware Workstation / Player (recommended)"
download_url = ( download_url = (
"https://github.com/GNS3/gns3-gui/releases/download/v{version}/GNS3.VM.Hyper-V.{version}.zip".format( f"https://github.com/GNS3/gns3-gui/releases/download/v{__version__}/GNS3.VM.Hyper-V.{__version__}.zip"
version=__version__
)
) )
hyperv_info = { hyperv_info = {
"engine_id": "hyper-v", "engine_id": "hyper-v",
@ -90,9 +86,7 @@ class GNS3VM:
} }
download_url = ( download_url = (
"https://github.com/GNS3/gns3-gui/releases/download/v{version}/GNS3.VM.VirtualBox.{version}.zip".format( f"https://github.com/GNS3/gns3-gui/releases/download/v{__version__}/GNS3.VM.VirtualBox.{__version__}.zip"
version=__version__
)
) )
virtualbox_info = { virtualbox_info = {
"engine_id": "virtualbox", "engine_id": "virtualbox",
@ -377,9 +371,7 @@ class GNS3VM:
if netmask: if netmask:
compute_network = ipaddress.ip_interface(f"{compute.host_ip}/{netmask}").network compute_network = ipaddress.ip_interface(f"{compute.host_ip}/{netmask}").network
if vm_network.compare_networks(compute_network) != 0: if vm_network.compare_networks(compute_network) != 0:
msg = "The GNS3 VM (IP={}, NETWORK={}) is not on the same network as the {} server (IP={}, NETWORK={}), please make sure the local server binding is in the same network as the GNS3 VM".format( msg = f"The GNS3 VM (IP={self.ip_address}, NETWORK={vm_network}) is not on the same network as the {compute_id} server (IP={compute.host_ip}, NETWORK={compute_network}), please make sure the local server binding is in the same network as the GNS3 VM"
self.ip_address, vm_network, compute_id, compute.host_ip, compute_network
)
self._controller.notification.controller_emit("log.warning", {"message": msg}) self._controller.notification.controller_emit("log.warning", {"message": msg})
except ComputeError as e: except ComputeError as e:
log.warning(f"Could not check the VM is in the same subnet as the local server: {e}") log.warning(f"Could not check the VM is in the same subnet as the local server: {e}")

View File

@ -138,7 +138,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
for info in result.splitlines(): for info in result.splitlines():
if "=" in info: if "=" in info:
name, value = info.split("=", 1) name, value = info.split("=", 1)
if name == "{}{}".format(backend_type, interface_number): if name == f"{backend_type}{interface_number}":
return value.strip('"') return value.strip('"')
return None return None
@ -179,7 +179,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
ip_address = match.group(1) ip_address = match.group(1)
netmask = match.group(2) netmask = match.group(2)
else: else:
raise GNS3VMError("Could not find IP address and netmask for vboxnet {}".format(vboxnet)) raise GNS3VMError(f"Could not find IP address and netmask for vboxnet {vboxnet}")
try: try:
interface = ipaddress.IPv4Interface(f"{ip_address}/{netmask}") interface = ipaddress.IPv4Interface(f"{ip_address}/{netmask}")
@ -189,17 +189,15 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
lower_ip = str(interface.ip + 2) lower_ip = str(interface.ip + 2)
upper_ip = str(subnet.network_address + subnet.num_addresses - 2) upper_ip = str(subnet.network_address + subnet.num_addresses - 2)
except ValueError: except ValueError:
raise GNS3VMError( raise GNS3VMError(f"Invalid IP address and netmask for vboxnet {vboxnet}: {ip_address}/{netmask}")
"Invalid IP address and netmask for vboxnet {}: {}/{}".format(vboxnet, ip_address, netmask)
)
dhcp_server_args = [ dhcp_server_args = [
"add", "add",
"--network=HostInterfaceNetworking-{}".format(vboxnet), f"--network=HostInterfaceNetworking-{vboxnet}",
"--server-ip={}".format(dhcp_server_ip), f"--server-ip={dhcp_server_ip}",
"--netmask={}".format(netmask), f"--netmask={netmask}",
"--lower-ip={}".format(lower_ip), f"--lower-ip={lower_ip}",
"--upper-ip={}".format(upper_ip), f"--upper-ip={upper_ip}",
"--enable", "--enable",
] ]
await self._execute("dhcpserver", dhcp_server_args) await self._execute("dhcpserver", dhcp_server_args)
@ -212,7 +210,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
:returns: boolean :returns: boolean
""" """
properties = await self._execute("list", ["{}".format(vboxnet_type)]) properties = await self._execute("list", [f"{vboxnet_type}"])
for prop in properties.splitlines(): for prop in properties.splitlines():
try: try:
name, value = prop.split(":", 1) name, value = prop.split(":", 1)
@ -287,9 +285,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
interface_number = await self._look_for_interface("hostonlynetwork") interface_number = await self._look_for_interface("hostonlynetwork")
if interface_number < 0: if interface_number < 0:
raise GNS3VMError( raise GNS3VMError(
'VM "{}" must have a network adapter attached to a host-only network in order to start'.format( f'VM "{self.vmname}" must have a network adapter attached to a host-only network in order to start'
self.vmname
)
) )
else: else:
backend_type = "hostonlyadapter" backend_type = "hostonlyadapter"
@ -299,17 +295,13 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
if interface_number < 0: if interface_number < 0:
raise GNS3VMError( raise GNS3VMError(
'VM "{}" must have a network adapter attached to a {} in order to start'.format( f'VM "{self.vmname}" must have a network adapter attached to a {backend_description} in order to start'
self.vmname, backend_description
)
) )
vboxnet = await self._look_for_vboxnet(backend_type, interface_number) vboxnet = await self._look_for_vboxnet(backend_type, interface_number)
if vboxnet is None: if vboxnet is None:
raise GNS3VMError( raise GNS3VMError(
'A VirtualBox host-only network could not be found on network adapter {} for "{}"'.format( f'A VirtualBox host-only network could not be found on network adapter {interface_number} for "{self._vmname}"'
interface_number, self._vmname
)
) )
if not (await self._check_vboxnet_exists(vboxnet, vboxnet_type)): if not (await self._check_vboxnet_exists(vboxnet, vboxnet_type)):
@ -319,9 +311,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
first_available_vboxnet = await self._find_first_available_vboxnet() first_available_vboxnet = await self._find_first_available_vboxnet()
if first_available_vboxnet is None: if first_available_vboxnet is None:
raise GNS3VMError( raise GNS3VMError(
'Please add a VirtualBox host-only network with DHCP enabled and attached it to network adapter {} for "{}"'.format( f'Please add a VirtualBox host-only network with DHCP enabled and attached it to network adapter {interface_number} for "{self._vmname}"'
interface_number, self._vmname
)
) )
await self.set_hostonly_network(interface_number, first_available_vboxnet) await self.set_hostonly_network(interface_number, first_available_vboxnet)
vboxnet = first_available_vboxnet vboxnet = first_available_vboxnet
@ -330,18 +320,14 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
await self._execute("hostonlyif", ["create"]) await self._execute("hostonlyif", ["create"])
except GNS3VMError: except GNS3VMError:
raise GNS3VMError( raise GNS3VMError(
'VirtualBox host-only network "{}" does not exist and could not be automatically created, please make the sure the network adapter {} configuration is valid for "{}"'.format( f'VirtualBox host-only network "{vboxnet}" does not exist and could not be automatically created, please make the sure the network adapter {interface_number} configuration is valid for "{self._vmname}"'
vboxnet, interface_number, self._vmname
)
) )
if backend_type == "hostonlyadapter" and not (await self._check_dhcp_server(vboxnet)): if backend_type == "hostonlyadapter" and not (await self._check_dhcp_server(vboxnet)):
try: try:
await self._add_dhcp_server(vboxnet) await self._add_dhcp_server(vboxnet)
except GNS3VMError as e: except GNS3VMError as e:
raise GNS3VMError( raise GNS3VMError(f"Could not add DHCP server for vboxnet {vboxnet}: {e}, please configure manually")
"Could not add DHCP server for vboxnet {}: {}, please configure manually".format(vboxnet, e)
)
vm_state = await self._get_state() vm_state = await self._get_state()
log.info(f'"{self._vmname}" state is {vm_state}') log.info(f'"{self._vmname}" state is {vm_state}')
@ -372,7 +358,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
else: else:
# if we can't get the IP address from the guest property, we try to get it from the GNS3 server (a NAT interface is required) # if we can't get the IP address from the guest property, we try to get it from the GNS3 server (a NAT interface is required)
if nat_interface_number < 0: if nat_interface_number < 0:
raise GNS3VMError("Could not find guest IP address for {}".format(self.vmname)) raise GNS3VMError(f"Could not find guest IP address for {self.vmname}")
log.warning("Could not find IP address from guest property, trying to get it from GNS3 server") log.warning("Could not find IP address from guest property, trying to get it from GNS3 server")
ip_address = "127.0.0.1" ip_address = "127.0.0.1"
try: try:
@ -382,33 +368,27 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
s.bind((ip_address, 0)) s.bind((ip_address, 0))
api_port = s.getsockname()[1] api_port = s.getsockname()[1]
except OSError as e: except OSError as e:
raise GNS3VMError("Error while getting random port: {}".format(e)) raise GNS3VMError(f"Error while getting random port: {e}")
if await self._check_vbox_port_forwarding(): if await self._check_vbox_port_forwarding():
# delete the GNS3VM NAT port forwarding rule if it exists # delete the GNS3VM NAT port forwarding rule if it exists
log.info("Removing GNS3VM NAT port forwarding rule from interface {}".format(nat_interface_number)) log.info(f"Removing GNS3VM NAT port forwarding rule from interface {nat_interface_number}")
await self._execute( await self._execute("controlvm", [self._vmname, f"natpf{nat_interface_number}", "delete", "GNS3VM"])
"controlvm", [self._vmname, "natpf{}".format(nat_interface_number), "delete", "GNS3VM"]
)
# add a GNS3VM NAT port forwarding rule to redirect 127.0.0.1 with random port to the port in the VM # add a GNS3VM NAT port forwarding rule to redirect 127.0.0.1 with random port to the port in the VM
log.info( log.info(f"Adding GNS3VM NAT port forwarding rule with port {api_port} to interface {nat_interface_number}")
"Adding GNS3VM NAT port forwarding rule with port {} to interface {}".format(
api_port, nat_interface_number
)
)
await self._execute( await self._execute(
"controlvm", "controlvm",
[ [
self._vmname, self._vmname,
"natpf{}".format(nat_interface_number), f"natpf{nat_interface_number}",
"GNS3VM,tcp,{},{},,{}".format(ip_address, api_port, self.port), f"GNS3VM,tcp,{ip_address},{api_port},,{self.port}",
], ],
) )
self.ip_address = await self._get_ip_from_server(interface_number, api_port) self.ip_address = await self._get_ip_from_server(interface_number, api_port)
log.info("GNS3 VM has been started with IP '{}'".format(self.ip_address)) log.info(f"GNS3 VM has been started with IP '{self.ip_address}'")
self.running = True self.running = True
async def _get_ip_from_guest_property(self): async def _get_ip_from_guest_property(self):
@ -446,8 +426,9 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
json_data = await resp.json() json_data = await resp.json()
if json_data: if json_data:
for interface in json_data: for interface in json_data:
if "name" in interface and interface["name"] == "eth{}".format( if (
hostonly_interface_number - 1 "name" in interface
and interface["name"] == f"eth{hostonly_interface_number - 1}"
): ):
if "ip_address" in interface and len(interface["ip_address"]) > 0: if "ip_address" in interface and len(interface["ip_address"]) > 0:
return interface["ip_address"] return interface["ip_address"]
@ -537,7 +518,5 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
"modifyvm", [self._vmname, f"--hostonlyadapter{adapter_number}", hostonly_network_name], timeout=3 "modifyvm", [self._vmname, f"--hostonlyadapter{adapter_number}", hostonly_network_name], timeout=3
) )
log.info( log.info(
'VirtualBox host-only network "{}" set on network adapter {} for "{}"'.format( f'VirtualBox host-only network "{hostonly_network_name}" set on network adapter {adapter_number} for "{self._vmname}"'
hostonly_network_name, adapter_number, self._vmname
)
) )

View File

@ -388,7 +388,7 @@ async def update_snapshots(snapshots_dir, project_path, project_name, project_id
async with aiofiles.open(snapshot_path, "wb+") as f: async with aiofiles.open(snapshot_path, "wb+") as f:
async for chunk in zstream: async for chunk in zstream:
await f.write(chunk) await f.write(chunk)
log.info("Project '{}': updated and repacked snapshot file '{}'".format(project_name, snapshot)) log.info(f"Project '{project_name}': updated and repacked snapshot file '{snapshot}'")
except OSError as e: except OSError as e:
raise ControllerError( raise ControllerError(
f"Cannot update snapshot '{os.path.basename(snapshot)}': the snapshot cannot be recreated: {e}" f"Cannot update snapshot '{os.path.basename(snapshot)}': the snapshot cannot be recreated: {e}"

View File

@ -552,14 +552,8 @@ class Link:
adapter_number = self.capture_node["adapter_number"] adapter_number = self.capture_node["adapter_number"]
port_number = self.capture_node["port_number"] port_number = self.capture_node["port_number"]
url = ( url = (
"/projects/{project_id}/{node_type}/nodes/{node_id}/adapters/{adapter_number}/" f"/projects/{self.project.id}/{node_type}/nodes/{node_id}/adapters/{adapter_number}/"
"ports/{port_number}/capture/stream".format( f"ports/{port_number}/capture/stream"
project_id=self.project.id,
node_type=node_type,
node_id=node_id,
adapter_number=adapter_number,
port_number=port_number,
)
) )
return compute._getUrl(url) return compute._getUrl(url)

View File

@ -1483,7 +1483,7 @@ class Project:
try: try:
snapshot = Snapshot(self, filename=snap) snapshot = Snapshot(self, filename=snap)
except ValueError: except ValueError:
log.error("Invalid snapshot file: {}".format(snap)) log.error(f"Invalid snapshot file: {snap}")
continue continue
self._snapshots[snapshot.id] = snapshot self._snapshots[snapshot.id] = snapshot
else: else:
@ -1492,13 +1492,13 @@ class Project:
try: try:
path = os.path.join(snapshot_dir, snapshot_entry["filename"]) path = os.path.join(snapshot_dir, snapshot_entry["filename"])
if not os.path.isfile(path): if not os.path.isfile(path):
log.warning("Snapshot file '{}' does not exist".format(path)) log.warning(f"Snapshot file '{path}' does not exist")
continue continue
snapshot_entry.pop("project_id") snapshot_entry.pop("project_id")
snapshot = Snapshot(self, **snapshot_entry) snapshot = Snapshot(self, **snapshot_entry)
self._snapshots[snapshot.id] = snapshot self._snapshots[snapshot.id] = snapshot
except KeyError: except KeyError:
log.error("Invalid entry in snapshot config file: {}".format(snapshot_entry)) log.error(f"Invalid entry in snapshot config file: {snapshot_entry}")
continue continue
self._save_snapshot_config() self._save_snapshot_config()
@ -1515,7 +1515,7 @@ class Project:
with open(self._snapshot_conf_path, "w+") as f: with open(self._snapshot_conf_path, "w+") as f:
json.dump(self._snapshot_conf, f, indent=4) json.dump(self._snapshot_conf, f, indent=4)
except OSError as e: except OSError as e:
log.error("Cannot write snapshot config '{}': {}".format(self._snapshot_conf_path, e)) log.error(f"Cannot write snapshot config '{self._snapshot_conf_path}': {e}")
@open_required @open_required
async def snapshot(self, name): async def snapshot(self, name):
@ -2142,7 +2142,7 @@ class Project:
# remote replication is not supported with remote computes # remote replication is not supported with remote computes
for compute in self.computes: for compute in self.computes:
if compute.id != "local": if compute.id != "local":
log.warning("Fast duplication is not supported with remote compute: '{}'".format(compute.id)) log.warning(f"Fast duplication is not supported with remote compute: '{compute.id}'")
return None return None
# work dir # work dir
p_work = pathlib.Path(location or self.path).parent.absolute() p_work = pathlib.Path(location or self.path).parent.absolute()
@ -2156,9 +2156,7 @@ class Project:
await wait_run_in_executor( await wait_run_in_executor(
shutil.copytree, self.path, new_project_path.as_posix(), symlinks=True, ignore_dangling_symlinks=True shutil.copytree, self.path, new_project_path.as_posix(), symlinks=True, ignore_dangling_symlinks=True
) )
log.info( log.info(f"Project content copied from '{self.path}' to '{new_project_path}' in {time.time() - t0}s")
"Project content copied from '{}' to '{}' in {}s".format(self.path, new_project_path, time.time() - t0)
)
# Read the topology file using the actual filename (self._filename), not self.name # Read the topology file using the actual filename (self._filename), not self.name
# This handles the case where a project has been renamed but we need to read the actual file # This handles the case where a project has been renamed but we need to read the actual file
@ -2177,7 +2175,7 @@ class Project:
regenerate_topology_ids(topology, new_project_path, reset_mac_addresses) regenerate_topology_ids(topology, new_project_path, reset_mac_addresses)
# dump the updated .gns3 project file # dump the updated .gns3 project file
dot_gns3_path = new_project_path.joinpath("{}.gns3".format(project_name)) dot_gns3_path = new_project_path.joinpath(f"{project_name}.gns3")
topology["project_id"] = new_project_id topology["project_id"] = new_project_id
with open(dot_gns3_path, "w+") as f: with open(dot_gns3_path, "w+") as f:
json.dump(topology, f, indent=4, sort_keys=True) json.dump(topology, f, indent=4, sort_keys=True)
@ -2190,7 +2188,7 @@ class Project:
# Remove the old .gns3 file (which has the original project name) # Remove the old .gns3 file (which has the original project name)
os.remove(old_gns3_file) os.remove(old_gns3_file)
project = await self.controller.load_project(dot_gns3_path, load=False) project = await self.controller.load_project(dot_gns3_path, load=False)
log.info("Project '{}': fast duplicated in {:.4f} seconds".format(project.name, time.time() - t0)) log.info(f"Project '{project.name}': fast duplicated in {time.time() - t0:.4f} seconds")
return project return project
def is_running(self): def is_running(self):

View File

@ -79,7 +79,7 @@ class Snapshot:
.replace(tzinfo=None) .replace(tzinfo=None)
.strftime(DESCRIPTION_DATETIME_FORMAT) .strftime(DESCRIPTION_DATETIME_FORMAT)
) )
description = "Snapshot '{}' taken on {}".format(self._name, date) description = f"Snapshot '{self._name}' taken on {date}"
self._description = description self._description = description
self._filename = filename self._filename = filename
@ -157,7 +157,7 @@ class Snapshot:
auto_open=self._project.auto_open, auto_open=self._project.auto_open,
auto_close=self._project.auto_close, auto_close=self._project.auto_close,
) )
log.info("Snapshot '{}' restored in {:.4f} seconds".format(self.name, time.time() - begin)) log.info(f"Snapshot '{self.name}' restored in {time.time() - begin:.4f} seconds")
except (OSError, PermissionError) as e: except (OSError, PermissionError) as e:
raise ControllerError(str(e)) raise ControllerError(str(e))
await project.open() await project.open()