とりあえず晒しとく。
PHP5.3ならもっとPython版に近い感じで書けただろうけど、自分の所の環境にあわせてPHP5.2で。
なんで諦めたかというと、PearのXML_RPCの返り値(XML_RPC_Message)が汚すぎて使う気が起きなかった。
他にもっといいパーサがあるならそっちを使いたいけど、PearのXML_RPCじゃ無理。
この手の型だと、PHPに無理があるのかもしれないけど。
それにしてもXML_RPC_Messageをprint_rするとやたらと時間がかかる。
PHPはオブジェクト指向じゃないのと命名規則に統一性がないから、よく引数の順番とか忘れる。
strposを使おうとする度にリファレンスを見に行ってる気がする…。
XenAPI.php
<?php require_once 'XML/RPC.php'; define('_RECONNECT_AND_RETRY', '_RECONNECT_AND_RETRY'); class Session { private $_host = null; private $_port = 0; public $session = null; public $last_login_method = null; public $last_login_params = null; public function __construct($uri) { if (strpos($uri, '://')===false) { $uri = 'http://'.$uri; } $hash = parse_url($uri); $this->_host = $hash['scheme'].'://'.$hash['host']; if (isset($hash['port'])) { $this->_port = $hash['port']; } } public function xenapi_request($methodname, $params) { if (strpos($methodname, 'login')) { $this->_login($methodname, $params); return null; } else { $retry_count = 0; while ($retry_count < 3) { $full_params = array_merge(array($this->session), $params); $result = _parse_result(call_user_func_array(array($this, $methodname), $full_params)); if ($result===_RECONNECT_AND_RETRY) { $retry_count++; if ($this->last_login_method) { $this->_login($this->last_login_method, $this->last_login_params); } else { throw new Exception('You must log in', 401); } } else { return $result; } } throw new Exception('Tried 3 times to get a valid session, but failed', 500); } } public function _login($method, $params) { $result = _parse_result(call_user_func_array(array($this, 'session.'.$method), $params)); if ($result===_RECONNECT_AND_RETRY) { throw new Exception('Received SESSION_INVALID when logging in', 500); } $this->session = $result->me['string']; $this->last_login_method = $method; $this->last_login_params = $params; } public function __get($name) { if ($name=='xenapi') { return new _Dispatcher($this, null); } return null; } public function __call($name, $args) { if (strpos($name, 'login')===0) { return $this->_login($name, $args); } return $this->_serverProxy($name, $args); } private function _serverProxy($name, $args) { $msg = new XML_RPC_Message($name); foreach ($args as $item) { $msg->addParam(new XML_RPC_Value($item)); } $cli = new XML_RPC_client('/', $this->_host, $this->_port); $resp = $cli->send($msg); return $resp; } } function _parse_result($result) { if (!isset($result->xv->me['struct']['Status']->me['string'])) { throw new Exception('Missing Status in response from server', 500); } if ($result->xv->me['struct']['Status']->me['string']=='Success') { if (isset($result->xv->me['struct']['Value'])) { return $result->xv->me['struct']['Value']; } else { throw new Exception('Missing Value in response from server', 500); } } else { if (isset($result->xv->me['struct']['ErrorDescription'])) { if ($result->xv->me['struct']['ErrorDescription']->me['array'][0]->me['string']=='SESSION_INVALID') { return _RECONNECT_AND_RETRY; } else { throw new Exception($result->xv->me['struct']['ErrorDescription']->me['array'][0]->me['string']); } } else { throw new Exception('Missing ErrorDescription in response from server', 500); } } } class _Dispatcher { private $_api = null; private $_name = null; public function __construct($api, $name) { $this->_api = $api; $this->_setName($name); } public function __toString() { if ($this->_name) { return sprintf("<XenAPI._Dispatcher for %s>\n", $this->_name); } return "<XenAPI._Dispatcher>\n"; } public function __get($name) { $this->_setName($name); return new _Dispatcher($this->_api, $this->_name); } public function __call($name, $args) { $this->_setName($name); return $this->_api->xenapi_request($this->_name, $args); } private function _setName($name) { if (is_null($this->_name)) { $this->_name = $name; } else { $this->_name .= '.'.$name; } } }
使い方
これ以外試してない。
<?php require_once 'XenAPI.php'; try { $session = new Session('http://192.168.0.30/'); $session->login_with_password('root', 'pass'); $vm_list = $session->xenapi->VM->get_all_records(); print_r($vm_list); } catch(Exception $e) { echo "### Exception ###\n"; echo $e->getMessage()."\n"; }