日期:2015/10/20 - 2016/4/26 time 11:47

主机:n86

目的:初探oVirt-体验REST-API

操作内容:

一、示例
1、列出资源,使用GET方法
######### 列出所有vm的资源(请注意,输出是 XML 格式) #########
【实例】获取所有的vm的信息
[root@n86 ~]# curl -s -k -u "admin@internal:TestVM" -H "Content-type: application/xml" -X GET https://e01.test/api/vms -o all.xml

######### 列出指定vm名称为“test02”(使用test02的uuid)的资源 #########
【实例】获取test02的信息
[root@n86 ~]# curl -s -k -u "admin@internal:TestVM" -H "Content-type: application/xml" -X GET https://e01.test/api/vms/7f64702f-9b6d-42cf-b899-4a506bd50d57

2、创建资源,POST方法
######### 新建一个VM #########
【实例】创建vm:test03,指定集群和模版名称
[root@n86 ~]# curl -s -k \
-u "admin@internal:TestVM" \
-H "Content-type: application/xml" \
-d '
<vm>
<name>test03</name>
<cluster><name>Host-Only</name></cluster>
<template><name>centos6u5x64-small-01</name></template>
</vm>
' \
'https://e01.test/api/vms' -o test03.xml


3、更新资源,PUT方法
【实例】更新test02的VM名称字段
[root@n86 ~]# echo '<vm><name>test02-renamed</name></vm>' >/tmp/upload.xml
[root@n86 ~]# curl -s -k \
-u "admin@internal:TestVM" \
-H "Content-type: application/xml" \
-T /tmp/upload.xml \
'https://e01.test/api/vms/7f64702f-9b6d-42cf-b899-4a506bd50d57' -o test02.xml


4、移除资源,DELETE方法
【实例】
[root@n86 ~]# curl -s -k \
-u "admin@internal:TestVM" \
-X DELETE \
'https://e01.test/api/vms/0d261c5d-ea5b-4184-bf7a-a7d18c14a45a'



二、配置cloud-init
注:本文是从ovirt-engine-sdk-python的3.5.4更新到3.6.0.3,关于版本的差异有一个主要区别是新增了这个选项:
<use_cloud_init>true</use_cloud_init>


1、调整主机名,重新生成ssh-keys,更改用户密码,设置网卡和dns
curl -s --cacert ca.crt \
-u "admin@internal:TestVM" \
-H "Content-type: application/xml" \
-d '
<action>
<use_cloud_init>true</use_cloud_init>
<vm>
    <initialization>
        <cloud_init>
            <host>
                <address>vm01</address>
            </host>
            <regenerate_ssh_keys>true</regenerate_ssh_keys>
            <users>
                <user>
                    <user_name>root</user_name>
                    <password>ttt111</password>
                </user>
            </users>
            <network_configuration>
                <nics>
                    <nic>
                        <name>eth0</name>
                        <boot_protocol>DHCP</boot_protocol>
                        <on_boot>false</on_boot>
                    </nic>
                    <nic>
                        <name>eth1</name>
                        <boot_protocol>static</boot_protocol>
                        <network>
                            <ip address=\"10.0.200.101\" netmask=\"255.255.255.0\" gateway=\"10.0.200.1\" />
                        </network>
                        <on_boot>true</on_boot>
                    </nic>
                </nics>
                <dns>
                    <servers>
                        <host>
                            <address>223.5.5.5</address>
                        </host>
                    </servers>
                </dns>
            </network_configuration>
        </cloud_init>
    </initialization>
</vm>
</action>
' https://e01.test/api/vms/387cd076-8dc5-4bdb-85c3-f13ddbc1455a/start -o 111.xml


2、自定义的yaml文件
1)一个简单的例子,演示如何执行自定义的命令,要注意引号的使用。   
curl -s --cacert ca.crt \
-u "admin@internal:TestVM" \
-H "Content-type: application/xml" \
-d '
<action>
<use_cloud_init>true</use_cloud_init>
<vm>
    <initialization>
        <cloud_init>
            <files>
                <file>
                    <name>aaa.txt</name>
                    <content>
runcmd:
- echo "test1111" >>/tmp/aaa.txt
                    </content>
                    <type>plaintext</type>
                </file>
            </files>
        </cloud_init>
    </initialization>
</vm>
</action>
' https://e01.test/api/vms/387cd076-8dc5-4bdb-85c3-f13ddbc1455a/start -o 111.xml


