python调用cloudstack api批量操作机器

前言:公司为优化资源占用和节约服务器成本,考虑自建私有云平台,最初领导有考虑用当下最火的docker完成,后来为保持稳定性和可维护性(实际情况是对docker不熟,怕出问题搞不定,而cloudstack之前略有接触),最终决定用cloudstack来搭建云平台,因为机房网络环境特殊,以及cloudstack的版本问题,前后折腾一个多星期,才把这个私有云环境搭好,对着界面撸了一阵后,发现他没有批量化操作,以及回收主机不回收磁盘等等毛病,于是想利用调官方提供的api来对它进行批量化操作,于是并有了这篇文章。

官方api地址:http://cloudstack.apache.org/docs/api/apidocs-4.2/TOC_Root_Admin.html

api脚本

cloud_api.py

#coding:utf-8

import urllib, urllib2
import hashlib
import hmac
import base64
import json
import sys
import argparse
from time import sleep

# 设置全局代理
#import socket, socks
#socks.set_default_proxy(socks.SOCKS5, "127.0.0.1", 1090)
#socket.socket = socks.socksocket

# 设置默认字符编码
reload(sys)
sys.setdefaultencoding('utf-8')

class CloudAPI():

    def __init__(self):
        # 配置 cloudstack api
        self.config = {
            'api_url': 'http://10.10.252.116:8080/client/api?',
            'api_response_type': 'json',
            #key
            'api_key': 'FkZxPdLWvMVYJBZW82NZvXWPwG7NSE0f8i6OgqS2XYUt-cd4uKZbfaAC94oDa9lf6HqbCvw2u-hYTBVCvZCDjg',
            'api_secretkey': 'l2WyVLtjVEv1Eb8jYWW9GF6t1U-xciwQagQUwkDEa77CtNmn7hvI_CMdouCsX5VBXXiWnQjZ3C9zUORU2ijUVA',

            'zoneid': '6f30bf67-7342-4025-977f-0e989a772e25'  # weimob_sub11
        }

    def apiresult(self,params):
        params['apikey'] = self.config['api_key']
        params['response'] = self.config['api_response_type']

        request_str = '&'.join(['='.join([k, urllib.quote_plus(params[k])]) for k in params.keys()])
        sig_str = '&'.join(['='.join([k.lower(), urllib.quote_plus(params[k].lower().replace('+', '%20'))]) for k in
                            sorted(params.iterkeys())])
        signature = urllib.quote_plus(
            base64.encodestring(hmac.new(self.config['api_secretkey'], sig_str, hashlib.sha1).digest()).strip())

        request_url = self.config['api_url'] + request_str + '&signature=' + signature
        result = urllib2.urlopen(request_url)
        response = json.loads(result.read())
        return response

    # 列出虚拟机实例
    def listVirtualMachines(self,hostname=''):
        print "查询主机信息"
        params = {
            'command':'listVirtualMachines',
            'name':hostname
        }

        response = self.apiresult(params)

        instances = []
        if not response['listvirtualmachinesresponse']:
            print "VirtualMachine is \033[31mnot exist\033[0m!"
            return False
        elif hostname:
            for instance in response['listvirtualmachinesresponse']['virtualmachine']:
                try:
                    if hostname == instance['name']:
                        instance_info = [instance['id'],instance['name'],instance['nic'][0]['ipaddress'],instance['hostname'],instance['templatename'],instance['serviceofferingname'],instance['state']]
                        print "HostName:\033[32m%s\033[0m HostIp:\033[32m%s\033[0m PhysicalHost:\033[32m%s\033[0m Template:\033[32m%s\033[0m Spec:\033[32m%s\033[0m Status:\033[32m%s\033[0m" \
                                % (instance_info[1],instance_info[2],instance_info[3],instance_info[4],instance_info[5],instance_info[6])
                        return [instance['id'],instance['state']]
                except KeyError as e:
                    print "\033[35m%s\033[0m status is \033[31merror\033[0m !" % instance['name']
                    return [instance['id'],instance['state']]
        else:
            for instance in response['listvirtualmachinesresponse']['virtualmachine']:
                try:
                    instance_info = [instance['id'],instance['name'],instance['nic'][0]['ipaddress'],instance['hostname'],instance['templatename'],instance['serviceofferingname'],instance['state']]
                    print "HostName:\033[32m%s\033[0m HostIp:\033[32m%s\033[0m PhysicalHost:\033[32m%s\033[0m Template:\033[32m%s\033[0m Spec:\033[32m%s\033[0m Status:\033[32m%s\033[0m" \
                                % (instance_info[1],instance_info[2],instance_info[3],instance_info[4],instance_info[5],instance_info[6])
                    instances.append(instance_info)
                except KeyError as e:
                    print "\033[35m%s status is error\033[0m !" % instance['name']
                    return [instance['id'],instance['state']]
            print "统计: %s" % len(instances)
            return instances

        return response

    # 列出模板
    def listTemplates(self,templatename=''):
        params = {
            'command':'listTemplates',
            'templatefilter':'all'
        }

        response = self.apiresult(params)

        instances = []
        if not response['listtemplatesresponse']:
            print "Template is not exist!"
            return False
        elif templatename:
            for instance in response['listtemplatesresponse']['template']:
                if templatename == instance['name']:
                    instance_info = [instance['id'], instance['name']]
                    print "TemplateId:\033[32m%s\033[0m TemplateName:\033[32m%s\033[0m" % (instance_info[0],instance_info[1])
                    return instance['id']
        else:
            for instance in response['listtemplatesresponse']['template']:
                instance_info = [instance['id'],instance['name']]
                print "TemplateId:\033[32m%s\033[0m TemplateName:\033[32m%s\033[0m" % (instance_info[0],instance_info[1])
                instances.append(instance_info)
            print "统计: %s" % len(instances)
            return instances

        return response

    # 列出计算方案
    def listServiceOfferings(self,serviceoffername=''):
        params = {
            'command':'listServiceOfferings'
        }

        response = self.apiresult(params)

        instances = []
        if not response['listserviceofferingsresponse']:
            print "ServiceOffer is not exist!"
            return False
        elif serviceoffername:
            for instance in response['listserviceofferingsresponse']['serviceoffering']:
                if serviceoffername == instance['name']:
                    instance_info = [instance['id'], instance['name'], instance['storagetype']]
                    print "ServiceOfferId:\033[32m%s\033[0m ServiceOfferName:\033[32m%s\033[0m StorageType:\033[32m%s\033[0m" % (instance_info[0],instance_info[1],instance_info[2])
                    return instance['id']
        else:
            for instance in response['listserviceofferingsresponse']['serviceoffering']:
                instance_info = [instance['id'], instance['name'], instance['storagetype']]
                print "ServiceOfferId:\033[32m%s\033[0m ServiceOfferName:\033[32m%s\033[0m StorageType:\033[32m%s\033[0m" % (instance_info[0],instance_info[1],instance_info[2])
                instances.append(instance_info)
            print "统计: %s" % len(instances)
            return instances

        return response

    # 列出磁盘方案
    def listDiskOfferings(self,diskoffername=''):
        params = {
            'command':'listDiskOfferings'
        }

        response = self.apiresult(params)

        instances = []
        if not response['listdiskofferingsresponse']:
            print "DiskOffer is not exist!"
            return False
        elif diskoffername:
            for instance in response['listdiskofferingsresponse']['diskoffering']:
                if diskoffername == instance['name']:
                    instance_info = [instance['id'], instance['name'], instance['disksize'], instance['storagetype']]
                    print "DiskOfferId:\033[32m%s\033[0m DiskOfferName:\033[32m%s\033[0m DiskSize:\033[32m%s\033[0m StorageType:\033[32m%s\033[0m" % (instance_info[0],instance_info[1],instance_info[2],instance_info[3])
                    return instance['id']
        else:
            for instance in response['listdiskofferingsresponse']['diskoffering']:
                instance_info = [instance['id'], instance['name'], instance['disksize'], instance['storagetype']]
                print "DiskOfferId:\033[32m%s\033[0m DiskOfferName:\033[32m%s\033[0m DiskSize:\033[32m%s\033[0m StorageType:\033[32m%s\033[0m" % (instance_info[0],instance_info[1],instance_info[2],instance_info[3])
                instances.append(instance_info)
            print "统计: %s" % len(instances)
            return instances

        return response

    # 列出数据磁盘
    def listVolumes(self, hostname=''):
        params = {
            'command':'listVolumes'
        }

        response = self.apiresult(params)

        instances = []
        if not response['listvolumesresponse']:
            print "listVolumes is not exist!"
            return False
        elif hostname:
            for instance in response['listvolumesresponse']['volume']:
                try:
                    if instance['type'] == 'DATADISK' and hostname == instance['vmname']:
                        instance_info = [instance['id'], instance['name'], instance['type'], instance['diskofferingdisplaytext'], instance['vmstate'], instance['vmname']]
                        print "VolumeId:\033[32m%s\033[0m VolumeName:\033[32m%s\033[0m VolumeType:\033[32m%s\033[0m VolumeSize:\033[32m%s\033[0m VolumeStatus:\033[32m%s\033[0m VolumeHost:\033[32m%s\033[0m" % (instance_info[0],instance_info[1],instance_info[2],instance_info[3], instance_info[4], instance_info[5])
                        return [instance['id'], instance['name']]
                except KeyError as e:
                    print "\033[35m%s status is error\033[0m !" % instance['name']
                    return response['listvolumesresponse']['volume']
        else:
            for instance in response['listvolumesresponse']['volume']:
                try:
                    if instance['type'] == 'DATADISK':
                        instance_info = [instance['id'], instance['name'], instance['type'], instance['diskofferingdisplaytext'], instance['vmstate'], instance['vmname']]
                        print "VolumeId:\033[32m%s\033[0m VolumeName:\033[32m%s\033[0m VolumeType:\033[32m%s\033[0m VolumeSize:\033[32m%s\033[0m VolumeStatus:\033[32m%s\033[0m VolumeHost:\033[32m%s\033[0m" % (instance_info[0],instance_info[1],instance_info[2],instance_info[3], instance_info[4], instance_info[5])
                        instances.append(instance_info)
                except KeyError as e:
                    print "\033[35m%s status is error\033[0m !" % instance['name']
                    return response['listvolumesresponse']['volume']
            print "统计: %s" % len(instances)
            return instances

        return response

    # 部署虚拟机实例
    def deployVirtualMachine(self, hostname='',templatename='',serviceoffername='',diskoffername=''):
        if self.listVirtualMachines(hostname=hostname) or not (hostname and serviceoffername and templatename and diskoffername):
            print "you need four args,or the hostname is exist!"
            sys.exit()

        serviceofferingid = self.listServiceOfferings(serviceoffername=serviceoffername)
        templateid = self.listTemplates(templatename=templatename)
        diskofferingid = self.listDiskOfferings(diskoffername=diskoffername)

        params = {
            'command': 'deployVirtualMachine',
            'zoneid': self.config['zoneid'],
            'name':hostname,
            'displayname':hostname,
            'serviceofferingid': serviceofferingid,
            'templateid':templateid,
            'diskofferingid':diskofferingid
        }

        response = self.apiresult(params)
        print "The \033[32m%s\033[0 mis will be \033[32mDeploy\033[0m...!" % hostname
        return response

    # 操作虚拟机实例,包括启动、停止、重启、销毁、重置密码
    def actionVirtualMachine(self, hostname='', action=''):
        status = {
            'start':'Running',
            'stop':'Stopped',
            'reboot':'',
            'destroy':''
        }

        hostinfo = self.listVirtualMachines(hostname=hostname)
        if not (hostname and action and hostinfo):
            print "You need \033[36mtwo args\033[0m!"
            sys.exit()

        if hostinfo[1] == status[action]:
            print "The \033[36m%s\033[0m Status is \033[33m%s\033[0m !" % (hostname,hostinfo[1])
            sys.exit()

        params = {
            'command': action + 'VirtualMachine',
            'id': hostinfo[0]
        }

        response = self.apiresult(params)
        sleep(3)

        # 回收机器时,删除挂载数据盘, 这个有点问题,因为机器删掉时还没关机,直接删磁盘会报错, 暂时注释掉
        # 我想的是先把挂载的磁盘name写入到一个文件,之后在根据这个文件删除数据磁盘
        # if action == 'destroy':
        #     volumeid = self.listVolumes(hostname=hostname)[0]
        #     params = {
        #         'command': 'deleteVolume',
        #         'id': volumeid
        #     }
        #     response = self.apiresult(params)

        print "the %s now is \033[32m%s\033[0m , it will be \033[31m%s\033[0m...!" % (hostname,hostinfo[1],action)
        return response

