#!/bin/bash
# Function to check resource availability on a specific node
check_resources() {
    local required_cpu=$1
    local required_ram=$2
    local node_name=$3

    # Step 1: Get the resource provider UUID for the given node name
    local node_uuid=$(openstack resource provider list --name "$node_name" -f value -c uuid)

    if [[ -z $node_uuid ]]; then
        echo "Error: Unable to find resource provider UUID for node '$node_name'."
        return 1
    fi

    # Step 2: Get inventory details for the resource provider
    local resource_data=$(openstack resource provider inventory list "$node_uuid" -f json)

    if [[ -z $resource_data ]]; then
        echo "Error: Unable to fetch resource inventory for node '$node_name' (UUID: $node_uuid)."
        return 1
    fi

    # Step 3: Parse the resource details
    local total_cpu=$(echo "$resource_data" | jq '.[] | select(.resource_class == "VCPU") | .total')
    local reserved_cpu=$(echo "$resource_data" | jq '.[] | select(.resource_class == "VCPU") | .reserved')
    local allocated_cpu=$(echo "$resource_data" | jq '.[] | select(.resource_class == "VCPU") | .allocation')

    local total_ram=$(echo "$resource_data" | jq '.[] | select(.resource_class == "MEMORY_MB") | .total')
    local reserved_ram=$(echo "$resource_data" | jq '.[] | select(.resource_class == "MEMORY_MB") | .reserved')
    local allocated_ram=$(echo "$resource_data" | jq '.[] | select(.resource_class == "MEMORY_MB") | .allocation')

    # Step 4: Calculate available resources
    local available_cpu=$((total_cpu - reserved_cpu - allocated_cpu))
    local available_ram=$((total_ram - reserved_ram - allocated_ram))

    # Step 5: Check if the required resources can be allocated
    if [[ $available_cpu -ge $required_cpu && $available_ram -ge $required_ram ]]; then
        return 0
    else
        echo "Node '$node_name' (UUID: $node_uuid) does NOT have sufficient resources."
        echo "Available CPU: $available_cpu, Required CPU: $required_cpu"
        echo "Available RAM: $available_ram MB, Required RAM: $required_ram MB"
        return 1
    fi
}

# 检查传入参数
if [ "$#" -ne 2 ]; then
    echo "Usage: $0 <target_host> <instance_uuid>"
    exit 1
fi

target_host=$1
instance_uuid=$2

# 获取实例的详细信息
instance_info=$(openstack server show "$instance_uuid" -f json)

if [ -z "$instance_info" ]; then
    echo "Error: Instance with UUID $instance_uuid not found."
    exit 1
fi

# 提取实例名称和当前主机
instance_name=$(echo "$instance_info" | jq -r '."OS-EXT-SRV-ATTR:instance_name"')
name=$(echo "$instance_info" | jq -r '."name"')
current_host=$(echo "$instance_info" | jq -r '."OS-EXT-SRV-ATTR:host"')
project_id=$(echo "$instance_info" | jq -r '.project_id')
flavor_info=$(echo "$instance_info" | jq -r '.flavor')

# 获取实例的端口信息
ports=$(openstack port list --server "$instance_uuid" -f json)

# 统计端口数量
port_count=$(echo "$ports" | jq '. | length')

if [ "$port_count" -gt 2 ]; then
    echo "Error: Instance has more than 2 ports. Operation aborted."
    exit 1
fi

# 识别业务端口
declareport_fixed_ips=""
business_ips=()  # 保存 IP 地址的数组
port_mac=""
port_id=""
network_id=""

# 遍历端口信息
port_count=$(echo "$ports" | jq 'length')