验证加载内容:
[root@n36 ~]# mount -o loop `ps -ef |grep --color vm_200_7 |grep payload |cut -d',' -f36 |cut -d'=' -f3` /mnt && cat /mnt/openstack/latest/* && umount /mnt/
{
  "launch_index" : "0",
  "availability_zone" : "nova",
  "uuid" : "8457ea78-eb13-4b2e-abfe-bbddff7e3b70",
  "meta" : {
    "essential" : "false",
    "role" : "server",
    "dsmode" : "local"
  }
}#cloud-config
ssh_pwauth: true
disable_root: 0
output:
  all: '>> /var/log/cloud-init-output.log'
chpasswd:
  expire: false
runcmd:
- 'sed -i ''/^datasource_list: /d'' /etc/cloud/cloud.cfg; echo ''datasource_list:
  ["NoCloud", "ConfigDrive"]'' >> /etc/cloud/cloud.cfg'

runcmd:
- echo "test1111" >>/tmp/aaa.txt
                    [root@n36 ~]# 
查看目标机器是否执行命令:
[root@vm_200_8 ~]# tail -n 1 /tmp/aaa.txt 
test1111
符合预期。

                    
2)复杂一点,下载并执行一个脚本
curl -s --cacert ca.crt \
-u "admin@internal:TestVM" \
-H "Content-type: application/xml" \
-d '
<action>
<use_cloud_init>true</use_cloud_init>
<vm>
    <initialization>
        <cloud_init>
            <files>
                <file>
                    <name>test.sh</name>
                    <content>
runcmd:
- curl http://office.test/ovirt/test.sh |bash -
                    </content>
                    <type>plaintext</type>
                </file>
            </files>
        </cloud_init>
    </initialization>
</vm>
</action>
' https://e01.test/api/vms/d07b3ef4-441b-4c1e-a872-194dc1887be9/start -o 111.xml

验证加载内容:
[root@n36 ~]# mount -o loop `ps -ef |grep --color vm_200_8 |grep -v grep |cut -d',' -f36 |cut -d'=' -f3` /mnt && cat /mnt/openstack/latest/* && umount /mnt 
{
  "launch_index" : "0",
  "availability_zone" : "nova",
  "uuid" : "226dfcd8-ff7e-466d-8133-f182999ee776",
  "meta" : {
    "essential" : "false",
    "role" : "server",
    "dsmode" : "local"
  }
}#cloud-config
ssh_pwauth: true
disable_root: 0
output:
  all: '>> /var/log/cloud-init-output.log'
chpasswd:
  expire: false
runcmd:
- 'sed -i ''/^datasource_list: /d'' /etc/cloud/cloud.cfg; echo ''datasource_list:
  ["NoCloud", "ConfigDrive"]'' >> /etc/cloud/cloud.cfg'

runcmd:
- curl http://office.test/ovirt/test.sh |bash -
                    [root@n36 ~]# 
查看目标机器是否执行命令:
[root@vm_200_8 ~]# tail -n 1 /tmp/test.sh.log 
Thu Nov 12 08:02:25 CST 2015 [test]
符合预期。


三、脚本示例
----------------------------------------------------------------------------
[root@n86 bin]# cat ovirt_api.sh 
#!/bin/bash
# 
# 2016/4/26
# __version__='0.2.10'
# for ovirt-engine-3.6.0.3

#
DEBUG=0
## ovirt engine 信息
oe_url='https://e01.test/api'
oe_user='admin@internal'
oe_password='TestVM'

## curl 运行时固有的参数
curl_opts='curl -s --cacert ca.crt'

## 列出所有的 vm
function vm_list() {
    local s_vm_name=$1
    local f_xml="vms.xml"
    
    ${curl_opts} \
-H "Content-Type: application/xml" \
-u "${oe_user}:${oe_password}" \
"${oe_url}/vms" -o ${f_xml}
    return 0
}

## 获取 vm id
function vm_uuid() {
    local s_vm_name="$1"
    local f_xml="vms.xml"
    
    vm_list
    s_vm_id=`grep -vE 'description|comment' ${f_xml} |grep -A 1 "<name>${s_vm_name}</name>"|grep 'href=' |cut -d'"' -f2 |cut -d'/' -f4`
    if [ -z ${s_vm_id} ]; then
        echo '[ERROR] Not found: VM id'
        exit 1
    fi
    return 0
}

