从 ZStack 获取物理机与云主机信息并导出 Excel 文件

发布于:2025-04-07 ⋅ 阅读:(30) ⋅ 点赞:(0)


从 ZStack 获取物理机与云主机信息并导出 Excel 文件

在云计算环境中,物理机和云主机的信息管理非常重要。ZStack 作为一款开源云平台,提供了丰富的 API 来获取相关数据。本文将通过一个 Python 脚本,展示如何从 ZStack 获取物理机和云主机的基本信息,并将这些信息整理成 Excel 文件,方便进行分析和报告。

环境

  • python 3.9
  • zstack 5.3.0

zstack 官网

用户手册
开发手册

客户端封装

为了与 ZStack 进行交互,我们首先需要封装一个 API 客户端。这个客户端提供了登录、请求数据和获取物理机、云主机信息的功能。

class ZstackInstancesApi:
    def __init__(self, host, user, passwd, port):
        self.zstack_api_url = f"http://{host}:{port}/zstack/"
        self.session_id = self._login(user, passwd)
        self.headers = {
            "Content-Type": "application/json;charset=UTF-8",
            "Authorization": f"OAuth {self.session_id}" if self.session_id else ""
        }
    
    def _login(self, user, passwd):
        login_data = {
            "logInByAccount": {
                "accountName": user,
                "password": hashlib.sha512(passwd.encode()).hexdigest(),
            }
        }
        try:
            res = requests.put(
                f"{self.zstack_api_url}v1/accounts/login",
                data=json.dumps(login_data),
                headers={"Content-Type": "application/json"},
            )
            res.raise_for_status()
            return res.json().get("inventory", {}).get("uuid")
        except requests.RequestException as e:
            print(f"Login failed: {e}")
            return None

讲解

  • ZstackInstancesApi 类封装了与 ZStack API 交互的基本操作。
  • __init__ 方法接收 ZStack 服务器的地址、用户名、密码和端口,初始化客户端并进行登录。
  • _login 方法使用 SHA-512 加密密码并发起登录请求,获取会话 ID。

获取物理机信息

接下来,我们通过 get_queryhost 方法获取物理机的基本信息。每台物理机的信息包括 UUID、名称、状态、管理 IP、CPU 核心数、内存容量等。

def get_queryhost(self):
    data = self._request("GET", "v1/hosts")
    if not data:
        return []
    
    instances = []
    for instance in data.get("inventories", []):
        instances.append({
            "uuid": instance.get("uuid"),
            "state": instance.get("state"),
            "status": instance.get("status"),
            "cpuNum": instance.get("cpuNum"),
            "name": instance.get("name"),
            "managementIp": instance.get("managementIp"),
            "cpuSockets": instance.get("cpuSockets"),
            "totalMemoryCapacity": round(instance.get("totalMemoryCapacity", 0) / (1024 * 1024 * 1024), 2)
        })
    
    return instances

讲解

  • get_queryhost 方法发送请求获取所有物理机的信息,并对返回的数据进行解析,将相关字段整理成一个易于使用的列表。
  • 物理机信息包括 UUID、状态、CPU 核心数、内存等数据,内存被转换为 GB 单位,保留两位小数。

获取云主机信息并关联物理机

通过 get_instances_info 方法,我们可以获取所有云主机的信息,并通过云主机的 hostUuid 与物理机的信息进行关联。每个云主机的详细信息包括名称、状态、CPU 核心数、内存、IP 地址、存储信息等。

