diff --git a/cloudshell/cp/proxmox/flows/connectivity_flow.py b/cloudshell/cp/proxmox/flows/connectivity_flow.py index 985fba4..76b133b 100644 --- a/cloudshell/cp/proxmox/flows/connectivity_flow.py +++ b/cloudshell/cp/proxmox/flows/connectivity_flow.py @@ -101,8 +101,8 @@ def get_vnics(self, vm_id: int) -> Collection[VnicInfo]: def get_vnic_info(vnic: dict) -> VnicInfo: return VnicInfo( vnic.get("name"), - int(self.vnic_name_to_index(vnic.name, vm)), - self._network_can_be_replaced(vnic.network), + int(vnic.get("index")), + True, ) return tuple(map(get_vnic_info, self._api.get_vm_ifaces_info(vm_id))) @@ -117,12 +117,13 @@ def set_vlan( network = self._networks[net_settings.name] logger.info(f"Connecting {network} to the {target}.{vnic_name} iface") - try: - vnic = target.get_vnic(vnic_name) - except VnicNotFound: - vnic = create_new_vnic(target, network, vnic_name) - else: - vnic.connect(network) + # try: + # vnic = target.get_vnic(vnic_name) + # except VnicNotFound: + # vnic = create_new_vnic(target, network, vnic_name) + # else: + # vnic.connect(network) + mac = "" return vnic.mac_address diff --git a/cloudshell/cp/proxmox/handlers/proxmox_handler.py b/cloudshell/cp/proxmox/handlers/proxmox_handler.py index 3b831df..067e9d3 100644 --- a/cloudshell/cp/proxmox/handlers/proxmox_handler.py +++ b/cloudshell/cp/proxmox/handlers/proxmox_handler.py @@ -1,6 +1,7 @@ from __future__ import annotations import logging +import re import time from functools import cached_property, partial from typing import TYPE_CHECKING, Any @@ -140,32 +141,49 @@ def get_vm_info(self, vm_id: int) -> dict: ) raise e - def get_vm_ifaces_info(self, vm_id: int) -> list[dict]: - """Get Virtual Machine network interfaces details.""" + def get_vm_ifaces_info(self, vm_id): + dataregexp = re.compile( + r"^(?P\w+)=(?P([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})+)," + r".*bridge=(?P\w+)," + r".*tag=(?P\d+)(,.*)?$" + ) + # net0: virtio=02:00:00:00:00:01,bridge=vmbr0,tag=10 + # [model=](e1000 | e1000-82540em | e1000-82544gc | e1000-82545em | e1000e + # | i82551 | i82557b | i82559er | ne2k_isa | ne2k_pci | pcnet | rtl8139 + # | virtio | vmxnet3) [,bridge=] [,firewall=] + # [,link_down=] [,macaddr=] [,mtu=] + # [,queues=] [,rate=] [,tag=] + # [,trunks=] + result = {} try: node = self.get_node_by_vmid(vm_id) - data = self._obj.get_net_ifaces(node=node, vm_id=vm_id) - - ifaces = [] - for iface in data.get("result", []): - iface_ipv4 = "Undefined" - iface_ipv6 = "Undefined" - for ip in iface.get(IP_LIST, []): - if ip.get(ADDRESS_TYPE) == "ipv4": - iface_ipv4 = ip.get(IP_ADDRESS) - elif ip.get(ADDRESS_TYPE) == "ipv6": - iface_ipv6 = ip.get(IP_ADDRESS) - - ifaces.append( - { - "name": iface.get(IFACE_NAME), - "mac": iface.get(MAC), - "ipv4": iface_ipv4, - "ipv6": iface_ipv6, - } - ) - - return ifaces + config = self._obj.get_vm_config(node=node, vm_id=vm_id) + guest_data = self._obj.get_net_ifaces(node=node, vm_id=vm_id) + guest_ifaces = {x.get(MAC): x for x in guest_data.get("result", [])} + for k, v in config.items(): + if k.startswith("net"): + vnic_data = dataregexp.search(v).groupdict() + mac = vnic_data.pop("mac") + iface = guest_ifaces.get(mac) + iface_ipv4 = "Undefined" + iface_ipv6 = "Undefined" + if vnic_data: + for ip in iface.get(IP_LIST, []): + if ip.get(ADDRESS_TYPE) == "ipv4": + iface_ipv4 = ip.get(IP_ADDRESS) + elif ip.get(ADDRESS_TYPE) == "ipv6": + iface_ipv6 = ip.get(IP_ADDRESS) + result[mac] = vnic_data.update( + {"name": k, + "index": k.replace("net", ""), + "guest_name": iface.get(IFACE_NAME), + "mac": iface.get(MAC), + "ipv4": iface_ipv4, + "ipv6": iface_ipv6, + } + ) + + return result except VmDoesNotExistException as e: logger.error( f"Virtual machine with vm_id {vm_id} doesn't exist." @@ -209,6 +227,17 @@ def _task_waiter( if status == "running" or exit_status.upper() != "OK": raise UnsuccessfulOperationException(msg) + def attach_interface(self, vm_id: int, vnic_id: int) -> None: + """Attach interface to Virtual Machine.""" + node = self.get_node_by_vmid(vm_id) + upid = self._obj.attach_interface(node=node, vm_id=vm_id, vnic_id=vnic_id) + + self._task_waiter( + node=node, + upid=upid, + msg=f"Failed to attach interface {vnic_id} during {{attempt*timeout}} sec" + ) + def get_snapshots_list(self, vm_id: int) -> list[int | bytes]: """Get list of existing snapshots.""" node = self.get_node_by_vmid(vm_id) diff --git a/cloudshell/cp/proxmox/handlers/rest_api_handler.py b/cloudshell/cp/proxmox/handlers/rest_api_handler.py index 918b075..a7b18fa 100644 --- a/cloudshell/cp/proxmox/handlers/rest_api_handler.py +++ b/cloudshell/cp/proxmox/handlers/rest_api_handler.py @@ -311,10 +311,38 @@ def assign_vlan( data += f",bridge={vlan_tag},tag={vlan_tag},firewall={int(enable_firewall)}" return self._do_post( path=f"nodes/{node}/qemu/{vm_id}/config", - json={interface_id: data}, + json={f"net{interface_id}": data}, http_error_map=error_map, cookies={COOKIES: self.ticket} ) + + @Decorators.get_data() + def attach_interface( + self, + node: str, + network_bridge: str, + interface_id: str, + vm_id: int, + vlan_tag: str, + interface_type: str = "virtio", + mac_address: str = "", + enable_firewall: bool = True, + ) -> requests.Response: + """""" + error_map = { + 400: ParamsException, + 401: AuthAPIException, + } + data = f"{interface_type}" + if mac_address: + data += f":{mac_address}" + data += f",bridge={network_bridge},tag={vlan_tag},firewall={int(enable_firewall)}" + return self._do_put( + path=f"nodes/{node}/qemu/{vm_id}/config", + http_error_map=error_map, + data=f"net{interface_id}={data}", + cookies={COOKIES: self.ticket} + ) # return self._do_post( # path=f"nodes/{node}/network", # json={"type": vlan_type, "iface": vlan_name, "autostart": int(autostart), @@ -604,6 +632,21 @@ def get_vm_osinfo(self, node: str, vm_id: int) -> requests.Response: cookies={COOKIES: self.ticket} ) + @Decorators.get_data() + def get_vm_config(self, node: str, vm_id: int) -> requests.Response: + """""" + error_map = { + 400: ParamsException, + 401: AuthAPIException, + } + # self.session.headers.update({}) + + return self._do_get( + path=f"/nodes/{node}/qemu/{vm_id}/config", + http_error_map=error_map, + cookies={COOKIES: self.ticket} + ) + if __name__ == "__main__": # session = requests.Session() @@ -624,7 +667,9 @@ def get_vm_osinfo(self, node: str, vm_id: int) -> requests.Response: password="Password1" ) api.connect() - res = api.get_vm_ifaces(node="proxmox1", vm_id=101) + res = api.attach_interface(node="proxmox1", vm_id=101, network_bridge="vmbr1", + interface_id="3", vlan_tag="65") + res1 = api.get_vm_ifaces(node="proxmox1", vm_id=101) res = api.get_task_status(node="proxmox1", upid="UPID:proxmox1:0034308A:11F8AFA1:660C23DC:qmsnapshot:100:root@pam:") # print(get_node_by_vmid(vm_id=102))