2014年6月11日水曜日

Python3 からコマンド (func_caller.py) 経由で Python2.7 の libvirt を使って KVM 操作

Python3 から XML-RPC 経由で Python2.7 の libvirt を使って KVM 操作 」では XML-RPC サーバーを起動しておく必要があり、管理が必要でした。

今回は「 Python3 から Python2.7 モジュールの関数をコマンド (func_caller.py) 経由で呼ぶ 」で紹介した方法を利用して Python3 からコマンド (func_caller.py) 経由で Python2.7 の libvirt を使って KVM 操作をします。この方法だと XML-RPC サーバーを起動しておく必要はありません。

概要

イメージです。

http://1.bp.blogspot.com/-m2QYvqoII54/U5fQS8Rwb9I/AAAAAAAAADk/GWCXbY2XxpI/s1600/Outline.png

Python2.7 の libvirt モジュールでは open() 関数で作成したオブジェクトのメソッドを呼んで GuestOS を操作するので、func_caller.py から libvirt モジュールを直接扱えません。

なので、func_caller.py からは「 Python3 から XML-RPC 経由で Python2.7 の libvirt を使って KVM 操作 」で使った mLibVirtConn モジュールを呼びます。

環境

Python2.7 側の準備

パッケージインストール

$ sudo apt-get install python-libvirt
以下の内容の mLibVirtConn.py を適当なディレクトリに設置
#!/usr/bin/env python2.7
# coding: utf-8

'''
        mLibVirtConn.py
        ~~~~~~~~~~~~~~~~~~~~~~~

        :copyright: Copyright 2014 by Fishrimper.
        :license: BSD
'''

from __future__ import unicode_literals
import  libvirt

# Class for XML-RPC instance.
class LibVirtConn:
        def __init__(self):
                self.__oConn = libvirt.open('qemu:///system')

        def __get_oDomain(self, xNameOrID):
                if isinstance(xNameOrID, unicode):
                        return self.__oConn.lookupByName(xNameOrID)
                else:
                        return self.__oConn.lookupByID(xNameOrID)

        def listDomainsID(self):
                return self.__oConn.listDomainsID()

        def listDefinedDomains(self):
                return self.__oConn.listDefinedDomains()

        def domain_name(self, xNameOrID):
                return self.__get_oDomain(xNameOrID).name()

        def domain_create(self, sName):
                return self.__oConn.lookupByName(sName).create()

        def domain_destroy(self, xNameOrID):
                return self.__get_oDomain(xNameOrID).destroy()

Python3 から XML-RPC 経由で Python2.7 の libvirt を使って KVM 操作 」の mLibVirtConn.py は unicode 文字列で処理するべき箇所をバイト文字列で処理してしまっていました。

後述の func_caller.py から mLibVirtConn モジュールをインポートして使う場合に、ちゃんと unicode で処理していないと都合が悪いので mLibVirtConn.py のそのあたりを修正してます。

以下の内容の func_caller.py を mLibVirtConn.py と同じディレクトリに設置 (mLibVirtConn へのモジュールパスが通っていれば別ディレクトリでも良いです。)
#!/usr/bin/env python2.7
# coding: utf-8

'''
        func_caller.py
        ~~~~~~~~~~~~~~~~~~~~~~~

        :copyright: Copyright 2014 by Fishrimper.
        :license: BSD
'''

from __future__ import unicode_literals
import argparse
import importlib
import inspect
import types
import json

from pprint import pprint

def callAndGetResult(ClassOrFunc, sArgsInJson):
        if sArgsInJson == None:
                return ClassOrFunc()
        else:
                oArgs = json.loads(sArgsInJson)
                if isinstance(oArgs, list):
                        return ClassOrFunc(*oArgs)
                elif isinstance(oArgs, dict):
                        return ClassOrFunc(**oArgs)
                else:
                        raise Exception(oArgs)