def get_instances_info(self):
    data = self._request("GET", "v1/vm-instances")
    if not data:
        return []
    
    physical_machines = self.get_queryhost()
    physical_machine_dict = {pm["uuid"]: pm["managementIp"] for pm in physical_machines}
    
    instances = []
    for instance in data.get("inventories", []):
        vm_nics = instance.get("vmNics", [])
        nic_ip = vm_nics[0].get("ip") if vm_nics else None
        
        vm_allVolumes = instance.get("allVolumes", [])
        volumes = []
        for volume in vm_allVolumes:
            if volume.get("type") != "Root":
                continue
            volume_info = {
                "size": round(volume.get("size") / (1024 * 1024 * 1024), 2),
            }
            volumes.append(volume_info)
        
        host_uuid = instance.get("hostUuid")
        physical_ip = physical_machine_dict.get(host_uuid)
        
        instances.append({
            "uuid": instance.get("uuid"),
            "name": instance.get("name"),
            "state": instance.get("state"),
            "platform": instance.get("platform"),
            "cpuNum": instance.get("cpuNum"),
            "memorySize": round(instance.get("memorySize", 0) / (1024 * 1024 * 1024), 2),
            "ip": nic_ip,
            "volumes": volumes[0].get("size") if volumes else None,
            "hostIp": physical_ip
        })
    
    return instances

讲解

  • get_instances_info 方法首先获取云主机信息,并通过物理机的 uuid 从物理机列表中查找对应的 IP。
  • 云主机的存储信息会被提取并转换为 GB 单位。关联物理机信息后,云主机信息中将包含物理机的 IP。

导出数据到 Excel 文件

在获取完物理机和云主机的所有信息后,我们将其导出到 Excel 文件中,分别保存在两个工作表里:一个存储物理机信息,另一个存储云主机信息。

def export_to_excel(physical_machines, instances):
    wb = openpyxl.Workbook()
    
    # 创建物理机 Sheet
    ws1 = wb.active
    ws1.title = "物理机信息"
    ws1.append(["物理机名称", "物理机IP", "物理机状态", "CPU", "内存", "磁盘"])
    
    for machine in physical_machines:
        total_capacity = f"{machine['totalCapacity']} GB" if "totalCapacity" in machine else "N/A"
        ws1.append([
            machine["name"],
            machine["managementIp"],
            machine["state"],
            machine["cpuNum"],
            f"{machine['totalMemoryCapacity']} GB",
            total_capacity
        ])
    
    # 创建云主机 Sheet
    ws2 = wb.create_sheet(title="虚拟机信息")
    ws2.append(["云主机名称", "云主机IP", "云主机状态", "CPU", "内存", "云主机磁盘", "物理机IP"])
    
    for instance in instances:
        ws2.append([
            instance["name"],
            instance["ip"],
            instance["state"],
            instance["cpuNum"],
            f"{instance['memorySize']} GB",
            f"{instance['volumes']} GB" if instance.get("volumes") else "N/A",
            instance.get("hostIp"),
        ])
    
    # 保存文件
    wb.save("cloud_and_physical_machines.xlsx")

讲解

  • export_to_excel 方法使用 openpyxl 库创建一个 Excel 文件,分两个工作表导出物理机和云主机的信息。
  • 物理机的磁盘容量(总容量)和云主机的磁盘信息(根卷大小)会分别输出到对应的工作表中。

运行主程序

最后,在 __main__ 代码块中,我们初始化 API 客户端,获取物理机和云主机信息,并导出到 Excel 文件。

if __name__ == "__main__":
    api = ZstackInstancesApi(host="192.168.1.1", user="admin", passwd="password", port=8080)
    physical_machines = api.get_queryhost()
    uuid = api.get_primary_storage_info()[0].get("uuid")
    physical_storage_info = api.get_physical_storage_info(uuid)

    for machine in physical_machines:
        for storage in physical_storage_info:
            if machine["uuid"] == storage["hostUuid"]:
                machine["totalCapacity"] = storage["totalCapacity"]
                break

    instances = api.get_instances_info()
    export_to_excel(physical_machines, instances)

讲解

  • 通过 ZstackInstancesApi 客户端获取物理机和云主机的信息。
  • 获取主存储信息并将磁盘容量添加到物理机信息中。
  • 最后将数据导出到 cloud_and_physical_machines.xlsx 文件。

总结