## 获取 vm 的状态
function vm_state() {
    local s_vm_name="$1"   
    vm_uuid ${s_vm_name}
    local f_xml="${s_vm_name}.state.xml"
    local state='unknown'
        
    echo -e '--------------------------------\n'
    echo -n 'Waiting..'
    while true
    do  
        ${curl_opts} \
-H "Content-Type: application/xml" \
-u "${oe_user}:${oe_password}" \
"${oe_url}/vms/${s_vm_id}" -o ${f_xml}

        state=`sed -nr 's/(.*)<state>(.*)<\/state>(.*)/\2/p' ${f_xml}`
        case ${state} in
            down)
                echo ' vm is down.'              
                break
                ;;
            up)
                echo ' vm is up.'
                break
                ;; 
            *)
                [ ${DEBUG} -eq 1 ] && echo " vm state: ${state}" || echo -n '.'
                sleep 1
        esac
    done
    echo -e '--------------------------------\n'
    echo "vm: ${s_vm_name}, id: ${s_vm_id}"
    [ ${DEBUG} -eq 0 ] && rm -fv ${f_xml}
    exit 0
}

## 检查 curl 请求返回的结果
function check_fault() {
    local f_xml=$1
    
    grep 'fault' ${f_xml} >/dev/null
    local r1=$?
    grep 'Request syntactically incorrect' ${f_xml} >/dev/null
    local r2=$?
    if [ $r1 -eq 0 ]; then
        echo "result: failed"
        echo "reason: `sed -nr 's/(.*)<reason>(.*)<\/reason>(.*)/\2/p' ${f_xml}`"
        echo "detail: `sed -nr 's/(.*)<detail>(.*)<\/detail>(.*)/\2/p' ${f_xml}`"
        exit 1
    fi
    if [ $r2 -eq 0 ]; then
        echo 'result: Request syntactically incorrect'
        exit 2
    fi

    state=`sed -nr 's/(.*)<state>(.*)<\/state>(.*)/\2/p' ${f_xml}`
    echo "result: ${state}"
    return 0
}

## 启动 vm
function vm_start() {
    local s_vm_name="$1"
    vm_uuid ${s_vm_name}
    local f_xml="${s_vm_name}.start.xml"
    
    ${curl_opts} \
-H "Content-Type: application/xml" \
-u "${oe_user}:${oe_password}" \
-d "
<action>
  <vm>
    <status>start</status>
  </vm>
</action>
" \
"${oe_url}/vms/${s_vm_id}/start" -o ${f_xml}

    check_fault ${f_xml}
    [ ${DEBUG} -eq 0 ] && rm -fv ${f_xml}
}

## 停止 vm
function vm_stop() {
    local s_vm_name="$1"
    vm_uuid ${s_vm_name}
    local f_xml="${s_vm_name}.stop.xml"
    
    ${curl_opts} \
-H "Content-Type: application/xml" \
-u "${oe_user}:${oe_password}" \
-d "
<action>
  <vm>
    <status>stop</status>
  </vm>
</action>
" \
"${oe_url}/vms/${s_vm_id}/stop" -o ${f_xml}

    check_fault ${f_xml}
    [ ${DEBUG} -eq 0 ] && rm -fv ${f_xml}
}

## 删除 vm
function vm_delete() {
    local s_vm_name="$1"
    vm_uuid ${s_vm_name}
    local f_xml="${s_vm_name}.delete.xml"
    
    ${curl_opts} \
-u "${oe_user}:${oe_password}" \
-X DELETE \
"${oe_url}/vms/${s_vm_id}" -o ${f_xml}

    check_fault ${f_xml}
    [ ${DEBUG} -eq 0 ] && rm -fv ${f_xml}
}

## 只运行一次,使用固定的模版配置 cloud-init
function vm_runonce() {
    local s_vm_name="$1"
    vm_uuid ${s_vm_name}
    local f_xml="${s_vm_name}.runonce.xml"
    
    local s_vm_password="$2"
    local s_vm_ip="$3"
    local s_vm_netmask="$4"
    local s_vm_gateway="$5"
    local s_vm_dns="$6"

    local tpl_cloud_init="
<action>
<use_cloud_init>true</use_cloud_init>
<vm>
    <initialization>
        <cloud_init>
            <host>
                <address>${s_vm_name}</address>
            </host>
            <regenerate_ssh_keys>true</regenerate_ssh_keys>
            <users>
                <user>
                    <user_name>root</user_name>
                    <password>${s_vm_password}</password>
                </user>
            </users>
            <network_configuration>
                <nics>
                    <nic>
                        <name>eth0</name>
                        <boot_protocol>DHCP</boot_protocol>
                        <on_boot>false</on_boot>
                    </nic>
                    <nic>
                        <name>eth1</name>
                        <boot_protocol>static</boot_protocol>
                        <network>
                            <ip address=\"${s_vm_ip}\" netmask=\"${s_vm_netmask}\" gateway=\"${s_vm_gateway}\" />
                        </network>
                        <on_boot>true</on_boot>
                    </nic>
                </nics>
                <dns>
                    <servers>
                        <host>
                            <address>${s_vm_dns}</address>
                        </host>
                    </servers>
                </dns>
            </network_configuration>
            <files>
                <file>
                    <name>post-init</name>
                    <content>
runcmd:
- curl http://office.test/ovirt/test.sh |bash -
                    </content>
                    <type>plaintext</type>
                </file>
            </files>
        </cloud_init>
    </initialization>
</vm>
</action>
"
    # 仅用作调试,输出 cloud-init 的 xml 文件
    local f_xml_init="${s_vm_name}.cloud-init.xml"
    [ ${DEBUG} -eq 1 ] && echo "${tpl_cloud_init}" >${f_xml_init}

    ${curl_opts} \
-H "Content-Type: application/xml" \
-u "${oe_user}:${oe_password}" \
-d "${tpl_cloud_init}" \
"${oe_url}/vms/${s_vm_id}/start" -o ${f_xml}

    check_fault ${f_xml}
    [ ${DEBUG} -eq 0 ] && rm -fv ${f_xml}
}