if __name__ == '__main__':
    cloudapi=CloudAPI()
    parser=argparse.ArgumentParser(description='cloudstack api ',usage='%(prog)s [options]')
    parser.add_argument('-l','--host',nargs='?',dest='listhost',default='host',help='查询主机')
    parser.add_argument('-s','--serviceoffer',nargs='?',dest='serviceoffer',default='serviceoffer',help='查询计算方案')
    parser.add_argument('-d','--diskoffer',nargs='?',dest='diskoffer',default='diskoffer',help='查询磁盘方案')
    parser.add_argument('-t','--template',nargs='?',dest='template',default='template',help='查询模板信息')
    parser.add_argument('-vd', '--volume', nargs='?', dest='volume', default='volume', help='查询挂载的数据磁盘')
    parser.add_argument('-A','--add-host',dest='addhost',nargs=4,metavar=('sh-aa-01','centos_6.5','S1-2c-2g-local','disk_10G'),help='部署主机,填写主机名、模板、计算方案、磁盘方案')
    parser.add_argument('-S','--start-host',dest='starthost',nargs=1,metavar=('sh-aa-01'),help='启动主机,填写主机名')
    parser.add_argument('-P','--stop-host',dest='stophost',nargs=1,metavar=('sh-aa-01'),help='停掉主机,填写主机名')
    parser.add_argument('-R','--reboot-host',dest='reboothost',nargs=1,metavar=('sh-aa-01'),help='重启主机,填写主机名')
    parser.add_argument('-D','--destroy-host',dest='destroyhost',nargs=1,metavar=('sh-aa-01'),help='销毁主机,填写主机名')
    parser.add_argument('-v','--version', action='version', version='%(prog)s 1.0')

    if len(sys.argv) == 1:    
        #html = parser.print_help()
        html = cloudapi.listVolumes(hostname='sh-ops-cloud-test-online-01')
        #html = cloudapi.listVirtualMachines(hostname='sh-ops-cloud-test-online-01')
        #html = cloudapi.deployVirtualMachine(hostname='sh-ops-cloud-test-online-03',serviceoffername='S1-2c-2g-local',templatename='centos_6.5',diskoffername='disk_10G')
        print html
    else:
        args = parser.parse_args()
        if args.listhost != 'host':
            if args.listhost:
                cloudapi.listVirtualMachines(args.listhost)
            else:
                cloudapi.listVirtualMachines()
        if args.serviceoffer != 'serviceoffer':
            if args.serviceoffer:
                cloudapi.listServiceOfferings(args.serviceoffer)
            else:
                cloudapi.listServiceOfferings()
        if args.diskoffer != 'diskoffer':
            if args.diskoffer:
                cloudapi.listDiskOfferings(args.diskoffer)
            else:
                cloudapi.listDiskOfferings()
        if args.template != 'template':
            if args.template:
                cloudapi.listTemplates(args.template)
            else:
                cloudapi.listTemplates()
        if args.volume != 'volume':
            if args.volume:
                cloudapi.listVolumes(args.volume)
            else:
                cloudapi.listVolumes()
        if args.addhost:
            cloudapi.deployVirtualMachine(args.addhost[0], args.addhost[1], args.addhost[2], args.addhost[3])
        if args.starthost:
            cloudapi.actionVirtualMachine(args.starthost[0],'start')
        if args.stophost:
            cloudapi.actionVirtualMachine(args.stophost[0],'stop')
        if args.reboothost:
            cloudapi.actionVirtualMachine(args.reboothost[0],'reboot')
        if args.destroyhost:
            cloudapi.actionVirtualMachine(args.destroyhost[0],'destroy')