def main():
        oParser = argparse.ArgumentParser()
        oParser.add_argument('module_name')
        oParser.add_argument('--class_name')
        oParser.add_argument('--class_init_args', help='class init args in json format.')
        oParser.add_argument('func_name')
        oParser.add_argument('--func_args', help='function args in json format.')
        oArgs = oParser.parse_args()

        mModule = importlib.import_module(oArgs.module_name)

        if oArgs.class_name == None:
                oFunc = getattr(mModule, oArgs.func_name)
        else:
                cClass = getattr(mModule, oArgs.class_name)
                assert inspect.isclass(cClass), oArgs

                oInstance = callAndGetResult(cClass, oArgs.class_init_args)
                oFunc = getattr(oInstance, oArgs.func_name)

        ltypeFuncs = [
                types.TypeType,
                types.BuiltinFunctionType,
                types.FunctionType,
                types.MethodType,
        ]
        for typeFunc in ltypeFuncs:
                if isinstance(oFunc, typeFunc):
                        break
        else:
                raise Exception(oArgs, type(oFunc))

        oResult = callAndGetResult(oFunc, oArgs.func_args)
        print(json.dumps(oResult))

if __name__ == '__main__':
        main()

mLibVirtConn.py と同様に「 Python3 から Python2.7 モジュールの関数をコマンド (func_caller.py) 経由で呼ぶ 」の func_caller.py から文字列の処理のあたりを修正しています。

func_caller.py に実行権限もつけときます。

$ chmod +x func_caller.py

試しに func_caller.py 動かしてみます。

$ ./func_caller.py mLibVirtConn --class_name LibVirtConn listDomainsID
[1]

起動しているゲスト OS は ID 1 のみでした。

virsh コマンドで確認できる結果とちゃんと一致します。

$ sudo virsh list
 Id Name                 State
----------------------------------
  1 KvmGuest1            running

Python3 側

mPy27_func_caller.py 設置

Python3 から Python2.7 モジュールの関数をコマンド (func_caller.py) 経由で呼ぶ 」 の mPy27_func_caller.py を func_caller.py と同じディレクトリに設置します。

mPy27_func_caller.py を設置したディレクトリに移動

Python3 を起動して Py27_func_caller のインスタンスを作成
$ python3
Python 3.2.3 (default, Feb 27 2014, 21:31:18)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> from mPy27_func_caller import Py27_func_caller
>>> oPy27 = Py27_func_caller('./func_caller.py')

Py27_func_caller() の引数で func_caller.py のパスを指定します。

起動している KVM ゲスト OS の ID リスト取得
>>> oPy27.call(sModuleName='mLibVirtConn', sClassName='LibVirtConn', sFuncName='listDomainsID')
[1]

起動している KVM ゲスト OS は 1個だけで、ID は 1 でした。

指定した ID の KVM ゲスト OS の名前を取得
>>> oPy27.call(sModuleName='mLibVirtConn', sClassName='LibVirtConn', sFuncName='domain_name', xFuncArgs={'xNameOrID': 1})
'KvmGuest1'

ID 1 の KVM ゲスト OS の名前は KvmGuest1 でした。

停止している KVM ゲスト OS の名前リスト取得
>>> oPy27.call(sModuleName='mLibVirtConn', sClassName='LibVirtConn', sFuncName='listDefinedDomains')
['TestDesktop', 'KvmGuest3', 'KvmGuest2']

停止している KVM ゲスト OS は TestDesktop, KvmGuest3, KvmGuest2 でした。

KVM ゲスト OS を起動
>>> oPy27.call(sModuleName='mLibVirtConn', sClassName='LibVirtConn', sFuncName='domain_create', xFuncArgs={'sName': 'TestDesktop'})
0

KVM ゲスト OS の TestDesktop を起動しました。

KVM ゲスト OS を強制停止
>>> oPy27.call(sModuleName='mLibVirtConn', sClassName='LibVirtConn', sFuncName='domain_destroy', xFuncArgs={'xNameOrID': 'TestDesktop'})
0

KVM ゲスト OS の TestDesktop を強制停止させました。(通常は shutdown コマンドで停止させて下さい)


0 件のコメント:

コメントを投稿