# 使用 for 循环遍历数组
for ((i=0; i<$port_count; i++)); do
    is_public=false

    port=$(echo "$ports" |jq ".[$i]")
    fixed_ips=$(echo "$port" |  jq -r '.["Fixed IP Addresses"] | .[].ip_address')

    for ip in $fixed_ips;
    do
        if [[ "$ip" =~ ^172\.16\.* || "$ip" =~ ^172\.17\.* ]]; then
            is_business_port=false
            break
        else
            is_public=true
        fi
    done

    if [ $is_public == true ];then
        for ip in $fixed_ips;
        do
            port_fixed_ips="$port_fixed_ips --fixed-ip ip-address=$ip"
            business_ips+=("$ip")
        done
        port_id_tmp=$(echo "$port" | jq -r '.ID')
        mac_address=$(echo "$port" | jq -r '."MAC Address"')
        network_id_tmp=$(openstack port show "$port_id_tmp" -f json | jq -r '.network_id')

        port_mac="$mac_address"
        port_id="$port_id_tmp"
        network_id="$network_id_tmp"

    fi



done

# 如果没有找到业务端口，退出
if [ -z "$port_id" ]; then
    echo "No business port found."
    exit 1
fi

## 检索计算规格
flavor_uuid=$(echo "$flavor_info" | grep -oP '\(\K[^\)]*')
if [ -z "$flavor_uuid" ];then
    echo "No flavor uuid"
    exit 1
fi
flavor_data=$(openstack flavor show $flavor_uuid -f json)
vcpu=$(echo $flavor_data | jq ".vcpus")
ram=$(echo $flavor_data | jq ".ram")



# 显示业务端口信息
echo "===================================="
echo "Name:" $name
echo "Instance Name: "$instance_name
echo "vCPU: $vcpu"
echo "vRAM: $ram"
# 显示业务端口信息
echo "Business Port:"
echo "Port ID: $port_id"
echo "MAC Address: $port_mac"
echo "Fixed IPs: ${business_ips[*]}"
echo "Network ID: $network_id"
echo ""
echo ""
echo "Host: " $current_host
echo "Target Host: "$target_host
echo "===================================="

## 预先判断一下资源是否够
check_resources $vcpu $ram $target_host
if [ $? -ne 0 ];then
    exit 1
fi

# 确认是否升级
read -p "Do you want to proceed with shutting down and migrating the instance? (yes/no): " confirm
if [ "$confirm" != "yes" ]; then
    echo "Operation canceled."
    exit 0
fi

# 关机实例
echo "Shutting down instance $instance_name..."
openstack server stop "$instance_uuid"

# 等待实例关机
while true; do
    status=$(openstack server show "$instance_uuid" -f value -c status)
    if [ "$status" == "SHUTOFF" ]; then
        echo "Instance is now shut off."
        break
    fi
    echo "Waiting for instance to shut down..."
    sleep 5
done

# 迁移实例

if [[ "$current_host" != "$target_host" ]];then
    echo "Migrating instance to host $target_host..."
    openstack server migrate --os-compute-api-version 2.90 --host "$target_host" --wait "$instance_uuid"

    # 确认迁移是否成功
    if [ $? -eq 0 ]; then
        echo "Migration initiated successfully. Confirming migration..."
        openstack server migration confirm "$instance_uuid"
    else
        echo "Migration failed. Check OpenStack logs for details."
        exit 1
    fi
fi

# 删除业务端口
echo "Deleting port: $port_id"
openstack port delete "$port_id"

# 创建新的业务端口
new_port_name="${instance_name}_SRIOV"
echo "Creating new business port with name $new_port_name..."
new_port=$(openstack port create --network "$network_id" $port_fixed_ips \
    --mac-address $port_mac --vnic-type direct --no-security-group --project "$project_id" "$new_port_name" -f json)
new_port_id=$(echo "$new_port" | jq -r '.id')

echo "Attaching new port $new_port_id to instance $instance_uuid"
openstack server add port "$instance_uuid" "$new_port_id" 

if [ $? -ne 0 ];then
    echo "Attaching new port to instance failed."
    exit 1
fi

# 硬重启实例
echo "Rebooting instance $instance_name..."
openstack server reboot --hard "$instance_uuid"

if [ $? -eq 0 ]; then
    echo "Instance $instance_name successfully rebooted with new business port."
else
    echo "Failed to reboot instance $instance_name. Check logs for details."
fi