查看执行效果

python cloud_api.py -l
查询
python cloud_api.py -h
help

对主机进行操作

python cloud_api.py -S sh-ops-cloud-test-online-02
action

批量执行脚本

cloud_cli.py

# -*- coding: utf-8 -*-

import os
import sys
import argparse

from cloud_api import CloudAPI

reload(sys)
sys.setdefaultencoding('utf-8')

host_file = 'target'
s_template = 'centos-6.5-templete'   # 这里是默认系统模板
s_diskoffer = ''    # 这里是默认规格磁盘
cmd = 'python cloud_api.py'


def deploy_hosts():
    # 实例化cloud_api
    cloudstack = CloudAPI()
    cloudstack.listServiceOfferings()

    s_serviceoffer = raw_input("Please Input ServiceOffer Name: ")
    # s_diskoffer = raw_input("Please Input DiskOffer Name: ")

    with open(host_file) as fb:
        host_info = list(line.strip() for line in fb)

    for s_hostname in host_info:
        cmd1 = ' '.join([cmd, sys.argv[1], s_hostname, s_template, s_serviceoffer])
        #cmd1 = ' '.join([cmd, sys.argv[1], s_hostname, s_template, s_serviceoffer, s_diskoffer])
        os.system(cmd1)

def action_hosts():
    with open(host_file) as fb:
        host_info = list(line.strip() for line in fb)
    for s_hostname in host_info:
        cmd1 = ' '.join([cmd, sys.argv[1], s_hostname])
        os.system(cmd1)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='cloudstack api ', usage='%(prog)s [options]')
    parser.add_argument('-l', '--listhost', nargs='?', dest='listhost', default='listhost', help='批量查询主机')
    parser.add_argument('-A', '--addhost', nargs='?', dest='addhost', default='addhost', help='批量部署主机')
    parser.add_argument('-S', '--starthost', nargs='?', dest='starthost', default='starthost', help='批量启动主机')
    parser.add_argument('-P', '--stophost', nargs='?', dest='stophost', default='stophost', help='批量关掉主机')
    parser.add_argument('-R', '--reboothost', nargs='?', dest='reboothost', default='reboothost', help='批量重启主机')
    parser.add_argument('-D', '--destroyhost', nargs='?', dest='destroyhost', default='destroyhost', help='批量销毁主机')

    if len(sys.argv) == 1:
        html = parser.print_help()
        print html
    elif sys.argv[1] == '-A':
        deploy_hosts()
    else:
        action_hosts()

查看执行效果

python cloud_cli.py -h
help

批量部署主机

填写target文件(被操作主机名)

> cat target
sh-ops-cloud-test-online-07
sh-ops-cloud-test-online-08

执行

 python cloud_cli.py -A
addhosts

批量查看主机

python cloud_cli.py -l
listhosts

批量启动主机

python cloud_cli.py -S
starthosts
  • 好了,关于cloudstack的api操作到此为止,有其它需求的朋友可参考官方api介绍自行修改脚本。
  • 近日在github上搜到一个写的很全的cloudapi项目,cloudstack-python-client,这个写的非常全,之后会以它为模板写。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • Docker — 云时代的程序分发方式 要说最近一年云计算业界有什么大事件?Google Compute Engi...
    ahohoho阅读 15,505评论 15 147
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,391评论 25 707
  • 女人无所谓正派,正派是以为受到的引诱不够;男人无所谓忠诚,忠诚是因为背叛 的筹码太低。 最近搞笑的贺岁片特别多!但...
    叔夜君阅读 506评论 0 1
  • 《滚滚红尘》,三毛的遗世之作。背景是二十世纪四十年代动荡不安的中国,更是一部让人太多想象的影片。故事写的是张爱玲和...
    青衫素履阅读 564评论 0 2