## 只运行一次,使用指定的模版
function vm_runonce_tpl() {
    local s_vm_name="$1"
    vm_uuid ${s_vm_name}
    local f_xml="${s_vm_name}.runonce.xml"
    local tpl_cloud_init="`cat $2`"


    ${curl_opts} \
-H "Content-Type: application/xml" \
-u "${oe_user}:${oe_password}" \
-d "${tpl_cloud_init}" \
"${oe_url}/vms/${s_vm_id}/start" -o ${f_xml}

    check_fault ${f_xml}
}

## 从模版创建 VM ,不是(Clone/Independent),而是(Thin/Dependent)
function vm_create_from_tpl() {
    local s_vm_name="$1"
    local s_tpl_name=$2
    local s_cluster_name=$3
    
    local f_xml="${s_vm_name}.create.xml"

    ${curl_opts} \
-H "Content-Type: application/xml" \
-u "${oe_user}:${oe_password}" \
-d "
<vm>
  <name>${s_vm_name}</name>
  <cluster><name>${s_cluster_name}</name></cluster>
  <template><name>${s_tpl_name}</name></template>
</vm>
" \
"${oe_url}/vms" -o ${f_xml}

    check_fault ${f_xml}
    [ ${DEBUG} -eq 0 ] && rm -fv ${f_xml}
}

## Usage
function usage() {
    echo "

usage: $0 [list|start|stop|delete|create|init|init-tpl] vm_name

    列出所有的VM:              list
    启动VM:                    start [vm_name]
    停止VM:                    stop [vm_name]
    删除VM:                    delete [vm_name]
    创建VM:                    create [vm_name template cluster]
    只运行一次:                init [vm_name root_password vm_ip vm_netmask vm_gateway vm_dns]
    只运行一次(指定模版):    init-tpl [vm_name template-file]

"
    exit 1
}

## Main
s_action=$1
s_vm_name=$2
## $3 to $7 预留给 vm_runonce_tpl

case ${s_action} in
    list)
        vm_list
        grep -E '<(name|comment|state)>' vms.xml |grep -vE '(Etc}|GMT|internal)' |sed 's/ //g' |sed -E 's/<\/(name|comment|state)>//g' |sed 's/<name>/--------\nname\t: /g' |sed 's/<//g' |sed 's/>/\t: /g'
        ;;
    start|stop)
        vm_${s_action} ${s_vm_name}
        vm_state ${s_vm_name}
        ;;
    delete)
        vm_${s_action} ${s_vm_name}
        ;;
    create)
        vm_create_from_tpl ${s_vm_name} 'tpl-m1' 'C01'
        vm_state ${s_vm_name}
        ;;
    init)
        if [ ! $# -eq 7 ]; then
            usage
        fi
        vm_runonce ${s_vm_name} $3 $4 $5 $6 $7
        vm_state ${s_vm_name}
        ;;
    init-tpl)
        if [ ! $# -eq 3 ]; then
            usage
        fi
        vm_runonce_tpl ${s_vm_name} $3
        vm_state ${s_vm_name}
        ;;
    *)
        usage
        ;;
esac
----------------------------------------------------------------------------


ZYXW、参考
1、docs
http://www.ovirt.org/REST-Api
http://www.ovirt.org/Features/Cloud-Init_Integration
https://access.redhat.com/documentation/zh-CN/Red_Hat_Enterprise_Virtualization/3.5/html-single/Technical_Guide/index.html#chap-REST_API_Quick_Start_Example