通过上述代码,我们能够从 ZStack 获取物理机和云主机的相关信息,并将这些信息以结构化的方式导出到 Excel 文件中。这样,管理员可以方便地查看和分析云环境中的各类资源。此方法不仅简化了信息提取的流程,也为进一步的资源管理和优化提供了有力支持。

最终文档效果

在这里插入图片描述
在这里插入图片描述

完整代码

"""
导出物理机信息以及关联云主机信息生成excel表格
物理机名称 物理机IP 物理机状态 物理机CPU 物理机内存 物理机磁盘
云主机名称 云主机IP 云主机状态 云主机CPU 云主机内存 云主机磁盘
"""
import requests
import json
import hashlib
import openpyxl

class ZstackInstancesApi:
    """
    ZStack API 客户端封装,用于管理云主机实例。
    """
    def __init__(self, host, user, passwd, port):
        """
        初始化 API 客户端,并尝试登录。
        :param host: ZStack 服务器地址
        :param user: 登录用户名
        :param passwd: 登录密码
        :param port: 服务器端口,默认为 8080
        """
        self.zstack_api_url = f"http://{host}:{port}/zstack/"
        self.session_id = self._login(user, passwd)
        self.headers = {
            "Content-Type": "application/json;charset=UTF-8",
            "Authorization": f"OAuth {self.session_id}" if self.session_id else ""
        }

    def _login(self, user, passwd):
        """
        登录 ZStack 并获取会话 ID。
        :param user: 账户名
        :param passwd: 密码
        :return: 会话 ID 或 None(登录失败时)
        """
        login_data = {
            "logInByAccount": {
                "accountName": user,
                "password": hashlib.sha512(passwd.encode()).hexdigest(),
            }
        }
        try:
            res = requests.put(
                f"{self.zstack_api_url}v1/accounts/login",
                data=json.dumps(login_data),
                headers={"Content-Type": "application/json"},
            )
            res.raise_for_status()
            return res.json().get("inventory", {}).get("uuid")
        except requests.RequestException as e:
            print(f"Login failed: {e}")
            return None

    def _request(self, method, endpoint, params=None):
        """
        统一封装 HTTP 请求方法。
        :param method: HTTP 方法(GET、POST、DELETE等)
        :param endpoint: API 端点
        :param params: 请求参数
        :return: JSON 响应数据或 None(请求失败时)
        """
        url = f"{self.zstack_api_url}{endpoint}"
        
        try:
            res = requests.request(method, url, headers=self.headers, params=params)
            res.raise_for_status()
            return res.json()
        except requests.RequestException as e:
            print(f"Request failed: {e}")
            return None

    def get_queryhost(self):
        """
        获取所有物理机的基本信息。
        :return: 包含物理机信息的列表
        """
        data = self._request("GET", "v1/hosts")
        # return data.get("inventories", []) if data else []
        if not data:
            return []

        instances = []
        for instance in data.get("inventories", []):
            instances.append({
                "uuid": instance.get("uuid"),
                "state": instance.get("state"),
                "status": instance.get("status"),
                "cpuNum": instance.get("cpuNum"),
                "name": instance.get("name"),
                "managementIp": instance.get("managementIp"),
                "cpuSockets": instance.get("cpuSockets"),
                "totalMemoryCapacity": round(instance.get("totalMemoryCapacity", 0) / (1024 * 1024 * 1024), 2)  # 转换为 GB,保留两位小数
            })
        
        return instances
    
    def get_primary_storage_info(self):
        """
        获取主存储信息。
        :return: 主存储信息列表
        """
        data = self._request("GET", "v1/primary-storage")
        if not data:
            return []
        
        storages = []
        for storage in data.get("inventories", []):
            storages.append({
                "uuid": storage.get("uuid"),
                "name": storage.get("name"),
                "state": storage.get("state"),
                "status": storage.get("status"),
                "totalCapacity": round(storage.get("totalCapacity", 0) / (1024 * 1024 * 1024), 2),  # 转换为 GB
                "availableCapacity": round(storage.get("availableCapacity", 0) / (1024 * 1024 * 1024), 2)
            })
        
        return storages

    def get_physical_storage_info(self, uuid):
        """
        获取指定主存储的物理存储信息。
        :param uuid: 主存储 UUID
        :return: 物理存储信息列表
        """
        data = self._request("GET", f"v1/primary-storage/local-storage/{uuid}/capacities")
        if not data:
            return []
        
        physical_storages = []
        for storage in data.get("inventories", []):
            physical_storages.append({
                "hostUuid": storage.get("hostUuid"),
                "totalCapacity": round(storage.get("totalCapacity", 0) / (1024 * 1024 * 1024), 2),
                # "availableCapacity": round(storage.get("availableCapacity", 0) / (1024 * 1024 * 1024), 2),
                # "totalPhysicalCapacity": round(storage.get("totalPhysicalCapacity", 0) / (1024 * 1024 * 1024), 2),
                # "availablePhysicalCapacity": round(storage.get("availablePhysicalCapacity", 0) / (1024 * 1024 * 1024), 2)
            })
        
        return physical_storages
    def get_instance_vm(self, uuid):
        """
        获取指定云主机的控制台地址。
        :param uuid: 云主机 UUID
        :return: JSON 格式的控制台地址信息
        """
        return self._request("GET", f"v1/vm-instances/{uuid}/console-addresses")

    # def get_instances_info(self):
    #     """
    #     获取所有云主机的基本信息。
    #     :return: 包含云主机信息的列表
    #     """
    #     data = self._request("GET", "v1/vm-instances")
    #     # return data.get("inventories", []) if data else []
    #     if not data:
    #         return []
    #     # for instance in instances:
    #     #     print(f"云主机名称: {instance['name']},memorySize: {instance['memorySize']}, cpuNum: {instance['cpuNum']}, state: {instance['state']}, vmNics: {instance['vmNics'][0].get('uuid')}, ")
        
    #     instances = []
  
    #     for instance in data.get("inventories", []):
    #         # 获取云主机的 IP 地址
    #         vm_nics = instance.get("vmNics", [])
    #         nic_ip = vm_nics[0].get("ip") if vm_nics else None
    #         # 获取云主机的存储信息
    #         vm_allVolumes = instance.get("allVolumes", [])
    #         volumes = []
            
    #         # 遍历所有卷并提取信息
    #         for volume in vm_allVolumes:
    #             # 只处理根卷
    #             if volume.get("type") != "Root":
    #                 continue
    #             volume_info = {
    #                 # "uuid": volume.get("uuid"),
    #                 # "name": volume.get("name"),
    #                 "size": round(volume.get("size") / (1024 * 1024 * 1024),2),  # 转换为 GB
    #             }
    #             volumes.append(volume_info)
           
    #         instances.append({
    #             "uuid": instance.get("uuid"),
    #             "hostUuid": instance.get("hostUuid"),
    #             "name": instance.get("name"),
    #             "state": instance.get("state"),
    #             "platform": instance.get("platform"),
    #             "cpuNum": instance.get("cpuNum"),
    #             "memorySize": round(instance.get("memorySize", 0) / (1024 * 1024 * 1024), 2),  # 转换为 GB
    #             "ip": nic_ip,
    #             "volumes": volumes[0].get("size") if volumes else None,  # 获取第一个卷的大小
    #         })

            
    #     return instances
    def get_instances_info(self):
        """
        获取所有云主机的基本信息,并与物理机信息关联。
        :return: 包含云主机信息的列表
        """
        data = self._request("GET", "v1/vm-instances")
        if not data:
            return []
        
        # 获取所有物理机信息
        physical_machines = self.get_queryhost()
        
        # 创建一个字典,快速查找物理机信息
        physical_machine_dict = {pm["uuid"]: pm["managementIp"] for pm in physical_machines}
        
        instances = []
        for instance in data.get("inventories", []):
            # 获取云主机的 IP 地址
            vm_nics = instance.get("vmNics", [])
            nic_ip = vm_nics[0].get("ip") if vm_nics else None
            
            # 获取云主机的存储信息
            vm_allVolumes = instance.get("allVolumes", [])
            volumes = []
            
            for volume in vm_allVolumes:
                if volume.get("type") != "Root":
                    continue
                volume_info = {
                    "size": round(volume.get("size") / (1024 * 1024 * 1024), 2),  # 转换为 GB
                }
                volumes.append(volume_info)
            
            # 查找对应的物理机信息(只获取物理机的 IP)
            host_uuid = instance.get("hostUuid")
            physical_ip = physical_machine_dict.get(host_uuid)
            
            instances.append({
                "uuid": instance.get("uuid"),
                "name": instance.get("name"),
                "state": instance.get("state"),
                "platform": instance.get("platform"),
                "cpuNum": instance.get("cpuNum"),
                "memorySize": round(instance.get("memorySize", 0) / (1024 * 1024 * 1024), 2),  # 转换为 GB
                "ip": nic_ip,
                "volumes": volumes[0].get("size") if volumes else None,
                "hostIp": physical_ip  # 只输出物理机的 IP
            })
        
        return instances
    def get_query_address_pool(self, uuid):
        """
        获取指定地址池的详细信息。
        :param uuid: 地址池 UUID
        :return: JSON 格式的地址池信息
        """
        return self._request("GET", f"v1/l3-networks/address-pools/{uuid}")

    def logout(self):
        """
        退出当前 ZStack 会话。
        """
        if not self.session_id:
            print("No active session to log out.")
            return
        self._request("DELETE", f"api/v2/session/{self.session_id}")

