XenAPI 2.14__tar.gz → 23.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- XenAPI-23.8.0/Makefile +12 -0
- XenAPI-23.8.0/PKG-INFO +37 -0
- {XenAPI-2.14 → XenAPI-23.8.0}/README.md +1 -1
- {XenAPI-2.14 → XenAPI-23.8.0}/XenAPI/XenAPI.py +14 -18
- XenAPI-23.8.0/XenAPI.egg-info/PKG-INFO +37 -0
- XenAPI-23.8.0/XenAPI.egg-info/SOURCES.txt +21 -0
- XenAPI-23.8.0/XenAPIPlugin.py +54 -0
- XenAPI-23.8.0/echo.py +15 -0
- XenAPI-23.8.0/exportimport.py +142 -0
- XenAPI-23.8.0/inventory.py +32 -0
- XenAPI-23.8.0/lvhd-api-test.py +29 -0
- XenAPI-23.8.0/mini-xenrt.py +142 -0
- XenAPI-23.8.0/monitor-unwanted-domains.py +89 -0
- XenAPI-23.8.0/provision.py +111 -0
- XenAPI-23.8.0/pyproject.toml +6 -0
- XenAPI-23.8.0/renameif.py +169 -0
- {XenAPI-2.14 → XenAPI-23.8.0}/setup.cfg +2 -4
- XenAPI-23.8.0/shell.py +118 -0
- XenAPI-23.8.0/xva.py +117 -0
- XenAPI-2.14/PKG-INFO +0 -38
- XenAPI-2.14/XenAPI.egg-info/PKG-INFO +0 -38
- XenAPI-2.14/XenAPI.egg-info/SOURCES.txt +0 -10
- XenAPI-2.14/XenAPI.egg-info/requires.txt +0 -1
- XenAPI-2.14/setup.py +0 -3
- {XenAPI-2.14 → XenAPI-23.8.0}/XenAPI/__init__.py +0 -0
- {XenAPI-2.14 → XenAPI-23.8.0}/XenAPI.egg-info/dependency_links.txt +0 -0
- {XenAPI-2.14 → XenAPI-23.8.0}/XenAPI.egg-info/top_level.txt +0 -0
XenAPI-23.8.0/Makefile
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
PROFILE=release
|
|
2
|
+
SETUPTOOLS_SCM_PRETEND_VERSION ?= ""
|
|
3
|
+
|
|
4
|
+
.PHONY: build clean
|
|
5
|
+
|
|
6
|
+
build:
|
|
7
|
+
SETUPTOOLS_SCM_PRETEND_VERSION=$(XAPI_VERSION) python -m build --wheel .
|
|
8
|
+
SETUPTOOLS_SCM_PRETEND_VERSION=$(XAPI_VERSION) python -m build --sdist .
|
|
9
|
+
|
|
10
|
+
clean:
|
|
11
|
+
dune clean
|
|
12
|
+
rm -rf dist/ build/ XenAPI.egg-info/
|
XenAPI-23.8.0/PKG-INFO
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: XenAPI
|
|
3
|
+
Version: 23.8.0
|
|
4
|
+
Summary: XenAPI SDK, for communication with XenServer.
|
|
5
|
+
Home-page: https://www.citrix.com/community/citrix-developer/citrix-hypervisor-developer/
|
|
6
|
+
Author: Citrix Systems, Inc.
|
|
7
|
+
Author-email: xen-api@lists.xenproject.org
|
|
8
|
+
License: BSD-2-Clause
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/xapi-project/xen-api/issues
|
|
10
|
+
Project-URL: Source Code, https://github.com/xapi-project/xen-api
|
|
11
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
12
|
+
Classifier: Development Status :: 6 - Mature
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Topic :: System :: Systems Administration
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,<4,>=2.7
|
|
17
|
+
Description-Content-Type: text/markdown; charset=UTF-8
|
|
18
|
+
|
|
19
|
+
Usage
|
|
20
|
+
=====
|
|
21
|
+
|
|
22
|
+
To install the package, enable the virtual environment where it's going to be used and run
|
|
23
|
+
`$ pip install XenAPI`
|
|
24
|
+
|
|
25
|
+
Examples
|
|
26
|
+
--------
|
|
27
|
+
|
|
28
|
+
The [examples](https://github.com/xapi-project/xen-api/tree/master/scripts/examples/python) will not work unless they have been placed in the same directory as `XenAPI.py` or `XenAPI` package from PyPI has been installed (`pip install XenAPI`)
|
|
29
|
+
|
|
30
|
+
Packaging
|
|
31
|
+
=========
|
|
32
|
+
|
|
33
|
+
`setup.py` is generated using an ocaml binary that fetches the api version string from xapi. An opam switch with the [xs-opam](https://github.com/xapi-project/xs-opam) repository is needed in order to build the binary.
|
|
34
|
+
|
|
35
|
+
To build the package `setuptools>=38.6.0` and `wheel` need to be installed in the system or in the active python virtualenv.
|
|
36
|
+
|
|
37
|
+
To build, use the command `make`.
|
|
@@ -7,7 +7,7 @@ To install the package, enable the virtual environment where it's going to be us
|
|
|
7
7
|
Examples
|
|
8
8
|
--------
|
|
9
9
|
|
|
10
|
-
The examples
|
|
10
|
+
The [examples](https://github.com/xapi-project/xen-api/tree/master/scripts/examples/python) will not work unless they have been placed in the same directory as `XenAPI.py` or `XenAPI` package from PyPI has been installed (`pip install XenAPI`)
|
|
11
11
|
|
|
12
12
|
Packaging
|
|
13
13
|
=========
|
|
@@ -55,11 +55,17 @@
|
|
|
55
55
|
# --------------------------------------------------------------------
|
|
56
56
|
|
|
57
57
|
import gettext
|
|
58
|
-
import six.moves.xmlrpc_client as xmlrpclib
|
|
59
|
-
import six.moves.http_client as httplib
|
|
60
58
|
import socket
|
|
61
59
|
import sys
|
|
62
60
|
|
|
61
|
+
if sys.version_info[0] == 2:
|
|
62
|
+
import httplib as httplib
|
|
63
|
+
import xmlrpclib as xmlrpclib
|
|
64
|
+
else:
|
|
65
|
+
import http.client as httplib
|
|
66
|
+
import xmlrpc.client as xmlrpclib
|
|
67
|
+
|
|
68
|
+
|
|
63
69
|
translation = gettext.translation('xen-xm', fallback = True)
|
|
64
70
|
|
|
65
71
|
API_VERSION_1_1 = '1.1'
|
|
@@ -92,26 +98,14 @@ class UDSHTTPConnection(httplib.HTTPConnection):
|
|
|
92
98
|
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
93
99
|
self.sock.connect(path)
|
|
94
100
|
|
|
95
|
-
class UDSHTTP(httplib.HTTPConnection):
|
|
96
|
-
_connection_class = UDSHTTPConnection
|
|
97
|
-
|
|
98
101
|
class UDSTransport(xmlrpclib.Transport):
|
|
99
|
-
def __init__(self, use_datetime=0):
|
|
100
|
-
self._use_datetime = use_datetime
|
|
101
|
-
self._extra_headers=[]
|
|
102
|
-
self._connection = (None, None)
|
|
103
102
|
def add_extra_header(self, key, value):
|
|
104
103
|
self._extra_headers += [ (key,value) ]
|
|
105
104
|
def make_connection(self, host):
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return UDSHTTPConnection(host)
|
|
111
|
-
def send_request(self, connection, handler, request_body):
|
|
112
|
-
connection.putrequest("POST", handler)
|
|
113
|
-
for key, value in self._extra_headers:
|
|
114
|
-
connection.putheader(key, value)
|
|
105
|
+
return UDSHTTPConnection(host)
|
|
106
|
+
|
|
107
|
+
def notimplemented(name, *args, **kwargs):
|
|
108
|
+
raise NotImplementedError("XMLRPC proxies do not support python magic methods", name, *args, **kwargs)
|
|
115
109
|
|
|
116
110
|
class Session(xmlrpclib.ServerProxy):
|
|
117
111
|
"""A server proxy and session manager for communicating with xapi using
|
|
@@ -215,6 +209,8 @@ class Session(xmlrpclib.ServerProxy):
|
|
|
215
209
|
return lambda *params: self._login(name, params)
|
|
216
210
|
elif name == 'logout':
|
|
217
211
|
return _Dispatcher(self.API_version, self.xenapi_request, "logout")
|
|
212
|
+
elif name.startswith('__') and name.endswith('__'):
|
|
213
|
+
return lambda *args, **kwargs: notimplemented(name, args, kwargs)
|
|
218
214
|
else:
|
|
219
215
|
return xmlrpclib.ServerProxy.__getattr__(self, name)
|
|
220
216
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: XenAPI
|
|
3
|
+
Version: 23.8.0
|
|
4
|
+
Summary: XenAPI SDK, for communication with XenServer.
|
|
5
|
+
Home-page: https://www.citrix.com/community/citrix-developer/citrix-hypervisor-developer/
|
|
6
|
+
Author: Citrix Systems, Inc.
|
|
7
|
+
Author-email: xen-api@lists.xenproject.org
|
|
8
|
+
License: BSD-2-Clause
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/xapi-project/xen-api/issues
|
|
10
|
+
Project-URL: Source Code, https://github.com/xapi-project/xen-api
|
|
11
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
12
|
+
Classifier: Development Status :: 6 - Mature
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Topic :: System :: Systems Administration
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,<4,>=2.7
|
|
17
|
+
Description-Content-Type: text/markdown; charset=UTF-8
|
|
18
|
+
|
|
19
|
+
Usage
|
|
20
|
+
=====
|
|
21
|
+
|
|
22
|
+
To install the package, enable the virtual environment where it's going to be used and run
|
|
23
|
+
`$ pip install XenAPI`
|
|
24
|
+
|
|
25
|
+
Examples
|
|
26
|
+
--------
|
|
27
|
+
|
|
28
|
+
The [examples](https://github.com/xapi-project/xen-api/tree/master/scripts/examples/python) will not work unless they have been placed in the same directory as `XenAPI.py` or `XenAPI` package from PyPI has been installed (`pip install XenAPI`)
|
|
29
|
+
|
|
30
|
+
Packaging
|
|
31
|
+
=========
|
|
32
|
+
|
|
33
|
+
`setup.py` is generated using an ocaml binary that fetches the api version string from xapi. An opam switch with the [xs-opam](https://github.com/xapi-project/xs-opam) repository is needed in order to build the binary.
|
|
34
|
+
|
|
35
|
+
To build the package `setuptools>=38.6.0` and `wheel` need to be installed in the system or in the active python virtualenv.
|
|
36
|
+
|
|
37
|
+
To build, use the command `make`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Makefile
|
|
2
|
+
README.md
|
|
3
|
+
XenAPIPlugin.py
|
|
4
|
+
echo.py
|
|
5
|
+
exportimport.py
|
|
6
|
+
inventory.py
|
|
7
|
+
lvhd-api-test.py
|
|
8
|
+
mini-xenrt.py
|
|
9
|
+
monitor-unwanted-domains.py
|
|
10
|
+
provision.py
|
|
11
|
+
pyproject.toml
|
|
12
|
+
renameif.py
|
|
13
|
+
setup.cfg
|
|
14
|
+
shell.py
|
|
15
|
+
xva.py
|
|
16
|
+
XenAPI/XenAPI.py
|
|
17
|
+
XenAPI/__init__.py
|
|
18
|
+
XenAPI.egg-info/PKG-INFO
|
|
19
|
+
XenAPI.egg-info/SOURCES.txt
|
|
20
|
+
XenAPI.egg-info/dependency_links.txt
|
|
21
|
+
XenAPI.egg-info/top_level.txt
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
|
|
3
|
+
# XenAPI python plugin boilerplate code
|
|
4
|
+
|
|
5
|
+
from __future__ import print_function
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
import XenAPI
|
|
10
|
+
|
|
11
|
+
if sys.version_info[0] == 2:
|
|
12
|
+
import xmlrpclib as xmlrpclib
|
|
13
|
+
else:
|
|
14
|
+
import xmlrpc.client as xmlrpclib
|
|
15
|
+
|
|
16
|
+
class Failure(Exception):
|
|
17
|
+
"""Provide compatibilty with plugins written against XenServer 5.5 API"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, code, params):
|
|
20
|
+
Exception.__init__(self)
|
|
21
|
+
self.params = [ code ] + params
|
|
22
|
+
def __str__(self):
|
|
23
|
+
return str(self.params)
|
|
24
|
+
|
|
25
|
+
def success_message(result):
|
|
26
|
+
rpcparams = { 'Status': 'Success', 'Value': result }
|
|
27
|
+
return xmlrpclib.dumps((rpcparams, ), '', True)
|
|
28
|
+
|
|
29
|
+
def failure_message(description):
|
|
30
|
+
rpcparams = { 'Status': 'Failure', 'ErrorDescription': description }
|
|
31
|
+
return xmlrpclib.dumps((rpcparams, ), '', True)
|
|
32
|
+
|
|
33
|
+
def dispatch(fn_table):
|
|
34
|
+
if len(sys.argv) != 2:
|
|
35
|
+
raise Exception("Incorrect number of commandline arguments")
|
|
36
|
+
params, methodname = xmlrpclib.loads(sys.argv[1])
|
|
37
|
+
session_id = params[0]
|
|
38
|
+
args = params[1]
|
|
39
|
+
if methodname in fn_table:
|
|
40
|
+
x = XenAPI.xapi_local()
|
|
41
|
+
x._session = session_id
|
|
42
|
+
try:
|
|
43
|
+
result = fn_table[methodname](x, args)
|
|
44
|
+
print(success_message(result))
|
|
45
|
+
except SystemExit:
|
|
46
|
+
# SystemExit should not be caught, as it is handled elsewhere in the plugin system.
|
|
47
|
+
raise
|
|
48
|
+
except Failure as e:
|
|
49
|
+
print(failure_message(e.params))
|
|
50
|
+
except Exception as e:
|
|
51
|
+
print(failure_message(['XENAPI_PLUGIN_FAILURE',
|
|
52
|
+
methodname, e.__class__.__name__, str(e)]))
|
|
53
|
+
else:
|
|
54
|
+
print(failure_message(['UNKNOWN_XENAPI_PLUGIN_FUNCTION', methodname]))
|
XenAPI-23.8.0/echo.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
# Simple XenAPI plugin
|
|
4
|
+
import XenAPIPlugin, time
|
|
5
|
+
|
|
6
|
+
def main(session, args):
|
|
7
|
+
if "sleep" in args:
|
|
8
|
+
secs = int(args["sleep"])
|
|
9
|
+
time.sleep(secs)
|
|
10
|
+
return "args were: %s" % (repr(args))
|
|
11
|
+
|
|
12
|
+
if __name__ == "__main__":
|
|
13
|
+
XenAPIPlugin.dispatch({"main": main})
|
|
14
|
+
|
|
15
|
+
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2014 Citrix, Inc.
|
|
4
|
+
#
|
|
5
|
+
# Permission to use, copy, modify, and distribute this software for any
|
|
6
|
+
# purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
# copyright notice and this permission notice appear in all copies.
|
|
8
|
+
#
|
|
9
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
16
|
+
|
|
17
|
+
# Demonstrate how to
|
|
18
|
+
# - export raw disk images
|
|
19
|
+
# - import raw disk images
|
|
20
|
+
# - connect an export to an import to copy a raw disk image
|
|
21
|
+
|
|
22
|
+
from __future__ import print_function
|
|
23
|
+
import sys, os, socket, urllib2, urlparse, XenAPI, traceback, ssl, time
|
|
24
|
+
|
|
25
|
+
def exportimport(url, xapi, session, src_vdi, dst_vdi):
|
|
26
|
+
# If an HTTP operation fails then it will record the error on the task
|
|
27
|
+
# object. Note you can't use the HTTP response code for this because
|
|
28
|
+
# it must be sent *before* the stream is processed.
|
|
29
|
+
import_task = xapi.xenapi.task.create("import " + dst_vdi, "")
|
|
30
|
+
export_task = xapi.xenapi.task.create("export " + src_vdi, "")
|
|
31
|
+
try:
|
|
32
|
+
# an HTTP GET of this will export a disk:
|
|
33
|
+
get_url = "/export_raw_vdi?session_id=%s&vdi=%s&task_id=%s" % (session, src_vdi, export_task)
|
|
34
|
+
# an HTTP PUT to this will import a disk:
|
|
35
|
+
put_url = "/import_raw_vdi?session_id=%s&vdi=%s&task_id=%s" % (session, dst_vdi, import_task)
|
|
36
|
+
|
|
37
|
+
# 'data' is the stream of raw data:
|
|
38
|
+
data = urllib2.urlopen(url + get_url)
|
|
39
|
+
|
|
40
|
+
# python's builtin library doesn't support HTTP PUT very well
|
|
41
|
+
# so we do it manually. Note xapi doesn't support Transfer-encoding:
|
|
42
|
+
# chunked so we must send the data raw.
|
|
43
|
+
url = urlparse.urlparse(url)
|
|
44
|
+
host = url.netloc.split(":")[0] # assume port 443
|
|
45
|
+
if url.scheme != "https":
|
|
46
|
+
print("Sorry, this example only supports HTTPS (not HTTP)", file=sys.stderr)
|
|
47
|
+
print("Plaintext HTTP has the following problems:", file=sys.stderr)
|
|
48
|
+
print(" - the data can be captured by other programs on the network", file=sys.stderr)
|
|
49
|
+
print(" - some network middleboxes will mangle the data", file=sys.stderr)
|
|
50
|
+
# time wasted debugging a problem caused by a middlebox: 3hrs
|
|
51
|
+
# Just use HTTPS!
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
55
|
+
output = ssl.wrap_socket(s)
|
|
56
|
+
output.connect((host, 443))
|
|
57
|
+
|
|
58
|
+
# HTTP/1.0 with no transfer-encoding
|
|
59
|
+
headers = [
|
|
60
|
+
"PUT %s HTTP/1.0" % put_url,
|
|
61
|
+
"Connection:close",
|
|
62
|
+
""
|
|
63
|
+
]
|
|
64
|
+
print("Sending HTTP request:")
|
|
65
|
+
for h in headers:
|
|
66
|
+
output.send("%s\r\n" % h)
|
|
67
|
+
print("%s\r\n" % h)
|
|
68
|
+
result = output.recv(1024)
|
|
69
|
+
print("Received HTTP response:")
|
|
70
|
+
print(result)
|
|
71
|
+
if "200 OK" not in result:
|
|
72
|
+
print("Expected an HTTP 200, got %s" % result, file=sys.stderr)
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
# Copy the raw bytes, signal completion by closing the socket
|
|
76
|
+
virtual_size = long(xapi.xenapi.VDI.get_virtual_size(src_vdi))
|
|
77
|
+
print("Copying %Ld bytes" % virtual_size)
|
|
78
|
+
left = virtual_size
|
|
79
|
+
while left > 0:
|
|
80
|
+
block = data.read(min(65536, left))
|
|
81
|
+
if block is None:
|
|
82
|
+
break
|
|
83
|
+
output.send(block)
|
|
84
|
+
left = left - len(block)
|
|
85
|
+
output.close()
|
|
86
|
+
|
|
87
|
+
# Wait for the tasks to complete and check whether they both
|
|
88
|
+
# succeeded. It takes a few seconds to detach the disk etc.
|
|
89
|
+
finished = False
|
|
90
|
+
while not finished:
|
|
91
|
+
import_status = xapi.xenapi.task.get_status(import_task)
|
|
92
|
+
export_status = xapi.xenapi.task.get_status(export_task)
|
|
93
|
+
finished = import_status != "pending" and export_task != "pending"
|
|
94
|
+
time.sleep(1)
|
|
95
|
+
if import_status == "success" and export_status == "success":
|
|
96
|
+
print("OK")
|
|
97
|
+
else:
|
|
98
|
+
print("FAILED")
|
|
99
|
+
if import_status != "success":
|
|
100
|
+
print("The import task failed with: ", " ".join(xapi.xenapi.task.get_error_info(import_task)))
|
|
101
|
+
if export_status != "success":
|
|
102
|
+
print("The export task failed with: ", " ".join(xapi.xenapi.task.get_error_info(export_task)))
|
|
103
|
+
|
|
104
|
+
finally:
|
|
105
|
+
# The task creator has to destroy them at the end:
|
|
106
|
+
xapi.xenapi.task.destroy(import_task)
|
|
107
|
+
xapi.xenapi.task.destroy(export_task)
|
|
108
|
+
|
|
109
|
+
if __name__ == "__main__":
|
|
110
|
+
if len(sys.argv) != 5:
|
|
111
|
+
print("Usage:")
|
|
112
|
+
print(sys.argv[0], " <url> <username> <password> <vdi-uuid>")
|
|
113
|
+
print(" -- creates a fresh VDI and streams the contents of <vdi-uuid> into it.")
|
|
114
|
+
print()
|
|
115
|
+
print("Example:")
|
|
116
|
+
print("SR=$(xe pool-list params=default-SR --minimal)")
|
|
117
|
+
print("VDI=$(xe vdi-create sr-uuid=$SR name-label=test virtual-size=128MiB type=user)")
|
|
118
|
+
print(sys.argv[0], "https://localhost password $VDI")
|
|
119
|
+
sys.exit(1)
|
|
120
|
+
url = sys.argv[1]
|
|
121
|
+
username = sys.argv[2]
|
|
122
|
+
password = sys.argv[3]
|
|
123
|
+
vdi_uuid = sys.argv[4]
|
|
124
|
+
# First acquire a valid session by logging in:
|
|
125
|
+
xapi = XenAPI.Session(url)
|
|
126
|
+
xapi.xenapi.login_with_password(username, password, '1.0', 'xen-api-scripts-exportimport.py')
|
|
127
|
+
dst_vdi = None
|
|
128
|
+
try:
|
|
129
|
+
src_vdi = xapi.xenapi.VDI.get_by_uuid(vdi_uuid)
|
|
130
|
+
sr = xapi.xenapi.VDI.get_SR(src_vdi)
|
|
131
|
+
# Create an empty VDI with the same initial parameters (e.g. size)
|
|
132
|
+
# to upload into
|
|
133
|
+
vdi_args = xapi.xenapi.VDI.get_record(src_vdi)
|
|
134
|
+
dst_vdi = xapi.xenapi.VDI.create(vdi_args)
|
|
135
|
+
exportimport(url, xapi, xapi._session, src_vdi, dst_vdi)
|
|
136
|
+
except Exception as e:
|
|
137
|
+
print("Caught %s: trying to clean up" % str(e))
|
|
138
|
+
traceback.print_exc()
|
|
139
|
+
if dst_vdi:
|
|
140
|
+
xapi.xenapi.VDI.destroy(dst_vdi)
|
|
141
|
+
finally:
|
|
142
|
+
xapi.xenapi.logout()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Simple functions to read the constants from the xensource-inventory file
|
|
2
|
+
|
|
3
|
+
INVENTORY="@INVENTORY@"
|
|
4
|
+
INSTALLATION_UUID="INSTALLATION_UUID"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def read_kvpairs(filename):
|
|
8
|
+
"""Read in a file of key-value pairs in the format used by the inventory file"""
|
|
9
|
+
f = open(filename)
|
|
10
|
+
all_entries = {}
|
|
11
|
+
try:
|
|
12
|
+
for line in f.readlines():
|
|
13
|
+
equals = line.index("=")
|
|
14
|
+
key = line[0:equals]
|
|
15
|
+
value = line[equals+1:].strip().strip("'")
|
|
16
|
+
all_entries[key] = value
|
|
17
|
+
finally:
|
|
18
|
+
f.close()
|
|
19
|
+
return all_entries
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def parse():
|
|
23
|
+
"""Return the contents of the xensource inventory file as a dictionary"""
|
|
24
|
+
try:
|
|
25
|
+
return read_kvpairs(INVENTORY)
|
|
26
|
+
except:
|
|
27
|
+
return {}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_localhost_uuid():
|
|
31
|
+
"""Return the UUID of the local host"""
|
|
32
|
+
return parse()[INSTALLATION_UUID]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
from __future__ import print_function
|
|
4
|
+
import XenAPI, sys
|
|
5
|
+
|
|
6
|
+
def go(x, name):
|
|
7
|
+
vm = x.xenapi.VM.get_by_name_label(name)[0]
|
|
8
|
+
vbds = x.xenapi.VM.get_VBDs(vm)
|
|
9
|
+
non_empty = filter(lambda y:not(x.xenapi.VBD.get_empty(y)), vbds)
|
|
10
|
+
vdis = map(lambda y:x.xenapi.VBD.get_VDI(y), non_empty)
|
|
11
|
+
|
|
12
|
+
print("Calling API call on %s" % (repr(vdis)))
|
|
13
|
+
result = x.xenapi.SR.lvhd_stop_using_these_vdis_and_call_script(vdis, "echo", "main", { "hello": "there", "sleep": "10" })
|
|
14
|
+
print(repr(result))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
if __name__ == "__main__":
|
|
18
|
+
if len(sys.argv) != 2:
|
|
19
|
+
print("Usage:", file=sys.stderr)
|
|
20
|
+
print(" %s <VM name-label>" % (sys.argv[0]), file=sys.stderr)
|
|
21
|
+
print(" -- Call SR.lvhd_stop_using_these_vdis_and_call_script with all VDIs with VBDs (attached or not) linking to specified VM", file=sys.stderr)
|
|
22
|
+
sys.exit(1)
|
|
23
|
+
name = sys.argv[1]
|
|
24
|
+
x = XenAPI.xapi_local()
|
|
25
|
+
x.xenapi.login_with_password("root", "", "1.0", "xen-api-scripts-lvhd-api-test.py")
|
|
26
|
+
try:
|
|
27
|
+
go(x, name)
|
|
28
|
+
finally:
|
|
29
|
+
x.xenapi.logout()
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
# Receive multiple VMs
|
|
4
|
+
# Issue parallel loops of: reboot, suspend/resume, migrate
|
|
5
|
+
|
|
6
|
+
from __future__ import print_function
|
|
7
|
+
import xmlrpclib
|
|
8
|
+
from threading import Thread
|
|
9
|
+
import time, sys
|
|
10
|
+
|
|
11
|
+
iso8601 = "%Y%m%dT%H:%M:%SZ"
|
|
12
|
+
|
|
13
|
+
stop_on_first_failure = True
|
|
14
|
+
stop = False
|
|
15
|
+
|
|
16
|
+
class Operation:
|
|
17
|
+
def __init__(self):
|
|
18
|
+
raise "this is supposed to be abstract, dummy"
|
|
19
|
+
def execute(self, server, session_id):
|
|
20
|
+
raise "this is supposed to be abstract, dummy"
|
|
21
|
+
|
|
22
|
+
class Reboot(Operation):
|
|
23
|
+
def __init__(self, vm):
|
|
24
|
+
self.vm = vm
|
|
25
|
+
def execute(self, server, session_id):
|
|
26
|
+
return server.VM.clean_reboot(session_id, self.vm)
|
|
27
|
+
def __str__(self):
|
|
28
|
+
return "clean_reboot(%s)" % self.vm
|
|
29
|
+
|
|
30
|
+
class SuspendResume(Operation):
|
|
31
|
+
def __init__(self, vm):
|
|
32
|
+
self.vm = vm
|
|
33
|
+
def execute(self, server, session_id):
|
|
34
|
+
x = { "ErrorDescription": [ "VM_MISSING_PV_DRIVERS" ] }
|
|
35
|
+
while "ErrorDescription" in x and x["ErrorDescription"][0] == "VM_MISSING_PV_DRIVERS":
|
|
36
|
+
x = server.VM.suspend(session_id, self.vm)
|
|
37
|
+
if "ErrorDescription" in x:
|
|
38
|
+
time.sleep(1)
|
|
39
|
+
if x["Status"] != "Success":
|
|
40
|
+
return x
|
|
41
|
+
return server.VM.resume(session_id, self.vm, False, False)
|
|
42
|
+
def __str__(self):
|
|
43
|
+
return "suspendresume(%s)" % self.vm
|
|
44
|
+
|
|
45
|
+
class ShutdownStart(Operation):
|
|
46
|
+
def __init__(self, vm):
|
|
47
|
+
self.vm = vm
|
|
48
|
+
def execute(self, server, session_id):
|
|
49
|
+
x = server.VM.clean_shutdown(session_id, self.vm)
|
|
50
|
+
if x["Status"] != "Success":
|
|
51
|
+
return x
|
|
52
|
+
return server.VM.start(session_id, self.vm, False, False)
|
|
53
|
+
#return { "Status": "bad", "ErrorDescription": "foo" }
|
|
54
|
+
def __str__(self):
|
|
55
|
+
return "shutdownstart(%s)" % self.vm
|
|
56
|
+
|
|
57
|
+
class LocalhostMigrate(Operation):
|
|
58
|
+
def __init__(self, vm):
|
|
59
|
+
self.vm = vm
|
|
60
|
+
def execute(self, server, session_id):
|
|
61
|
+
return server.VM.pool_migrate(session_id, self.vm, server.VM.get_resident_on(session_id, self.vm)["Value"], { "live": "true" } )
|
|
62
|
+
def __str__(self):
|
|
63
|
+
return "localhostmigrate(%s)" % self.vm
|
|
64
|
+
|
|
65
|
+
# Use this to give each thread a different ID
|
|
66
|
+
worker_count = 0
|
|
67
|
+
|
|
68
|
+
class Worker(Thread):
|
|
69
|
+
def __init__(self, server, session_id, operations):
|
|
70
|
+
Thread.__init__(self)
|
|
71
|
+
self.server = server
|
|
72
|
+
self.session_id = session_id
|
|
73
|
+
self.operations = operations
|
|
74
|
+
self.num_successes = 0
|
|
75
|
+
self.num_failures = 0
|
|
76
|
+
global worker_count
|
|
77
|
+
self.id = worker_count
|
|
78
|
+
worker_count = worker_count + 1
|
|
79
|
+
def run(self):
|
|
80
|
+
global iso8601
|
|
81
|
+
global stop_on_first_failure, stop
|
|
82
|
+
for op in self.operations:
|
|
83
|
+
description = str(op)
|
|
84
|
+
|
|
85
|
+
if stop:
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
start = time.strftime(iso8601, time.gmtime(time.time ()))
|
|
89
|
+
result = op.execute(self.server, self.session_id)
|
|
90
|
+
end = time.strftime(iso8601, time.gmtime(time.time ()))
|
|
91
|
+
|
|
92
|
+
if result["Status"] == "Success":
|
|
93
|
+
print("SUCCESS %d %s %s %s" % (self.id, start, end, description))
|
|
94
|
+
self.num_successes = self.num_successes + 1
|
|
95
|
+
else:
|
|
96
|
+
error_descr = result["ErrorDescription"]
|
|
97
|
+
print("FAILURE %d %s %s %s %s" % (self.id, start, end, error_descr[0], description))
|
|
98
|
+
self.num_failures = self.num_failures + 1
|
|
99
|
+
if stop_on_first_failure:
|
|
100
|
+
stop = True
|
|
101
|
+
|
|
102
|
+
def make_operation_list(vm):
|
|
103
|
+
return [ Reboot(vm), SuspendResume(vm), LocalhostMigrate(vm) ] * 100
|
|
104
|
+
|
|
105
|
+
if __name__ == "__main__":
|
|
106
|
+
if len(sys.argv) != 3:
|
|
107
|
+
print("Usage:")
|
|
108
|
+
print(" %s <URL> <other-config key>" % (sys.argv[0]))
|
|
109
|
+
print(" -- performs parallel operations on VMs with the specified other-config key")
|
|
110
|
+
sys.exit(1)
|
|
111
|
+
|
|
112
|
+
x = xmlrpclib.Server(sys.argv[1])
|
|
113
|
+
key = sys.argv[2]
|
|
114
|
+
session = x.session.login_with_password("root", "xenroot", "1.0", "xen-api-scripts-minixenrt.py")["Value"]
|
|
115
|
+
vms = x.VM.get_all_records(session)["Value"]
|
|
116
|
+
|
|
117
|
+
workers = []
|
|
118
|
+
for vm in vms.keys():
|
|
119
|
+
if key in vms[vm]["other_config"]:
|
|
120
|
+
allowed_ops = vms[vm]["allowed_operations"]
|
|
121
|
+
for op in [ "clean_reboot", "suspend", "pool_migrate" ]:
|
|
122
|
+
if op not in allowed_ops:
|
|
123
|
+
raise "VM %s is not in a state where it can %s" % (vms[vm]["name_label"], op)
|
|
124
|
+
workers.append(Worker(x, session, make_operation_list(vm)))
|
|
125
|
+
for w in workers:
|
|
126
|
+
w.start()
|
|
127
|
+
for w in workers:
|
|
128
|
+
w.join()
|
|
129
|
+
successes = 0
|
|
130
|
+
failures = 0
|
|
131
|
+
for w in workers:
|
|
132
|
+
successes = successes + w.num_successes
|
|
133
|
+
failures = failures + w.num_failures
|
|
134
|
+
print("Total successes = %d" % successes)
|
|
135
|
+
print("Total failures = %d" % failures)
|
|
136
|
+
if failures == 0:
|
|
137
|
+
print("PASS")
|
|
138
|
+
sys.exit(0)
|
|
139
|
+
else:
|
|
140
|
+
print("FAIL")
|
|
141
|
+
sys.exit(1)
|
|
142
|
+
|