def export_to_excel(physical_machines, instances):
    wb = openpyxl.Workbook()
    
    # 创建物理机 Sheet
    ws1 = wb.active
    ws1.title = "物理机信息"
    ws1.append(["物理机名称", "物理机IP", "物理机状态", "CPU", "内存", "磁盘"])
    
    for machine in physical_machines:
        total_capacity = f"{machine['totalCapacity']} GB" if "totalCapacity" in machine else "N/A"
        ws1.append([
            machine["name"],
            machine["managementIp"],
            machine["state"],
            machine["cpuNum"],
            f"{machine['totalMemoryCapacity']} GB",
            total_capacity  # 添加磁盘总容量
        ])
    
    # 创建云主机 Sheet
    ws2 = wb.create_sheet(title="虚拟机信息")
    ws2.append(["云主机名称", "云主机IP", "云主机状态", "CPU", "内存", "云主机磁盘", "物理机IP"])
    
    for instance in instances:
        ws2.append([
            instance["name"],
            instance["ip"],
            instance["state"],
            instance["cpuNum"],
            f"{instance['memorySize']} GB",
            f"{instance['volumes']} GB" if instance.get("volumes") else "N/A",
            instance.get("hostIp"),  # 输出物理机的 IP
        ])
    
    # 保存文件
    wb.save("cloud_and_physical_machines.xlsx")

if __name__ == "__main__":
    # 示例: 初始化 API 客户端并获取云主机信息
    api = ZstackInstancesApi(host="192.168.1.1", user="admin", passwd="password", port=8080)
    # 获取所有物理机信息
    physical_machines = api.get_queryhost()

    # 获取主存储信息并根据物理机 UUID 进行匹配
    uuid = api.get_primary_storage_info()[0].get("uuid")
    physical_storage_info = api.get_physical_storage_info(uuid)

    # 遍历物理机信息并添加 totalCapacity 信息
    for machine in physical_machines:
        for storage in physical_storage_info:
            if machine["uuid"] == storage["hostUuid"]:
                # 在物理机信息中添加 totalCapacity
                machine["totalCapacity"] = storage["totalCapacity"]
                break  # 一旦找到对应的物理存储,跳出循环

    # 获取所有云主机信息
    instances = api.get_instances_info()

    # 导出数据到 Excel 文件
    export_to_excel(physical_machines, instances)

网站公告

今日签到

点亮在社区的每一天
去签到