mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
bitkeeper revision 1.1026.1.8 (40e1b09foCFBM0EuIgrSA1uLJrWuzA)
Restructuring the python code and libs - first stage.
This commit is contained in:
1 parent
4db27a4b7a
commit
48e9a9c636
136 files changed
+367
-651
No files matched your search
@@ -0,0 +1,15 @@
|
||||
|
||||
all:
|
||||
python setup.py build
|
||||
|
||||
install: all
|
||||
if [ "$(prefix)" = "" ]; then \
|
||||
python setup.py install; \
|
||||
elif [ "$(dist)" = "yes" ]; then \
|
||||
python setup.py install --home="$(prefix)"; \
|
||||
else \
|
||||
python setup.py install --root="$(prefix)"; \
|
||||
fi
|
||||
|
||||
clean:
|
||||
rm -rf build *.pyc *.pyo *.o *.a *~
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
from distutils.core import setup, Extension
|
||||
|
||||
XEN_ROOT = "../.."
|
||||
|
||||
extra_compile_args = ["-fno-strict-aliasing"]
|
||||
|
||||
|
||||
include_dirs = [ XEN_ROOT + "/xen/include/hypervisor-ifs",
|
||||
XEN_ROOT + "/linux-xen-sparse/include",
|
||||
XEN_ROOT + "/tools/python/xen/ext/xu",
|
||||
XEN_ROOT + "/tools/libxc",
|
||||
XEN_ROOT + "/tools/libxutil",
|
||||
]
|
||||
|
||||
library_dirs = [ XEN_ROOT + "/tools/libxc",
|
||||
XEN_ROOT + "/tools/libxutil",
|
||||
]
|
||||
|
||||
libraries = [ "xc", "xutil" ]
|
||||
|
||||
xc = Extension("xc",
|
||||
extra_compile_args = extra_compile_args,
|
||||
include_dirs = include_dirs + [ "xen/ext/xc" ],
|
||||
library_dirs = library_dirs,
|
||||
libraries = libraries,
|
||||
sources = [ "xen/ext/xc/xc.c" ])
|
||||
|
||||
xu = Extension("xu",
|
||||
extra_compile_args = extra_compile_args,
|
||||
include_dirs = include_dirs + [ "xen/ext/xu" ],
|
||||
library_dirs = library_dirs,
|
||||
libraries = libraries,
|
||||
sources = [ "xen/ext/xu/xu.c" ])
|
||||
|
||||
setup(name = 'xen',
|
||||
version = '2.0',
|
||||
description = 'Xen',
|
||||
packages = ['xen',
|
||||
'xen.ext',
|
||||
'xen.util',
|
||||
'xen.xend',
|
||||
'xen.xend.server',
|
||||
'xen.xm',
|
||||
],
|
||||
ext_package = "xen.ext",
|
||||
ext_modules = [ xc, xu ]
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
all:
|
||||
python setup.py build
|
||||
|
||||
install: all
|
||||
if [ "$(prefix)" = "" ]; then \
|
||||
python setup.py install; \
|
||||
elif [ "$(dist)" = "yes" ]; then \
|
||||
python setup.py install --home="$(prefix)"; \
|
||||
else \
|
||||
python setup.py install --root="$(prefix)"; \
|
||||
fi
|
||||
|
||||
clean:
|
||||
rm -rf build *.pyc *.pyo *.o *.a *~
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
from distutils.core import setup, Extension
|
||||
|
||||
module = Extension("xc",
|
||||
extra_compile_args = ["-fno-strict-aliasing"],
|
||||
include_dirs = ["../lib",
|
||||
"../../../xen/include/hypervisor-ifs",
|
||||
"../../../linux-xen-sparse/include",
|
||||
"../../xu/lib",
|
||||
"../../lib" ],
|
||||
library_dirs = ["../lib",
|
||||
"../../lib" ],
|
||||
libraries = ["xc"],
|
||||
sources = ["Xc.c"])
|
||||
|
||||
setup(name = "xc",
|
||||
version = "2.0",
|
||||
ext_package = "xen.ext",
|
||||
ext_modules = [module])
|
||||
File diff suppressed because it is too large.
Load diff
Whitespace-only changes.
@@ -0,0 +1,532 @@
|
||||
/******************************************************************************
|
||||
* domain_controller.h
|
||||
*
|
||||
* Interface to server controller (e.g., 'xend'). This header file defines the
|
||||
* interface that is shared with guest OSes.
|
||||
*
|
||||
* Copyright (c) 2004, K A Fraser
|
||||
*/
|
||||
|
||||
#ifndef __DOMAIN_CONTROLLER_H__
|
||||
#define __DOMAIN_CONTROLLER_H__
|
||||
|
||||
|
||||
#ifndef BASIC_START_INFO
|
||||
#error "Xen header file hypervisor-if.h must already be included here."
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* EXTENDED BOOTSTRAP STRUCTURE FOR NEW DOMAINS.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
BASIC_START_INFO;
|
||||
u16 domain_controller_evtchn; /* 320 */
|
||||
} PACKED extended_start_info_t; /* 322 bytes */
|
||||
#define SIF_BLK_BE_DOMAIN (1<<4) /* Is this a block backend domain? */
|
||||
#define SIF_NET_BE_DOMAIN (1<<5) /* Is this a net backend domain? */
|
||||
|
||||
|
||||
/*
|
||||
* Reason codes for SCHEDOP_shutdown. These are opaque to Xen but may be
|
||||
* interpreted by control software to determine the appropriate action. These
|
||||
* are only really advisories: the controller can actually do as it likes.
|
||||
*/
|
||||
#define SHUTDOWN_poweroff 0 /* Domain exited normally. Clean up and kill. */
|
||||
#define SHUTDOWN_reboot 1 /* Clean up, kill, and then restart. */
|
||||
#define SHUTDOWN_suspend 2 /* Clean up, save suspend info, kill. */
|
||||
|
||||
|
||||
/*
|
||||
* CONTROLLER MESSAGING INTERFACE.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
u8 type; /* 0: echoed in response */
|
||||
u8 subtype; /* 1: echoed in response */
|
||||
u8 id; /* 2: echoed in response */
|
||||
u8 length; /* 3: number of bytes in 'msg' */
|
||||
u8 msg[60]; /* 4: type-specific message data */
|
||||
} PACKED control_msg_t; /* 64 bytes */
|
||||
|
||||
#define CONTROL_RING_SIZE 8
|
||||
typedef u32 CONTROL_RING_IDX;
|
||||
#define MASK_CONTROL_IDX(_i) ((_i)&(CONTROL_RING_SIZE-1))
|
||||
|
||||
typedef struct {
|
||||
control_msg_t tx_ring[CONTROL_RING_SIZE]; /* 0: guest -> controller */
|
||||
control_msg_t rx_ring[CONTROL_RING_SIZE]; /* 512: controller -> guest */
|
||||
CONTROL_RING_IDX tx_req_prod, tx_resp_prod; /* 1024, 1028 */
|
||||
CONTROL_RING_IDX rx_req_prod, rx_resp_prod; /* 1032, 1036 */
|
||||
} PACKED control_if_t; /* 1040 bytes */
|
||||
|
||||
/*
|
||||
* Top-level command types.
|
||||
*/
|
||||
#define CMSG_CONSOLE 0 /* Console */
|
||||
#define CMSG_BLKIF_BE 1 /* Block-device backend */
|
||||
#define CMSG_BLKIF_FE 2 /* Block-device frontend */
|
||||
#define CMSG_NETIF_BE 3 /* Network-device backend */
|
||||
#define CMSG_NETIF_FE 4 /* Network-device frontend */
|
||||
#define CMSG_SHUTDOWN 6 /* Shutdown messages */
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* CONSOLE DEFINITIONS
|
||||
*/
|
||||
|
||||
/*
|
||||
* Subtypes for console messages.
|
||||
*/
|
||||
#define CMSG_CONSOLE_DATA 0
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* BLOCK-INTERFACE FRONTEND DEFINITIONS
|
||||
*/
|
||||
|
||||
/* Messages from domain controller to guest. */
|
||||
#define CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED 0
|
||||
|
||||
/* Messages from guest to domain controller. */
|
||||
#define CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED 32
|
||||
#define CMSG_BLKIF_FE_INTERFACE_CONNECT 33
|
||||
#define CMSG_BLKIF_FE_INTERFACE_DISCONNECT 34
|
||||
|
||||
/* These are used by both front-end and back-end drivers. */
|
||||
#define blkif_vdev_t u16
|
||||
#define blkif_pdev_t u16
|
||||
#define blkif_sector_t u64
|
||||
|
||||
/*
|
||||
* CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED:
|
||||
* Notify a guest about a status change on one of its block interfaces.
|
||||
* If the interface is DESTROYED or DOWN then the interface is disconnected:
|
||||
* 1. The shared-memory frame is available for reuse.
|
||||
* 2. Any unacknowledged messgaes pending on the interface were dropped.
|
||||
*/
|
||||
#define BLKIF_INTERFACE_STATUS_DESTROYED 0 /* Interface doesn't exist. */
|
||||
#define BLKIF_INTERFACE_STATUS_DISCONNECTED 1 /* Exists but is disconnected. */
|
||||
#define BLKIF_INTERFACE_STATUS_CONNECTED 2 /* Exists and is connected. */
|
||||
typedef struct {
|
||||
u32 handle; /* 0 */
|
||||
u32 status; /* 4 */
|
||||
u16 evtchn; /* 8: (only if status == BLKIF_INTERFACE_STATUS_CONNECTED). */
|
||||
} PACKED blkif_fe_interface_status_changed_t; /* 10 bytes */
|
||||
|
||||
/*
|
||||
* CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED:
|
||||
* Notify the domain controller that the front-end driver is DOWN or UP.
|
||||
* When the driver goes DOWN then the controller will send no more
|
||||
* status-change notifications. When the driver comes UP then the controller
|
||||
* will send a notification for each interface that currently exists.
|
||||
* If the driver goes DOWN while interfaces are still UP, the domain
|
||||
* will automatically take the interfaces DOWN.
|
||||
*/
|
||||
#define BLKIF_DRIVER_STATUS_DOWN 0
|
||||
#define BLKIF_DRIVER_STATUS_UP 1
|
||||
typedef struct {
|
||||
/* IN */
|
||||
u32 status; /* 0: BLKIF_DRIVER_STATUS_??? */
|
||||
/* OUT */
|
||||
/*
|
||||
* Tells driver how many interfaces it should expect to immediately
|
||||
* receive notifications about.
|
||||
*/
|
||||
u32 nr_interfaces; /* 4 */
|
||||
} PACKED blkif_fe_driver_status_changed_t; /* 8 bytes */
|
||||
|
||||
/*
|
||||
* CMSG_BLKIF_FE_INTERFACE_CONNECT:
|
||||
* If successful, the domain controller will acknowledge with a
|
||||
* STATUS_CONNECTED message.
|
||||
*/
|
||||
typedef struct {
|
||||
u32 handle; /* 0 */
|
||||
u32 __pad;
|
||||
memory_t shmem_frame; /* 8 */
|
||||
MEMORY_PADDING;
|
||||
} PACKED blkif_fe_interface_connect_t; /* 16 bytes */
|
||||
|
||||
/*
|
||||
* CMSG_BLKIF_FE_INTERFACE_DISCONNECT:
|
||||
* If successful, the domain controller will acknowledge with a
|
||||
* STATUS_DISCONNECTED message.
|
||||
*/
|
||||
typedef struct {
|
||||
u32 handle; /* 0 */
|
||||
} PACKED blkif_fe_interface_disconnect_t; /* 4 bytes */
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* BLOCK-INTERFACE BACKEND DEFINITIONS
|
||||
*/
|
||||
|
||||
/* Messages from domain controller. */
|
||||
#define CMSG_BLKIF_BE_CREATE 0 /* Create a new block-device interface. */
|
||||
#define CMSG_BLKIF_BE_DESTROY 1 /* Destroy a block-device interface. */
|
||||
#define CMSG_BLKIF_BE_CONNECT 2 /* Connect i/f to remote driver. */
|
||||
#define CMSG_BLKIF_BE_DISCONNECT 3 /* Disconnect i/f from remote driver. */
|
||||
#define CMSG_BLKIF_BE_VBD_CREATE 4 /* Create a new VBD for an interface. */
|
||||
#define CMSG_BLKIF_BE_VBD_DESTROY 5 /* Delete a VBD from an interface. */
|
||||
#define CMSG_BLKIF_BE_VBD_GROW 6 /* Append an extent to a given VBD. */
|
||||
#define CMSG_BLKIF_BE_VBD_SHRINK 7 /* Remove last extent from a given VBD. */
|
||||
|
||||
/* Messages to domain controller. */
|
||||
#define CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED 32
|
||||
|
||||
/*
|
||||
* Message request/response definitions for block-device messages.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
blkif_sector_t sector_start; /* 0 */
|
||||
blkif_sector_t sector_length; /* 8 */
|
||||
blkif_pdev_t device; /* 16 */
|
||||
u16 __pad; /* 18 */
|
||||
} PACKED blkif_extent_t; /* 20 bytes */
|
||||
|
||||
/* Non-specific 'okay' return. */
|
||||
#define BLKIF_BE_STATUS_OKAY 0
|
||||
/* Non-specific 'error' return. */
|
||||
#define BLKIF_BE_STATUS_ERROR 1
|
||||
/* The following are specific error returns. */
|
||||
#define BLKIF_BE_STATUS_INTERFACE_EXISTS 2
|
||||
#define BLKIF_BE_STATUS_INTERFACE_NOT_FOUND 3
|
||||
#define BLKIF_BE_STATUS_INTERFACE_CONNECTED 4
|
||||
#define BLKIF_BE_STATUS_VBD_EXISTS 5
|
||||
#define BLKIF_BE_STATUS_VBD_NOT_FOUND 6
|
||||
#define BLKIF_BE_STATUS_OUT_OF_MEMORY 7
|
||||
#define BLKIF_BE_STATUS_EXTENT_NOT_FOUND 8
|
||||
#define BLKIF_BE_STATUS_MAPPING_ERROR 9
|
||||
|
||||
/* This macro can be used to create an array of descriptive error strings. */
|
||||
#define BLKIF_BE_STATUS_ERRORS { \
|
||||
"Okay", \
|
||||
"Non-specific error", \
|
||||
"Interface already exists", \
|
||||
"Interface not found", \
|
||||
"Interface is still connected", \
|
||||
"VBD already exists", \
|
||||
"VBD not found", \
|
||||
"Out of memory", \
|
||||
"Extent not found for VBD", \
|
||||
"Could not map domain memory" }
|
||||
|
||||
/*
|
||||
* CMSG_BLKIF_BE_CREATE:
|
||||
* When the driver sends a successful response then the interface is fully
|
||||
* created. The controller will send a DOWN notification to the front-end
|
||||
* driver.
|
||||
*/
|
||||
typedef struct {
|
||||
/* IN */
|
||||
domid_t domid; /* 0: Domain attached to new interface. */
|
||||
u32 blkif_handle; /* 4: Domain-specific interface handle. */
|
||||
/* OUT */
|
||||
u32 status; /* 8 */
|
||||
} PACKED blkif_be_create_t; /* 12 bytes */
|
||||
|
||||
/*
|
||||
* CMSG_BLKIF_BE_DESTROY:
|
||||
* When the driver sends a successful response then the interface is fully
|
||||
* torn down. The controller will send a DESTROYED notification to the
|
||||
* front-end driver.
|
||||
*/
|
||||
typedef struct {
|
||||
/* IN */
|
||||
domid_t domid; /* 0: Identify interface to be destroyed. */
|
||||
u32 blkif_handle; /* 4: ...ditto... */
|
||||
/* OUT */
|
||||
u32 status; /* 8 */
|
||||
} PACKED blkif_be_destroy_t; /* 12 bytes */
|
||||
|
||||
/*
|
||||
* CMSG_BLKIF_BE_CONNECT:
|
||||
* When the driver sends a successful response then the interface is fully
|
||||
* connected. The controller will send a CONNECTED notification to the
|
||||
* front-end driver.
|
||||
*/
|
||||
typedef struct {
|
||||
/* IN */
|
||||
domid_t domid; /* 0: Domain attached to new interface. */
|
||||
u32 blkif_handle; /* 4: Domain-specific interface handle. */
|
||||
memory_t shmem_frame; /* 8: Page cont. shared comms window. */
|
||||
MEMORY_PADDING;
|
||||
u32 evtchn; /* 16: Event channel for notifications. */
|
||||
/* OUT */
|
||||
u32 status; /* 20 */
|
||||
} PACKED blkif_be_connect_t; /* 24 bytes */
|
||||
|
||||
/*
|
||||
* CMSG_BLKIF_BE_DISCONNECT:
|
||||
* When the driver sends a successful response then the interface is fully
|
||||
* disconnected. The controller will send a DOWN notification to the front-end
|
||||
* driver.
|
||||
*/
|
||||
typedef struct {
|
||||
/* IN */
|
||||
domid_t domid; /* 0: Domain attached to new interface. */
|
||||
u32 blkif_handle; /* 4: Domain-specific interface handle. */
|
||||
/* OUT */
|
||||
u32 status; /* 8 */
|
||||
} PACKED blkif_be_disconnect_t; /* 12 bytes */
|
||||
|
||||
/* CMSG_BLKIF_BE_VBD_CREATE */
|
||||
typedef struct {
|
||||
/* IN */
|
||||
domid_t domid; /* 0: Identify blkdev interface. */
|
||||
u32 blkif_handle; /* 4: ...ditto... */
|
||||
blkif_vdev_t vdevice; /* 8: Interface-specific id for this VBD. */
|
||||
u16 readonly; /* 10: Non-zero -> VBD isn't writeable. */
|
||||
/* OUT */
|
||||
u32 status; /* 12 */
|
||||
} PACKED blkif_be_vbd_create_t; /* 16 bytes */
|
||||
|
||||
/* CMSG_BLKIF_BE_VBD_DESTROY */
|
||||
typedef struct {
|
||||
/* IN */
|
||||
domid_t domid; /* 0: Identify blkdev interface. */
|
||||
u32 blkif_handle; /* 4: ...ditto... */
|
||||
blkif_vdev_t vdevice; /* 8: Interface-specific id of the VBD. */
|
||||
u16 __pad; /* 10 */
|
||||
/* OUT */
|
||||
u32 status; /* 12 */
|
||||
} PACKED blkif_be_vbd_destroy_t; /* 16 bytes */
|
||||
|
||||
/* CMSG_BLKIF_BE_VBD_GROW */
|
||||
typedef struct {
|
||||
/* IN */
|
||||
domid_t domid; /* 0: Identify blkdev interface. */
|
||||
u32 blkif_handle; /* 4: ...ditto... */
|
||||
blkif_extent_t extent; /* 8: Physical extent to append to VBD. */
|
||||
blkif_vdev_t vdevice; /* 28: Interface-specific id of the VBD. */
|
||||
u16 __pad; /* 30 */
|
||||
/* OUT */
|
||||
u32 status; /* 32 */
|
||||
} PACKED blkif_be_vbd_grow_t; /* 36 bytes */
|
||||
|
||||
/* CMSG_BLKIF_BE_VBD_SHRINK */
|
||||
typedef struct {
|
||||
/* IN */
|
||||
domid_t domid; /* 0: Identify blkdev interface. */
|
||||
u32 blkif_handle; /* 4: ...ditto... */
|
||||
blkif_vdev_t vdevice; /* 8: Interface-specific id of the VBD. */
|
||||
u16 __pad; /* 10 */
|
||||
/* OUT */
|
||||
u32 status; /* 12 */
|
||||
} PACKED blkif_be_vbd_shrink_t; /* 16 bytes */
|
||||
|
||||
/*
|
||||
* CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED:
|
||||
* Notify the domain controller that the back-end driver is DOWN or UP.
|
||||
* If the driver goes DOWN while interfaces are still UP, the controller
|
||||
* will automatically send DOWN notifications.
|
||||
*/
|
||||
typedef struct {
|
||||
u32 status; /* 0: BLKIF_DRIVER_STATUS_??? */
|
||||
} PACKED blkif_be_driver_status_changed_t; /* 4 bytes */
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* NETWORK-INTERFACE FRONTEND DEFINITIONS
|
||||
*/
|
||||
|
||||
/* Messages from domain controller to guest. */
|
||||
#define CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED 0
|
||||
|
||||
/* Messages from guest to domain controller. */
|
||||
#define CMSG_NETIF_FE_DRIVER_STATUS_CHANGED 32
|
||||
#define CMSG_NETIF_FE_INTERFACE_CONNECT 33
|
||||
#define CMSG_NETIF_FE_INTERFACE_DISCONNECT 34
|
||||
|
||||
/*
|
||||
* CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED:
|
||||
* Notify a guest about a status change on one of its network interfaces.
|
||||
* If the interface is DESTROYED or DOWN then the interface is disconnected:
|
||||
* 1. The shared-memory frame is available for reuse.
|
||||
* 2. Any unacknowledged messgaes pending on the interface were dropped.
|
||||
*/
|
||||
#define NETIF_INTERFACE_STATUS_DESTROYED 0 /* Interface doesn't exist. */
|
||||
#define NETIF_INTERFACE_STATUS_DISCONNECTED 1 /* Exists but is disconnected. */
|
||||
#define NETIF_INTERFACE_STATUS_CONNECTED 2 /* Exists and is connected. */
|
||||
typedef struct {
|
||||
u32 handle; /* 0 */
|
||||
u32 status; /* 4 */
|
||||
u16 evtchn; /* 8: status == NETIF_INTERFACE_STATUS_CONNECTED */
|
||||
u8 mac[6]; /* 10: status == NETIF_INTERFACE_STATUS_CONNECTED */
|
||||
} PACKED netif_fe_interface_status_changed_t; /* 16 bytes */
|
||||
|
||||
/*
|
||||
* CMSG_NETIF_FE_DRIVER_STATUS_CHANGED:
|
||||
* Notify the domain controller that the front-end driver is DOWN or UP.
|
||||
* When the driver goes DOWN then the controller will send no more
|
||||
* status-change notifications. When the driver comes UP then the controller
|
||||
* will send a notification for each interface that currently exists.
|
||||
* If the driver goes DOWN while interfaces are still UP, the domain
|
||||
* will automatically take the interfaces DOWN.
|
||||
*/
|
||||
#define NETIF_DRIVER_STATUS_DOWN 0
|
||||
#define NETIF_DRIVER_STATUS_UP 1
|
||||
typedef struct {
|
||||
/* IN */
|
||||
u32 status; /* 0: NETIF_DRIVER_STATUS_??? */
|
||||
/* OUT */
|
||||
/*
|
||||
* Tells driver how many interfaces it should expect to immediately
|
||||
* receive notifications about.
|
||||
*/
|
||||
u32 nr_interfaces; /* 4 */
|
||||
} PACKED netif_fe_driver_status_changed_t; /* 8 bytes */
|
||||
|
||||
/*
|
||||
* CMSG_NETIF_FE_INTERFACE_CONNECT:
|
||||
* If successful, the domain controller will acknowledge with a
|
||||
* STATUS_CONNECTED message.
|
||||
*/
|
||||
typedef struct {
|
||||
u32 handle; /* 0 */
|
||||
u32 __pad; /* 4 */
|
||||
memory_t tx_shmem_frame; /* 8 */
|
||||
MEMORY_PADDING;
|
||||
memory_t rx_shmem_frame; /* 16 */
|
||||
MEMORY_PADDING;
|
||||
} PACKED netif_fe_interface_connect_t; /* 24 bytes */
|
||||
|
||||
/*
|
||||
* CMSG_NETIF_FE_INTERFACE_DISCONNECT:
|
||||
* If successful, the domain controller will acknowledge with a
|
||||
* STATUS_DISCONNECTED message.
|
||||
*/
|
||||
typedef struct {
|
||||
u32 handle; /* 0 */
|
||||
} PACKED netif_fe_interface_disconnect_t; /* 4 bytes */
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* NETWORK-INTERFACE BACKEND DEFINITIONS
|
||||
*/
|
||||
|
||||
/* Messages from domain controller. */
|
||||
#define CMSG_NETIF_BE_CREATE 0 /* Create a new net-device interface. */
|
||||
#define CMSG_NETIF_BE_DESTROY 1 /* Destroy a net-device interface. */
|
||||
#define CMSG_NETIF_BE_CONNECT 2 /* Connect i/f to remote driver. */
|
||||
#define CMSG_NETIF_BE_DISCONNECT 3 /* Disconnect i/f from remote driver. */
|
||||
|
||||
/* Messages to domain controller. */
|
||||
#define CMSG_NETIF_BE_DRIVER_STATUS_CHANGED 32
|
||||
|
||||
/*
|
||||
* Message request/response definitions for net-device messages.
|
||||
*/
|
||||
|
||||
/* Non-specific 'okay' return. */
|
||||
#define NETIF_BE_STATUS_OKAY 0
|
||||
/* Non-specific 'error' return. */
|
||||
#define NETIF_BE_STATUS_ERROR 1
|
||||
/* The following are specific error returns. */
|
||||
#define NETIF_BE_STATUS_INTERFACE_EXISTS 2
|
||||
#define NETIF_BE_STATUS_INTERFACE_NOT_FOUND 3
|
||||
#define NETIF_BE_STATUS_INTERFACE_CONNECTED 4
|
||||
#define NETIF_BE_STATUS_OUT_OF_MEMORY 5
|
||||
#define NETIF_BE_STATUS_MAPPING_ERROR 6
|
||||
|
||||
/* This macro can be used to create an array of descriptive error strings. */
|
||||
#define NETIF_BE_STATUS_ERRORS { \
|
||||
"Okay", \
|
||||
"Non-specific error", \
|
||||
"Interface already exists", \
|
||||
"Interface not found", \
|
||||
"Interface is still connected", \
|
||||
"Out of memory", \
|
||||
"Could not map domain memory" }
|
||||
|
||||
/*
|
||||
* CMSG_NETIF_BE_CREATE:
|
||||
* When the driver sends a successful response then the interface is fully
|
||||
* created. The controller will send a DOWN notification to the front-end
|
||||
* driver.
|
||||
*/
|
||||
typedef struct {
|
||||
/* IN */
|
||||
domid_t domid; /* 0: Domain attached to new interface. */
|
||||
u32 netif_handle; /* 4: Domain-specific interface handle. */
|
||||
u8 mac[6]; /* 8 */
|
||||
u16 __pad; /* 14 */
|
||||
/* OUT */
|
||||
u32 status; /* 16 */
|
||||
} PACKED netif_be_create_t; /* 20 bytes */
|
||||
|
||||
/*
|
||||
* CMSG_NETIF_BE_DESTROY:
|
||||
* When the driver sends a successful response then the interface is fully
|
||||
* torn down. The controller will send a DESTROYED notification to the
|
||||
* front-end driver.
|
||||
*/
|
||||
typedef struct {
|
||||
/* IN */
|
||||
domid_t domid; /* 0: Identify interface to be destroyed. */
|
||||
u32 netif_handle; /* 4: ...ditto... */
|
||||
/* OUT */
|
||||
u32 status; /* 8 */
|
||||
} PACKED netif_be_destroy_t; /* 12 bytes */
|
||||
|
||||
/*
|
||||
* CMSG_NETIF_BE_CONNECT:
|
||||
* When the driver sends a successful response then the interface is fully
|
||||
* connected. The controller will send a CONNECTED notification to the
|
||||
* front-end driver.
|
||||
*/
|
||||
typedef struct {
|
||||
/* IN */
|
||||
domid_t domid; /* 0: Domain attached to new interface. */
|
||||
u32 netif_handle; /* 4: Domain-specific interface handle. */
|
||||
memory_t tx_shmem_frame; /* 8: Page cont. tx shared comms window. */
|
||||
MEMORY_PADDING;
|
||||
memory_t rx_shmem_frame; /* 16: Page cont. rx shared comms window. */
|
||||
MEMORY_PADDING;
|
||||
u16 evtchn; /* 24: Event channel for notifications. */
|
||||
u16 __pad; /* 26 */
|
||||
/* OUT */
|
||||
u32 status; /* 28 */
|
||||
} PACKED netif_be_connect_t; /* 32 bytes */
|
||||
|
||||
/*
|
||||
* CMSG_NETIF_BE_DISCONNECT:
|
||||
* When the driver sends a successful response then the interface is fully
|
||||
* disconnected. The controller will send a DOWN notification to the front-end
|
||||
* driver.
|
||||
*/
|
||||
typedef struct {
|
||||
/* IN */
|
||||
domid_t domid; /* 0: Domain attached to new interface. */
|
||||
u32 netif_handle; /* 4: Domain-specific interface handle. */
|
||||
/* OUT */
|
||||
u32 status; /* 8 */
|
||||
} PACKED netif_be_disconnect_t; /* 12 bytes */
|
||||
|
||||
/*
|
||||
* CMSG_NETIF_BE_DRIVER_STATUS_CHANGED:
|
||||
* Notify the domain controller that the back-end driver is DOWN or UP.
|
||||
* If the driver goes DOWN while interfaces are still UP, the domain
|
||||
* will automatically send DOWN notifications.
|
||||
*/
|
||||
typedef struct {
|
||||
u32 status; /* 0: NETIF_DRIVER_STATUS_??? */
|
||||
} PACKED netif_be_driver_status_changed_t; /* 4 bytes */
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* SHUTDOWN DEFINITIONS
|
||||
*/
|
||||
|
||||
/*
|
||||
* Subtypes for shutdown messages.
|
||||
*/
|
||||
#define CMSG_SHUTDOWN_POWEROFF 0 /* Clean shutdown (SHUTDOWN_poweroff). */
|
||||
#define CMSG_SHUTDOWN_REBOOT 1 /* Clean shutdown (SHUTDOWN_reboot). */
|
||||
#define CMSG_SHUTDOWN_SUSPEND 2 /* Create suspend info, then */
|
||||
/* SHUTDOWN_suspend. */
|
||||
|
||||
#endif /* __DOMAIN_CONTROLLER_H__ */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,160 @@
|
||||
"""Bridge control utilities.
|
||||
"""
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import sys
|
||||
|
||||
os.defpath = os.defpath + ':/sbin:/usr/sbin:/usr/local/sbin'
|
||||
CMD_IFCONFIG = 'ifconfig'
|
||||
CMD_ROUTE = 'route'
|
||||
CMD_BRCTL = 'brctl'
|
||||
CMD_IPTABLES = "iptables"
|
||||
|
||||
opts = None
|
||||
|
||||
class Opts:
|
||||
|
||||
def __init__(self, defaults):
|
||||
for (k, v) in defaults.items():
|
||||
setattr(self, k, v)
|
||||
pass
|
||||
|
||||
def cmd(p, s):
|
||||
"""Print and execute command 'p' with args 's'.
|
||||
"""
|
||||
global opts
|
||||
c = p + ' ' + s
|
||||
if opts.verbose: print c
|
||||
if not opts.dryrun:
|
||||
os.system(c)
|
||||
|
||||
def vif_bridge_add(params):
|
||||
"""Add the network interface for vif on dom to a bridge.
|
||||
"""
|
||||
cmd(CMD_BRCTL, 'addif %(bridge)s %(vif)s' % params)
|
||||
|
||||
def vif_bridge_rem(params):
|
||||
"""Remove the network interface for vif on dom from a bridge.
|
||||
"""
|
||||
cmd(CMD_BRCTL, 'delif %(bridge)s %(vif)s' % params)
|
||||
|
||||
def vif_restrict_addr(vif, addr, delete=0):
|
||||
d = { 'vif': vif, 'addr': addr}
|
||||
if delete:
|
||||
d['flag'] = '-D'
|
||||
else:
|
||||
d['flag'] = '-A'
|
||||
cmd(CMD_IPTABLES, '-P FORWARD DROP')
|
||||
cmd(CMD_IPTABLES, '%(flag)s FORWARD -m physdev --physdev-in %(vif)s -s %(addr)s -j ACCEPT' % d)
|
||||
cmd(CMD_IPTABLES, '%(flag)s FORWARD -m physdev --physdev-out %(vif)s -d %(addr)s -j ACCEPT' % d)
|
||||
|
||||
def bridge_create(bridge, **kwd):
|
||||
"""Create a bridge.
|
||||
Defaults hello time to 0, forward delay to 0 and stp off.
|
||||
"""
|
||||
cmd(CMD_BRCTL, 'addbr %s' % bridge)
|
||||
if kwd.get('hello', None) is None:
|
||||
kwd['hello'] = 0
|
||||
if kwd.get('fd', None) is None:
|
||||
kwd['fd'] = 0
|
||||
if kwd.get('stp', None) is None:
|
||||
kwd['stp'] = 'off'
|
||||
bridge_set(bridge, **kwd)
|
||||
|
||||
def bridge_set(bridge, hello=None, fd=None, stp=None):
|
||||
"""Set bridge parameters.
|
||||
"""
|
||||
if hello is not None:
|
||||
cmd(CMD_BRCTL, 'sethello %s %d' % (bridge, hello))
|
||||
if fd is not None:
|
||||
cmd(CMD_BRCTL, 'setfd %s %d' % (bridge, fd))
|
||||
if stp is not None:
|
||||
cmd(CMD_BRCTL, 'stp %s %s' % (bridge, stp))
|
||||
|
||||
def bridge_del(bridge):
|
||||
"""Delete a bridge.
|
||||
"""
|
||||
cmd(CMD_BRCTL, 'delbr %s' % bridge)
|
||||
|
||||
def routes():
|
||||
"""Return a list of the routes.
|
||||
"""
|
||||
fin = os.popen(CMD_ROUTE + ' -n', 'r')
|
||||
routes = []
|
||||
for x in fin:
|
||||
if x.startswith('Kernel'): continue
|
||||
if x.startswith('Destination'): continue
|
||||
x = x.strip()
|
||||
y = x.split()
|
||||
z = { 'destination': y[0],
|
||||
'gateway' : y[1],
|
||||
'mask' : y[2],
|
||||
'flags' : y[3],
|
||||
'metric' : y[4],
|
||||
'ref' : y[5],
|
||||
'use' : y[6],
|
||||
'interface' : y[7] }
|
||||
routes.append(z)
|
||||
return routes
|
||||
|
||||
def ifconfig(interface):
|
||||
"""Return the ip config for an interface,
|
||||
"""
|
||||
fin = os.popen(CMD_IFCONFIG + ' %s' % interface, 'r')
|
||||
inetre = re.compile('\s*inet\s*addr:(?P<address>\S*)\s*Bcast:(?P<broadcast>\S*)\s*Mask:(?P<mask>\S*)')
|
||||
info = None
|
||||
for x in fin:
|
||||
m = inetre.match(x)
|
||||
if not m: continue
|
||||
info = m.groupdict()
|
||||
info['interface'] = interface
|
||||
break
|
||||
return info
|
||||
|
||||
def reconfigure(interface, bridge):
|
||||
"""Reconfigure an interface to be attached to a bridge, and give the bridge
|
||||
the IP address etc. from interface. Move the default route to the interface
|
||||
to the bridge.
|
||||
|
||||
"""
|
||||
global opts
|
||||
intf_info = ifconfig(interface)
|
||||
if not intf_info:
|
||||
print >>sys.stderr, 'Interface not found:', interface
|
||||
return
|
||||
#bridge_info = ifconfig(bridge)
|
||||
#if not bridge_info:
|
||||
# print >>sys.stderr, 'Bridge not found:', bridge
|
||||
# return
|
||||
route_info = routes()
|
||||
intf_info['bridge'] = bridge
|
||||
intf_info['gateway'] = None
|
||||
for r in route_info:
|
||||
if (r['destination'] == '0.0.0.0' and
|
||||
'G' in r['flags'] and
|
||||
r['interface'] == interface):
|
||||
intf_info['gateway'] = r['gateway']
|
||||
if not intf_info['gateway']:
|
||||
print >>sys.stderr, 'Gateway not found: ', interface
|
||||
return
|
||||
cmd(CMD_IFCONFIG,
|
||||
'%(bridge)s %(address)s netmask %(mask)s broadcast %(broadcast)s up'
|
||||
% intf_info)
|
||||
cmd(CMD_ROUTE,
|
||||
'add default gateway %(gateway)s dev %(bridge)s'
|
||||
% intf_info)
|
||||
cmd(CMD_BRCTL, 'addif %(bridge)s %(interface)s' % intf_info)
|
||||
cmd(CMD_IFCONFIG, '%(interface)s 0.0.0.0' % intf_info)
|
||||
|
||||
defaults = {
|
||||
'verbose' : 1,
|
||||
'dryrun' : 0,
|
||||
}
|
||||
|
||||
opts = Opts(defaults)
|
||||
|
||||
def set_opts(val):
|
||||
global opts
|
||||
opts = val
|
||||
return opts
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
##############################################
|
||||
# Console client for Xen guest OSes
|
||||
# Copyright (c) 2004, K A Fraser
|
||||
##############################################
|
||||
|
||||
import errno, os, signal, socket, struct, sys
|
||||
|
||||
from termios import *
|
||||
# Indexes into termios.tcgetattr() list.
|
||||
IFLAG = 0
|
||||
OFLAG = 1
|
||||
CFLAG = 2
|
||||
LFLAG = 3
|
||||
ISPEED = 4
|
||||
OSPEED = 5
|
||||
CC = 6
|
||||
|
||||
def __child_death(signum, frame):
|
||||
global stop
|
||||
stop = True
|
||||
|
||||
def __recv_from_sock(sock):
|
||||
global stop
|
||||
stop = False
|
||||
while not stop:
|
||||
try:
|
||||
data = sock.recv(1024)
|
||||
os.write(1, data)
|
||||
except socket.error, error:
|
||||
if error[0] != errno.EINTR:
|
||||
raise
|
||||
os.wait()
|
||||
|
||||
def __send_to_sock(sock):
|
||||
while 1:
|
||||
data = os.read(0,1024)
|
||||
if ord(data[0]) == ord(']')-64:
|
||||
break
|
||||
try:
|
||||
sock.send(data)
|
||||
except socket.error, error:
|
||||
if error[0] == errno.EPIPE:
|
||||
sys.exit(0)
|
||||
if error[0] != errno.EINTR:
|
||||
raise
|
||||
sys.exit(0)
|
||||
|
||||
def connect(host,port):
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
|
||||
sock.connect((host,port))
|
||||
|
||||
oattrs = tcgetattr(0)
|
||||
nattrs = tcgetattr(0)
|
||||
nattrs[IFLAG] = nattrs[IFLAG] & ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON)
|
||||
nattrs[OFLAG] = nattrs[OFLAG] & ~(OPOST)
|
||||
nattrs[CFLAG] = nattrs[CFLAG] & ~(CSIZE | PARENB)
|
||||
nattrs[CFLAG] = nattrs[CFLAG] | CS8
|
||||
nattrs[LFLAG] = nattrs[LFLAG] & ~(ECHO | ICANON | IEXTEN | ISIG)
|
||||
nattrs[CC][VMIN] = 1
|
||||
nattrs[CC][VTIME] = 0
|
||||
|
||||
if os.fork():
|
||||
signal.signal(signal.SIGCHLD, __child_death)
|
||||
print "************ REMOTE CONSOLE: CTRL-] TO QUIT ********"
|
||||
tcsetattr(0, TCSAFLUSH, nattrs)
|
||||
try:
|
||||
__recv_from_sock(sock)
|
||||
finally:
|
||||
tcsetattr(0, TCSAFLUSH, oattrs)
|
||||
print
|
||||
print "************ REMOTE CONSOLE EXITED *****************"
|
||||
else:
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_IGN)
|
||||
__send_to_sock(sock)
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) != 3:
|
||||
print sys.argv[0] + " <host> <port>"
|
||||
sys.exit(1)
|
||||
connect(str(sys.argv[1]),int(sys.argv[2]))
|
||||
@@ -0,0 +1,113 @@
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import struct
|
||||
|
||||
def readlines(fd):
|
||||
"""Version of readlines safe against EINTR.
|
||||
"""
|
||||
import errno
|
||||
|
||||
lines = []
|
||||
while 1:
|
||||
try:
|
||||
line = fd.readline()
|
||||
except IOError, ex:
|
||||
if ex.errno == errno.EINTR:
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
if line == '': break
|
||||
lines.append(line)
|
||||
return lines
|
||||
|
||||
def readline(fd):
|
||||
"""Version of readline safe against EINTR.
|
||||
"""
|
||||
while 1:
|
||||
try:
|
||||
return fd.readline()
|
||||
except IOError, ex:
|
||||
if ex.errno == errno.EINTR:
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
|
||||
##### Networking-related functions
|
||||
|
||||
"""Bridge for network backend.
|
||||
When bridging is used, eth0 may not have an IP address,
|
||||
as it may have been moved onto the bridge.
|
||||
"""
|
||||
NBE_BRIDGE = 'nbe-br'
|
||||
|
||||
def get_current_ipaddr(dev='eth0'):
|
||||
"""Return a string containing the primary IP address for the given
|
||||
network interface (default 'eth0').
|
||||
"""
|
||||
fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
|
||||
lines = readlines(fd)
|
||||
for line in lines:
|
||||
m = re.search( '^\s+inet addr:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
|
||||
line )
|
||||
if m:
|
||||
return m.group(1)
|
||||
if dev == 'eth0':
|
||||
return get_current_ipaddr(NBE_BRIDGE)
|
||||
return None
|
||||
|
||||
def get_current_ipmask(dev='eth0'):
|
||||
"""Return a string containing the primary IP netmask for the given
|
||||
network interface (default 'eth0').
|
||||
"""
|
||||
fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
|
||||
lines = readlines(fd)
|
||||
for line in lines:
|
||||
m = re.search( '^.+Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
|
||||
line )
|
||||
if m:
|
||||
return m.group(1)
|
||||
if dev == 'eth0':
|
||||
return get_current_ipmask(NBE_BRIDGE)
|
||||
return None
|
||||
|
||||
def get_current_ipgw(dev='eth0'):
|
||||
"""Return a string containing the IP gateway for the given
|
||||
network interface (default 'eth0').
|
||||
"""
|
||||
fd = os.popen( '/sbin/route -n' )
|
||||
lines = readlines(fd)
|
||||
for line in lines:
|
||||
m = re.search( '^\S+\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)' +
|
||||
'\s+\S+\s+\S*G.*' + dev + '.*', line )
|
||||
if m:
|
||||
return m.group(1)
|
||||
if dev == 'eth0':
|
||||
return get_current_ipgw(NBE_BRIDGE)
|
||||
return None
|
||||
|
||||
def inet_aton(addr):
|
||||
"""Convert an IP addr in IPv4 dot notation into an int.
|
||||
"""
|
||||
b = socket.inet_aton(addr)
|
||||
return struct.unpack('!I', b)[0]
|
||||
|
||||
def inet_ntoa(n):
|
||||
"""Convert an int into an IP addr in IPv4 dot notation.
|
||||
"""
|
||||
b = struct.pack('!I', n)
|
||||
return socket.inet_ntoa(b)
|
||||
|
||||
def add_offset_to_ip(addr, offset):
|
||||
"""Add a numerical offset to an IP addr in IPv4 dot notation.
|
||||
"""
|
||||
n = inet_aton(addr)
|
||||
n += offset
|
||||
return inet_ntoa(n)
|
||||
|
||||
def check_subnet( ip, network, netmask ):
|
||||
n_ip = inet_aton(ip)
|
||||
n_net = inet_aton(network)
|
||||
n_mask = inet_aton(netmask)
|
||||
return (n_ip & n_mask) == (n_net & n_mask)
|
||||
|
||||
@@ -0,0 +1,451 @@
|
||||
"""Temporary files.
|
||||
|
||||
This module provides generic, low- and high-level interfaces for
|
||||
creating temporary files and directories. The interfaces listed
|
||||
as "safe" just below can be used without fear of race conditions.
|
||||
Those listed as "unsafe" cannot, and are provided for backward
|
||||
compatibility only.
|
||||
|
||||
This module also provides some data items to the user:
|
||||
|
||||
TMP_MAX - maximum number of names that will be tried before
|
||||
giving up.
|
||||
template - the default prefix for all temporary names.
|
||||
You may change this to control the default prefix.
|
||||
tempdir - If this is set to a string before the first use of
|
||||
any routine from this module, it will be considered as
|
||||
another candidate location to store temporary files.
|
||||
"""
|
||||
|
||||
__all__ = [
|
||||
"NamedTemporaryFile", "TemporaryFile", # high level safe interfaces
|
||||
"mkstemp", "mkdtemp", # low level safe interfaces
|
||||
"mktemp", # deprecated unsafe interface
|
||||
"TMP_MAX", "gettempprefix", # constants
|
||||
"tempdir", "gettempdir"
|
||||
]
|
||||
|
||||
|
||||
# Imports.
|
||||
|
||||
import os as _os
|
||||
import errno as _errno
|
||||
from random import Random as _Random
|
||||
|
||||
if _os.name == 'mac':
|
||||
import Carbon.Folder as _Folder
|
||||
import Carbon.Folders as _Folders
|
||||
|
||||
try:
|
||||
import fcntl as _fcntl
|
||||
# If PYTHONCASEOK is set on Windows, stinking FCNTL.py gets
|
||||
# imported, and we don't get an ImportError then. Provoke
|
||||
# an AttributeError instead in that case.
|
||||
_fcntl.fcntl
|
||||
except (ImportError, AttributeError):
|
||||
def _set_cloexec(fd):
|
||||
pass
|
||||
else:
|
||||
def _set_cloexec(fd):
|
||||
flags = _fcntl.fcntl(fd, _fcntl.F_GETFD, 0)
|
||||
if flags >= 0:
|
||||
# flags read successfully, modify
|
||||
flags |= _fcntl.FD_CLOEXEC
|
||||
_fcntl.fcntl(fd, _fcntl.F_SETFD, flags)
|
||||
|
||||
|
||||
try:
|
||||
import thread as _thread
|
||||
except ImportError:
|
||||
import dummy_thread as _thread
|
||||
_allocate_lock = _thread.allocate_lock
|
||||
|
||||
_text_openflags = _os.O_RDWR | _os.O_CREAT | _os.O_EXCL
|
||||
if hasattr(_os, 'O_NOINHERIT'):
|
||||
_text_openflags |= _os.O_NOINHERIT
|
||||
if hasattr(_os, 'O_NOFOLLOW'):
|
||||
_text_openflags |= _os.O_NOFOLLOW
|
||||
|
||||
_bin_openflags = _text_openflags
|
||||
if hasattr(_os, 'O_BINARY'):
|
||||
_bin_openflags |= _os.O_BINARY
|
||||
|
||||
if hasattr(_os, 'TMP_MAX'):
|
||||
TMP_MAX = _os.TMP_MAX
|
||||
else:
|
||||
TMP_MAX = 10000
|
||||
|
||||
template = "tmp"
|
||||
|
||||
tempdir = None
|
||||
|
||||
# Internal routines.
|
||||
|
||||
_once_lock = _allocate_lock()
|
||||
|
||||
class _RandomNameSequence:
|
||||
"""An instance of _RandomNameSequence generates an endless
|
||||
sequence of unpredictable strings which can safely be incorporated
|
||||
into file names. Each string is six characters long. Multiple
|
||||
threads can safely use the same instance at the same time.
|
||||
|
||||
_RandomNameSequence is an iterator."""
|
||||
|
||||
characters = ("abcdefghijklmnopqrstuvwxyz" +
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
|
||||
"0123456789-_")
|
||||
|
||||
def __init__(self):
|
||||
self.mutex = _allocate_lock()
|
||||
self.rng = _Random()
|
||||
self.normcase = _os.path.normcase
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
m = self.mutex
|
||||
c = self.characters
|
||||
choose = self.rng.choice
|
||||
|
||||
m.acquire()
|
||||
try:
|
||||
letters = [choose(c) for dummy in "123456"]
|
||||
finally:
|
||||
m.release()
|
||||
|
||||
return self.normcase(''.join(letters))
|
||||
|
||||
def _candidate_tempdir_list():
|
||||
"""Generate a list of candidate temporary directories which
|
||||
_get_default_tempdir will try."""
|
||||
|
||||
dirlist = []
|
||||
|
||||
# First, try the environment.
|
||||
for envname in 'TMPDIR', 'TEMP', 'TMP':
|
||||
dirname = _os.getenv(envname)
|
||||
if dirname: dirlist.append(dirname)
|
||||
|
||||
# Failing that, try OS-specific locations.
|
||||
if _os.name == 'mac':
|
||||
try:
|
||||
fsr = _Folder.FSFindFolder(_Folders.kOnSystemDisk,
|
||||
_Folders.kTemporaryFolderType, 1)
|
||||
dirname = fsr.as_pathname()
|
||||
dirlist.append(dirname)
|
||||
except _Folder.error:
|
||||
pass
|
||||
elif _os.name == 'riscos':
|
||||
dirname = _os.getenv('Wimp$ScrapDir')
|
||||
if dirname: dirlist.append(dirname)
|
||||
elif _os.name == 'nt':
|
||||
dirlist.extend([ r'c:\temp', r'c:\tmp', r'\temp', r'\tmp' ])
|
||||
else:
|
||||
dirlist.extend([ '/tmp', '/var/tmp', '/usr/tmp' ])
|
||||
|
||||
# As a last resort, the current directory.
|
||||
try:
|
||||
dirlist.append(_os.getcwd())
|
||||
except (AttributeError, _os.error):
|
||||
dirlist.append(_os.curdir)
|
||||
|
||||
return dirlist
|
||||
|
||||
def _get_default_tempdir():
|
||||
"""Calculate the default directory to use for temporary files.
|
||||
This routine should be called exactly once.
|
||||
|
||||
We determine whether or not a candidate temp dir is usable by
|
||||
trying to create and write to a file in that directory. If this
|
||||
is successful, the test file is deleted. To prevent denial of
|
||||
service, the name of the test file must be randomized."""
|
||||
|
||||
namer = _RandomNameSequence()
|
||||
dirlist = _candidate_tempdir_list()
|
||||
flags = _text_openflags
|
||||
|
||||
for dir in dirlist:
|
||||
if dir != _os.curdir:
|
||||
dir = _os.path.normcase(_os.path.abspath(dir))
|
||||
# Try only a few names per directory.
|
||||
for seq in xrange(100):
|
||||
name = namer.next()
|
||||
filename = _os.path.join(dir, name)
|
||||
try:
|
||||
fd = _os.open(filename, flags, 0600)
|
||||
fp = _os.fdopen(fd, 'w')
|
||||
fp.write('blat')
|
||||
fp.close()
|
||||
_os.unlink(filename)
|
||||
del fp, fd
|
||||
return dir
|
||||
except (OSError, IOError), e:
|
||||
if e[0] != _errno.EEXIST:
|
||||
break # no point trying more names in this directory
|
||||
pass
|
||||
raise IOError, (_errno.ENOENT,
|
||||
("No usable temporary directory found in %s" % dirlist))
|
||||
|
||||
_name_sequence = None
|
||||
|
||||
def _get_candidate_names():
|
||||
"""Common setup sequence for all user-callable interfaces."""
|
||||
|
||||
global _name_sequence
|
||||
if _name_sequence is None:
|
||||
_once_lock.acquire()
|
||||
try:
|
||||
if _name_sequence is None:
|
||||
_name_sequence = _RandomNameSequence()
|
||||
finally:
|
||||
_once_lock.release()
|
||||
return _name_sequence
|
||||
|
||||
|
||||
def _mkstemp_inner(dir, pre, suf, flags):
|
||||
"""Code common to mkstemp, TemporaryFile, and NamedTemporaryFile."""
|
||||
|
||||
names = _get_candidate_names()
|
||||
|
||||
for seq in xrange(TMP_MAX):
|
||||
name = names.next()
|
||||
file = _os.path.join(dir, pre + name + suf)
|
||||
try:
|
||||
fd = _os.open(file, flags, 0600)
|
||||
_set_cloexec(fd)
|
||||
return (fd, file)
|
||||
except OSError, e:
|
||||
if e.errno == _errno.EEXIST:
|
||||
continue # try again
|
||||
raise
|
||||
|
||||
raise IOError, (_errno.EEXIST, "No usable temporary file name found")
|
||||
|
||||
|
||||
# User visible interfaces.
|
||||
|
||||
def gettempprefix():
|
||||
"""Accessor for tempdir.template."""
|
||||
return template
|
||||
|
||||
tempdir = None
|
||||
|
||||
def gettempdir():
|
||||
"""Accessor for tempdir.tempdir."""
|
||||
global tempdir
|
||||
if tempdir is None:
|
||||
_once_lock.acquire()
|
||||
try:
|
||||
if tempdir is None:
|
||||
tempdir = _get_default_tempdir()
|
||||
finally:
|
||||
_once_lock.release()
|
||||
return tempdir
|
||||
|
||||
def mkstemp(suffix="", prefix=template, dir=None, text=False):
|
||||
"""mkstemp([suffix, [prefix, [dir, [text]]]])
|
||||
User-callable function to create and return a unique temporary
|
||||
file. The return value is a pair (fd, name) where fd is the
|
||||
file descriptor returned by os.open, and name is the filename.
|
||||
|
||||
If 'suffix' is specified, the file name will end with that suffix,
|
||||
otherwise there will be no suffix.
|
||||
|
||||
If 'prefix' is specified, the file name will begin with that prefix,
|
||||
otherwise a default prefix is used.
|
||||
|
||||
If 'dir' is specified, the file will be created in that directory,
|
||||
otherwise a default directory is used.
|
||||
|
||||
If 'text' is specified and true, the file is opened in text
|
||||
mode. Else (the default) the file is opened in binary mode. On
|
||||
some operating systems, this makes no difference.
|
||||
|
||||
The file is readable and writable only by the creating user ID.
|
||||
If the operating system uses permission bits to indicate whether a
|
||||
file is executable, the file is executable by no one. The file
|
||||
descriptor is not inherited by children of this process.
|
||||
|
||||
Caller is responsible for deleting the file when done with it.
|
||||
"""
|
||||
|
||||
if dir is None:
|
||||
dir = gettempdir()
|
||||
|
||||
if text:
|
||||
flags = _text_openflags
|
||||
else:
|
||||
flags = _bin_openflags
|
||||
|
||||
return _mkstemp_inner(dir, prefix, suffix, flags)
|
||||
|
||||
|
||||
def mkdtemp(suffix="", prefix=template, dir=None):
|
||||
"""mkdtemp([suffix, [prefix, [dir]]])
|
||||
User-callable function to create and return a unique temporary
|
||||
directory. The return value is the pathname of the directory.
|
||||
|
||||
Arguments are as for mkstemp, except that the 'text' argument is
|
||||
not accepted.
|
||||
|
||||
The directory is readable, writable, and searchable only by the
|
||||
creating user.
|
||||
|
||||
Caller is responsible for deleting the directory when done with it.
|
||||
"""
|
||||
|
||||
if dir is None:
|
||||
dir = gettempdir()
|
||||
|
||||
names = _get_candidate_names()
|
||||
|
||||
for seq in xrange(TMP_MAX):
|
||||
name = names.next()
|
||||
file = _os.path.join(dir, prefix + name + suffix)
|
||||
try:
|
||||
_os.mkdir(file, 0700)
|
||||
return file
|
||||
except OSError, e:
|
||||
if e.errno == _errno.EEXIST:
|
||||
continue # try again
|
||||
raise
|
||||
|
||||
raise IOError, (_errno.EEXIST, "No usable temporary directory name found")
|
||||
|
||||
def mktemp(suffix="", prefix=template, dir=None):
|
||||
"""mktemp([suffix, [prefix, [dir]]])
|
||||
User-callable function to return a unique temporary file name. The
|
||||
file is not created.
|
||||
|
||||
Arguments are as for mkstemp, except that the 'text' argument is
|
||||
not accepted.
|
||||
|
||||
This function is unsafe and should not be used. The file name
|
||||
refers to a file that did not exist at some point, but by the time
|
||||
you get around to creating it, someone else may have beaten you to
|
||||
the punch.
|
||||
"""
|
||||
|
||||
## from warnings import warn as _warn
|
||||
## _warn("mktemp is a potential security risk to your program",
|
||||
## RuntimeWarning, stacklevel=2)
|
||||
|
||||
if dir is None:
|
||||
dir = gettempdir()
|
||||
|
||||
names = _get_candidate_names()
|
||||
for seq in xrange(TMP_MAX):
|
||||
name = names.next()
|
||||
file = _os.path.join(dir, prefix + name + suffix)
|
||||
if not _os.path.exists(file):
|
||||
return file
|
||||
|
||||
raise IOError, (_errno.EEXIST, "No usable temporary filename found")
|
||||
|
||||
class _TemporaryFileWrapper:
|
||||
"""Temporary file wrapper
|
||||
|
||||
This class provides a wrapper around files opened for
|
||||
temporary use. In particular, it seeks to automatically
|
||||
remove the file when it is no longer needed.
|
||||
"""
|
||||
|
||||
def __init__(self, file, name):
|
||||
self.file = file
|
||||
self.name = name
|
||||
self.close_called = False
|
||||
|
||||
def __getattr__(self, name):
|
||||
file = self.__dict__['file']
|
||||
a = getattr(file, name)
|
||||
if type(a) != type(0):
|
||||
setattr(self, name, a)
|
||||
return a
|
||||
|
||||
# NT provides delete-on-close as a primitive, so we don't need
|
||||
# the wrapper to do anything special. We still use it so that
|
||||
# file.name is useful (i.e. not "(fdopen)") with NamedTemporaryFile.
|
||||
if _os.name != 'nt':
|
||||
|
||||
# Cache the unlinker so we don't get spurious errors at
|
||||
# shutdown when the module-level "os" is None'd out. Note
|
||||
# that this must be referenced as self.unlink, because the
|
||||
# name TemporaryFileWrapper may also get None'd out before
|
||||
# __del__ is called.
|
||||
unlink = _os.unlink
|
||||
|
||||
def close(self):
|
||||
if not self.close_called:
|
||||
self.close_called = True
|
||||
self.file.close()
|
||||
self.unlink(self.name)
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
def NamedTemporaryFile(mode='w+b', bufsize=-1, suffix="",
|
||||
prefix=template, dir=None):
|
||||
"""Create and return a temporary file.
|
||||
Arguments:
|
||||
'prefix', 'suffix', 'dir' -- as for mkstemp.
|
||||
'mode' -- the mode argument to os.fdopen (default "w+b").
|
||||
'bufsize' -- the buffer size argument to os.fdopen (default -1).
|
||||
The file is created as mkstemp() would do it.
|
||||
|
||||
Returns a file object; the name of the file is accessible as
|
||||
file.name. The file will be automatically deleted when it is
|
||||
closed.
|
||||
"""
|
||||
|
||||
if dir is None:
|
||||
dir = gettempdir()
|
||||
|
||||
if 'b' in mode:
|
||||
flags = _bin_openflags
|
||||
else:
|
||||
flags = _text_openflags
|
||||
|
||||
# Setting O_TEMPORARY in the flags causes the OS to delete
|
||||
# the file when it is closed. This is only supported by Windows.
|
||||
if _os.name == 'nt':
|
||||
flags |= _os.O_TEMPORARY
|
||||
|
||||
(fd, name) = _mkstemp_inner(dir, prefix, suffix, flags)
|
||||
file = _os.fdopen(fd, mode, bufsize)
|
||||
return _TemporaryFileWrapper(file, name)
|
||||
|
||||
if _os.name != 'posix' or _os.sys.platform == 'cygwin':
|
||||
# On non-POSIX and Cygwin systems, assume that we cannot unlink a file
|
||||
# while it is open.
|
||||
TemporaryFile = NamedTemporaryFile
|
||||
|
||||
else:
|
||||
def TemporaryFile(mode='w+b', bufsize=-1, suffix="",
|
||||
prefix=template, dir=None):
|
||||
"""Create and return a temporary file.
|
||||
Arguments:
|
||||
'prefix', 'suffix', 'directory' -- as for mkstemp.
|
||||
'mode' -- the mode argument to os.fdopen (default "w+b").
|
||||
'bufsize' -- the buffer size argument to os.fdopen (default -1).
|
||||
The file is created as mkstemp() would do it.
|
||||
|
||||
Returns a file object. The file has no name, and will cease to
|
||||
exist when it is closed.
|
||||
"""
|
||||
|
||||
if dir is None:
|
||||
dir = gettempdir()
|
||||
|
||||
if 'b' in mode:
|
||||
flags = _bin_openflags
|
||||
else:
|
||||
flags = _text_openflags
|
||||
|
||||
(fd, name) = _mkstemp_inner(dir, prefix, suffix, flags)
|
||||
try:
|
||||
_os.unlink(name)
|
||||
return _os.fdopen(fd, mode, bufsize)
|
||||
except:
|
||||
_os.close(fd)
|
||||
raise
|
||||
@@ -0,0 +1,126 @@
|
||||
import sxp
|
||||
|
||||
class ArgError(StandardError):
|
||||
pass
|
||||
|
||||
class Args:
|
||||
"""Argument encoding support for HTTP.
|
||||
"""
|
||||
|
||||
def __init__(self, paramspec, keyspec):
|
||||
self.arg_ord = []
|
||||
self.arg_dict = {}
|
||||
self.key_ord = []
|
||||
self.key_dict = {}
|
||||
for (name, type) in paramspec:
|
||||
self.arg_ord.append(name)
|
||||
self.arg_dict[name] = type
|
||||
for (name, type) in keyspec:
|
||||
self.key_ord.append(name)
|
||||
self.key_dict[name] = type
|
||||
|
||||
def get_args(self, d, xargs=None):
|
||||
args = {}
|
||||
keys = {}
|
||||
params = []
|
||||
if xargs:
|
||||
self.split_args(xargs, args, keys)
|
||||
self.split_args(d, args, keys)
|
||||
for a in self.arg_ord:
|
||||
if a in args:
|
||||
params.append(args[a])
|
||||
else:
|
||||
raise ArgError('Missing parameter: %s' % a)
|
||||
return (params, keys)
|
||||
|
||||
def split_args(self, d, args, keys):
|
||||
for (k, v) in d.items():
|
||||
if k in self.arg_dict:
|
||||
type = self.arg_dict[k]
|
||||
val = self.coerce(type, v)
|
||||
args[k] = val
|
||||
elif k in self.key_dict:
|
||||
type = self.key_dict[k]
|
||||
val = self.coerce(type, v)
|
||||
keys[k] = val
|
||||
else:
|
||||
raise ArgError('Invalid parameter: %s' % k)
|
||||
|
||||
def get_form_args(self, f, xargs=None):
|
||||
d = {}
|
||||
for (k, v) in f.items():
|
||||
n = len(v)
|
||||
if ((k not in self.arg_dict) and
|
||||
(k not in self.key_dict)):
|
||||
continue
|
||||
if n == 0:
|
||||
continue
|
||||
elif n == 1:
|
||||
d[k] = v[0]
|
||||
else:
|
||||
raise ArgError('Too many values for %s' % k)
|
||||
return self.get_args(d, xargs=xargs)
|
||||
|
||||
def coerce(self, type, v):
|
||||
try:
|
||||
if type == 'int':
|
||||
return int(v)
|
||||
if type == 'str':
|
||||
return str(v)
|
||||
if type == 'sxpr':
|
||||
return self.sxpr(v)
|
||||
except ArgError:
|
||||
raise
|
||||
except StandardError, ex:
|
||||
raise ArgError(str(ex))
|
||||
|
||||
def sxpr(self, v):
|
||||
if instanceof(v, types.ListType):
|
||||
return v
|
||||
if instanceof(v, types.File) or hasattr(v, 'readline'):
|
||||
return sxpr_file(v)
|
||||
if instanceof(v, types.StringType):
|
||||
return sxpr_file(StringIO(v))
|
||||
return str(v)
|
||||
|
||||
def sxpr_file(self, fin):
|
||||
try:
|
||||
vals = sxp.parse(fin)
|
||||
except:
|
||||
raise ArgError('Coercion to sxpr failed')
|
||||
if len(vals) == 1:
|
||||
return vals[0]
|
||||
else:
|
||||
raise ArgError('Too many sxprs')
|
||||
|
||||
def call_with_args(self, fn, args, xargs=None):
|
||||
(params, keys) = self.get_args(args, xargs=xargs)
|
||||
fn(*params, **keys)
|
||||
|
||||
def call_with_form_args(self, fn, fargs, xargs=None):
|
||||
(params, keys) = self.get_form_args(fargs, xargs=xargs)
|
||||
fn(*params, **keys)
|
||||
|
||||
class ArgFn(Args):
|
||||
"""Represent a remote HTTP operation as a function.
|
||||
Used on the client.
|
||||
"""
|
||||
|
||||
def __init__(self, fn, paramspec, keyspec={}):
|
||||
Args.__init__(self, paramspec, keyspec)
|
||||
self.fn = fn
|
||||
|
||||
def __call__(self, fargs, xargs=None):
|
||||
return self.call_with_args(self.fn, fargs, xargs=xargs)
|
||||
|
||||
class FormFn(Args):
|
||||
"""Represent an operation as a function over a form.
|
||||
Used in the HTTP server.
|
||||
"""
|
||||
|
||||
def __init__(self, fn, paramspec, keyspec={}):
|
||||
Args.__init__(self, paramspec, keyspec)
|
||||
self.fn = fn
|
||||
|
||||
def __call__(self, fargs, xargs=None):
|
||||
return self.call_with_form_args(self.fn, fargs, xargs=xargs)
|
||||
@@ -0,0 +1,215 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
"""Simple publish/subscribe event server.
|
||||
|
||||
"""
|
||||
import string
|
||||
|
||||
from twisted.internet import reactor
|
||||
|
||||
# subscribe a.b.c h: map a.b.c -> h
|
||||
# subscribe a.b.* h: map a.b.* -> h
|
||||
# subscribe a.b.? h: map a.b.? -> h
|
||||
#
|
||||
# for event a.b.c.d:
|
||||
#
|
||||
# lookup a.b.c.d, call handlers
|
||||
#
|
||||
# lookup a.b.c.?, call handlers
|
||||
#
|
||||
# lookup a.b.c.d.*, call handlers
|
||||
# lookup a.b.c.*, call handlers
|
||||
# lookup a.b.*, call handlers
|
||||
# lookup a.*, call handlers
|
||||
# lookup *, call handlers
|
||||
|
||||
# a.b.c.d = (a b c d)
|
||||
# a.b.c.? = (a b c _)
|
||||
# a.b.c.* = (a b c . _)
|
||||
|
||||
class EventServer:
|
||||
|
||||
DOT = '.'
|
||||
QUERY = '?'
|
||||
DOT_QUERY = DOT + QUERY
|
||||
STAR = '*'
|
||||
DOT_STAR = DOT + STAR
|
||||
|
||||
def __init__(self, run=0):
|
||||
self.handlers = {}
|
||||
self.run = run
|
||||
self.queue = []
|
||||
|
||||
def start(self):
|
||||
"""Enable event handling. Sends any queued events.
|
||||
"""
|
||||
self.run = 1
|
||||
for (e,v) in self.queue:
|
||||
self.inject(e, v)
|
||||
self.queue = []
|
||||
|
||||
def stop(self):
|
||||
"""Suspend event handling. Events injected while suspended
|
||||
are queued until we are started again.
|
||||
"""
|
||||
self.run = 0
|
||||
|
||||
def subscribe(self, event, handler):
|
||||
"""Subscribe to an event. For example 'a.b.c.d'.
|
||||
A subcription like 'a.b.c.?' ending in '?' matches any value
|
||||
for the '?'. A subscription like 'a.b.c.*' ending in '*' matches
|
||||
any event type with the same prefix, 'a.b.c' in this case.
|
||||
|
||||
event event name
|
||||
handler event handler fn(event, val)
|
||||
"""
|
||||
hl = self.handlers.get(event)
|
||||
if hl is None:
|
||||
self.handlers[event] = [handler]
|
||||
else:
|
||||
hl.append(handler)
|
||||
|
||||
def unsubscribe_all(self, event=None):
|
||||
"""Unsubscribe all handlers for a given event, or all handlers.
|
||||
|
||||
event event (optional)
|
||||
"""
|
||||
if event == None:
|
||||
self.handlers.clear()
|
||||
elif event in self.handlers:
|
||||
del self.handlers[event]
|
||||
|
||||
def unsubscribe(self, event, handler):
|
||||
"""Unsubscribe a given event and handler.
|
||||
|
||||
event event
|
||||
handler handler
|
||||
"""
|
||||
hl = self.handlers.get(event)
|
||||
if hl is None:
|
||||
return
|
||||
if handler in hl:
|
||||
hl.remove(handler)
|
||||
|
||||
def inject(self, event, val, async=1):
|
||||
"""Inject an event. Handlers for it are called if running, otherwise
|
||||
it is queued.
|
||||
|
||||
event event type
|
||||
val event value
|
||||
"""
|
||||
if self.run:
|
||||
if async:
|
||||
reactor.callLater(0, self.call_handlers, event, val)
|
||||
else:
|
||||
self.notify_handlers(event, val)
|
||||
else:
|
||||
self.queue.append( (event, val) )
|
||||
|
||||
def call_handlers(self, event, val):
|
||||
"""Internal method to call event handlers.
|
||||
"""
|
||||
#print ">event", event, val
|
||||
self.call_event_handlers(event, event, val)
|
||||
self.call_query_handlers(event, val)
|
||||
self.call_star_handlers(event, val)
|
||||
|
||||
def call_event_handlers(self, key, event, val):
|
||||
"""Call the handlers for an event.
|
||||
It is safe for handlers to subscribe or unsubscribe.
|
||||
|
||||
key key for handler list
|
||||
event event type
|
||||
val event value
|
||||
"""
|
||||
hl = self.handlers.get(key)
|
||||
if hl is None:
|
||||
return
|
||||
# Copy the handler list so that handlers can call
|
||||
# subscribe/unsubscribe safely - python list iteration
|
||||
# is not safe against list modification.
|
||||
for h in hl[:]:
|
||||
try:
|
||||
h(event, val)
|
||||
except:
|
||||
pass
|
||||
|
||||
def call_query_handlers(self, event, val):
|
||||
"""Call regex handlers for events matching 'event' that end in '?'.
|
||||
|
||||
event event type
|
||||
val event value
|
||||
"""
|
||||
dot_idx = event.rfind(self.DOT)
|
||||
if dot_idx == -1:
|
||||
self.call_event_handlers(self.QUERY, event, val)
|
||||
else:
|
||||
event_query = event[0:dot_idx] + self.DOT_QUERY
|
||||
self.call_event_handlers(event_query, event, val)
|
||||
|
||||
def call_star_handlers(self, event, val):
|
||||
"""Call regex handlers for events matching 'event' that end in '*'.
|
||||
|
||||
event event type
|
||||
val event value
|
||||
"""
|
||||
etype = string.split(event, self.DOT)
|
||||
for i in range(len(etype), 0, -1):
|
||||
event_star = self.DOT.join(etype[0:i]) + self.DOT_STAR
|
||||
self.call_event_handlers(event_star, event, val)
|
||||
self.call_event_handlers(self.STAR, event, val)
|
||||
|
||||
def instance():
|
||||
global inst
|
||||
try:
|
||||
inst
|
||||
except:
|
||||
inst = EventServer()
|
||||
inst.start()
|
||||
return inst
|
||||
|
||||
def main():
|
||||
def sys_star(event, val):
|
||||
print 'sys_star', event, val
|
||||
|
||||
def sys_foo(event, val):
|
||||
print 'sys_foo', event, val
|
||||
s.unsubscribe('sys.foo', sys_foo)
|
||||
|
||||
def sys_foo2(event, val):
|
||||
print 'sys_foo2', event, val
|
||||
|
||||
def sys_bar(event, val):
|
||||
print 'sys_bar', event, val
|
||||
|
||||
def sys_foo_bar(event, val):
|
||||
print 'sys_foo_bar', event, val
|
||||
|
||||
def foo_bar(event, val):
|
||||
print 'foo_bar', event, val
|
||||
|
||||
s = EventServer()
|
||||
s.start()
|
||||
s.subscribe('sys.*', sys_star)
|
||||
s.subscribe('sys.foo', sys_foo)
|
||||
s.subscribe('sys.foo', sys_foo2)
|
||||
s.subscribe('sys.bar', sys_bar)
|
||||
s.subscribe('sys.foo.bar', sys_foo_bar)
|
||||
s.subscribe('foo.bar', foo_bar)
|
||||
s.inject('sys.foo', 'hello')
|
||||
print
|
||||
s.inject('sys.bar', 'hello again')
|
||||
print
|
||||
s.inject('sys.foo.bar', 'hello again')
|
||||
print
|
||||
s.inject('foo.bar', 'hello again')
|
||||
print
|
||||
s.inject('foo', 'hello again')
|
||||
print
|
||||
s.start()
|
||||
s.unsubscribe('sys.*', sys_star)
|
||||
s.unsubscribe_all('sys.*')
|
||||
s.inject('sys.foo', 'hello')
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
## XEND_DOMAIN_CREATE = "xend.domain.create": dom
|
||||
## create:
|
||||
## xend.domain.destroy: dom, reason:died/crashed
|
||||
## xend.domain.up ?
|
||||
|
||||
## xend.domain.unpause: dom
|
||||
## xend.domain.pause: dom
|
||||
## xend.domain.shutdown: dom
|
||||
## xend.domain.destroy: dom
|
||||
|
||||
## xend.domain.migrate.begin: dom, to
|
||||
## Begin tells: src host, src domain uri, dst host. Dst id known?
|
||||
## err: src host, src domain uri, dst host, dst id if known, status (of domain: ok, dead,...), reason
|
||||
## end: src host, src domain uri, dst host, dst uri
|
||||
|
||||
## Events for both ends of migrate: for exporter and importer?
|
||||
## Include migrate id so can tie together.
|
||||
## Have uri /xend/migrate/<id> for migrate info (migrations in progress).
|
||||
|
||||
## (xend.domain.migrate.begin (src <host>) (src.domain <id>)
|
||||
## (dst <host>) (id <migrate id>))
|
||||
|
||||
## xend.domain.migrate.end:
|
||||
## (xend.domain.migrate.end (domain <id>) (to <host>)
|
||||
|
||||
## xend.node.up: xend uri
|
||||
## xend.node.down: xend uri
|
||||
|
||||
## xend.error ?
|
||||
|
||||
## format:
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
"""General pretty-printer, including support for SXP.
|
||||
|
||||
"""
|
||||
import sys
|
||||
import types
|
||||
import StringIO
|
||||
import sxp
|
||||
|
||||
class PrettyItem:
|
||||
|
||||
def __init__(self, width):
|
||||
self.width = width
|
||||
|
||||
def insert(self, block):
|
||||
block.addtoline(self)
|
||||
|
||||
def get_width(self):
|
||||
return self.width
|
||||
|
||||
def output(self, out):
|
||||
print '***PrettyItem>output>', self
|
||||
pass
|
||||
|
||||
def prettyprint(self, out, width):
|
||||
print '***PrettyItem>prettyprint>', self
|
||||
return width
|
||||
|
||||
class PrettyString(PrettyItem):
|
||||
|
||||
def __init__(self, x):
|
||||
PrettyItem.__init__(self, len(x))
|
||||
self.value = x
|
||||
|
||||
def output(self, out):
|
||||
out.write(self.value)
|
||||
|
||||
def prettyprint(self, line):
|
||||
line.output(self)
|
||||
|
||||
def show(self, out):
|
||||
print >> out, ("(string (width %d) '%s')" % (self.width, self.value))
|
||||
|
||||
class PrettySpace(PrettyItem):
|
||||
|
||||
def output(self, out):
|
||||
out.write(' ' * self.width)
|
||||
|
||||
def prettyprint(self, line):
|
||||
line.output(self)
|
||||
|
||||
def show(self, out):
|
||||
print >> out, ("(space (width %d))" % self.width)
|
||||
|
||||
class PrettyBreak(PrettyItem):
|
||||
|
||||
def __init__(self, width, indent):
|
||||
PrettyItem.__init__(self, width)
|
||||
self.indent = indent
|
||||
self.space = 0
|
||||
self.active = 0
|
||||
|
||||
def output(self, out):
|
||||
out.write(' ' * self.width)
|
||||
|
||||
def prettyprint(self, line):
|
||||
if line.breaks(self.space):
|
||||
self.active = 1
|
||||
line.newline(self.indent)
|
||||
else:
|
||||
line.output(self)
|
||||
|
||||
def show(self, out):
|
||||
print >> out, ("(break (width %d) (indent %d) (space %d) (active %d))"
|
||||
% (self.width, self.indent, self.space, self.lspace, self.active))
|
||||
|
||||
class PrettyNewline(PrettySpace):
|
||||
|
||||
def __init__(self, indent):
|
||||
PrettySpace.__init__(self, indent)
|
||||
|
||||
def insert(self, block):
|
||||
block.newline()
|
||||
block.addtoline(self)
|
||||
|
||||
def output(self, out):
|
||||
out.write(' ' * self.width)
|
||||
|
||||
def prettyprint(self, line):
|
||||
line.newline(0)
|
||||
line.output(self)
|
||||
|
||||
def show(self, out):
|
||||
print >> out, ("(nl (indent %d))" % self.indent)
|
||||
|
||||
class PrettyLine(PrettyItem):
|
||||
def __init__(self):
|
||||
PrettyItem.__init__(self, 0)
|
||||
self.content = []
|
||||
|
||||
def write(self, x):
|
||||
self.content.append(x)
|
||||
|
||||
def end(self):
|
||||
width = 0
|
||||
lastwidth = 0
|
||||
lastbreak = None
|
||||
for x in self.content:
|
||||
if isinstance(x, PrettyBreak):
|
||||
if lastbreak:
|
||||
lastbreak.space = (width - lastwidth)
|
||||
lastbreak = x
|
||||
lastwidth = width
|
||||
width += x.get_width()
|
||||
if lastbreak:
|
||||
lastbreak.space = (width - lastwidth)
|
||||
self.width = width
|
||||
|
||||
def prettyprint(self, line):
|
||||
for x in self.content:
|
||||
x.prettyprint(line)
|
||||
|
||||
def show(self, out):
|
||||
print >> out, '(LINE (width %d)' % self.width
|
||||
for x in self.content:
|
||||
x.show(out)
|
||||
print >> out, ')'
|
||||
|
||||
class PrettyBlock(PrettyItem):
|
||||
|
||||
def __init__(self, all=0, parent=None):
|
||||
self.width = 0
|
||||
self.lines = []
|
||||
self.parent = parent
|
||||
self.indent = 0
|
||||
self.all = all
|
||||
self.broken = 0
|
||||
self.newline()
|
||||
|
||||
def add(self, item):
|
||||
item.insert(self)
|
||||
|
||||
def end(self):
|
||||
self.width = 0
|
||||
for l in self.lines:
|
||||
l.end()
|
||||
if self.width < l.width:
|
||||
self.width = l.width
|
||||
|
||||
def breaks(self, n):
|
||||
return self.all and self.broken
|
||||
|
||||
def newline(self):
|
||||
self.lines.append(PrettyLine())
|
||||
|
||||
def addtoline(self, x):
|
||||
self.lines[-1].write(x)
|
||||
|
||||
def prettyprint(self, line):
|
||||
self.indent = line.used
|
||||
line.block = self
|
||||
if not line.fits(self.width):
|
||||
self.broken = 1
|
||||
for l in self.lines:
|
||||
l.prettyprint(line)
|
||||
line.block = self.parent
|
||||
|
||||
def show(self, out):
|
||||
print >> out, ('(BLOCK (width %d) (indent %d) (all %d) (broken %d)' %
|
||||
(self.width, self.indent, self.all, self.broken))
|
||||
for l in self.lines:
|
||||
l.show(out)
|
||||
print >> out, ')'
|
||||
|
||||
class Line:
|
||||
|
||||
def __init__(self, out, width):
|
||||
self.out = out
|
||||
self.width = width
|
||||
self.used = 0
|
||||
self.space = self.width
|
||||
|
||||
def newline(self, indent):
|
||||
indent += self.block.indent
|
||||
self.out.write('\n')
|
||||
self.out.write(' ' * indent)
|
||||
self.used = indent
|
||||
self.space = self.width - self.used
|
||||
|
||||
def fits(self, n):
|
||||
return self.space - n >= 0
|
||||
|
||||
def breaks(self, n):
|
||||
return self.block.breaks(n) or not self.fits(n)
|
||||
|
||||
def output(self, x):
|
||||
n = x.get_width()
|
||||
self.space -= n
|
||||
self.used += n
|
||||
if self.space < 0:
|
||||
self.space = 0
|
||||
x.output(self.out)
|
||||
|
||||
class PrettyPrinter:
|
||||
"""A prettyprinter based on what I remember of Derek Oppen's
|
||||
prettyprint algorithm from TOPLAS way back.
|
||||
"""
|
||||
|
||||
def __init__(self, width=40):
|
||||
self.width = width
|
||||
self.block = None
|
||||
self.top = None
|
||||
|
||||
def write(self, x):
|
||||
self.block.add(PrettyString(x))
|
||||
|
||||
def add(self, item):
|
||||
self.block.add(item)
|
||||
|
||||
def addbreak(self, width=1, indent=4):
|
||||
self.add(PrettyBreak(width, indent))
|
||||
|
||||
def addspace(self, width=1):
|
||||
self.add(PrettySpace(width))
|
||||
|
||||
def addnl(self, indent=0):
|
||||
self.add(PrettyNewline(indent))
|
||||
|
||||
def begin(self, all=0):
|
||||
block = PrettyBlock(all=all, parent=self.block)
|
||||
self.block = block
|
||||
|
||||
def end(self):
|
||||
self.block.end()
|
||||
if self.block.parent:
|
||||
self.block.parent.add(self.block)
|
||||
else:
|
||||
self.top = self.block
|
||||
self.block = self.block.parent
|
||||
|
||||
def prettyprint(self, out=sys.stdout):
|
||||
line = Line(out, self.width)
|
||||
self.top.prettyprint(line)
|
||||
|
||||
class SXPPrettyPrinter(PrettyPrinter):
|
||||
"""An SXP prettyprinter.
|
||||
"""
|
||||
|
||||
def pstring(self, x):
|
||||
io = StringIO.StringIO()
|
||||
sxp.show(x, out=io)
|
||||
io.seek(0)
|
||||
val = io.getvalue()
|
||||
io.close()
|
||||
return val
|
||||
|
||||
def pprint(self, l):
|
||||
if isinstance(l, types.ListType):
|
||||
self.begin(all=1)
|
||||
self.write('(')
|
||||
i = 0
|
||||
for x in l:
|
||||
if(i): self.addbreak()
|
||||
self.pprint(x)
|
||||
i += 1
|
||||
self.addbreak(width=0, indent=0)
|
||||
self.write(')')
|
||||
self.end()
|
||||
else:
|
||||
self.write(self.pstring(l))
|
||||
|
||||
def prettyprint(sxpr, out=sys.stdout, width=80):
|
||||
"""Prettyprint an SXP form.
|
||||
|
||||
sxpr s-expression
|
||||
out destination
|
||||
width maximum output width
|
||||
"""
|
||||
if isinstance(sxpr, types.ListType):
|
||||
pp = SXPPrettyPrinter(width=width)
|
||||
pp.pprint(sxpr)
|
||||
pp.prettyprint(out=out)
|
||||
else:
|
||||
sxp.show(sxpr, out=out)
|
||||
print >> out
|
||||
|
||||
def main():
|
||||
pin = sxp.Parser()
|
||||
while 1:
|
||||
buf = sys.stdin.read(100)
|
||||
pin.input(buf)
|
||||
if buf == '': break
|
||||
l = pin.get_val()
|
||||
prettyprint(l, width=80)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
VIFCTL = '/etc/xen/xend/vifctl'
|
||||
|
||||
def init():
|
||||
os.system(VIFCTL + ' init ')
|
||||
|
||||
def up(vif, mac=None, bridge=None, ipaddr=[]):
|
||||
args = ['vif=%s' % vif]
|
||||
if mac:
|
||||
args.append('mac=%s' % mac)
|
||||
if bridge:
|
||||
args.append('bridge=%s' % bridge)
|
||||
if ipaddr:
|
||||
args.append('ipaddr=%s' % ','.join(ipaddr))
|
||||
os.system(VIFCTL + ' up ' + ' '.join(args))
|
||||
|
||||
def down(vif, mac=None, bridge=None, ipaddr=[]):
|
||||
args = ['vif=%s' % vif]
|
||||
if mac:
|
||||
args.append('mac=%s' % mac)
|
||||
if bridge:
|
||||
args.append('bridge=%s' % bridge)
|
||||
if ipaddr:
|
||||
args.append('ipaddr=%s' % ','.join(ipaddr))
|
||||
os.system(VIFCTL + ' down ' + ' '.join(args))
|
||||
@@ -0,0 +1,311 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
"""Client API for the HTTP interface on xend.
|
||||
Callable as a script - see main().
|
||||
"""
|
||||
import sys
|
||||
import httplib
|
||||
import types
|
||||
from StringIO import StringIO
|
||||
import urlparse
|
||||
|
||||
from encode import *
|
||||
import sxp
|
||||
import PrettyPrint
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
class Foo(httplib.HTTPResponse):
|
||||
|
||||
def begin(self):
|
||||
fin = self.fp
|
||||
while(1):
|
||||
buf = fin.readline()
|
||||
print "***", buf
|
||||
if buf == '':
|
||||
print
|
||||
sys.exit()
|
||||
|
||||
|
||||
def sxprio(sxpr):
|
||||
io = StringIO()
|
||||
sxp.show(sxpr, out=io)
|
||||
print >> io
|
||||
io.seek(0)
|
||||
return io
|
||||
|
||||
def fileof(val):
|
||||
"""Converter for passing configs.
|
||||
Handles lists, files directly.
|
||||
Assumes a string is a file name and passes its contents.
|
||||
"""
|
||||
if isinstance(val, types.ListType):
|
||||
return sxprio(val)
|
||||
if isinstance(val, types.StringType):
|
||||
return file(val)
|
||||
if hasattr(val, 'readlines'):
|
||||
return val
|
||||
|
||||
# todo: need to sort of what urls/paths are using for objects.
|
||||
# e.g. for domains at the moment return '0'.
|
||||
# should probably return abs path w.r.t. server, e.g. /xend/domain/0.
|
||||
# As an arg, assume abs path is obj uri, otherwise just id.
|
||||
|
||||
# Function to convert to full url: Xend.uri(path), e.g.
|
||||
# maps /xend/domain/0 to http://wray-m-3.hpl.hp.com:8000/xend/domain/0
|
||||
# And should accept urls for ids?
|
||||
|
||||
def urljoin(location, root, prefix='', rest=''):
|
||||
prefix = str(prefix)
|
||||
rest = str(rest)
|
||||
base = 'http://' + location + root + prefix
|
||||
url = urlparse.urljoin(base, rest)
|
||||
return url
|
||||
|
||||
def nodeurl(location, root, id=''):
|
||||
return urljoin(location, root, 'node/', id)
|
||||
|
||||
def domainurl(location, root, id=''):
|
||||
return urljoin(location, root, 'domain/', id)
|
||||
|
||||
def consoleurl(location, root, id=''):
|
||||
return urljoin(location, root, 'console/', id)
|
||||
|
||||
def deviceurl(location, root, id=''):
|
||||
return urljoin(location, root, 'device/', id)
|
||||
|
||||
def vneturl(location, root, id=''):
|
||||
return urljoin(location, root, 'vnet/', id)
|
||||
|
||||
def eventurl(location, root, id=''):
|
||||
return urljoin(location, root, 'event/', id)
|
||||
|
||||
def xend_request(url, method, data=None):
|
||||
urlinfo = urlparse.urlparse(url)
|
||||
(uproto, ulocation, upath, uparam, uquery, ufrag) = urlinfo
|
||||
if DEBUG: print url, urlinfo
|
||||
if uproto != 'http':
|
||||
raise StandardError('Invalid protocol: ' + uproto)
|
||||
if DEBUG: print '>xend_request', ulocation, upath, method, data
|
||||
(hdr, args) = encode_data(data)
|
||||
if data and method == 'GET':
|
||||
upath += '?' + args
|
||||
args = None
|
||||
if method == "POST" and upath.endswith('/'):
|
||||
upath = upath[:-1]
|
||||
if DEBUG: print "ulocation=", ulocation, "upath=", upath, "args=", args
|
||||
#hdr['User-Agent'] = 'Mozilla'
|
||||
#hdr['Accept'] = 'text/html,text/plain'
|
||||
conn = httplib.HTTPConnection(ulocation)
|
||||
#conn.response_class = Foo
|
||||
if DEBUG: conn.set_debuglevel(1)
|
||||
conn.request(method, upath, args, hdr)
|
||||
resp = conn.getresponse()
|
||||
if DEBUG: print resp.status, resp.reason
|
||||
if DEBUG: print resp.msg.headers
|
||||
if resp.status in [204, 404]:
|
||||
return None
|
||||
if resp.status not in [200, 201, 202, 203]:
|
||||
raise RuntimeError(resp.reason)
|
||||
pin = sxp.Parser()
|
||||
data = resp.read()
|
||||
if DEBUG: print "***data" , data
|
||||
if DEBUG: print "***"
|
||||
pin.input(data);
|
||||
pin.input_eof()
|
||||
conn.close()
|
||||
val = pin.get_val()
|
||||
#if isinstance(val, types.ListType) and sxp.name(val) == 'val':
|
||||
# val = val[1]
|
||||
if isinstance(val, types.ListType) and sxp.name(val) == 'err':
|
||||
raise RuntimeError(val[1])
|
||||
if DEBUG: print '**val='; sxp.show(val); print
|
||||
return val
|
||||
|
||||
def xend_get(url, args=None):
|
||||
return xend_request(url, "GET", args)
|
||||
|
||||
def xend_call(url, data):
|
||||
return xend_request(url, "POST", data)
|
||||
|
||||
class Xend:
|
||||
|
||||
SRV_DEFAULT = "localhost:8000"
|
||||
ROOT_DEFAULT = "/xend/"
|
||||
|
||||
def __init__(self, srv=None, root=None):
|
||||
self.bind(srv, root)
|
||||
|
||||
def bind(self, srv=None, root=None):
|
||||
if srv is None: srv = self.SRV_DEFAULT
|
||||
if root is None: root = self.ROOT_DEFAULT
|
||||
if not root.endswith('/'): root += '/'
|
||||
self.location = srv
|
||||
self.root = root
|
||||
|
||||
def nodeurl(self, id=''):
|
||||
return nodeurl(self.location, self.root, id)
|
||||
|
||||
def domainurl(self, id=''):
|
||||
return domainurl(self.location, self.root, id)
|
||||
|
||||
def consoleurl(self, id=''):
|
||||
return consoleurl(self.location, self.root, id)
|
||||
|
||||
def deviceurl(self, id=''):
|
||||
return deviceurl(self.location, self.root, id)
|
||||
|
||||
def vneturl(self, id=''):
|
||||
return vneturl(self.location, self.root, id)
|
||||
|
||||
def eventurl(self, id=''):
|
||||
return eventurl(self.location, self.root, id)
|
||||
|
||||
def xend(self):
|
||||
return xend_get(urljoin(self.location, self.root))
|
||||
|
||||
def xend_node(self):
|
||||
return xend_get(self.nodeurl())
|
||||
|
||||
def xend_node_cpu_rrobin_slice_set(self, slice):
|
||||
return xend_call(self.nodeurl(),
|
||||
{'op' : 'cpu_rrobin_slice_set',
|
||||
'slice' : slice })
|
||||
|
||||
def xend_node_cpu_bvt_slice_set(self, slice):
|
||||
return xend_call(self.nodeurl(),
|
||||
{'op' : 'cpu_bvt_slice_set',
|
||||
'slice' : slice })
|
||||
|
||||
def xend_domains(self):
|
||||
return xend_get(self.domainurl())
|
||||
|
||||
def xend_domain_create(self, conf):
|
||||
return xend_call(self.domainurl(),
|
||||
{'op' : 'create',
|
||||
'config' : fileof(conf) })
|
||||
|
||||
def xend_domain(self, id):
|
||||
return xend_get(self.domainurl(id))
|
||||
|
||||
def xend_domain_unpause(self, id):
|
||||
return xend_call(self.domainurl(id),
|
||||
{'op' : 'unpause'})
|
||||
|
||||
def xend_domain_pause(self, id):
|
||||
return xend_call(self.domainurl(id),
|
||||
{'op' : 'pause'})
|
||||
|
||||
def xend_domain_shutdown(self, id):
|
||||
return xend_call(self.domainurl(id),
|
||||
{'op' : 'shutdown'})
|
||||
|
||||
def xend_domain_destroy(self, id):
|
||||
return xend_call(self.domainurl(id),
|
||||
{'op' : 'destroy'})
|
||||
|
||||
def xend_domain_save(self, id, filename):
|
||||
return xend_call(self.domainurl(id),
|
||||
{'op' : 'save',
|
||||
'file' : filename})
|
||||
|
||||
def xend_domain_restore(self, id, filename):
|
||||
return xend_call(self.domainurl(id),
|
||||
{'op' : 'restore',
|
||||
'file' : filename })
|
||||
|
||||
def xend_domain_migrate(self, id, dst):
|
||||
return xend_call(self.domainurl(id),
|
||||
{'op' : 'migrate',
|
||||
'destination': dst})
|
||||
|
||||
def xend_domain_pincpu(self, id, cpu):
|
||||
return xend_call(self.domainurl(id),
|
||||
{'op' : 'pincpu',
|
||||
'cpu' : cpu})
|
||||
|
||||
def xend_domain_cpu_bvt_set(self, id, mcuadv, warp, warpl, warpu):
|
||||
return xend_call(self.domainurl(id),
|
||||
{'op' : 'cpu_bvt_set',
|
||||
'mcuadv' : mvuadv,
|
||||
'warp' : warp,
|
||||
'warpl' : warpl,
|
||||
'warpu' : warpu })
|
||||
|
||||
def xend_domain_cpu_atropos_set(self, id, period, slice, latency, xtratime):
|
||||
return xend_call(self.domainurl(id),
|
||||
{'op' : 'cpu_atropos_set',
|
||||
'period' : period,
|
||||
'slice' : slice,
|
||||
'latency' : latency,
|
||||
'xtratime': xtratime })
|
||||
|
||||
def xend_domain_vifs(self, id):
|
||||
return xend_get(self.domainurl(id),
|
||||
{ 'op' : 'vifs' })
|
||||
|
||||
def xend_domain_vif_ip_add(self, id, vif, ipaddr):
|
||||
return xend_call(self.domainurl(id),
|
||||
{'op' : 'vif_ip_add',
|
||||
'vif' : vif,
|
||||
'ip' : ipaddr })
|
||||
|
||||
def xend_domain_vbds(self, id):
|
||||
return xend_get(self.domainurl(id),
|
||||
{'op' : 'vbds'})
|
||||
|
||||
def xend_domain_vbd(self, id, vbd):
|
||||
return xend_get(self.domainurl(id),
|
||||
{'op' : 'vbd',
|
||||
'vbd' : vbd})
|
||||
|
||||
def xend_consoles(self):
|
||||
return xend_get(self.consoleurl())
|
||||
|
||||
def xend_console(self, id):
|
||||
return xend_get(self.consoleurl(id))
|
||||
|
||||
def xend_vnets(self):
|
||||
return xend_get(self.vneturl())
|
||||
|
||||
def xend_vnet_create(self, conf):
|
||||
return xend_call(self.vneturl(),
|
||||
{'op': 'create', 'config': fileof(conf) })
|
||||
|
||||
def xend_vnet(self, id):
|
||||
return xend_get(self.vneturl(id))
|
||||
|
||||
def xend_vnet_delete(self, id):
|
||||
return xend_call(self.vneturl(id),
|
||||
{'op': 'delete'})
|
||||
|
||||
def xend_event_inject(self, sxpr):
|
||||
val = xend_call(self.eventurl(),
|
||||
{'op': 'inject', 'event': fileof(sxpr) })
|
||||
|
||||
|
||||
def main(argv):
|
||||
"""Call an API function:
|
||||
|
||||
python XendClient.py fn args...
|
||||
|
||||
The leading 'xend_' on the function can be omitted.
|
||||
Example:
|
||||
|
||||
> python XendClient.py domains
|
||||
(domain 0 8)
|
||||
> python XendClient.py domain 0
|
||||
(domain (id 0) (name Domain-0) (memory 128))
|
||||
"""
|
||||
server = Xend()
|
||||
fn = argv[1]
|
||||
if not fn.startswith('xend'):
|
||||
fn = 'xend_' + fn
|
||||
args = argv[2:]
|
||||
val = getattr(server, fn)(*args)
|
||||
PrettyPrint.prettyprint(val)
|
||||
print
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
||||
else:
|
||||
server = Xend()
|
||||
@@ -0,0 +1,179 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
import socket
|
||||
import xen.ext.xc
|
||||
xc = xen.ext.xc.new()
|
||||
|
||||
import sxp
|
||||
import XendRoot
|
||||
xroot = XendRoot.instance()
|
||||
import XendDB
|
||||
|
||||
import EventServer
|
||||
eserver = EventServer.instance()
|
||||
|
||||
from xen.xend.server import SrvDaemon
|
||||
daemon = SrvDaemon.instance()
|
||||
|
||||
class XendConsoleInfo:
|
||||
"""Console information record.
|
||||
"""
|
||||
|
||||
def __init__(self, console, dom1, port1, dom2, port2, conn=None):
|
||||
self.console = console
|
||||
self.dom1 = int(dom1)
|
||||
self.port1 = int(port1)
|
||||
self.dom2 = int(dom2)
|
||||
self.port2 = int(port2)
|
||||
self.conn = conn
|
||||
#self.id = "%d.%d-%d.%d" % (self.dom1, self.port1, self.dom2, self.port2)
|
||||
self.id = str(port1)
|
||||
|
||||
def __str__(self):
|
||||
s = "console"
|
||||
s += " id=%s" % self.id
|
||||
s += " src=%d.%d" % (self.dom1, self.port1)
|
||||
s += " dst=%d.%d" % (self.dom2, self.port2)
|
||||
s += " port=%s" % self.console
|
||||
if self.conn:
|
||||
s += " conn=%s:%s" % (self.conn[0], self.conn[1])
|
||||
return s
|
||||
|
||||
def sxpr(self):
|
||||
sxpr = ['console',
|
||||
['id', self.id],
|
||||
['src', self.dom1, self.port1],
|
||||
['dst', self.dom2, self.port2],
|
||||
['port', self.console],
|
||||
]
|
||||
if self.conn:
|
||||
sxpr.append(['connected', self.conn[0], self.conn[1]])
|
||||
return sxpr
|
||||
|
||||
def connection(self):
|
||||
return self.conn
|
||||
|
||||
def update(self, consinfo):
|
||||
conn = sxp.child(consinfo, 'connected')
|
||||
if conn:
|
||||
self.conn = conn[1:]
|
||||
else:
|
||||
self.conn = None
|
||||
|
||||
def uri(self):
|
||||
"""Get the uri to use to connect to the console.
|
||||
This will be a telnet: uri.
|
||||
|
||||
return uri
|
||||
"""
|
||||
host = socket.gethostname()
|
||||
return "telnet://%s:%s" % (host, self.console)
|
||||
|
||||
class XendConsole:
|
||||
|
||||
dbpath = "console"
|
||||
|
||||
def __init__(self):
|
||||
self.db = XendDB.XendDB(self.dbpath)
|
||||
self.console = {}
|
||||
self.console_db = self.db.fetchall("")
|
||||
if xroot.get_rebooted():
|
||||
print 'XendConsole> rebooted: removing all console info'
|
||||
self.rm_all()
|
||||
eserver.subscribe('xend.domain.died', self.onDomainDied)
|
||||
eserver.subscribe('xend.domain.destroy', self.onDomainDied)
|
||||
|
||||
def rm_all(self):
|
||||
"""Remove all console info. Used after reboot.
|
||||
"""
|
||||
for (k, v) in self.console_db.items():
|
||||
self._delete_console(k)
|
||||
|
||||
def refresh(self):
|
||||
consoles = daemon.consoles()
|
||||
cons = {}
|
||||
for consinfo in consoles:
|
||||
id = str(sxp.child_value(consinfo, 'id'))
|
||||
cons[id] = consinfo
|
||||
if id not in self.console:
|
||||
self._new_console(consinfo)
|
||||
for c in self.console.values():
|
||||
consinfo = cons.get(c.id)
|
||||
if consinfo:
|
||||
c.update(consinfo)
|
||||
else:
|
||||
self._delete_console(c.id)
|
||||
|
||||
def onDomainDied(self, event, val):
|
||||
dom = int(val)
|
||||
#print 'XendConsole>onDomainDied', 'event', event, "dom=", dom
|
||||
for c in self.consoles():
|
||||
#print 'onDomainDied', "dom=", dom, "dom1=", c.dom1, "dom2=", c.dom2
|
||||
if (c.dom1 == dom) or (c.dom2 == dom):
|
||||
'XendConsole>onDomainDied', 'delete console dom=', dom
|
||||
ctrl = daemon.get_domain_console(dom)
|
||||
if ctrl:
|
||||
ctrl.close()
|
||||
self._delete_console(c.id)
|
||||
|
||||
def sync(self):
|
||||
self.db.saveall("", self.console_db)
|
||||
|
||||
def sync_console(self, id):
|
||||
self.db.save(id, self.console_db[id])
|
||||
|
||||
def _new_console(self, consinfo):
|
||||
# todo: xen needs a call to get current domain id.
|
||||
dom1 = 0
|
||||
port1 = sxp.child_value(consinfo, 'local_port')
|
||||
dom2 = sxp.child_value(consinfo, 'domain')
|
||||
port2 = sxp.child_value(consinfo, 'remote_port')
|
||||
console = sxp.child_value(consinfo, 'console_port')
|
||||
info = XendConsoleInfo(console, dom1, int(port1), int(dom2), int(port2))
|
||||
info.update(consinfo)
|
||||
self._add_console(info.id, info)
|
||||
return info
|
||||
|
||||
def _add_console(self, id, info):
|
||||
self.console[id] = info
|
||||
self.console_db[id] = info.sxpr()
|
||||
self.sync_console(id)
|
||||
|
||||
def _delete_console(self, id):
|
||||
if id in self.console:
|
||||
del self.console[id]
|
||||
if id in self.console_db:
|
||||
del self.console_db[id]
|
||||
self.db.delete(id)
|
||||
|
||||
def console_ls(self):
|
||||
self.refresh()
|
||||
return self.console.keys()
|
||||
|
||||
def consoles(self):
|
||||
self.refresh()
|
||||
return self.console.values()
|
||||
|
||||
def console_create(self, dom):
|
||||
consinfo = daemon.console_create(dom)
|
||||
info = self._new_console(consinfo)
|
||||
return info
|
||||
|
||||
def console_get(self, id):
|
||||
self.refresh()
|
||||
return self.console.get(id)
|
||||
|
||||
def console_delete(self, id):
|
||||
self._delete_console(id)
|
||||
|
||||
def console_disconnect(self, id):
|
||||
id = int(id)
|
||||
daemon.console_disconnect(id)
|
||||
|
||||
def instance():
|
||||
global inst
|
||||
try:
|
||||
inst
|
||||
except:
|
||||
inst = XendConsole()
|
||||
return inst
|
||||
@@ -0,0 +1,91 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import errno
|
||||
import dircache
|
||||
import time
|
||||
|
||||
import sxp
|
||||
import XendRoot
|
||||
xroot = XendRoot.instance()
|
||||
|
||||
class XendDB:
|
||||
"""Persistence for Xend. Stores data in files and directories.
|
||||
"""
|
||||
|
||||
def __init__(self, path=None):
|
||||
self.dbpath = xroot.get_dbroot()
|
||||
if path:
|
||||
self.dbpath = os.path.join(self.dbpath, path)
|
||||
pass
|
||||
|
||||
def filepath(self, path):
|
||||
return os.path.join(self.dbpath, path)
|
||||
|
||||
def fetch(self, path):
|
||||
fpath = self.filepath(path)
|
||||
return self.fetchfile(fpath)
|
||||
|
||||
def fetchfile(self, fpath):
|
||||
pin = sxp.Parser()
|
||||
fin = file(fpath, "rb")
|
||||
try:
|
||||
while 1:
|
||||
try:
|
||||
buf = fin.read(1024)
|
||||
except IOError, ex:
|
||||
if ex.errno == errno.EINTR:
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
pin.input(buf)
|
||||
if buf == '':
|
||||
pin.input_eof()
|
||||
break
|
||||
finally:
|
||||
fin.close()
|
||||
return pin.get_val()
|
||||
|
||||
def save(self, path, sxpr):
|
||||
fpath = self.filepath(path)
|
||||
return self.savefile(fpath, sxpr)
|
||||
|
||||
def savefile(self, fpath, sxpr):
|
||||
fdir = os.path.dirname(fpath)
|
||||
if not os.path.isdir(fdir):
|
||||
os.makedirs(fdir)
|
||||
fout = file(fpath, "wb+")
|
||||
try:
|
||||
t = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||||
fout.write("# %s %s\n" % (fpath, t))
|
||||
sxp.show(sxpr, out=fout)
|
||||
finally:
|
||||
fout.close()
|
||||
|
||||
def fetchall(self, path):
|
||||
dpath = self.filepath(path)
|
||||
d = {}
|
||||
for k in dircache.listdir(dpath):
|
||||
try:
|
||||
v = self.fetchfile(os.path.join(dpath, k))
|
||||
d[k] = v
|
||||
except:
|
||||
pass
|
||||
return d
|
||||
|
||||
def saveall(self, path, d):
|
||||
for (k, v) in d.items():
|
||||
self.save(os.path.join(path, k), v)
|
||||
|
||||
def delete(self, path):
|
||||
dpath = self.filepath(path)
|
||||
os.unlink(dpath)
|
||||
|
||||
def ls(self, path):
|
||||
dpath = self.filepath(path)
|
||||
return dircache.listdir(dpath)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,369 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
"""Handler for domain operations.
|
||||
Nothing here is persistent (across reboots).
|
||||
Needs to be persistent for one uptime.
|
||||
"""
|
||||
import sys
|
||||
|
||||
from twisted.internet import defer
|
||||
|
||||
import xen.ext.xc; xc = xen.ext.xc.new()
|
||||
|
||||
import sxp
|
||||
import XendRoot
|
||||
xroot = XendRoot.instance()
|
||||
import XendDB
|
||||
import XendDomainInfo
|
||||
import XendConsole
|
||||
import EventServer
|
||||
|
||||
from xen.xend.server import SrvDaemon
|
||||
xend = SrvDaemon.instance()
|
||||
|
||||
eserver = EventServer.instance()
|
||||
|
||||
__all__ = [ "XendDomain" ]
|
||||
|
||||
class XendDomain:
|
||||
"""Index of all domains. Singleton.
|
||||
"""
|
||||
|
||||
dbpath = "domain"
|
||||
domain = {}
|
||||
|
||||
def __init__(self):
|
||||
self.xconsole = XendConsole.instance()
|
||||
# Table of domain info indexed by domain id.
|
||||
self.db = XendDB.XendDB(self.dbpath)
|
||||
#self.domain = {}
|
||||
self.domain_db = self.db.fetchall("")
|
||||
if xroot.get_rebooted():
|
||||
print 'XendDomain> rebooted: removing all domain info'
|
||||
self.rm_all()
|
||||
eserver.subscribe('xend.virq', self.onVirq)
|
||||
self.initial_refresh()
|
||||
|
||||
def onVirq(self, event, val):
|
||||
print 'XendDomain> virq', val
|
||||
self.reap()
|
||||
|
||||
def rm_all(self):
|
||||
"""Remove all domain info. Used after reboot.
|
||||
"""
|
||||
for (k, v) in self.domain_db.items():
|
||||
self._delete_domain(k, notify=0)
|
||||
|
||||
def initial_refresh(self):
|
||||
"""Refresh initial domain info from domain_db.
|
||||
"""
|
||||
print "initial_refresh>"
|
||||
for d in self.domain_db.values(): print 'db dom=', d
|
||||
domlist = xc.domain_getinfo()
|
||||
for d in domlist: print 'xc dom=', d
|
||||
doms = {}
|
||||
for d in domlist:
|
||||
domid = str(d['dom'])
|
||||
doms[domid] = d
|
||||
dlist = []
|
||||
for config in self.domain_db.values():
|
||||
domid = str(sxp.child_value(config, 'id'))
|
||||
print "dom=", domid, "config=", config
|
||||
if domid in doms:
|
||||
print "dom=", domid, "new"
|
||||
deferred = self._new_domain(config, doms[domid])
|
||||
dlist.append(deferred)
|
||||
else:
|
||||
print "dom=", domid, "del"
|
||||
self._delete_domain(domid)
|
||||
deferred = defer.DeferredList(dlist, fireOnOneErrback=1)
|
||||
def cbok(val):
|
||||
#print "doms:"
|
||||
#for d in self.domain.values(): print 'dom', d
|
||||
self.refresh()
|
||||
print "XendDomain>initial_refresh> doms:"
|
||||
for d in self.domain.values(): print 'dom', d
|
||||
deferred.addCallback(cbok)
|
||||
|
||||
def sync(self):
|
||||
"""Sync domain db to disk.
|
||||
"""
|
||||
self.db.saveall("", self.domain_db)
|
||||
|
||||
def sync_domain(self, dom):
|
||||
"""Sync info for a domain to disk.
|
||||
|
||||
dom domain id (string)
|
||||
"""
|
||||
self.db.save(dom, self.domain_db[dom])
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
def _new_domain(self, savedinfo, info):
|
||||
"""Create a domain entry from saved info.
|
||||
"""
|
||||
## console = None
|
||||
## kernel = None
|
||||
## id = sxp.child_value(info, 'id')
|
||||
## dom = int(id)
|
||||
## name = sxp.child_value(info, 'name')
|
||||
## memory = int(sxp.child_value(info, 'memory'))
|
||||
## consoleinfo = sxp.child(info, 'console')
|
||||
## if consoleinfo:
|
||||
## consoleid = sxp.child_value(consoleinfo, 'id')
|
||||
## console = self.xconsole.console_get(consoleid)
|
||||
## if dom and console is None:
|
||||
## # Try to connect a console.
|
||||
## console = self.xconsole.console_create(dom)
|
||||
## config = sxp.child(info, 'config')
|
||||
## if config:
|
||||
## image = sxp.child(info, 'image')
|
||||
## if image:
|
||||
## image = sxp.child0(image)
|
||||
## kernel = sxp.child_value(image, 'kernel')
|
||||
## dominfo = XendDomainInfo.XendDomainInfo(
|
||||
## config, dom, name, memory, kernel, console)
|
||||
config = sxp.child_value(savedinfo, 'config')
|
||||
deferred = XendDomainInfo.vm_recreate(config, info)
|
||||
def fn(dominfo):
|
||||
self.domain[dominfo.id] = dominfo
|
||||
deferred.addCallback(fn)
|
||||
return deferred
|
||||
|
||||
def _add_domain(self, id, info, notify=1):
|
||||
self.domain[id] = info
|
||||
self.domain_db[id] = info.sxpr()
|
||||
self.sync_domain(id)
|
||||
if notify: eserver.inject('xend.domain.created', id)
|
||||
|
||||
def _delete_domain(self, id, notify=1):
|
||||
if id in self.domain:
|
||||
if notify: eserver.inject('xend.domain.died', id)
|
||||
del self.domain[id]
|
||||
if id in self.domain_db:
|
||||
del self.domain_db[id]
|
||||
self.db.delete(id)
|
||||
|
||||
def reap(self):
|
||||
"""Go through the domains looking for ones that have crashed or stopped.
|
||||
Tidy them up.
|
||||
"""
|
||||
print 'XendDomain>reap>'
|
||||
domlist = xc.domain_getinfo()
|
||||
casualties = []
|
||||
for d in domlist:
|
||||
#print 'dom', d
|
||||
dead = 0
|
||||
dead = dead or (d['crashed'] or d['shutdown'])
|
||||
dead = dead or (d['dying'] and
|
||||
not(d['running'] or d['paused'] or d['blocked']))
|
||||
if dead:
|
||||
casualties.append(d)
|
||||
for d in casualties:
|
||||
id = str(d['dom'])
|
||||
print 'XendDomain>reap> died id=', id, d
|
||||
dominfo = self.domain.get(id)
|
||||
if not dominfo: continue
|
||||
dominfo.died()
|
||||
self.domain_destroy(id, refresh=0)
|
||||
print 'XendDomain>reap<'
|
||||
|
||||
def refresh(self):
|
||||
"""Refresh domain list from Xen.
|
||||
"""
|
||||
domlist = xc.domain_getinfo()
|
||||
# Index the domlist by id.
|
||||
# Add entries for any domains we don't know about.
|
||||
doms = {}
|
||||
for d in domlist:
|
||||
id = str(d['dom'])
|
||||
doms[id] = d
|
||||
if id not in self.domain:
|
||||
config = None
|
||||
#image = None
|
||||
#newinfo = XendDomainInfo.XendDomainInfo(
|
||||
# config, d['dom'], d['name'], d['mem_kb']/1024, image=image, info=d)
|
||||
deferred = XendDomainInfo.vm_recreate(config, d)
|
||||
def fn(dominfo):
|
||||
self._add_domain(dominfo.id, dominfo)
|
||||
deferred.addCallback(fn)
|
||||
# Remove entries for domains that no longer exist.
|
||||
for d in self.domain.values():
|
||||
dominfo = doms.get(d.id)
|
||||
if dominfo:
|
||||
d.update(dominfo)
|
||||
else:
|
||||
self._delete_domain(d.id)
|
||||
self.reap()
|
||||
|
||||
def refresh_domain(self, id):
|
||||
dom = int(id)
|
||||
dominfo = xc.domain_getinfo(dom, 1)
|
||||
if dominfo == [] or dominfo[0]['dom'] != dom:
|
||||
try:
|
||||
self._delete_domain(id)
|
||||
except:
|
||||
print 'refresh_domain: error'
|
||||
raise
|
||||
pass
|
||||
else:
|
||||
d = self.domain.get(id)
|
||||
if d:
|
||||
d.update(dominfo[0])
|
||||
|
||||
def domain_ls(self):
|
||||
# List domains.
|
||||
# Update info from kernel first.
|
||||
self.refresh()
|
||||
return self.domain.keys()
|
||||
|
||||
def domains(self):
|
||||
self.refresh()
|
||||
return self.domain.values()
|
||||
|
||||
def domain_create(self, config):
|
||||
# Create domain, log it.
|
||||
deferred = XendDomainInfo.vm_create(config)
|
||||
def fn(dominfo):
|
||||
self._add_domain(dominfo.id, dominfo)
|
||||
return dominfo
|
||||
deferred.addCallback(fn)
|
||||
return deferred
|
||||
|
||||
def domain_get(self, id):
|
||||
id = str(id)
|
||||
self.refresh_domain(id)
|
||||
return self.domain.get(id)
|
||||
|
||||
def domain_unpause(self, id):
|
||||
"""(Re)start domain running.
|
||||
"""
|
||||
dom = int(id)
|
||||
eserver.inject('xend.domain.unpause', id)
|
||||
return xc.domain_unpause(dom=dom)
|
||||
|
||||
def domain_pause(self, id):
|
||||
"""Pause domain execution.
|
||||
"""
|
||||
dom = int(id)
|
||||
eserver.inject('xend.domain.pause', id)
|
||||
return xc.domain_pause(dom=dom)
|
||||
|
||||
def domain_shutdown(self, id, reason='poweroff'):
|
||||
"""Shutdown domain (nicely).
|
||||
"""
|
||||
dom = int(id)
|
||||
if dom <= 0:
|
||||
return 0
|
||||
eserver.inject('xend.domain.shutdown', [id, reason])
|
||||
val = xend.domain_shutdown(dom, reason)
|
||||
self.refresh()
|
||||
return val
|
||||
|
||||
def domain_destroy(self, id, refresh=1):
|
||||
"""Terminate domain immediately.
|
||||
"""
|
||||
dom = int(id)
|
||||
if dom <= 0:
|
||||
return 0
|
||||
eserver.inject('xend.domain.destroy', id)
|
||||
val = xc.domain_destroy(dom=dom)
|
||||
if refresh: self.refresh()
|
||||
return val
|
||||
|
||||
def domain_migrate(self, id, dst):
|
||||
"""Start domain migration.
|
||||
"""
|
||||
# Need a cancel too?
|
||||
pass
|
||||
|
||||
def domain_save(self, id, dst, progress=0):
|
||||
"""Save domain state to file, destroy domain.
|
||||
"""
|
||||
dom = int(id)
|
||||
dominfo = self.domain_get(id)
|
||||
if not dominfo:
|
||||
return -1
|
||||
vmconfig = sxp.to_string(dominfo.sxpr())
|
||||
self.domain_pause(id)
|
||||
eserver.inject('xend.domain.save', id)
|
||||
rc = xc.linux_save(dom=dom, state_file=dst, vmconfig=vmconfig, progress=progress)
|
||||
if rc == 0:
|
||||
self.domain_destroy(id)
|
||||
return rc
|
||||
|
||||
def domain_restore(self, src, progress=0):
|
||||
"""Restore domain from file.
|
||||
"""
|
||||
dominfo = XendDomainInfo.vm_restore(src, progress=progress)
|
||||
self._add_domain(dominfo.id, dominfo)
|
||||
return dominfo
|
||||
|
||||
#============================================================================
|
||||
# Backward compatibility stuff from here on.
|
||||
|
||||
def domain_pincpu(self, dom, cpu):
|
||||
dom = int(dom)
|
||||
return xc.domain_pincpu(dom, cpu)
|
||||
|
||||
def domain_cpu_bvt_set(self, dom, mcuadv, warp, warpl, warpu):
|
||||
dom = int(dom)
|
||||
return xc.bvtsched_domain_set(dom=dom, mcuadv=mcuadv,
|
||||
warp=warp, warpl=warpl, warpu=warpu)
|
||||
|
||||
def domain_cpu_bvt_get(self, dom):
|
||||
dom = int(dom)
|
||||
return xc.bvtsched_domain_get(dom)
|
||||
|
||||
def domain_cpu_atropos_set(self, dom, period, slice, latency, xtratime):
|
||||
dom = int(dom)
|
||||
return xc.atropos_domain_set(dom, period, slice, latency, xtratime)
|
||||
|
||||
def domain_cpu_atropos_get(self, dom):
|
||||
dom = int(dom)
|
||||
return xc.atropos_domain_get(dom)
|
||||
|
||||
def domain_vif_ls(self, dom):
|
||||
dominfo = self.domain_get(dom)
|
||||
if not dominfo: return None
|
||||
devs = dominfo.get_devices('vif')
|
||||
return range(0, len(devs))
|
||||
|
||||
def domain_vif_get(self, dom, vif):
|
||||
dominfo = self.domain_get(dom)
|
||||
if not dominfo: return None
|
||||
return dominfo.get_device_by_index(vif)
|
||||
|
||||
## def domain_vif_ip_add(self, dom, vif, ip):
|
||||
## dom = int(dom)
|
||||
## return xenctl.ip.setup_vfr_rules_for_vif(dom, vif, ip)
|
||||
|
||||
def domain_vbd_ls(self, dom):
|
||||
dominfo = self.domain_get(dom)
|
||||
if not dominfo: return []
|
||||
devs = dominfo.get_devices('vbd')
|
||||
return [ sxp.child_value(v, 'dev') for v in devs ]
|
||||
|
||||
def domain_vbd_get(self, dom, vbd):
|
||||
dominfo = self.domain_get(dom)
|
||||
if not dominfo: return None
|
||||
devs = dominfo.get_devices('vbd')
|
||||
for v in devs:
|
||||
if sxp.child_value(v, 'dev') == vbd:
|
||||
return v
|
||||
return None
|
||||
|
||||
def domain_shadow_control(self, dom, op):
|
||||
dom = int(dom)
|
||||
return xc.shadow_control(dom, op)
|
||||
|
||||
#============================================================================
|
||||
|
||||
def instance():
|
||||
global inst
|
||||
try:
|
||||
inst
|
||||
except:
|
||||
inst = XendDomain()
|
||||
return inst
|
||||
@@ -0,0 +1,44 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
"""Handler for persistent domain configs.
|
||||
|
||||
"""
|
||||
|
||||
import sxp
|
||||
import XendDB
|
||||
import XendDomain
|
||||
|
||||
__all__ = [ "XendDomainConfig" ]
|
||||
|
||||
class XendDomainConfig:
|
||||
|
||||
dbpath = 'config'
|
||||
|
||||
def __init__(self):
|
||||
self.db = XendDB.XendDB(self.dbpath)
|
||||
|
||||
def domain_config_ls(self, path):
|
||||
return self.db.ls(path)
|
||||
|
||||
def domain_config_create(self, path, sxpr):
|
||||
self.db.save(path, sxpr)
|
||||
pass
|
||||
|
||||
def domain_config_delete(self, path):
|
||||
self.db.delete(path)
|
||||
|
||||
def domain_config_instance(self, path):
|
||||
"""Create a domain from a config.
|
||||
"""
|
||||
config = self.db.fetch(path)
|
||||
xd = XendDomain.instance()
|
||||
newdom = xd.domain_create(config)
|
||||
return newdom
|
||||
|
||||
def instance():
|
||||
global inst
|
||||
try:
|
||||
inst
|
||||
except:
|
||||
inst = XendDomainConfig()
|
||||
return inst
|
||||
@@ -0,0 +1,908 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
"""Representation of a single domain.
|
||||
Includes support for domain construction, using
|
||||
open-ended configurations.
|
||||
|
||||
Author: Mike Wray <mike.wray@hpl.hp.com>
|
||||
|
||||
"""
|
||||
|
||||
import string
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
|
||||
from twisted.internet import defer
|
||||
|
||||
import xen.ext.xc; xc = xen.ext.xc.new()
|
||||
import xen.util.ip
|
||||
|
||||
import sxp
|
||||
|
||||
import XendConsole
|
||||
xendConsole = XendConsole.instance()
|
||||
|
||||
import server.SrvDaemon
|
||||
xend = server.SrvDaemon.instance()
|
||||
|
||||
SIF_BLK_BE_DOMAIN = (1<<4)
|
||||
SIF_NET_BE_DOMAIN = (1<<5)
|
||||
|
||||
def readlines(fd):
|
||||
"""Version of readlines safe against EINTR.
|
||||
"""
|
||||
import errno
|
||||
|
||||
lines = []
|
||||
while 1:
|
||||
try:
|
||||
line = fd.readline()
|
||||
except IOError, ex:
|
||||
if ex.errno == errno.EINTR:
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
if line == '': break
|
||||
lines.append(line)
|
||||
return lines
|
||||
|
||||
class VmError(ValueError):
|
||||
"""Vm construction error."""
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
|
||||
def blkdev_name_to_number(name):
|
||||
"""Take the given textual block-device name (e.g., '/dev/sda1',
|
||||
'hda') and return the device number used by the OS. """
|
||||
|
||||
if not re.match( '/dev/', name ):
|
||||
name = '/dev/' + name
|
||||
|
||||
return os.stat(name).st_rdev
|
||||
|
||||
def lookup_raw_partn(partition):
|
||||
"""Take the given block-device name (e.g., '/dev/sda1', 'hda')
|
||||
and return a dictionary { device, start_sector,
|
||||
nr_sectors, type }
|
||||
device: Device number of the given partition
|
||||
start_sector: Index of first sector of the partition
|
||||
nr_sectors: Number of sectors comprising this partition
|
||||
type: 'Disk' or identifying name for partition type
|
||||
"""
|
||||
|
||||
if not re.match( '/dev/', partition ):
|
||||
partition = '/dev/' + partition
|
||||
|
||||
drive = re.split( '[0-9]', partition )[0]
|
||||
|
||||
if drive == partition:
|
||||
fd = os.popen( '/sbin/sfdisk -s ' + drive + ' 2>/dev/null' )
|
||||
line = readline(fd)
|
||||
if line:
|
||||
return [ { 'device' : blkdev_name_to_number(drive),
|
||||
'start_sector' : long(0),
|
||||
'nr_sectors' : long(line) * 2,
|
||||
'type' : 'Disk' } ]
|
||||
return None
|
||||
|
||||
# determine position on disk
|
||||
fd = os.popen( '/sbin/sfdisk -d ' + drive + ' 2>/dev/null' )
|
||||
|
||||
#['/dev/sda3 : start= 16948575, size=16836120, Id=83, bootable\012']
|
||||
lines = readlines(fd)
|
||||
for line in lines:
|
||||
m = re.search( '^' + partition + '\s*: start=\s*([0-9]+), ' +
|
||||
'size=\s*([0-9]+), Id=\s*(\S+).*$', line)
|
||||
if m:
|
||||
return [ { 'device' : blkdev_name_to_number(drive),
|
||||
'start_sector' : long(m.group(1)),
|
||||
'nr_sectors' : long(m.group(2)),
|
||||
'type' : m.group(3) } ]
|
||||
|
||||
return None
|
||||
|
||||
def lookup_disk_uname(uname):
|
||||
"""Lookup a list of segments for a physical device.
|
||||
uname [string]: name of the device in the format \'phy:dev\' for a physical device
|
||||
returns [list of dicts]: list of extents that make up the named device
|
||||
"""
|
||||
( type, d_name ) = string.split( uname, ':' )
|
||||
|
||||
if type == "phy":
|
||||
segments = lookup_raw_partn( d_name )
|
||||
else:
|
||||
segments = None
|
||||
return segments
|
||||
|
||||
def make_disk(dom, uname, dev, mode, recreate=0):
|
||||
"""Create a virtual disk device for a domain.
|
||||
|
||||
@returns Deferred
|
||||
"""
|
||||
segments = lookup_disk_uname(uname)
|
||||
if not segments:
|
||||
raise VmError("vbd: Segments not found: uname=%s" % uname)
|
||||
if len(segments) > 1:
|
||||
raise VmError("vbd: Multi-segment vdisk: uname=%s" % uname)
|
||||
segment = segments[0]
|
||||
vdev = blkdev_name_to_number(dev)
|
||||
ctrl = xend.blkif_create(dom, recreate=recreate)
|
||||
|
||||
def fn(ctrl):
|
||||
return xend.blkif_dev_create(dom, vdev, mode, segment, recreate=recreate)
|
||||
ctrl.addCallback(fn)
|
||||
return ctrl
|
||||
|
||||
def make_vif(dom, vif, vmac, recreate=0):
|
||||
"""Create a virtual network device for a domain.
|
||||
|
||||
|
||||
@returns Deferred
|
||||
"""
|
||||
xend.netif_create(dom, recreate=recreate)
|
||||
d = xend.netif_dev_create(dom, vif, vmac, recreate=recreate)
|
||||
return d
|
||||
|
||||
def vif_up(iplist):
|
||||
"""send an unsolicited ARP reply for all non link-local IP addresses.
|
||||
|
||||
iplist IP addresses
|
||||
"""
|
||||
|
||||
IP_NONLOCAL_BIND = '/proc/sys/net/ipv4/ip_nonlocal_bind'
|
||||
|
||||
def get_ip_nonlocal_bind():
|
||||
return int(open(IP_NONLOCAL_BIND, 'r').read()[0])
|
||||
|
||||
def set_ip_nonlocal_bind(v):
|
||||
print >> open(IP_NONLOCAL_BIND, 'w'), str(v)
|
||||
|
||||
def link_local(ip):
|
||||
return xen.util.ip.check_subnet(ip, '169.254.0.0', '255.255.0.0')
|
||||
|
||||
def arping(ip, gw):
|
||||
cmd = '/usr/sbin/arping -A -b -I eth0 -c 1 -s %s %s' % (ip, gw)
|
||||
print cmd
|
||||
os.system(cmd)
|
||||
|
||||
gateway = xen.util.ip.get_current_ipgw() or '255.255.255.255'
|
||||
nlb = get_ip_nonlocal_bind()
|
||||
if not nlb: set_ip_nonlocal_bind(1)
|
||||
try:
|
||||
for ip in iplist:
|
||||
if not link_local(ip):
|
||||
arping(ip, gateway)
|
||||
finally:
|
||||
if not nlb: set_ip_nonlocal_bind(0)
|
||||
|
||||
config_handlers = {}
|
||||
|
||||
def add_config_handler(name, h):
|
||||
"""Add a handler for a config field.
|
||||
|
||||
name field name
|
||||
h handler: fn(vm, config, field, index)
|
||||
"""
|
||||
config_handlers[name] = h
|
||||
|
||||
def get_config_handler(name):
|
||||
"""Get a handler for a config field.
|
||||
|
||||
returns handler or None
|
||||
"""
|
||||
return config_handlers.get(name)
|
||||
|
||||
"""Table of handlers for virtual machine images.
|
||||
Indexed by image type.
|
||||
"""
|
||||
image_handlers = {}
|
||||
|
||||
def add_image_handler(name, h):
|
||||
"""Add a handler for an image type
|
||||
name image type
|
||||
h handler: fn(config, name, memory, image)
|
||||
"""
|
||||
image_handlers[name] = h
|
||||
|
||||
def get_image_handler(name):
|
||||
"""Get the handler for an image type.
|
||||
name image type
|
||||
|
||||
returns handler or None
|
||||
"""
|
||||
return image_handlers.get(name)
|
||||
|
||||
"""Table of handlers for devices.
|
||||
Indexed by device type.
|
||||
"""
|
||||
device_handlers = {}
|
||||
|
||||
def add_device_handler(name, h):
|
||||
"""Add a handler for a device type.
|
||||
|
||||
name device type
|
||||
h handler: fn(vm, dev)
|
||||
"""
|
||||
device_handlers[name] = h
|
||||
|
||||
def get_device_handler(name):
|
||||
"""Get the handler for a device type.
|
||||
|
||||
name device type
|
||||
|
||||
returns handler or None
|
||||
"""
|
||||
return device_handlers.get(name)
|
||||
|
||||
def vm_create(config):
|
||||
"""Create a VM from a configuration.
|
||||
If a vm has been partially created and there is an error it
|
||||
is destroyed.
|
||||
|
||||
config configuration
|
||||
|
||||
returns Deferred
|
||||
raises VmError for invalid configuration
|
||||
"""
|
||||
print 'vm_create>'
|
||||
vm = XendDomainInfo()
|
||||
return vm.construct(config)
|
||||
|
||||
def vm_recreate(config, info):
|
||||
"""Create the VM object for an existing domain.
|
||||
"""
|
||||
vm = XendDomainInfo()
|
||||
vm.recreate = 1
|
||||
vm.setdom(info['dom'])
|
||||
vm.name = info['name']
|
||||
vm.memory = info['mem_kb']/1024
|
||||
if config:
|
||||
d = vm.construct(config)
|
||||
else:
|
||||
d = defer.Deferred()
|
||||
d.callback(vm)
|
||||
return d
|
||||
|
||||
def vm_restore(src, progress=0):
|
||||
"""Restore a VM from a disk image.
|
||||
|
||||
src saved state to restore
|
||||
progress progress reporting flag
|
||||
returns deferred
|
||||
raises VmError for invalid configuration
|
||||
"""
|
||||
vm = XendDomainInfo()
|
||||
ostype = "linux" #todo Set from somewhere (store in the src?).
|
||||
restorefn = getattr(xc, "%s_restore" % ostype)
|
||||
d = restorefn(state_file=src, progress=progress)
|
||||
dom = int(d['dom'])
|
||||
if dom < 0:
|
||||
raise VMError('restore failed')
|
||||
vmconfig = sxp.from_string(d['vmconfig'])
|
||||
vm.config = sxp.child_value(vmconfig, 'config')
|
||||
deferred = vm.dom_configure(dom)
|
||||
def vifs_cb(val, vm):
|
||||
vif_up(vm.ipaddrs)
|
||||
deferred.addCallback(vifs_cb, vm)
|
||||
return deferred
|
||||
|
||||
def dom_get(dom):
|
||||
domlist = xc.domain_getinfo(dom=dom)
|
||||
if domlist and dom == domlist[0]['dom']:
|
||||
return domlist[0]
|
||||
return None
|
||||
|
||||
|
||||
def append_deferred(dlist, v):
|
||||
if isinstance(v, defer.Deferred):
|
||||
dlist.append(v)
|
||||
|
||||
def _vm_configure1(val, vm):
|
||||
d = vm.create_devices()
|
||||
print '_vm_configure1> made devices...'
|
||||
def cbok(x):
|
||||
print '_vm_configure1> cbok', x
|
||||
return x
|
||||
d.addCallback(cbok)
|
||||
d.addCallback(_vm_configure2, vm)
|
||||
print '_vm_configure1<'
|
||||
return d
|
||||
|
||||
def _vm_configure2(val, vm):
|
||||
print '>callback _vm_configure2...'
|
||||
d = vm.configure_fields()
|
||||
def cbok(results):
|
||||
print '_vm_configure2> cbok', results
|
||||
return vm
|
||||
def cberr(err):
|
||||
print '_vm_configure2> cberr', err
|
||||
vm.destroy()
|
||||
return err
|
||||
d.addCallback(cbok)
|
||||
d.addErrback(cberr)
|
||||
print '<_vm_configure2'
|
||||
return d
|
||||
|
||||
class XendDomainInfo:
|
||||
"""Virtual machine object."""
|
||||
|
||||
def __init__(self):
|
||||
self.recreate = 0
|
||||
self.config = None
|
||||
self.id = None
|
||||
self.dom = None
|
||||
self.name = None
|
||||
self.memory = None
|
||||
self.image = None
|
||||
self.ramdisk = None
|
||||
self.cmdline = None
|
||||
self.console = None
|
||||
self.devices = {}
|
||||
self.configs = []
|
||||
self.info = None
|
||||
self.ipaddrs = []
|
||||
self.blkif_backend = 0
|
||||
self.netif_backend = 0
|
||||
#todo: state: running, suspended
|
||||
self.state = 'running'
|
||||
#todo: set to migrate info if migrating
|
||||
self.migrate = None
|
||||
|
||||
def setdom(self, dom):
|
||||
self.dom = int(dom)
|
||||
self.id = str(dom)
|
||||
|
||||
def update(self, info):
|
||||
"""Update with info from xc.domain_getinfo().
|
||||
"""
|
||||
self.info = info
|
||||
self.memory = self.info['mem_kb'] / 1024
|
||||
|
||||
def __str__(self):
|
||||
s = "domain"
|
||||
s += " id=" + self.id
|
||||
s += " name=" + self.name
|
||||
s += " memory=" + str(self.memory)
|
||||
if self.console:
|
||||
s += " console=" + self.console.id
|
||||
if self.image:
|
||||
s += " image=" + self.image
|
||||
s += ""
|
||||
return s
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
def sxpr(self):
|
||||
sxpr = ['domain',
|
||||
['id', self.id],
|
||||
['name', self.name],
|
||||
['memory', self.memory] ]
|
||||
if self.info:
|
||||
run = (self.info['running'] and 'r') or '-'
|
||||
block = (self.info['blocked'] and 'b') or '-'
|
||||
stop = (self.info['paused'] and 'p') or '-'
|
||||
susp = (self.info['shutdown'] and 's') or '-'
|
||||
crash = (self.info['crashed'] and 'c') or '-'
|
||||
state = run + block + stop + susp + crash
|
||||
sxpr.append(['state', state])
|
||||
if self.info['shutdown']:
|
||||
reasons = ["poweroff", "reboot", "suspend"]
|
||||
reason = reasons[self.info['shutdown_reason']]
|
||||
sxpr.append(['shutdown_reason', reason])
|
||||
sxpr.append(['cpu', self.info['cpu']])
|
||||
sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
|
||||
if self.console:
|
||||
sxpr.append(self.console.sxpr())
|
||||
if self.config:
|
||||
sxpr.append(['config', self.config])
|
||||
return sxpr
|
||||
|
||||
def construct(self, config):
|
||||
# todo - add support for scheduling params?
|
||||
self.config = config
|
||||
try:
|
||||
self.name = sxp.child_value(config, 'name')
|
||||
self.memory = int(sxp.child_value(config, 'memory', '128'))
|
||||
self.configure_backends()
|
||||
image = sxp.child_value(config, 'image')
|
||||
image_name = sxp.name(image)
|
||||
image_handler = get_image_handler(image_name)
|
||||
if image_handler is None:
|
||||
raise VmError('unknown image type: ' + image_name)
|
||||
image_handler(self, image)
|
||||
deferred = self.configure()
|
||||
except StandardError, ex:
|
||||
# Catch errors, cleanup and re-raise.
|
||||
self.destroy()
|
||||
raise
|
||||
def cbok(x):
|
||||
print 'vm_create> cbok', x
|
||||
return x
|
||||
deferred.addCallback(cbok)
|
||||
print 'vm_create<'
|
||||
return deferred
|
||||
|
||||
def config_devices(self, name):
|
||||
"""Get a list of the 'device' nodes of a given type from the config.
|
||||
|
||||
name device type
|
||||
return list of device configs
|
||||
"""
|
||||
devices = []
|
||||
for d in sxp.children(self.config, 'device'):
|
||||
dev = sxp.child0(d)
|
||||
if dev is None: continue
|
||||
if name == sxp.name(dev):
|
||||
devices.append(dev)
|
||||
return devices
|
||||
|
||||
def add_device(self, type, dev):
|
||||
"""Add a device to a virtual machine.
|
||||
|
||||
dev device to add
|
||||
"""
|
||||
dl = self.devices.get(type, [])
|
||||
dl.append(dev)
|
||||
self.devices[type] = dl
|
||||
|
||||
def get_devices(self, type):
|
||||
val = self.devices.get(type, [])
|
||||
return val
|
||||
|
||||
def get_device_by_id(self, type, id):
|
||||
"""Get the device with the given id.
|
||||
|
||||
id device id
|
||||
|
||||
returns device or None
|
||||
"""
|
||||
dl = self.get_devices(type)
|
||||
for d in dl:
|
||||
if d.getprop('id') == id:
|
||||
return d
|
||||
return None
|
||||
|
||||
def get_device_by_index(self, type, idx):
|
||||
"""Get the device with the given index.
|
||||
|
||||
idx device index
|
||||
|
||||
returns device or None
|
||||
"""
|
||||
dl = self.get_devices(type)
|
||||
if 0 <= idx < len(dl):
|
||||
return dl[idx]
|
||||
else:
|
||||
return None
|
||||
|
||||
def add_config(self, val):
|
||||
"""Add configuration data to a virtual machine.
|
||||
|
||||
val data to add
|
||||
"""
|
||||
self.configs.append(val)
|
||||
|
||||
def destroy(self):
|
||||
if self.dom <= 0:
|
||||
return 0
|
||||
return xc.domain_destroy(dom=self.dom)
|
||||
|
||||
def died(self):
|
||||
print 'died>', self.dom
|
||||
self.release_devices()
|
||||
|
||||
def release_devices(self):
|
||||
print 'release_devices>', self.dom
|
||||
self.release_vifs()
|
||||
self.release_vbds()
|
||||
self.devices = {}
|
||||
|
||||
def release_vifs(self):
|
||||
print 'release_vifs>', self.dom
|
||||
if self.dom is None: return
|
||||
ctrl = xend.netif_get(self.dom)
|
||||
if ctrl:
|
||||
ctrl.destroy()
|
||||
|
||||
def release_vbds(self):
|
||||
print 'release_vbds>', self.dom
|
||||
if self.dom is None: return
|
||||
ctrl = xend.blkif_get(self.dom)
|
||||
if ctrl:
|
||||
ctrl.destroy()
|
||||
|
||||
def show(self):
|
||||
"""Print virtual machine info.
|
||||
"""
|
||||
print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory)
|
||||
print "image:"
|
||||
sxp.show(self.image)
|
||||
print
|
||||
for dl in self.devices:
|
||||
for dev in dl:
|
||||
print "device:"
|
||||
sxp.show(dev)
|
||||
print
|
||||
for val in self.configs:
|
||||
print "config:"
|
||||
sxp.show(val)
|
||||
print
|
||||
print "]"
|
||||
|
||||
def init_domain(self):
|
||||
"""Initialize the domain memory.
|
||||
"""
|
||||
if self.recreate: return
|
||||
memory = self.memory
|
||||
name = self.name
|
||||
cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
|
||||
print 'init_domain>', memory, name, cpu
|
||||
dom = xc.domain_create(mem_kb= memory * 1024, name= name, cpu= cpu)
|
||||
if dom <= 0:
|
||||
raise VmError('Creating domain failed: name=%s memory=%d'
|
||||
% (name, memory))
|
||||
self.setdom(dom)
|
||||
|
||||
def build_domain(self, ostype, kernel, ramdisk, cmdline, vifs_n):
|
||||
"""Build the domain boot image.
|
||||
"""
|
||||
if self.recreate: return
|
||||
if len(cmdline) >= 256:
|
||||
print 'Warning: kernel cmdline too long'
|
||||
dom = self.dom
|
||||
buildfn = getattr(xc, '%s_build' % ostype)
|
||||
print 'build_domain>', ostype, dom, kernel, cmdline, ramdisk
|
||||
flags = 0
|
||||
if self.netif_backend: flags |= SIF_NET_BE_DOMAIN
|
||||
if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
|
||||
err = buildfn(dom = dom,
|
||||
image = kernel,
|
||||
control_evtchn = self.console.port2,
|
||||
cmdline = cmdline,
|
||||
ramdisk = ramdisk,
|
||||
flags = flags)
|
||||
if err != 0:
|
||||
raise VmError('Building domain failed: type=%s dom=%d err=%d'
|
||||
% (ostype, dom, err))
|
||||
|
||||
def create_domain(self, ostype, kernel, ramdisk, cmdline, vifs_n):
|
||||
"""Create a domain. Builds the image but does not configure it.
|
||||
|
||||
ostype OS type
|
||||
kernel kernel image
|
||||
ramdisk kernel ramdisk
|
||||
cmdline kernel commandline
|
||||
vifs_n number of network interfaces
|
||||
"""
|
||||
print 'create_domain>', ostype, kernel
|
||||
if not self.recreate:
|
||||
if not os.path.isfile(kernel):
|
||||
raise VmError('Kernel image does not exist: %s' % kernel)
|
||||
if ramdisk and not os.path.isfile(ramdisk):
|
||||
raise VMError('Kernel ramdisk does not exist: %s' % ramdisk)
|
||||
print 'create-domain> init_domain...'
|
||||
self.init_domain()
|
||||
print 'create_domain>', 'dom=', self.dom
|
||||
self.console = xendConsole.console_create(self.dom)
|
||||
self.build_domain(ostype, kernel, ramdisk, cmdline, vifs_n)
|
||||
self.image = kernel
|
||||
self.ramdisk = ramdisk
|
||||
self.cmdline = cmdline
|
||||
|
||||
def create_devices(self):
|
||||
"""Create the devices for a vm.
|
||||
|
||||
returns Deferred
|
||||
raises VmError for invalid devices
|
||||
"""
|
||||
print '>create_devices'
|
||||
dlist = []
|
||||
devices = sxp.children(self.config, 'device')
|
||||
index = {}
|
||||
for d in devices:
|
||||
dev = sxp.child0(d)
|
||||
if dev is None:
|
||||
raise VmError('invalid device')
|
||||
dev_name = sxp.name(dev)
|
||||
dev_index = index.get(dev_name, 0)
|
||||
dev_handler = get_device_handler(dev_name)
|
||||
if dev_handler is None:
|
||||
raise VmError('unknown device type: ' + dev_name)
|
||||
v = dev_handler(self, dev, dev_index)
|
||||
append_deferred(dlist, v)
|
||||
index[dev_name] = dev_index + 1
|
||||
deferred = defer.DeferredList(dlist, fireOnOneErrback=1)
|
||||
print '<create_devices'
|
||||
return deferred
|
||||
|
||||
def configure_backends(self):
|
||||
"""Set configuration flags if the vm is a backend for netif of blkif.
|
||||
"""
|
||||
for c in sxp.children(self.config, 'backend'):
|
||||
name = sxp.name(sxp.child0(c))
|
||||
if name == 'blkif':
|
||||
self.blkif_backend = 1
|
||||
elif name == 'netif':
|
||||
self.netif_backend = 1
|
||||
else:
|
||||
raise VmError('invalid backend type:' + str(name))
|
||||
|
||||
def create_backends(self):
|
||||
"""Setup the netif and blkif backends.
|
||||
"""
|
||||
if self.blkif_backend:
|
||||
xend.blkif_set_control_domain(self.dom, recreate=self.recreate)
|
||||
if self.netif_backend:
|
||||
xend.netif_set_control_domain(self.dom, recreate=self.recreate)
|
||||
|
||||
def configure(self):
|
||||
"""Configure a vm.
|
||||
|
||||
vm virtual machine
|
||||
config configuration
|
||||
|
||||
returns Deferred - calls callback with vm
|
||||
"""
|
||||
if self.blkif_backend:
|
||||
d = defer.Deferred()
|
||||
d.callback(1)
|
||||
else:
|
||||
d = xend.blkif_create(self.dom, recreate=self.recreate)
|
||||
d.addCallback(_vm_configure1, self)
|
||||
return d
|
||||
|
||||
def dom_configure(self, dom):
|
||||
"""Configure a domain.
|
||||
|
||||
dom domain id
|
||||
returns deferred
|
||||
"""
|
||||
d = dom_get(dom)
|
||||
if not d:
|
||||
raise VMError("Domain not found: %d" % dom)
|
||||
try:
|
||||
self.setdom(dom)
|
||||
self.name = d['name']
|
||||
self.memory = d['memory']/1024
|
||||
deferred = self.configure()
|
||||
except StandardError, ex:
|
||||
self.destroy()
|
||||
raise
|
||||
return deferred
|
||||
|
||||
def configure_fields(self):
|
||||
dlist = []
|
||||
index = {}
|
||||
for field in sxp.children(self.config):
|
||||
field_name = sxp.name(field)
|
||||
field_index = index.get(field_name, 0)
|
||||
field_handler = get_config_handler(field_name)
|
||||
# Ignore unknown fields. Warn?
|
||||
if field_handler:
|
||||
v = field_handler(self, self.config, field, field_index)
|
||||
append_deferred(dlist, v)
|
||||
index[field_name] = field_index + 1
|
||||
d = defer.DeferredList(dlist, fireOnOneErrback=1)
|
||||
return d
|
||||
|
||||
|
||||
def vm_image_linux(vm, image):
|
||||
"""Create a VM for a linux image.
|
||||
|
||||
name vm name
|
||||
memory vm memory
|
||||
image image config
|
||||
|
||||
returns vm
|
||||
"""
|
||||
kernel = sxp.child_value(image, "kernel")
|
||||
cmdline = ""
|
||||
ip = sxp.child_value(image, "ip", "dhcp")
|
||||
if ip:
|
||||
cmdline += " ip=" + ip
|
||||
root = sxp.child_value(image, "root")
|
||||
if root:
|
||||
cmdline += " root=" + root
|
||||
args = sxp.child_value(image, "args")
|
||||
if args:
|
||||
cmdline += " " + args
|
||||
ramdisk = sxp.child_value(image, "ramdisk", '')
|
||||
vifs = vm.config_devices("vif")
|
||||
vm.create_domain("linux", kernel, ramdisk, cmdline, len(vifs))
|
||||
return vm
|
||||
|
||||
def vm_image_netbsd(vm, image):
|
||||
"""Create a VM for a bsd image.
|
||||
|
||||
name vm name
|
||||
memory vm memory
|
||||
image image config
|
||||
|
||||
returns vm
|
||||
"""
|
||||
#todo: Same as for linux. Is that right? If so can unify them.
|
||||
kernel = sxp.child_value(image, "kernel")
|
||||
cmdline = ""
|
||||
ip = sxp.child_value(image, "ip", "dhcp")
|
||||
if ip:
|
||||
cmdline += "ip=" + ip
|
||||
root = sxp.child_value(image, "root")
|
||||
if root:
|
||||
cmdline += "root=" + root
|
||||
args = sxp.child_value(image, "args")
|
||||
if args:
|
||||
cmdline += " " + args
|
||||
ramdisk = sxp.child_value(image, "ramdisk")
|
||||
vifs = vm.config_devices("vif")
|
||||
vm.create_domain("netbsd", kernel, ramdisk, cmdline, len(vifs))
|
||||
return vm
|
||||
|
||||
|
||||
def vm_dev_vif(vm, val, index):
|
||||
"""Create a virtual network interface (vif).
|
||||
|
||||
vm virtual machine
|
||||
val vif config
|
||||
index vif index
|
||||
"""
|
||||
if vm.netif_backend:
|
||||
raise VmError('vif: vif in netif backend domain')
|
||||
vif = index #todo
|
||||
vmac = sxp.child_value(val, "mac")
|
||||
defer = make_vif(vm.dom, vif, vmac, vm.recreate)
|
||||
def fn(id):
|
||||
dev = xend.netif_dev(vm.dom, vif)
|
||||
devid = sxp.attribute(val, 'id')
|
||||
if devid:
|
||||
dev.setprop('id', devid)
|
||||
bridge = sxp.child_value(val, "bridge")
|
||||
dev.up(bridge)
|
||||
vm.add_device('vif', dev)
|
||||
print 'vm_dev_vif> created', dev
|
||||
return id
|
||||
defer.addCallback(fn)
|
||||
return defer
|
||||
|
||||
def vm_dev_vbd(vm, val, index):
|
||||
"""Create a virtual block device (vbd).
|
||||
|
||||
vm virtual machine
|
||||
val vbd config
|
||||
index vbd index
|
||||
"""
|
||||
if vm.blkif_backend:
|
||||
raise VmError('vbd: vbd in blkif backend domain')
|
||||
vdev = index
|
||||
uname = sxp.child_value(val, 'uname')
|
||||
if not uname:
|
||||
raise VMError('vbd: Missing uname')
|
||||
dev = sxp.child_value(val, 'dev')
|
||||
if not dev:
|
||||
raise VMError('vbd: Missing dev')
|
||||
mode = sxp.child_value(val, 'mode', 'r')
|
||||
defer = make_disk(vm.dom, uname, dev, mode, vm.recreate)
|
||||
def fn(vbd):
|
||||
dev = xend.blkif_dev(vm.dom, vdev)
|
||||
vm.add_device('vbd', dev)
|
||||
return vbd
|
||||
defer.addCallback(fn)
|
||||
return defer
|
||||
|
||||
def parse_pci(val):
|
||||
if isinstance(val, StringType):
|
||||
radix = 10
|
||||
if val.startswith('0x') or val.startswith('0X'):
|
||||
radix = 16
|
||||
v = int(val, radix)
|
||||
else:
|
||||
v = val
|
||||
return v
|
||||
|
||||
def vm_dev_pci(vm, val, index):
|
||||
bus = sxp.child_value(val, 'bus')
|
||||
if not bus:
|
||||
raise VMError('pci: Missing bus')
|
||||
dev = sxp.child_value(val, 'dev')
|
||||
if not dev:
|
||||
raise VMError('pci: Missing dev')
|
||||
func = sxp.child_value(val, 'func')
|
||||
if not func:
|
||||
raise VMError('pci: Missing func')
|
||||
try:
|
||||
bus = parse_pci(bus)
|
||||
dev = parse_pci(dev)
|
||||
func = parse_pci(func)
|
||||
except:
|
||||
raise VMError('pci: invalid parameter')
|
||||
rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev,
|
||||
func=func, enable=1)
|
||||
if rc < 0:
|
||||
#todo non-fatal
|
||||
raise VMError('pci: Failed to configure device: bus=%s dev=%s func=%s' %
|
||||
(bus, dev, func))
|
||||
return rc
|
||||
|
||||
|
||||
def vm_field_vfr(vm, config, val, index):
|
||||
"""Handle a vfr field in a config.
|
||||
|
||||
vm virtual machine
|
||||
config vm config
|
||||
val vfr field
|
||||
"""
|
||||
# Get the rules and add them.
|
||||
# (vfr (vif (id foo) (ip x.x.x.x)) ... )
|
||||
list = sxp.children(val, 'vif')
|
||||
ipaddrs = []
|
||||
for v in list:
|
||||
id = sxp.child_value(v, 'id')
|
||||
if id is None:
|
||||
raise VmError('vfr: missing vif id')
|
||||
id = int(id)
|
||||
dev = vm.get_device_by_index('vif', id)
|
||||
if not dev:
|
||||
raise VmError('vfr: invalid vif id %d' % id)
|
||||
vif = sxp.child_value(dev, 'vif')
|
||||
ip = sxp.child_value(v, 'ip')
|
||||
if not ip:
|
||||
raise VmError('vfr: missing ip address')
|
||||
ipaddrs.append(ip);
|
||||
# todo: Configure the ipaddrs.
|
||||
vm.ipaddrs = ipaddrs
|
||||
|
||||
def vnet_bridge(vnet, vmac, dom, idx):
|
||||
"""Add the device for the vif to the bridge for its vnet.
|
||||
"""
|
||||
vif = "vif%d.%d" % (dom, idx)
|
||||
try:
|
||||
cmd = "(vif.conn (vif %s) (vnet %s) (vmac %s))" % (vif, vnet, vmac)
|
||||
print "*** vnet_bridge>", cmd
|
||||
out = file("/proc/vnet/policy", "wb")
|
||||
out.write(cmd)
|
||||
err = out.close()
|
||||
print "vnet_bridge>", "err=", err
|
||||
except IOError, ex:
|
||||
print "vnet_bridge>", ex
|
||||
|
||||
def vm_field_vnet(vm, config, val, index):
|
||||
"""Handle a vnet field in a config.
|
||||
|
||||
vm virtual machine
|
||||
config vm config
|
||||
val vnet field
|
||||
index index
|
||||
"""
|
||||
# Get the vif children. For each vif look up the vif device
|
||||
# with the given id and configure its vnet.
|
||||
# (vnet (vif (id foo) (vnet 2) (mac x:x:x:x:x:x)) ... )
|
||||
vif_vnets = sxp.children(val, 'vif')
|
||||
for v in vif_vnets:
|
||||
id = sxp.child_value(v, 'id')
|
||||
if id is None:
|
||||
raise VmError('vnet: missing vif id')
|
||||
dev = vm.get_device_by_id('vif', id)
|
||||
#vnet = sxp.child_value(v, 'vnet', 1)
|
||||
#mac = sxp.child_value(dev, 'mac')
|
||||
#vif = sxp.child_value(dev, 'vif')
|
||||
#vnet_bridge(vnet, mac, vm.dom, 0)
|
||||
#vm.add_config([ 'vif.vnet', ['id', id], ['vnet', vnet], ['mac', mac]])
|
||||
|
||||
# Register image handlers for linux and bsd.
|
||||
add_image_handler('linux', vm_image_linux)
|
||||
add_image_handler('netbsd', vm_image_netbsd)
|
||||
|
||||
# Register device handlers for vifs and vbds.
|
||||
add_device_handler('vif', vm_dev_vif)
|
||||
add_device_handler('vbd', vm_dev_vbd)
|
||||
add_device_handler('pci', vm_dev_pci)
|
||||
|
||||
# Register config handlers for vfr and vnet.
|
||||
add_config_handler('vfr', vm_field_vfr)
|
||||
add_config_handler('vnet', vm_field_vnet)
|
||||
@@ -0,0 +1,103 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
import sys
|
||||
import socket
|
||||
|
||||
import sxp
|
||||
import XendDB
|
||||
import EventServer; eserver = EventServer.instance()
|
||||
|
||||
class XendMigrateInfo:
|
||||
|
||||
# states: begin, active, failed, succeeded?
|
||||
|
||||
def __init__(self, id, dom, dst):
|
||||
self.id = id
|
||||
self.state = 'begin'
|
||||
self.src_host = socket.gethostname()
|
||||
self.src_dom = dom
|
||||
self.dst_host = dst
|
||||
self.dst_dom = None
|
||||
|
||||
def set_state(self, state):
|
||||
self.state = state
|
||||
|
||||
def get_state(self):
|
||||
return self.state
|
||||
|
||||
def sxpr(self):
|
||||
sxpr = ['migrate', ['id', self.id], ['state', self.state] ]
|
||||
sxpr_src = ['src', ['host', self.src_host], ['domain', self.src_dom] ]
|
||||
sxpr.append(sxpr_src)
|
||||
sxpr_dst = ['dst', ['host', self.dst] ]
|
||||
if self.dst_dom:
|
||||
sxpr_dst.append(['domain', self.dst_dom])
|
||||
sxpr.append(sxpr_dst)
|
||||
return sxpr
|
||||
|
||||
|
||||
class XendMigrate:
|
||||
# Represents migration in progress.
|
||||
# Use log for indications of begin/end/errors?
|
||||
# Need logging of: domain create/halt, migrate begin/end/fail
|
||||
# Log via event server?
|
||||
|
||||
dbpath = "migrate"
|
||||
|
||||
def __init__(self):
|
||||
self.db = XendDB.XendDB(self.dbpath)
|
||||
self.migrate = {}
|
||||
self.migrate_db = self.db.fetchall("")
|
||||
self.id = 0
|
||||
|
||||
def nextid(self):
|
||||
self.id += 1
|
||||
return "%d" % self.id
|
||||
|
||||
def sync(self):
|
||||
self.db.saveall("", self.migrate_db)
|
||||
|
||||
def sync_migrate(self, id):
|
||||
self.db.save(id, self.migrate_db[id])
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
def _add_migrate(self, id, info):
|
||||
self.migrate[id] = info
|
||||
self.migrate_db[id] = info.sxpr()
|
||||
self.sync_migrate(id)
|
||||
#eserver.inject('xend.migrate.begin', info.sxpr())
|
||||
|
||||
def _delete_migrate(self, id):
|
||||
#eserver.inject('xend.migrate.end', id)
|
||||
del self.migrate[id]
|
||||
del self.migrate_db[id]
|
||||
self.db.delete(id)
|
||||
|
||||
def migrate_ls(self):
|
||||
return self.migrate.keys()
|
||||
|
||||
def migrates(self):
|
||||
return self.migrate.values()
|
||||
|
||||
def migrate_get(self, id):
|
||||
return self.migrate.get(id)
|
||||
|
||||
def migrate_begin(self, dom, dst):
|
||||
# Check dom for existence, not migrating already.
|
||||
# Create migrate info, tell xend to migrate it?
|
||||
# - or fork migrate command ourselves?
|
||||
# Subscribe to migrate notifications (for updating).
|
||||
id = self.nextid()
|
||||
info = XenMigrateInfo(id, dom, dst)
|
||||
self._add_migrate(id, info)
|
||||
return id
|
||||
|
||||
def instance():
|
||||
global inst
|
||||
try:
|
||||
inst
|
||||
except:
|
||||
inst = XendMigrate()
|
||||
return inst
|
||||
@@ -0,0 +1,71 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
"""Handler for node operations.
|
||||
Has some persistent state:
|
||||
- logs
|
||||
- notification urls
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import xen.ext.xc
|
||||
|
||||
class XendNode:
|
||||
|
||||
def __init__(self):
|
||||
self.xc = xen.ext.xc.new()
|
||||
|
||||
def shutdown(self):
|
||||
return 0
|
||||
|
||||
def reboot(self):
|
||||
return 0
|
||||
|
||||
def notify(self, uri):
|
||||
return 0
|
||||
|
||||
def cpu_bvt_slice_set(self, slice):
|
||||
ret = 0
|
||||
#ret = self.xc.bvtsched_global_set(ctx_allow=slice)
|
||||
return ret
|
||||
|
||||
def cpu_bvt_slice_get(self, slice):
|
||||
ret = 0
|
||||
#ret = self.xc.bvtsched_global_get()
|
||||
return ret
|
||||
|
||||
def cpu_rrobin_slice_set(self, slice):
|
||||
ret = 0
|
||||
#ret = self.xc.rrobin_global_set(slice)
|
||||
return ret
|
||||
|
||||
def info(self):
|
||||
return self.nodeinfo() + self.physinfo()
|
||||
|
||||
def nodeinfo(self):
|
||||
(sys, host, rel, ver, mch) = os.uname()
|
||||
return [['system', sys],
|
||||
['host', host],
|
||||
['release', rel],
|
||||
['version', ver],
|
||||
['machine', mch]]
|
||||
|
||||
def physinfo(self):
|
||||
pinfo = self.xc.physinfo()
|
||||
info = [['cores', pinfo['cores']],
|
||||
['hyperthreads_per_core', pinfo['ht_per_core']],
|
||||
['cpu_mhz', pinfo['cpu_khz']/1000],
|
||||
['memory', pinfo['total_pages']/256],
|
||||
['free_memory', pinfo['free_pages']/256]]
|
||||
return info
|
||||
|
||||
|
||||
|
||||
def instance():
|
||||
global inst
|
||||
try:
|
||||
inst
|
||||
except:
|
||||
inst = XendNode()
|
||||
return inst
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
"""Xend root class.
|
||||
Creates the event server and handles configuration.
|
||||
"""
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
import EventServer
|
||||
|
||||
# Initial create of the event server.
|
||||
eserver = EventServer.instance()
|
||||
|
||||
import sxp
|
||||
|
||||
def reboots():
|
||||
"""Get a list of system reboots from wtmp.
|
||||
"""
|
||||
out = os.popen('/usr/bin/last reboot', 'r')
|
||||
list = [ x.strip() for x in out if x.startswith('reboot') ]
|
||||
return list
|
||||
|
||||
def last_reboot():
|
||||
"""Get the last known system reboot.
|
||||
"""
|
||||
l = reboots()
|
||||
return (l and l[-1]) or None
|
||||
|
||||
class XendRoot:
|
||||
"""Root of the management classes."""
|
||||
|
||||
lastboot_default = "/var/xen/lastboot"
|
||||
|
||||
"""Default path to the root of the database."""
|
||||
dbroot_default = "/var/xen/xend-db"
|
||||
|
||||
"""Default path to the config file."""
|
||||
config_default = "/etc/xen/xend-config.sxp"
|
||||
|
||||
"""Environment variable used to override config_default."""
|
||||
config_var = "XEND_CONFIG"
|
||||
|
||||
def __init__(self):
|
||||
self.rebooted = 0
|
||||
self.last_reboot = None
|
||||
self.dbroot = None
|
||||
self.config_path = None
|
||||
self.config = None
|
||||
self.configure()
|
||||
self.check_lastboot()
|
||||
eserver.subscribe('xend.*', self.event_handler)
|
||||
#eserver.subscribe('xend.domain.created', self.event_handler)
|
||||
#eserver.subscribe('xend.domain.died', self.event_handler)
|
||||
|
||||
def start(self):
|
||||
eserver.inject('xend.start', self.rebooted)
|
||||
|
||||
def event_handler(self, event, val):
|
||||
print >> sys.stderr, "EVENT>", event, val
|
||||
|
||||
def read_lastboot(self):
|
||||
try:
|
||||
val = file(self.lastboot, 'rb').readlines()[0]
|
||||
except StandardError, ex:
|
||||
print 'warning: Error reading', self.lastboot, ex
|
||||
val = None
|
||||
return val
|
||||
|
||||
def write_lastboot(self, val):
|
||||
if not val: return
|
||||
try:
|
||||
fdir = os.path.dirname(self.lastboot)
|
||||
if not os.path.isdir(fdir):
|
||||
os.makedirs(fdir)
|
||||
out = file(self.lastboot, 'wb+')
|
||||
out.write(val)
|
||||
out.close()
|
||||
except IOError, ex:
|
||||
print 'warning: Error writing', self.lastboot, ex
|
||||
pass
|
||||
|
||||
def check_lastboot(self):
|
||||
"""Check if there has been a system reboot since we saved lastboot.
|
||||
"""
|
||||
last_val = self.read_lastboot()
|
||||
this_val = last_reboot()
|
||||
if this_val == last_val:
|
||||
self.rebooted = 0
|
||||
else:
|
||||
self.rebooted = 1
|
||||
self.write_lastboot(this_val)
|
||||
self.last_reboot = this_val
|
||||
|
||||
def get_last_reboot(self):
|
||||
return self.last_reboot
|
||||
|
||||
def get_rebooted(self):
|
||||
return self.rebooted
|
||||
|
||||
def configure(self):
|
||||
self.set_config()
|
||||
self.dbroot = self.get_config_value("dbroot", self.dbroot_default)
|
||||
self.lastboot = self.get_config_value("lastboot", self.lastboot_default)
|
||||
|
||||
def get_dbroot(self):
|
||||
"""Get the path to the database root.
|
||||
"""
|
||||
return self.dbroot
|
||||
|
||||
def set_config(self):
|
||||
"""If the config file exists, read it. If not, ignore it.
|
||||
|
||||
The config file is a sequence of sxp forms.
|
||||
"""
|
||||
self.config_path = os.getenv(self.config_var, self.config_default)
|
||||
if os.path.exists(self.config_path):
|
||||
fin = file(self.config_path, 'rb')
|
||||
try:
|
||||
config = sxp.parse(fin)
|
||||
config.insert(0, 'config')
|
||||
self.config = config
|
||||
finally:
|
||||
fin.close()
|
||||
else:
|
||||
self.config = ['config']
|
||||
|
||||
def get_config(self, name=None):
|
||||
"""Get the configuration element with the given name, or
|
||||
the whole configuration if no name is given.
|
||||
|
||||
name element name (optional)
|
||||
returns config or none
|
||||
"""
|
||||
if name is None:
|
||||
val = self.config
|
||||
else:
|
||||
val = sxp.child(self.config, name)
|
||||
return val
|
||||
|
||||
def get_config_value(self, name, val=None):
|
||||
"""Get the value of an atomic configuration element.
|
||||
|
||||
name element name
|
||||
val default value (optional, defaults to None)
|
||||
returns value
|
||||
"""
|
||||
return sxp.child_value(self.config, name, val=val)
|
||||
|
||||
def instance():
|
||||
global inst
|
||||
try:
|
||||
inst
|
||||
except:
|
||||
inst = XendRoot()
|
||||
return inst
|
||||
@@ -0,0 +1,69 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
"""Handler for vnet operations.
|
||||
"""
|
||||
|
||||
import sxp
|
||||
import XendDB
|
||||
|
||||
class XendVnet:
|
||||
"""Index of all vnets. Singleton.
|
||||
"""
|
||||
|
||||
dbpath = "vnet"
|
||||
|
||||
def __init__(self):
|
||||
# Table of vnet info indexed by vnet id.
|
||||
self.vnet = {}
|
||||
self.db = XendDB.XendDB(self.dbpath)
|
||||
self.vnet = self.db.fetchall("")
|
||||
|
||||
def vnet_ls(self):
|
||||
"""List all vnets.
|
||||
"""
|
||||
return self.vnet.keys()
|
||||
|
||||
def vnets(self):
|
||||
return self.vnet.values()
|
||||
|
||||
def vnet_get(self, id):
|
||||
"""Get a vnet.
|
||||
|
||||
id vnet id
|
||||
"""
|
||||
return self.vnet.get(id)
|
||||
|
||||
def vnet_create(self, info):
|
||||
"""Create a vnet.
|
||||
|
||||
info config
|
||||
"""
|
||||
self.vnet_configure(info)
|
||||
|
||||
def vnet_configure(self, info):
|
||||
"""Configure a vnet.
|
||||
id vnet id
|
||||
info config
|
||||
"""
|
||||
# Need to configure for real.
|
||||
# Only sync if succeeded - otherwise need to back out.
|
||||
self.vnet[info.id] = info
|
||||
self.db.save(info.id, info)
|
||||
|
||||
def vnet_delete(self, id):
|
||||
"""Delete a vnet.
|
||||
|
||||
id vnet id
|
||||
"""
|
||||
# Need to delete for real. What if fails?
|
||||
if id in self.vnet:
|
||||
del self.vnet[id]
|
||||
self.db.delete(id)
|
||||
|
||||
def instance():
|
||||
global inst
|
||||
try:
|
||||
inst
|
||||
except:
|
||||
inst = XendVnet()
|
||||
return inst
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
"""Encoding for arguments to HTTP calls.
|
||||
Uses the url-encoding with MIME type 'application/x-www-form-urlencoded'
|
||||
if the data does not include files. Otherwise it uses the encoding with
|
||||
MIME type 'multipart/form-data'. See the HTML4 spec for details.
|
||||
|
||||
"""
|
||||
import sys
|
||||
import types
|
||||
from StringIO import StringIO
|
||||
|
||||
import urllib
|
||||
import httplib
|
||||
import random
|
||||
import md5
|
||||
|
||||
# Extract from HTML4 spec.
|
||||
## The following example illustrates "multipart/form-data"
|
||||
## encoding. Suppose we have the following form:
|
||||
|
||||
## <FORM action="http://server.com/cgi/handle"
|
||||
## enctype="multipart/form-data"
|
||||
## method="post">
|
||||
## <P>
|
||||
## What is your name? <INPUT type="text" name="submit-name"><BR>
|
||||
## What files are you sending? <INPUT type="file" name="files"><BR>
|
||||
## <INPUT type="submit" value="Send"> <INPUT type="reset">
|
||||
## </FORM>
|
||||
|
||||
## If the user enters "Larry" in the text input, and selects the text
|
||||
## file "file1.txt", the user agent might send back the following data:
|
||||
|
||||
## Content-Type: multipart/form-data; boundary=AaB03x
|
||||
|
||||
## --AaB03x
|
||||
## Content-Disposition: form-data; name="submit-name"
|
||||
|
||||
## Larry
|
||||
## --AaB03x
|
||||
## Content-Disposition: form-data; name="files"; filename="file1.txt"
|
||||
## Content-Type: text/plain
|
||||
|
||||
## ... contents of file1.txt ...
|
||||
## --AaB03x--
|
||||
|
||||
## If the user selected a second (image) file "file2.gif", the user agent
|
||||
## might construct the parts as follows:
|
||||
|
||||
## Content-Type: multipart/form-data; boundary=AaB03x
|
||||
|
||||
## --AaB03x
|
||||
## Content-Disposition: form-data; name="submit-name"
|
||||
|
||||
## Larry
|
||||
## --AaB03x
|
||||
## Content-Disposition: form-data; name="files"
|
||||
## Content-Type: multipart/mixed; boundary=BbC04y
|
||||
|
||||
## --BbC04y
|
||||
## Content-Disposition: file; filename="file1.txt"
|
||||
## Content-Type: text/plain
|
||||
|
||||
## ... contents of file1.txt ...
|
||||
## --BbC04y
|
||||
## Content-Disposition: file; filename="file2.gif"
|
||||
## Content-Type: image/gif
|
||||
## Content-Transfer-Encoding: binary
|
||||
|
||||
## ...contents of file2.gif...
|
||||
## --BbC04y--
|
||||
## --AaB03x--
|
||||
|
||||
__all__ = ['encode_data', 'encode_multipart', 'encode_form', 'mime_boundary' ]
|
||||
|
||||
def data_values(d):
|
||||
if isinstance(d, types.DictType):
|
||||
return d.items()
|
||||
else:
|
||||
return d
|
||||
|
||||
def encode_data(d):
|
||||
"""Encode some data for HTTP transport.
|
||||
The encoding used is stored in 'Content-Type' in the headers.
|
||||
|
||||
d data - sequence of tuples or dictionary
|
||||
returns a 2-tuple of the headers and the encoded data
|
||||
"""
|
||||
val = ({}, None)
|
||||
if d is None: return val
|
||||
multipart = 0
|
||||
for (k, v) in data_values(d):
|
||||
if encode_isfile(v):
|
||||
multipart = 1
|
||||
break
|
||||
if multipart:
|
||||
val = encode_multipart(d)
|
||||
else:
|
||||
val = encode_form(d)
|
||||
return val
|
||||
|
||||
def encode_isfile(v):
|
||||
if isinstance(v, types.FileType):
|
||||
return 1
|
||||
if hasattr(v, 'readlines'):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def encode_multipart(d):
|
||||
boundary = mime_boundary()
|
||||
hdr = { 'Content-Type': 'multipart/form-data; boundary=' + boundary }
|
||||
out = StringIO()
|
||||
for (k,v) in data_values(d):
|
||||
out.write('--')
|
||||
out.write(boundary)
|
||||
out.write('\r\n')
|
||||
if encode_isfile(v):
|
||||
out.write('Content-Disposition: form-data; name="')
|
||||
out.write(k)
|
||||
if hasattr(v, 'name'):
|
||||
out.write('"; filename="')
|
||||
out.write(v.name)
|
||||
out.write('"\r\n')
|
||||
out.write('Content-Type: application/octet-stream\r\n')
|
||||
out.write('\r\n')
|
||||
for l in v.readlines():
|
||||
out.write(l)
|
||||
else:
|
||||
out.write('Content-Disposition: form-data; name="')
|
||||
out.write(k)
|
||||
out.write('"\r\n')
|
||||
out.write('\r\n')
|
||||
out.write(str(v))
|
||||
out.write('\r\n')
|
||||
out.write('--')
|
||||
out.write(boundary)
|
||||
out.write('--')
|
||||
out.write('\r\n')
|
||||
return (hdr, out.getvalue())
|
||||
|
||||
def mime_boundary():
|
||||
random.seed()
|
||||
m = md5.new()
|
||||
for i in range(0, 10):
|
||||
c = chr(random.randint(1, 255))
|
||||
m.update(c)
|
||||
b = m.hexdigest()
|
||||
return b[0:16]
|
||||
|
||||
def encode_form(d):
|
||||
hdr = { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
val = urllib.urlencode(d)
|
||||
return (hdr, val)
|
||||
|
||||
def main():
|
||||
#d = {'a': 1, 'b': 'x y', 'c': file('conf.sxp') }
|
||||
#d = {'a': 1, 'b': 'x y' }
|
||||
d = [ ('a', 1), ('b', 'x y'), ('c', file('conf.sxp')) ]
|
||||
#d = [ ('a', 1), ('b', 'x y')]
|
||||
v = encode_data(d)
|
||||
print v[0]
|
||||
sys.stdout.write(v[1])
|
||||
print
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,137 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
import cgi
|
||||
|
||||
import os
|
||||
import sys
|
||||
import types
|
||||
import StringIO
|
||||
|
||||
from twisted.internet import defer
|
||||
from twisted.internet import reactor
|
||||
from twisted.web import error
|
||||
from twisted.web import resource
|
||||
from twisted.web import server
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import PrettyPrint
|
||||
|
||||
def uri_pathlist(p):
|
||||
"""Split a path into a list.
|
||||
p path
|
||||
return list of path elements
|
||||
"""
|
||||
l = []
|
||||
for x in p.split('/'):
|
||||
if x == '': continue
|
||||
l.append(x)
|
||||
return l
|
||||
|
||||
class SrvBase(resource.Resource):
|
||||
"""Base class for services.
|
||||
"""
|
||||
|
||||
def parse_form(self, req, method):
|
||||
"""Parse the data for a request, GET using the URL, POST using encoded data.
|
||||
Posts should use enctype='multipart/form-data' in the <form> tag,
|
||||
rather than 'application/x-www-form-urlencoded'. Only 'multipart/form-data'
|
||||
handles file upload.
|
||||
|
||||
req request
|
||||
returns a cgi.FieldStorage instance
|
||||
"""
|
||||
env = {}
|
||||
env['REQUEST_METHOD'] = method
|
||||
if self.query:
|
||||
env['QUERY_STRING'] = self.query
|
||||
val = cgi.FieldStorage(fp=req.rfile, headers=req.headers, environ=env)
|
||||
return val
|
||||
|
||||
def use_sxp(self, req):
|
||||
"""Determine whether to send an SXP response to a request.
|
||||
Uses SXP if there is no User-Agent, no Accept, or application/sxp is in Accept.
|
||||
|
||||
req request
|
||||
returns 1 for SXP, 0 otherwise
|
||||
"""
|
||||
ok = 0
|
||||
user_agent = req.getHeader('User-Agent')
|
||||
accept = req.getHeader('Accept')
|
||||
if (not user_agent) or (not accept) or (accept.find(sxp.mime_type) >= 0):
|
||||
ok = 1
|
||||
return ok
|
||||
|
||||
def get_op_method(self, op):
|
||||
"""Get the method for an operation.
|
||||
For operation 'foo' looks for 'op_foo'.
|
||||
|
||||
op operation name
|
||||
returns method or None
|
||||
"""
|
||||
op_method_name = 'op_' + op
|
||||
return getattr(self, op_method_name, None)
|
||||
|
||||
def perform(self, req):
|
||||
"""General operation handler for posted operations.
|
||||
For operation 'foo' looks for a method op_foo and calls
|
||||
it with op_foo(op, req). Replies with code 500 if op_foo
|
||||
is not found.
|
||||
|
||||
The method must return a list when req.use_sxp is true
|
||||
and an HTML string otherwise (or list).
|
||||
Methods may also return a Deferred (for incomplete processing).
|
||||
|
||||
req request
|
||||
"""
|
||||
op = req.args.get('op')
|
||||
if op is None or len(op) != 1:
|
||||
req.setResponseCode(404, "Invalid")
|
||||
return ''
|
||||
op = op[0]
|
||||
op_method = self.get_op_method(op)
|
||||
if op_method is None:
|
||||
req.setResponseCode(501, "Not implemented")
|
||||
req.setHeader("Content-Type", "text/plain")
|
||||
req.write("Not implemented: " + op)
|
||||
return ''
|
||||
else:
|
||||
val = op_method(op, req)
|
||||
if isinstance(val, defer.Deferred):
|
||||
val.addCallback(self._cb_perform, req, 1)
|
||||
return server.NOT_DONE_YET
|
||||
else:
|
||||
self._cb_perform(val, req, 0)
|
||||
return ''
|
||||
|
||||
def _cb_perform(self, val, req, dfr):
|
||||
"""Callback to complete the request.
|
||||
May be called from a Deferred.
|
||||
"""
|
||||
if isinstance(val, error.ErrorPage):
|
||||
req.write(val.render(req))
|
||||
elif self.use_sxp(req):
|
||||
req.setHeader("Content-Type", sxp.mime_type)
|
||||
sxp.show(val, req)
|
||||
else:
|
||||
req.write('<html><head></head><body>')
|
||||
self.print_path(req)
|
||||
if isinstance(val, types.ListType):
|
||||
req.write('<code><pre>')
|
||||
PrettyPrint.prettyprint(val, out=req)
|
||||
req.write('</pre></code>')
|
||||
else:
|
||||
req.write(str(val))
|
||||
req.write('</body></html>')
|
||||
if dfr:
|
||||
req.finish()
|
||||
|
||||
def print_path(self, req):
|
||||
"""Print the path with hyperlinks.
|
||||
"""
|
||||
pathlist = [x for x in req.prepath if x != '' ]
|
||||
s = "/"
|
||||
req.write('<h1><a href="/">/</a>')
|
||||
for x in pathlist:
|
||||
s += x + "/"
|
||||
req.write(' <a href="%s">%s</a>/' % (s, x))
|
||||
req.write("</h1>")
|
||||
@@ -0,0 +1,42 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import XendConsole
|
||||
from SrvDir import SrvDir
|
||||
|
||||
class SrvConsole(SrvDir):
|
||||
"""An individual console.
|
||||
"""
|
||||
|
||||
def __init__(self, info):
|
||||
SrvDir.__init__(self)
|
||||
self.info = info
|
||||
self.xc = XendConsole.instance()
|
||||
|
||||
def op_disconnect(self, op, req):
|
||||
val = self.xc.console_disconnect(self.info.id)
|
||||
return val
|
||||
|
||||
def render_POST(self, req):
|
||||
return self.perform(req)
|
||||
|
||||
def render_GET(self, req):
|
||||
if self.use_sxp(req):
|
||||
req.setHeader("Content-Type", sxp.mime_type)
|
||||
sxp.show(self.info.sxpr(), out=req)
|
||||
else:
|
||||
req.write('<html><head></head><body>')
|
||||
self.print_path(req)
|
||||
#self.ls()
|
||||
req.write('<p>%s</p>' % self.info)
|
||||
req.write('<p><a href="%s">Connect to domain %d</a></p>'
|
||||
% (self.info.uri(), self.info.dom2))
|
||||
self.form(req)
|
||||
req.write('</body></html>')
|
||||
return ''
|
||||
|
||||
def form(self, req):
|
||||
req.write('<form method="post" action="%s">' % req.prePathURL())
|
||||
if self.info.connection():
|
||||
req.write('<input type="submit" name="op" value="disconnect">')
|
||||
req.write('</form>')
|
||||
@@ -0,0 +1,59 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
from SrvDir import SrvDir
|
||||
from SrvConsole import SrvConsole
|
||||
from xen.xend import XendConsole
|
||||
from xen.xend import sxp
|
||||
|
||||
class SrvConsoleDir(SrvDir):
|
||||
"""Console directory.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
SrvDir.__init__(self)
|
||||
self.xconsole = XendConsole.instance()
|
||||
|
||||
def console(self, x):
|
||||
val = None
|
||||
try:
|
||||
info = self.xconsole.console_get(x)
|
||||
val = SrvConsole(info)
|
||||
except KeyError, ex:
|
||||
print 'SrvConsoleDir>', ex
|
||||
pass
|
||||
return val
|
||||
|
||||
def get(self, x):
|
||||
v = SrvDir.get(self, x)
|
||||
if v is not None:
|
||||
return v
|
||||
v = self.console(x)
|
||||
return v
|
||||
|
||||
def render_GET(self, req):
|
||||
if self.use_sxp(req):
|
||||
req.setHeader("Content-Type", sxp.mime_type)
|
||||
self.ls_console(req, 1)
|
||||
else:
|
||||
req.write("<html><head></head><body>")
|
||||
self.print_path(req)
|
||||
self.ls(req)
|
||||
self.ls_console(req)
|
||||
#self.form(req.wfile)
|
||||
req.write("</body></html>")
|
||||
return ''
|
||||
|
||||
def ls_console(self, req, use_sxp=0):
|
||||
url = req.prePathURL()
|
||||
if not url.endswith('/'):
|
||||
url += '/'
|
||||
if use_sxp:
|
||||
consoles = self.xconsole.console_ls()
|
||||
sxp.show(consoles, out=req)
|
||||
else:
|
||||
consoles = self.xconsole.consoles()
|
||||
consoles.sort(lambda x, y: cmp(x.id, y.id))
|
||||
req.write('<ul>')
|
||||
for c in consoles:
|
||||
req.write('<li><a href="%s%s"> %s</a></li>' % (url, c.id, c))
|
||||
req.write('</ul>')
|
||||
@@ -0,0 +1,751 @@
|
||||
###########################################################
|
||||
## Xen controller daemon
|
||||
## Copyright (c) 2004, K A Fraser (University of Cambridge)
|
||||
## Copyright (C) 2004, Mike Wray <mike.wray@hp.com>
|
||||
###########################################################
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import signal
|
||||
import sys
|
||||
import threading
|
||||
import linecache
|
||||
import socket
|
||||
import pwd
|
||||
import re
|
||||
import StringIO
|
||||
|
||||
from twisted.internet import pollreactor
|
||||
pollreactor.install()
|
||||
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet import protocol
|
||||
from twisted.internet import abstract
|
||||
from twisted.internet import defer
|
||||
|
||||
from xen.ext import xu
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import PrettyPrint
|
||||
from xen.xend import EventServer
|
||||
eserver = EventServer.instance()
|
||||
|
||||
from xen.xend.server import SrvServer
|
||||
|
||||
import channel
|
||||
import blkif
|
||||
import netif
|
||||
import console
|
||||
import domain
|
||||
from params import *
|
||||
|
||||
DEBUG = 1
|
||||
|
||||
class MgmtProtocol(protocol.DatagramProtocol):
|
||||
"""Handler for the management socket (unix-domain).
|
||||
"""
|
||||
|
||||
def __init__(self, daemon):
|
||||
#protocol.DatagramProtocol.__init__(self)
|
||||
self.daemon = daemon
|
||||
|
||||
def write(self, data, addr):
|
||||
return self.transport.write(data, addr)
|
||||
|
||||
def datagramReceived(self, data, addr):
|
||||
if DEBUG: print 'datagramReceived> addr=', addr, 'data=', data
|
||||
io = StringIO.StringIO(data)
|
||||
try:
|
||||
vals = sxp.parse(io)
|
||||
res = self.dispatch(vals[0])
|
||||
self.send_result(addr, res)
|
||||
except SystemExit:
|
||||
raise
|
||||
except:
|
||||
if DEBUG:
|
||||
raise
|
||||
else:
|
||||
self.send_error(addr)
|
||||
|
||||
def send_reply(self, addr, sxpr):
|
||||
io = StringIO.StringIO()
|
||||
sxp.show(sxpr, out=io)
|
||||
io.seek(0)
|
||||
self.write(io.getvalue(), addr)
|
||||
|
||||
def send_result(self, addr, res):
|
||||
|
||||
def fn(res, self=self, addr=addr):
|
||||
self.send_reply(addr, ['ok', res])
|
||||
|
||||
if isinstance(res, defer.Deferred):
|
||||
res.addCallback(fn)
|
||||
else:
|
||||
fn(res)
|
||||
|
||||
def send_error(self, addr):
|
||||
(extype, exval) = sys.exc_info()[:2]
|
||||
self.send_reply(addr, ['err',
|
||||
['type', str(extype) ],
|
||||
['value', str(exval) ] ] )
|
||||
|
||||
def opname(self, name):
|
||||
"""Get the name of the method for an operation.
|
||||
"""
|
||||
return 'op_' + name.replace('.', '_')
|
||||
|
||||
def operror(self, name, v):
|
||||
"""Default operation handler - signals an error.
|
||||
"""
|
||||
raise NotImplementedError('Invalid operation: ' +name)
|
||||
|
||||
def dispatch(self, req):
|
||||
"""Dispatch a request to its handler.
|
||||
"""
|
||||
op_name = sxp.name(req)
|
||||
op_method_name = self.opname(op_name)
|
||||
op_method = getattr(self, op_method_name, self.operror)
|
||||
return op_method(op_name, req)
|
||||
|
||||
def op_console_create(self, name, req):
|
||||
"""Create a new control interface - console for a domain.
|
||||
"""
|
||||
print name, req
|
||||
dom = sxp.child_value(req, 'domain')
|
||||
if not dom: raise ValueError('Missing domain')
|
||||
dom = int(dom)
|
||||
console_port = sxp.child_value(req, 'console_port')
|
||||
if console_port:
|
||||
console_port = int(console_port)
|
||||
resp = self.daemon.console_create(dom, console_port)
|
||||
print name, resp
|
||||
return resp
|
||||
|
||||
def op_consoles(self, name, req):
|
||||
"""Get a list of the consoles.
|
||||
"""
|
||||
return self.daemon.consoles()
|
||||
|
||||
def op_console_disconnect(self, name, req):
|
||||
id = sxp.child_value(req, 'id')
|
||||
if not id:
|
||||
raise ValueError('Missing console id')
|
||||
id = int(id)
|
||||
console = self.daemon.get_console(id)
|
||||
if not console:
|
||||
raise ValueError('Invalid console id')
|
||||
if console.conn:
|
||||
console.conn.loseConnection()
|
||||
return ['ok']
|
||||
|
||||
def op_blkifs(self, name, req):
|
||||
pass
|
||||
|
||||
def op_blkif_devs(self, name, req):
|
||||
pass
|
||||
|
||||
def op_blkif_create(self, name, req):
|
||||
pass
|
||||
|
||||
def op_blkif_dev_create(self, name, req):
|
||||
pass
|
||||
|
||||
def op_netifs(self, name, req):
|
||||
pass
|
||||
|
||||
def op_netif_devs(self, name, req):
|
||||
pass
|
||||
|
||||
def op_netif_create(self, name, req):
|
||||
pass
|
||||
|
||||
def op_netif_dev_create(self, name, req):
|
||||
pass
|
||||
|
||||
class NotifierProtocol(protocol.Protocol):
|
||||
"""Asynchronous handler for i/o on the notifier (event channel).
|
||||
"""
|
||||
|
||||
def __init__(self, channelFactory):
|
||||
self.channelFactory = channelFactory
|
||||
|
||||
def notificationReceived(self, idx, type):
|
||||
#print 'NotifierProtocol>notificationReceived>', idx, type
|
||||
channel = self.channelFactory.getChannel(idx)
|
||||
if not channel:
|
||||
return
|
||||
#print 'NotifierProtocol>notificationReceived> channel', channel
|
||||
channel.notificationReceived(type)
|
||||
|
||||
def connectionLost(self, reason=None):
|
||||
pass
|
||||
|
||||
def doStart(self):
|
||||
pass
|
||||
|
||||
def doStop(self):
|
||||
pass
|
||||
|
||||
def startProtocol(self):
|
||||
pass
|
||||
|
||||
def stopProtocol(self):
|
||||
pass
|
||||
|
||||
class NotifierPort(abstract.FileDescriptor):
|
||||
"""Transport class for the event channel.
|
||||
"""
|
||||
|
||||
def __init__(self, daemon, notifier, proto, reactor=None):
|
||||
assert isinstance(proto, NotifierProtocol)
|
||||
abstract.FileDescriptor.__init__(self, reactor)
|
||||
self.daemon = daemon
|
||||
self.notifier = notifier
|
||||
self.protocol = proto
|
||||
|
||||
def startListening(self):
|
||||
self._bindNotifier()
|
||||
self._connectToProtocol()
|
||||
|
||||
def stopListening(self):
|
||||
if self.connected:
|
||||
result = self.d = defer.Deferred()
|
||||
else:
|
||||
result = None
|
||||
self.loseConnection()
|
||||
return result
|
||||
|
||||
def fileno(self):
|
||||
return self.notifier.fileno()
|
||||
|
||||
def _bindNotifier(self):
|
||||
self.connected = 1
|
||||
|
||||
def _connectToProtocol(self):
|
||||
self.protocol.makeConnection(self)
|
||||
self.startReading()
|
||||
|
||||
def loseConnection(self):
|
||||
if self.connected:
|
||||
self.stopReading()
|
||||
self.disconnecting = 1
|
||||
reactor.callLater(0, self.connectionLost)
|
||||
|
||||
def connectionLost(self, reason=None):
|
||||
abstract.FileDescriptor.connectionLost(self, reason)
|
||||
if hasattr(self, 'protocol'):
|
||||
self.protocol.doStop()
|
||||
self.connected = 0
|
||||
#self.notifier.close() # Not implemented.
|
||||
os.close(self.fileno())
|
||||
del self.notifier
|
||||
if hasattr(self, 'd'):
|
||||
self.d.callback(None)
|
||||
del self.d
|
||||
|
||||
def doRead(self):
|
||||
#print 'NotifierPort>doRead>', self
|
||||
count = 0
|
||||
while 1:
|
||||
#print 'NotifierPort>doRead>', count
|
||||
notification = self.notifier.read()
|
||||
if not notification:
|
||||
break
|
||||
(idx, type) = notification
|
||||
self.protocol.notificationReceived(idx, type)
|
||||
self.notifier.unmask(idx)
|
||||
count += 1
|
||||
#print 'NotifierPort>doRead<'
|
||||
|
||||
class EventProtocol(protocol.Protocol):
|
||||
"""Asynchronous handler for a connected event socket.
|
||||
"""
|
||||
|
||||
def __init__(self, daemon):
|
||||
#protocol.Protocol.__init__(self)
|
||||
self.daemon = daemon
|
||||
# Event queue.
|
||||
self.queue = []
|
||||
# Subscribed events.
|
||||
self.events = []
|
||||
self.parser = sxp.Parser()
|
||||
self.pretty = 0
|
||||
|
||||
# For debugging subscribe to everything and make output pretty.
|
||||
self.subscribe(['*'])
|
||||
self.pretty = 1
|
||||
|
||||
def dataReceived(self, data):
|
||||
try:
|
||||
self.parser.input(data)
|
||||
if self.parser.ready():
|
||||
val = self.parser.get_val()
|
||||
res = self.dispatch(val)
|
||||
self.send_result(res)
|
||||
if self.parser.at_eof():
|
||||
self.loseConnection()
|
||||
except SystemExit:
|
||||
raise
|
||||
except:
|
||||
if DEBUG:
|
||||
raise
|
||||
else:
|
||||
self.send_error()
|
||||
|
||||
def loseConnection(self):
|
||||
if self.transport:
|
||||
self.transport.loseConnection()
|
||||
if self.connected:
|
||||
reactor.callLater(0, self.connectionLost)
|
||||
|
||||
def connectionLost(self, reason=None):
|
||||
self.unsubscribe()
|
||||
|
||||
def send_reply(self, sxpr):
|
||||
io = StringIO.StringIO()
|
||||
if self.pretty:
|
||||
PrettyPrint.prettyprint(sxpr, out=io)
|
||||
else:
|
||||
sxp.show(sxpr, out=io)
|
||||
print >> io
|
||||
io.seek(0)
|
||||
return self.transport.write(io.getvalue())
|
||||
|
||||
def send_result(self, res):
|
||||
return self.send_reply(['ok', res])
|
||||
|
||||
def send_error(self):
|
||||
(extype, exval) = sys.exc_info()[:2]
|
||||
return self.send_reply(['err',
|
||||
['type', str(extype)],
|
||||
['value', str(exval)]])
|
||||
|
||||
def send_event(self, val):
|
||||
return self.send_reply(['event', val[0], val[1]])
|
||||
|
||||
def unsubscribe(self):
|
||||
for event in self.events:
|
||||
eserver.unsubscribe(event, self.queue_event)
|
||||
|
||||
def subscribe(self, events):
|
||||
self.unsubscribe()
|
||||
for event in events:
|
||||
eserver.subscribe(event, self.queue_event)
|
||||
self.events = events
|
||||
|
||||
def queue_event(self, name, v):
|
||||
# Despite the name we dont' queue the event here.
|
||||
# We send it because the transport will queue it.
|
||||
self.send_event([name, v])
|
||||
|
||||
def opname(self, name):
|
||||
return 'op_' + name.replace('.', '_')
|
||||
|
||||
def operror(self, name, req):
|
||||
raise NotImplementedError('Invalid operation: ' +name)
|
||||
|
||||
def dispatch(self, req):
|
||||
op_name = sxp.name(req)
|
||||
op_method_name = self.opname(op_name)
|
||||
op_method = getattr(self, op_method_name, self.operror)
|
||||
return op_method(op_name, req)
|
||||
|
||||
def op_help(self, name, req):
|
||||
def nameop(x):
|
||||
if x.startswith('op_'):
|
||||
return x[3:].replace('_', '.')
|
||||
else:
|
||||
return x
|
||||
|
||||
l = [ nameop(k) for k in dir(self) if k.startswith('op_') ]
|
||||
return l
|
||||
|
||||
def op_quit(self, name, req):
|
||||
self.loseConnection()
|
||||
|
||||
def op_exit(self, name, req):
|
||||
sys.exit(0)
|
||||
|
||||
def op_pretty(self, name, req):
|
||||
self.pretty = 1
|
||||
return ['ok']
|
||||
|
||||
def op_console_disconnect(self, name, req):
|
||||
id = sxp.child_value(req, 'id')
|
||||
if not id:
|
||||
raise ValueError('Missing console id')
|
||||
self.daemon.console_disconnect(id)
|
||||
return ['ok']
|
||||
|
||||
def op_info(self, name, req):
|
||||
val = ['info']
|
||||
val += self.daemon.consoles()
|
||||
val += self.daemon.blkifs()
|
||||
val += self.daemon.netifs()
|
||||
return val
|
||||
|
||||
def op_sys_subscribe(self, name, v):
|
||||
# (sys.subscribe event*)
|
||||
# Subscribe to the events:
|
||||
self.subscribe(v[1:])
|
||||
return ['ok']
|
||||
|
||||
def op_sys_inject(self, name, v):
|
||||
# (sys.inject event)
|
||||
event = v[1]
|
||||
eserver.inject(sxp.name(event), event)
|
||||
return ['ok']
|
||||
|
||||
|
||||
class EventFactory(protocol.Factory):
|
||||
"""Asynchronous handler for the event server socket.
|
||||
"""
|
||||
protocol = EventProtocol
|
||||
service = None
|
||||
|
||||
def __init__(self, daemon):
|
||||
#protocol.Factory.__init__(self)
|
||||
self.daemon = daemon
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
proto = self.protocol(self.daemon)
|
||||
proto.factory = self
|
||||
return proto
|
||||
|
||||
class VirqClient:
|
||||
def __init__(self, daemon):
|
||||
self.daemon = daemon
|
||||
|
||||
def virqReceived(self, virq):
|
||||
print 'VirqClient.virqReceived>', virq
|
||||
eserver.inject('xend.virq', virq)
|
||||
|
||||
def lostChannel(self, channel):
|
||||
print 'VirqClient.lostChannel>', channel
|
||||
|
||||
class Daemon:
|
||||
"""The xend daemon.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.shutdown = 0
|
||||
|
||||
def daemon_pids(self):
|
||||
pids = []
|
||||
pidex = '(?P<pid>\d+)'
|
||||
pythonex = '(?P<python>\S*python\S*)'
|
||||
cmdex = '(?P<cmd>.*)'
|
||||
procre = re.compile('^\s*' + pidex + '\s*' + pythonex + '\s*' + cmdex + '$')
|
||||
xendre = re.compile('^/usr/sbin/xend\s*(start|restart)\s*.*$')
|
||||
procs = os.popen('ps -e -o pid,args 2>/dev/null')
|
||||
for proc in procs:
|
||||
pm = procre.match(proc)
|
||||
if not pm: continue
|
||||
xm = xendre.match(pm.group('cmd'))
|
||||
if not xm: continue
|
||||
#print 'pid=', pm.group('pid'), 'cmd=', pm.group('cmd')
|
||||
pids.append(int(pm.group('pid')))
|
||||
return pids
|
||||
|
||||
def new_cleanup(self, kill=0):
|
||||
err = 0
|
||||
pids = self.daemon_pids()
|
||||
if kill:
|
||||
for pid in pids:
|
||||
print "Killing daemon pid=%d" % pid
|
||||
os.kill(pid, signal.SIGHUP)
|
||||
elif pids:
|
||||
err = 1
|
||||
print "Daemon already running: ", pids
|
||||
return err
|
||||
|
||||
def cleanup(self, kill=False):
|
||||
# No cleanup to do if PID_FILE is empty.
|
||||
if not os.path.isfile(PID_FILE) or not os.path.getsize(PID_FILE):
|
||||
return 0
|
||||
# Read the pid of the previous invocation and search active process list.
|
||||
pid = open(PID_FILE, 'r').read()
|
||||
lines = os.popen('ps ' + pid + ' 2>/dev/null').readlines()
|
||||
for line in lines:
|
||||
if re.search('^ *' + pid + '.+xend', line):
|
||||
if not kill:
|
||||
print "Daemon is already running (pid %d)" % int(pid)
|
||||
return 1
|
||||
# Old daemon is still active: terminate it.
|
||||
os.kill(int(pid), 1)
|
||||
# Delete the stale PID_FILE.
|
||||
os.remove(PID_FILE)
|
||||
return 0
|
||||
|
||||
def install_child_reaper(self):
|
||||
#signal.signal(signal.SIGCHLD, self.onSIGCHLD)
|
||||
# Ensure that zombie children are automatically reaped.
|
||||
xu.autoreap()
|
||||
|
||||
def onSIGCHLD(self, signum, frame):
|
||||
code = 1
|
||||
while code > 0:
|
||||
code = os.waitpid(-1, os.WNOHANG)
|
||||
|
||||
def start(self,trace=0):
|
||||
if self.cleanup(kill=False):
|
||||
return 1
|
||||
|
||||
# Detach from TTY.
|
||||
if not DEBUG:
|
||||
os.setsid()
|
||||
|
||||
if self.set_user():
|
||||
return 1
|
||||
|
||||
self.install_child_reaper()
|
||||
|
||||
# Fork -- parent writes PID_FILE and exits.
|
||||
pid = os.fork()
|
||||
if pid:
|
||||
# Parent
|
||||
pidfile = open(PID_FILE, 'w')
|
||||
pidfile.write(str(pid))
|
||||
pidfile.close()
|
||||
return 0
|
||||
# Child
|
||||
logfile = self.open_logfile()
|
||||
self.redirect_output(logfile)
|
||||
if trace:
|
||||
self.tracefile = open('/var/log/xend.trace', 'w+', 1)
|
||||
self.traceindent = 0
|
||||
sys.settrace(self.trace)
|
||||
try:
|
||||
threading.settrace(self.trace) # Only in Python >= 2.3
|
||||
except:
|
||||
pass
|
||||
self.run()
|
||||
return 0
|
||||
|
||||
def print_trace(self,str):
|
||||
for i in range(self.traceindent):
|
||||
self.tracefile.write(" ")
|
||||
self.tracefile.write(str)
|
||||
|
||||
def trace(self, frame, event, arg):
|
||||
if event == 'call':
|
||||
code = frame.f_code
|
||||
filename = code.co_filename
|
||||
m = re.search('.*xenmgr/(.*)', code.co_filename)
|
||||
if not m:
|
||||
return None
|
||||
modulename = m.group(1)
|
||||
if re.search('sxp.py', modulename):
|
||||
return None
|
||||
self.traceindent += 1
|
||||
self.print_trace("++++ %s:%s\n"
|
||||
% (modulename, code.co_name))
|
||||
elif event == 'line':
|
||||
filename = frame.f_code.co_filename
|
||||
lineno = frame.f_lineno
|
||||
self.print_trace("%4d %s" %
|
||||
(lineno, linecache.getline(filename, lineno)))
|
||||
elif event == 'return':
|
||||
code = frame.f_code
|
||||
filename = code.co_filename
|
||||
m = re.search('.*xenmgr/(.*)', code.co_filename)
|
||||
if not m:
|
||||
return None
|
||||
modulename = m.group(1)
|
||||
self.print_trace("---- %s:%s\n"
|
||||
% (modulename, code.co_name))
|
||||
self.traceindent -= 1
|
||||
elif event == 'exception':
|
||||
pass
|
||||
return self.trace
|
||||
|
||||
def open_logfile(self):
|
||||
if not os.path.exists(CONTROL_DIR):
|
||||
os.makedirs(CONTROL_DIR)
|
||||
|
||||
# Open log file. Truncate it if non-empty, and request line buffering.
|
||||
if os.path.isfile(LOG_FILE):
|
||||
os.rename(LOG_FILE, LOG_FILE+'.old')
|
||||
logfile = open(LOG_FILE, 'w+', 1)
|
||||
return logfile
|
||||
|
||||
def set_user(self):
|
||||
# Set the UID.
|
||||
try:
|
||||
os.setuid(pwd.getpwnam(USER)[2])
|
||||
return 0
|
||||
except KeyError, error:
|
||||
print "Error: no such user '%s'" % USER
|
||||
return 1
|
||||
|
||||
def redirect_output(self, logfile):
|
||||
if DEBUG: return
|
||||
# Close down standard file handles
|
||||
try:
|
||||
os.close(0) # stdin
|
||||
os.close(1) # stdout
|
||||
os.close(2) # stderr
|
||||
except:
|
||||
pass
|
||||
# Redirect output to log file.
|
||||
sys.stdout = sys.stderr = logfile
|
||||
|
||||
def stop(self):
|
||||
return self.cleanup(kill=True)
|
||||
|
||||
def run(self):
|
||||
self.createFactories()
|
||||
self.listenMgmt()
|
||||
self.listenEvent()
|
||||
self.listenNotifier()
|
||||
self.listenVirq()
|
||||
SrvServer.create(bridge=1)
|
||||
reactor.run()
|
||||
|
||||
def createFactories(self):
|
||||
self.channelF = channel.channelFactory()
|
||||
self.domainCF = domain.DomainControllerFactory()
|
||||
self.blkifCF = blkif.BlkifControllerFactory()
|
||||
self.netifCF = netif.NetifControllerFactory()
|
||||
self.consoleCF = console.ConsoleControllerFactory()
|
||||
|
||||
def listenMgmt(self):
|
||||
protocol = MgmtProtocol(self)
|
||||
s = os.path.join(CONTROL_DIR, MGMT_SOCK)
|
||||
if os.path.exists(s):
|
||||
os.unlink(s)
|
||||
return reactor.listenUNIXDatagram(s, protocol)
|
||||
|
||||
def listenEvent(self):
|
||||
protocol = EventFactory(self)
|
||||
return reactor.listenTCP(EVENT_PORT, protocol)
|
||||
|
||||
def listenNotifier(self):
|
||||
protocol = NotifierProtocol(self.channelF)
|
||||
p = NotifierPort(self, self.channelF.notifier, protocol, reactor)
|
||||
p.startListening()
|
||||
return p
|
||||
|
||||
def listenVirq(self):
|
||||
virqChan = self.channelF.virqChannel(channel.VIRQ_DOM_EXC)
|
||||
virqChan.registerClient(VirqClient(self))
|
||||
|
||||
def exit(self):
|
||||
reactor.diconnectAll()
|
||||
sys.exit(0)
|
||||
|
||||
def blkif_set_control_domain(self, dom, recreate=0):
|
||||
"""Set the block device backend control domain.
|
||||
"""
|
||||
return self.blkifCF.setControlDomain(dom, recreate=recreate)
|
||||
|
||||
def blkif_get_control_domain(self, dom):
|
||||
"""Get the block device backend control domain.
|
||||
"""
|
||||
return self.blkifCF.getControlDomain()
|
||||
|
||||
def blkif_create(self, dom, recreate=0):
|
||||
"""Create a block device interface controller.
|
||||
|
||||
Returns Deferred
|
||||
"""
|
||||
d = self.blkifCF.createInstance(dom, recreate=recreate)
|
||||
return d
|
||||
|
||||
def blkifs(self):
|
||||
return [ x.sxpr() for x in self.blkifCF.getInstances() ]
|
||||
|
||||
def blkif_get(self, dom):
|
||||
return self.blkifCF.getInstanceByDom(dom)
|
||||
|
||||
def blkif_dev(self, dom, vdev):
|
||||
return self.blkifCF.getDomainDevice(dom, vdev)
|
||||
|
||||
def blkif_dev_create(self, dom, vdev, mode, segment, recreate=0):
|
||||
"""Create a block device.
|
||||
|
||||
Returns Deferred
|
||||
"""
|
||||
ctrl = self.blkifCF.getInstanceByDom(dom)
|
||||
if not ctrl:
|
||||
raise ValueError('No blkif controller: %d' % dom)
|
||||
print 'blkif_dev_create>', dom, vdev, mode, segment
|
||||
d = ctrl.attachDevice(vdev, mode, segment, recreate=recreate)
|
||||
return d
|
||||
|
||||
def netif_set_control_domain(self, dom, recreate=0):
|
||||
"""Set the network interface backend control domain.
|
||||
"""
|
||||
return self.netifCF.setControlDomain(dom, recreate=recreate)
|
||||
|
||||
def netif_get_control_domain(self, dom):
|
||||
"""Get the network interface backend control domain.
|
||||
"""
|
||||
return self.netifCF.getControlDomain()
|
||||
|
||||
def netif_create(self, dom, recreate=0):
|
||||
"""Create a network interface controller.
|
||||
|
||||
"""
|
||||
return self.netifCF.createInstance(dom, recreate=recreate)
|
||||
|
||||
def netifs(self):
|
||||
return [ x.sxpr() for x in self.netifCF.getInstances() ]
|
||||
|
||||
def netif_get(self, dom):
|
||||
return self.netifCF.getInstanceByDom(dom)
|
||||
|
||||
def netif_dev_create(self, dom, vif, vmac, recreate=0):
|
||||
"""Create a network device.
|
||||
|
||||
todo
|
||||
"""
|
||||
ctrl = self.netifCF.getInstanceByDom(dom)
|
||||
if not ctrl:
|
||||
raise ValueError('No netif controller: %d' % dom)
|
||||
d = ctrl.attachDevice(vif, vmac, recreate=recreate)
|
||||
return d
|
||||
|
||||
def netif_dev(self, dom, vif):
|
||||
return self.netifCF.getDomainDevice(dom, vif)
|
||||
|
||||
def console_create(self, dom, console_port=None):
|
||||
"""Create a console for a domain.
|
||||
"""
|
||||
console = self.consoleCF.getInstanceByDom(dom)
|
||||
if console is None:
|
||||
console = self.consoleCF.createInstance(dom, console_port)
|
||||
return console.sxpr()
|
||||
|
||||
def consoles(self):
|
||||
return [ c.sxpr() for c in self.consoleCF.getInstances() ]
|
||||
|
||||
def get_console(self, id):
|
||||
return self.consoleCF.getInstance(id)
|
||||
|
||||
def get_domain_console(self, dom):
|
||||
return self.consoleCF.getInstanceByDom(dom)
|
||||
|
||||
def console_disconnect(self, id):
|
||||
"""Disconnect any connected console client.
|
||||
"""
|
||||
console = self.get_console(id)
|
||||
if not console:
|
||||
raise ValueError('Invalid console id')
|
||||
console.disconnect()
|
||||
|
||||
def domain_shutdown(self, dom, reason):
|
||||
"""Shutdown a domain.
|
||||
"""
|
||||
ctrl = self.domainCF.getInstanceByDom(dom)
|
||||
if not ctrl:
|
||||
raise ValueError('No domain controller: %d' % dom)
|
||||
ctrl.shutdown(reason)
|
||||
return 0
|
||||
|
||||
def instance():
|
||||
global inst
|
||||
try:
|
||||
inst
|
||||
except:
|
||||
inst = Daemon()
|
||||
return inst
|
||||
@@ -0,0 +1,9 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
from SrvDir import SrvDir
|
||||
|
||||
class SrvDeviceDir(SrvDir):
|
||||
"""Device directory.
|
||||
"""
|
||||
|
||||
pass
|
||||
@@ -0,0 +1,91 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
from twisted.web import error
|
||||
from xen.xend import sxp
|
||||
from SrvBase import SrvBase
|
||||
|
||||
class SrvConstructor:
|
||||
"""Delayed constructor for sub-servers.
|
||||
Does not import the sub-server class or create the object until needed.
|
||||
"""
|
||||
|
||||
def __init__(self, klass):
|
||||
"""Create a constructor. It is assumed that the class
|
||||
should be imported as 'import klass from klass'.
|
||||
|
||||
klass name of its class
|
||||
"""
|
||||
self.klass = klass
|
||||
self.obj = None
|
||||
|
||||
def getobj(self):
|
||||
"""Get the sub-server object, importing its class and instantiating it if
|
||||
necessary.
|
||||
"""
|
||||
if not self.obj:
|
||||
exec 'from %s import %s' % (self.klass, self.klass)
|
||||
klassobj = eval(self.klass)
|
||||
self.obj = klassobj()
|
||||
return self.obj
|
||||
|
||||
class SrvDir(SrvBase):
|
||||
"""Base class for directory servlets.
|
||||
"""
|
||||
isLeaf = False
|
||||
|
||||
def __init__(self):
|
||||
SrvBase.__init__(self)
|
||||
self.table = {}
|
||||
self.order = []
|
||||
|
||||
def getChild(self, x, req):
|
||||
if x == '': return self
|
||||
val = self.get(x)
|
||||
if val is None:
|
||||
return error.NoResource('Not found')
|
||||
else:
|
||||
return val
|
||||
|
||||
def get(self, x):
|
||||
val = self.table.get(x)
|
||||
if val is not None:
|
||||
val = val.getobj()
|
||||
return val
|
||||
|
||||
def add(self, x, xclass = None):
|
||||
if xclass is None:
|
||||
xclass = 'SrvDir'
|
||||
self.table[x] = SrvConstructor(xclass)
|
||||
self.order.append(x)
|
||||
|
||||
def render_GET(self, req):
|
||||
if self.use_sxp(req):
|
||||
req.setHeader("Content-type", sxp.mime_type)
|
||||
self.ls(req, 1)
|
||||
else:
|
||||
req.write('<html><head></head><body>')
|
||||
self.print_path(req)
|
||||
self.ls(req)
|
||||
self.form(req)
|
||||
req.write('</body></html>')
|
||||
return ''
|
||||
|
||||
def ls(self, req, use_sxp=0):
|
||||
url = req.prePathURL()
|
||||
if not url.endswith('/'):
|
||||
url += '/'
|
||||
if use_sxp:
|
||||
req.write('(ls ')
|
||||
for k in self.order:
|
||||
req.write(' ' + k)
|
||||
req.write(')')
|
||||
else:
|
||||
req.write('<ul>')
|
||||
for k in self.order:
|
||||
v = self.get(k)
|
||||
req.write('<li><a href="%s%s">%s</a></li>'
|
||||
% (url, k, k))
|
||||
req.write('</ul>')
|
||||
|
||||
def form(self, req):
|
||||
pass
|
||||
@@ -0,0 +1,195 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import XendDomain
|
||||
from xen.xend import XendConsole
|
||||
from xen.xend import PrettyPrint
|
||||
from xen.xend.Args import FormFn
|
||||
|
||||
from SrvDir import SrvDir
|
||||
|
||||
class SrvDomain(SrvDir):
|
||||
"""Service managing a single domain.
|
||||
"""
|
||||
|
||||
def __init__(self, dom):
|
||||
SrvDir.__init__(self)
|
||||
self.dom = dom
|
||||
self.xd = XendDomain.instance()
|
||||
self.xconsole = XendConsole.instance()
|
||||
|
||||
def op_unpause(self, op, req):
|
||||
val = self.xd.domain_unpause(self.dom.id)
|
||||
return val
|
||||
|
||||
def op_pause(self, op, req):
|
||||
val = self.xd.domain_pause(self.dom.id)
|
||||
return val
|
||||
|
||||
def op_shutdown(self, op, req):
|
||||
val = self.xd.domain_shutdown(self.dom.id)
|
||||
req.setResponseCode(202)
|
||||
req.setHeader("Location", "%s/.." % req.prePathURL())
|
||||
return val
|
||||
|
||||
def op_destroy(self, op, req):
|
||||
val = self.xd.domain_destroy(self.dom.id)
|
||||
req.setHeader("Location", "%s/.." % req.prePathURL())
|
||||
return val
|
||||
|
||||
def op_save(self, op, req):
|
||||
fn = FormFn(self.xd.domain_save,
|
||||
[['dom', 'int'],
|
||||
['file', 'str']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_migrate(self, op, req):
|
||||
fn = FormFn(self.xd.domain_migrate,
|
||||
[['dom', 'int'],
|
||||
['destination', 'str']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
val = 0 # Some migrate id.
|
||||
req.setResponseCode(202)
|
||||
#req.send_header("Location", "%s/.." % self.path) # Some migrate url.
|
||||
return val
|
||||
|
||||
def op_pincpu(self, op, req):
|
||||
fn = FormFn(self.xd.domain_pincpu,
|
||||
[['dom', 'int'],
|
||||
['cpu', 'int']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_cpu_bvt_set(self, op, req):
|
||||
fn = FormFn(self.xd.domain_cpu_bvt_set,
|
||||
[['dom', 'int'],
|
||||
['mcuadv', 'int'],
|
||||
['warp', 'int'],
|
||||
['warpl', 'int'],
|
||||
['warpu', 'int']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_cpu_atropos_set(self, op, req):
|
||||
fn = FormFn(self.xd.domain_cpu_atropos_set,
|
||||
[['dom', 'int'],
|
||||
['period', 'int'],
|
||||
['slice', 'int'],
|
||||
['latency', 'int'],
|
||||
['xtratime', 'int']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_vifs(self, op, req):
|
||||
return self.xd.domain_vif_ls(self.dom.id)
|
||||
|
||||
def op_vif(self, op, req):
|
||||
fn = FormFn(self.xd.domain_vif_get,
|
||||
[['dom', 'int'],
|
||||
['vif', 'int']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_vif_stats(self, op, req):
|
||||
#todo
|
||||
fn = FormFn(self.xd.domain_vif_stats,
|
||||
[['dom', 'int'],
|
||||
['vif', 'int']])
|
||||
#val = fn(req.args, {'dom': self.dom.id})
|
||||
val = 999
|
||||
#return val
|
||||
return val
|
||||
|
||||
def op_vif_ip_add(self, op, req):
|
||||
fn = FormFn(self.xd.domain_vif_ip_add,
|
||||
[['dom', 'int'],
|
||||
['vif', 'int'],
|
||||
['ip', 'str']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_vif_scheduler_set(self, op, req):
|
||||
fn = FormFn(self.xd.domain_vif_scheduler_set,
|
||||
[['dom', 'int'],
|
||||
['vif', 'int'],
|
||||
['bytes', 'int'],
|
||||
['usecs', 'int']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_vif_scheduler_get(self, op, req):
|
||||
fn = FormFn(self.xd.domain_vif_scheduler_set,
|
||||
[['dom', 'int'],
|
||||
['vif', 'int']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_vbds(self, op, req):
|
||||
return self.xd.domain_vbd_ls(self.dom.id)
|
||||
|
||||
def op_vbd(self, op, req):
|
||||
fn = FormFn(self.xd.domain_vbd_get,
|
||||
[['dom', 'int'],
|
||||
['vbd', 'int']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_vbd_add(self, op, req):
|
||||
fn = FormFn(self.xd.domain_vbd_add,
|
||||
[['dom', 'int'],
|
||||
['uname', 'str'],
|
||||
['dev', 'str'],
|
||||
['mode', 'str']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_vbd_remove(self, op, req):
|
||||
fn = FormFn(self.xd.domain_vbd_remove,
|
||||
[['dom', 'int'],
|
||||
['dev', 'str']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def render_POST(self, req):
|
||||
return self.perform(req)
|
||||
|
||||
def render_GET(self, req):
|
||||
op = req.args.get('op')
|
||||
if op and op[0] in ['vifs', 'vif', 'vif_stats', 'vbds', 'vbd']:
|
||||
return self.perform(req)
|
||||
if self.use_sxp(req):
|
||||
req.setHeader("Content-Type", sxp.mime_type)
|
||||
sxp.show(self.dom.sxpr(), out=req)
|
||||
else:
|
||||
req.write('<html><head></head><body>')
|
||||
self.print_path(req)
|
||||
#self.ls()
|
||||
req.write('<p>%s</p>' % self.dom)
|
||||
if self.dom.console:
|
||||
cinfo = self.dom.console
|
||||
cid = cinfo.id
|
||||
#todo: Local xref: need to know server prefix.
|
||||
req.write('<p><a href="/xend/console/%s">Console %s</a></p>'
|
||||
% (cid, cid))
|
||||
req.write('<p><a href="%s">Connect to console</a></p>'
|
||||
% cinfo.uri())
|
||||
if self.dom.config:
|
||||
req.write("<code><pre>")
|
||||
PrettyPrint.prettyprint(self.dom.config, out=req)
|
||||
req.write("</pre></code>")
|
||||
req.write('<a href="%s?op=vif_stats&vif=0">vif 0 stats</a>'
|
||||
% req.prePathURL())
|
||||
self.form(req)
|
||||
req.write('</body></html>')
|
||||
return ''
|
||||
|
||||
def form(self, req):
|
||||
req.write('<form method="post" action="%s">' % req.prePathURL())
|
||||
req.write('<input type="submit" name="op" value="unpause">')
|
||||
req.write('<input type="submit" name="op" value="pause">')
|
||||
req.write('<input type="submit" name="op" value="shutdown">')
|
||||
req.write('<input type="submit" name="op" value="destroy">')
|
||||
req.write('<br><input type="submit" name="op" value="migrate">')
|
||||
req.write('To: <input type="text" name="destination">')
|
||||
req.write('</form>')
|
||||
@@ -0,0 +1,144 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
from StringIO import StringIO
|
||||
|
||||
from twisted.protocols import http
|
||||
from twisted.web import error
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import XendDomain
|
||||
from xen.xend.Args import FormFn
|
||||
|
||||
from SrvDir import SrvDir
|
||||
from SrvDomain import SrvDomain
|
||||
|
||||
class SrvDomainDir(SrvDir):
|
||||
"""Service that manages the domain directory.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
SrvDir.__init__(self)
|
||||
self.xd = XendDomain.instance()
|
||||
|
||||
def domain(self, x):
|
||||
val = None
|
||||
try:
|
||||
dom = self.xd.domain_get(x)
|
||||
val = SrvDomain(dom)
|
||||
except KeyError, ex:
|
||||
print 'SrvDomainDir>', ex
|
||||
pass
|
||||
return val
|
||||
|
||||
def get(self, x):
|
||||
v = SrvDir.get(self, x)
|
||||
if v is not None:
|
||||
return v
|
||||
v = self.domain(x)
|
||||
return v
|
||||
|
||||
def op_create(self, op, req):
|
||||
ok = 0
|
||||
try:
|
||||
configstring = req.args.get('config')[0]
|
||||
print 'config:', configstring
|
||||
pin = sxp.Parser()
|
||||
pin.input(configstring)
|
||||
pin.input_eof()
|
||||
config = pin.get_val()
|
||||
ok = 1
|
||||
except Exception, ex:
|
||||
print 'op_create>', ex
|
||||
if not ok:
|
||||
req.setResponseCode(http.BAD_REQUEST, "Invalid configuration")
|
||||
return "Invalid configuration"
|
||||
return error.ErrorPage(http.BAD_REQUEST,
|
||||
"Invalid",
|
||||
"Invalid configuration")
|
||||
try:
|
||||
deferred = self.xd.domain_create(config)
|
||||
deferred.addCallback(self._cb_op_create, configstring, req)
|
||||
return deferred
|
||||
except Exception, ex:
|
||||
raise
|
||||
#return ['err', str(ex) ]
|
||||
#req.setResponseCode(http.BAD_REQUEST, "Error creating domain")
|
||||
#return str(ex)
|
||||
#return error.ErrorPage(http.BAD_REQUEST,
|
||||
# "Error creating domain",
|
||||
# str(ex))
|
||||
|
||||
|
||||
def _cb_op_create(self, dominfo, configstring, req):
|
||||
"""Callback to handle deferred domain creation.
|
||||
"""
|
||||
dom = dominfo.id
|
||||
domurl = "%s/%s" % (req.prePathURL(), dom)
|
||||
req.setResponseCode(201, "created")
|
||||
req.setHeader("Location", domurl)
|
||||
if self.use_sxp(req):
|
||||
return dominfo.sxpr()
|
||||
else:
|
||||
out = StringIO()
|
||||
print >> out, ('<p> Created <a href="%s">Domain %s</a></p>'
|
||||
% (domurl, dom))
|
||||
print >> out, '<p><pre>'
|
||||
print >> out, configstring
|
||||
print >> out, '</pre></p>'
|
||||
val = out.getvalue()
|
||||
out.close()
|
||||
return val
|
||||
|
||||
def op_restore(self, op, req):
|
||||
fn = FormFn(self.xd.domain_restore,
|
||||
[['file', 'str']])
|
||||
val = fn(req.args)
|
||||
return val
|
||||
|
||||
def render_POST(self, req):
|
||||
return self.perform(req)
|
||||
|
||||
def render_GET(self, req):
|
||||
if self.use_sxp(req):
|
||||
req.setHeader("Content-Type", sxp.mime_type)
|
||||
self.ls_domain(req, 1)
|
||||
else:
|
||||
req.write("<html><head></head><body>")
|
||||
self.print_path(req)
|
||||
self.ls(req)
|
||||
self.ls_domain(req)
|
||||
self.form(req)
|
||||
req.write("</body></html>")
|
||||
return ''
|
||||
|
||||
def ls_domain(self, req, use_sxp=0):
|
||||
url = req.prePathURL()
|
||||
if not url.endswith('/'):
|
||||
url += '/'
|
||||
if use_sxp:
|
||||
domains = self.xd.domain_ls()
|
||||
sxp.show(domains, out=req)
|
||||
else:
|
||||
domains = self.xd.domains()
|
||||
domains.sort(lambda x, y: cmp(x.id, y.id))
|
||||
req.write('<ul>')
|
||||
for d in domains:
|
||||
req.write('<li><a href="%s%s"> Domain %s</a>'
|
||||
% (url, d.id, d.id))
|
||||
req.write('name=%s' % d.name)
|
||||
req.write('memory=%d'% d.memory)
|
||||
req.write('</li>')
|
||||
req.write('</ul>')
|
||||
|
||||
def form(self, req):
|
||||
req.write('<form method="post" action="%s" enctype="multipart/form-data">'
|
||||
% req.prePathURL())
|
||||
req.write('<button type="submit" name="op" value="create">Create Domain</button>')
|
||||
req.write('Config <input type="file" name="config"><br>')
|
||||
req.write('</form>')
|
||||
req.write('<form method="post" action="%s" enctype="multipart/form-data">'
|
||||
% req.prePathURL())
|
||||
req.write('<button type="submit" name="op" value="create">Restore Domain</button>')
|
||||
req.write('State <input type="string" name="state"><br>')
|
||||
req.write('</form>')
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import EventServer
|
||||
from SrvDir import SrvDir
|
||||
|
||||
class SrvEventDir(SrvDir):
|
||||
"""Event directory.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
SrvDir.__init__(self)
|
||||
self.eserver = EventServer.instance()
|
||||
|
||||
def op_inject(self, op, req):
|
||||
eventstring = req.args.get('event')
|
||||
pin = sxp.Parser()
|
||||
pin.input(eventstring)
|
||||
pin.input_eof()
|
||||
sxpr = pin.get_val()
|
||||
self.eserver.inject(sxp.name(sxpr), sxpr)
|
||||
if req.use_sxp:
|
||||
sxp.name(sxpr)
|
||||
else:
|
||||
return '<code>' + eventstring + '</code>'
|
||||
|
||||
def render_POST(self, req):
|
||||
return self.perform(req)
|
||||
|
||||
def form(self, req):
|
||||
action = req.prePathURL()
|
||||
req.write('<form method="post" action="%s" enctype="multipart/form-data">'
|
||||
% action)
|
||||
req.write('<button type="submit" name="op" value="inject">Inject</button>')
|
||||
req.write('Event <input type="text" name="event" size="40"><br>')
|
||||
req.write('</form>')
|
||||
req.write('<form method="post" action="%s" enctype="multipart/form-data">'
|
||||
% action)
|
||||
req.write('<button type="submit" name="op" value="inject">Inject</button>')
|
||||
req.write('Event file<input type="file" name="event"><br>')
|
||||
req.write('</form>')
|
||||
@@ -0,0 +1,54 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
import os
|
||||
from SrvDir import SrvDir
|
||||
from xen.xend import sxp
|
||||
from xen.xend import XendNode
|
||||
|
||||
class SrvNode(SrvDir):
|
||||
"""Information about the node.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
SrvDir.__init__(self)
|
||||
self.xn = XendNode.instance()
|
||||
|
||||
def op_shutdown(self, op, req):
|
||||
val = self.xn.shutdown()
|
||||
return val
|
||||
|
||||
def op_reboot(self, op, req):
|
||||
val = self.xn.reboot()
|
||||
return val
|
||||
|
||||
def op_cpu_rrobin_slice_set(self, op, req):
|
||||
fn = FormFn(self.xn.cpu_rrobin_slice_set,
|
||||
[['slice', 'int']])
|
||||
val = fn(req.args, {})
|
||||
return val
|
||||
|
||||
def op_cpu_bvt_slice_set(self, op, req):
|
||||
fn = FormFn(self.xn.cpu_bvt_slice_set,
|
||||
[['slice', 'int']])
|
||||
val = fn(req.args, {})
|
||||
return val
|
||||
|
||||
def render_POST(self, req):
|
||||
return self.perform(req)
|
||||
|
||||
def render_GET(self, req):
|
||||
if self.use_sxp(req):
|
||||
req.setHeader("Content-Type", sxp.mime_type)
|
||||
sxp.show(['node'] + self.info(), out=req)
|
||||
else:
|
||||
req.write('<html><head></head><body>')
|
||||
self.print_path(req)
|
||||
req.write('<ul>')
|
||||
for d in self.info():
|
||||
req.write('<li> %10s: %s' % (d[0], str(d[1])))
|
||||
req.write('</ul>')
|
||||
req.write('</body></html>')
|
||||
return ''
|
||||
|
||||
def info(self):
|
||||
return self.xn.info()
|
||||
@@ -0,0 +1,30 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
from xen.xend import XendRoot
|
||||
xroot = XendRoot.instance()
|
||||
from SrvDir import SrvDir
|
||||
|
||||
class SrvRoot(SrvDir):
|
||||
"""The root of the xend server.
|
||||
"""
|
||||
|
||||
"""Server sub-components. Each entry is (name, class), where
|
||||
'name' is the entry name and 'class' is the name of its class.
|
||||
"""
|
||||
#todo Get this list from the XendRoot config.
|
||||
subdirs = [
|
||||
('node', 'SrvNode' ),
|
||||
('domain', 'SrvDomainDir' ),
|
||||
('console', 'SrvConsoleDir' ),
|
||||
('event', 'SrvEventDir' ),
|
||||
('device', 'SrvDeviceDir' ),
|
||||
('vnet', 'SrvVnetDir' ),
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
SrvDir.__init__(self)
|
||||
for (name, klass) in self.subdirs:
|
||||
self.add(name, klass)
|
||||
for (name, klass) in self.subdirs:
|
||||
self.get(name)
|
||||
xroot.start()
|
||||
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/python2
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
"""Example xend HTTP and console server.
|
||||
|
||||
Can be accessed from a browser or from a program.
|
||||
Do 'python SrvServer.py' to run the server.
|
||||
Then point a web browser at http://localhost:8000/xend and follow the links.
|
||||
Most are stubs, except /domain which has a list of domains and a 'create domain'
|
||||
button.
|
||||
|
||||
You can also access the server from a program.
|
||||
Do 'python XendClient.py' to run a few test operations.
|
||||
|
||||
The data served differs depending on the client (as defined by User-Agent
|
||||
and Accept in the HTTP headers). If the client is a browser, data
|
||||
is returned in HTML, with interactive forms. If the client is a program,
|
||||
data is returned in SXP format, with no forms.
|
||||
|
||||
The server serves to the world by default. To restrict it to the local host
|
||||
change 'interface' in main().
|
||||
|
||||
Mike Wray <mike.wray@hp.com>
|
||||
"""
|
||||
# todo Support security settings etc. in the config file.
|
||||
# todo Support command-line args.
|
||||
|
||||
from twisted.web import server
|
||||
from twisted.web import resource
|
||||
from twisted.internet import reactor
|
||||
|
||||
from xen.xend import XendRoot
|
||||
xroot = XendRoot.instance()
|
||||
|
||||
from xen.xend import Vifctl
|
||||
|
||||
from SrvRoot import SrvRoot
|
||||
|
||||
def create(port=None, interface=None, bridge=0):
|
||||
if port is None: port = 8000
|
||||
if interface is None: interface = ''
|
||||
if bridge or xroot.rebooted:
|
||||
init_bridge()
|
||||
root = resource.Resource()
|
||||
xend = SrvRoot()
|
||||
root.putChild('xend', xend)
|
||||
site = server.Site(root)
|
||||
reactor.listenTCP(port, site, interface=interface)
|
||||
|
||||
def init_bridge():
|
||||
Vifctl.init()
|
||||
|
||||
def main(port=None, interface=None):
|
||||
create(port, interface)
|
||||
reactor.run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,9 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
from SrvDir import SrvDir
|
||||
|
||||
class SrvVnetDir(SrvDir):
|
||||
"""Vnet directory.
|
||||
"""
|
||||
|
||||
pass
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
Executable
+341
@@ -0,0 +1,341 @@
|
||||
from twisted.internet import defer
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import PrettyPrint
|
||||
|
||||
import channel
|
||||
import controller
|
||||
from messages import *
|
||||
|
||||
class BlkifControllerFactory(controller.ControllerFactory):
|
||||
"""Factory for creating block device interface controllers.
|
||||
Also handles the 'back-end' channel to the device driver domain.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
controller.ControllerFactory.__init__(self)
|
||||
|
||||
self.majorTypes = [ CMSG_BLKIF_BE ]
|
||||
|
||||
self.subTypes = {
|
||||
CMSG_BLKIF_BE_CREATE : self.recv_be_create,
|
||||
CMSG_BLKIF_BE_CONNECT : self.recv_be_connect,
|
||||
CMSG_BLKIF_BE_VBD_CREATE : self.recv_be_vbd_create,
|
||||
CMSG_BLKIF_BE_VBD_GROW : self.recv_be_vbd_grow,
|
||||
CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED: self.recv_be_driver_status_changed,
|
||||
}
|
||||
self.attached = 1
|
||||
self.registerChannel()
|
||||
|
||||
def createInstance(self, dom, recreate=0):
|
||||
d = self.addDeferred()
|
||||
blkif = self.getInstanceByDom(dom)
|
||||
if blkif:
|
||||
self.callDeferred(blkif)
|
||||
else:
|
||||
blkif = BlkifController(self, dom)
|
||||
self.addInstance(blkif)
|
||||
if recreate:
|
||||
self.callDeferred(blkif)
|
||||
else:
|
||||
blkif.send_be_create()
|
||||
return d
|
||||
|
||||
def getDomainDevices(self, dom):
|
||||
blkif = self.getInstanceByDom(dom)
|
||||
return (blkif and blkif.getDevices()) or []
|
||||
|
||||
def getDomainDevice(self, dom, vdev):
|
||||
blkif = self.getInstanceByDom(dom)
|
||||
return (blkif and blkif.getDevice(vdev)) or None
|
||||
|
||||
def setControlDomain(self, dom, recreate=0):
|
||||
if self.dom == dom: return
|
||||
self.deregisterChannel()
|
||||
if not recreate:
|
||||
self.attached = 0
|
||||
self.dom = dom
|
||||
self.registerChannel()
|
||||
#
|
||||
#if xend.blkif.be_port:
|
||||
# xend.blkif.recovery = True
|
||||
#xend.blkif.be_port = xend.main.port_from_dom(dom)
|
||||
|
||||
def getControlDomain(self):
|
||||
return self.dom
|
||||
|
||||
def reattachDevice(self, dom, vdev):
|
||||
blkif = self.getInstanceByDom(dom)
|
||||
if blkif:
|
||||
blkif.reattachDevice(vdev)
|
||||
self.attached = self.devicesAttached()
|
||||
if self.attached:
|
||||
self.reattached()
|
||||
|
||||
def devicesAttached(self):
|
||||
"""Check if all devices are attached.
|
||||
"""
|
||||
attached = 1
|
||||
for blkif in self.getInstances():
|
||||
if not blkif.attached:
|
||||
attached = 0
|
||||
break
|
||||
return attached
|
||||
|
||||
def reattached(self):
|
||||
for blkif in self.getInstances():
|
||||
blkif.reattached()
|
||||
|
||||
def recv_be_create(self, msg, req):
|
||||
#print 'recv_be_create>'
|
||||
val = unpackMsg('blkif_be_create_t', msg)
|
||||
blkif = self.getInstanceByDom(val['domid'])
|
||||
self.callDeferred(blkif)
|
||||
|
||||
def recv_be_connect(self, msg, req):
|
||||
#print 'recv_be_create>'
|
||||
val = unpackMsg('blkif_be_connect_t', msg)
|
||||
blkif = self.getInstanceByDom(val['domid'])
|
||||
if blkif:
|
||||
blkif.send_fe_interface_status_changed()
|
||||
else:
|
||||
pass
|
||||
|
||||
def recv_be_vbd_create(self, msg, req):
|
||||
#print 'recv_be_vbd_create>'
|
||||
val = unpackMsg('blkif_be_vbd_create_t', msg)
|
||||
blkif = self.getInstanceByDom(val['domid'])
|
||||
if blkif:
|
||||
blkif.send_be_vbd_grow(val['vdevice'])
|
||||
else:
|
||||
pass
|
||||
|
||||
def recv_be_vbd_grow(self, msg, req):
|
||||
#print 'recv_be_vbd_grow>'
|
||||
val = unpackMsg('blkif_be_vbd_grow_t', msg)
|
||||
# Check status?
|
||||
if self.attached:
|
||||
self.callDeferred(0)
|
||||
else:
|
||||
self.reattachDevice(val['domid'], val['vdevice'])
|
||||
|
||||
def recv_be_driver_status_changed(self, msg, req):
|
||||
val = unpackMsg('blkif_be_driver_status_changed_t', msg)
|
||||
status = val['status']
|
||||
if status == BLKIF_DRIVER_STATUS_UP and not self.attached:
|
||||
for blkif in self.getInstances():
|
||||
blkif.detach()
|
||||
|
||||
class BlkDev(controller.Dev):
|
||||
"""Info record for a block device.
|
||||
"""
|
||||
|
||||
def __init__(self, ctrl, vdev, mode, segment):
|
||||
controller.Dev.__init__(self, ctrl)
|
||||
self.vdev = vdev
|
||||
self.mode = mode
|
||||
self.device = segment['device']
|
||||
self.start_sector = segment['start_sector']
|
||||
self.nr_sectors = segment['nr_sectors']
|
||||
self.attached = 1
|
||||
|
||||
def readonly(self):
|
||||
return 'w' not in self.mode
|
||||
|
||||
def sxpr(self):
|
||||
val = ['blkdev', ['vdev', self.vdev], ['mode', self.mode] ]
|
||||
return val
|
||||
|
||||
def destroy(self):
|
||||
print 'BlkDev>destroy>', self.vdev
|
||||
PrettyPrint.prettyprint(self.sxpr())
|
||||
self.controller.send_be_vbd_destroy(self.vdev)
|
||||
|
||||
class BlkifController(controller.Controller):
|
||||
"""Block device interface controller. Handles all block devices
|
||||
for a domain.
|
||||
"""
|
||||
|
||||
def __init__(self, factory, dom):
|
||||
#print 'BlkifController> dom=', dom
|
||||
controller.Controller.__init__(self, factory, dom)
|
||||
self.devices = {}
|
||||
|
||||
self.majorTypes = [ CMSG_BLKIF_FE ]
|
||||
|
||||
self.subTypes = {
|
||||
CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED:
|
||||
self.recv_fe_driver_status_changed,
|
||||
CMSG_BLKIF_FE_INTERFACE_CONNECT :
|
||||
self.recv_fe_interface_connect,
|
||||
}
|
||||
self.attached = 1
|
||||
self.evtchn = None
|
||||
self.registerChannel()
|
||||
#print 'BlkifController<', 'dom=', self.dom, 'idx=', self.idx
|
||||
|
||||
def sxpr(self):
|
||||
val = ['blkif', ['dom', self.dom]]
|
||||
if self.evtchn:
|
||||
val.append(['evtchn',
|
||||
self.evtchn['port1'],
|
||||
self.evtchn['port2']])
|
||||
return val
|
||||
|
||||
def lostChannel(self):
|
||||
print 'BlkifController>lostChannel>', 'dom=', self.dom
|
||||
#self.destroyDevices()
|
||||
controller.Controller.lostChannel(self)
|
||||
|
||||
def getDevices(self):
|
||||
return self.devices.values()
|
||||
|
||||
def getDevice(self, vdev):
|
||||
return self.devices.get(vdev)
|
||||
|
||||
def addDevice(self, vdev, mode, segment):
|
||||
if vdev in self.devices: return None
|
||||
dev = BlkDev(self, vdev, mode, segment)
|
||||
self.devices[vdev] = dev
|
||||
return dev
|
||||
|
||||
def attachDevice(self, vdev, mode, segment, recreate=0):
|
||||
"""Attach a device to the specified interface.
|
||||
"""
|
||||
#print 'BlkifController>attach_device>', self.dom, vdev, mode, segment
|
||||
dev = self.addDevice(vdev, mode, segment)
|
||||
if not dev: return -1
|
||||
if recreate:
|
||||
d = defer.Deferred()
|
||||
d.callback(self)
|
||||
else:
|
||||
self.send_be_vbd_create(vdev)
|
||||
d = self.factory.addDeferred()
|
||||
return d
|
||||
|
||||
def destroy(self):
|
||||
print 'BlkifController>destroy> dom=', self.dom
|
||||
def cb_destroy(val):
|
||||
self.send_be_destroy()
|
||||
d = self.factory.addDeferred()
|
||||
d.addCallback(cb_destroy)
|
||||
self.send_be_disconnect()
|
||||
#self.destroyDevices()
|
||||
|
||||
def destroyDevices(self):
|
||||
for dev in self.getDevices():
|
||||
dev.destroy()
|
||||
|
||||
def detach(self):
|
||||
"""Detach all devices, when the back-end control domain has changed.
|
||||
"""
|
||||
self.attached = 0
|
||||
for dev in self.devices.values():
|
||||
dev.attached = 0
|
||||
self.send_be_vbd_create(vdev)
|
||||
|
||||
def reattachDevice(self, vdev):
|
||||
"""Reattach a device, when the back-end control domain has changed.
|
||||
"""
|
||||
dev = self.devices[vdev]
|
||||
dev.attached = 1
|
||||
attached = 1
|
||||
for dev in self.devices.values():
|
||||
if not dev.attached:
|
||||
attached = 0
|
||||
break
|
||||
self.attached = attached
|
||||
return self.attached
|
||||
|
||||
def reattached(self):
|
||||
"""All devices have been reattached after the back-end control
|
||||
domain has changed.
|
||||
"""
|
||||
msg = packMsg('blkif_fe_interface_status_changed_t',
|
||||
{ 'handle' : 0,
|
||||
'status' : BLKIF_INTERFACE_STATUS_DISCONNECTED})
|
||||
self.writeRequest(msg)
|
||||
|
||||
def recv_fe_driver_status_changed(self, msg, req):
|
||||
msg = packMsg('blkif_fe_interface_status_changed_t',
|
||||
{ 'handle' : 0,
|
||||
'status' : BLKIF_INTERFACE_STATUS_DISCONNECTED,
|
||||
'evtchn' : 0 })
|
||||
self.writeRequest(msg)
|
||||
|
||||
def recv_fe_interface_connect(self, msg, req):
|
||||
val = unpackMsg('blkif_fe_interface_connect_t', msg)
|
||||
self.evtchn = channel.eventChannel(0, self.dom)
|
||||
print 'recv_fe_interface_connect>'
|
||||
PrettyPrint.prettyprint(self.sxpr())
|
||||
msg = packMsg('blkif_be_connect_t',
|
||||
{ 'domid' : self.dom,
|
||||
'blkif_handle' : val['handle'],
|
||||
'evtchn' : self.evtchn['port1'],
|
||||
'shmem_frame' : val['shmem_frame'] })
|
||||
self.factory.writeRequest(msg)
|
||||
pass
|
||||
|
||||
#def recv_fe_interface_status_changed(self, msg, req):
|
||||
# (hnd, status, chan) = unpackMsg('blkif_fe_interface_status_changed_t', msg)
|
||||
# print 'recv_fe_interface_status_changed>', hnd, status, chan
|
||||
# pass
|
||||
|
||||
def send_fe_interface_status_changed(self):
|
||||
msg = packMsg('blkif_fe_interface_status_changed_t',
|
||||
{ 'handle' : 0,
|
||||
'status' : BLKIF_INTERFACE_STATUS_CONNECTED,
|
||||
'evtchn' : self.evtchn['port2'] })
|
||||
self.writeRequest(msg)
|
||||
|
||||
def send_be_create(self):
|
||||
msg = packMsg('blkif_be_create_t',
|
||||
{ 'domid' : self.dom,
|
||||
'blkif_handle' : 0 })
|
||||
self.factory.writeRequest(msg)
|
||||
|
||||
def send_be_disconnect(self):
|
||||
print '>BlkifController>send_be_disconnect>', 'dom=', self.dom
|
||||
msg = packMsg('blkif_be_disconnect_t',
|
||||
{ 'domid' : self.dom,
|
||||
'blkif_handle' : 0 })
|
||||
self.factory.writeRequest(msg)
|
||||
|
||||
def send_be_destroy(self):
|
||||
print '>BlkifController>send_be_destroy>', 'dom=', self.dom
|
||||
msg = packMsg('blkif_be_destroy_t',
|
||||
{ 'domid' : self.dom,
|
||||
'blkif_handle' : 0 })
|
||||
self.factory.writeRequest(msg)
|
||||
|
||||
def send_be_vbd_create(self, vdev):
|
||||
dev = self.devices[vdev]
|
||||
msg = packMsg('blkif_be_vbd_create_t',
|
||||
{ 'domid' : self.dom,
|
||||
'blkif_handle' : 0,
|
||||
'vdevice' : dev.vdev,
|
||||
'readonly' : dev.readonly() })
|
||||
self.factory.writeRequest(msg)
|
||||
|
||||
def send_be_vbd_grow(self, vdev):
|
||||
dev = self.devices[vdev]
|
||||
msg = packMsg('blkif_be_vbd_grow_t',
|
||||
{ 'domid' : self.dom,
|
||||
'blkif_handle' : 0,
|
||||
'vdevice' : dev.vdev,
|
||||
'extent.device' : dev.device,
|
||||
'extent.sector_start' : dev.start_sector,
|
||||
'extent.sector_length' : dev.nr_sectors })
|
||||
self.factory.writeRequest(msg)
|
||||
|
||||
def send_be_vbd_destroy(self, vdev):
|
||||
print '>BlkifController>send_be_vbd_destroy>', 'dom=', self.dom, 'vdev=', vdev
|
||||
PrettyPrint.prettyprint(self.sxpr())
|
||||
dev = self.devices[vdev]
|
||||
msg = packMsg('blkif_be_vbd_destroy_t',
|
||||
{ 'domid' : self.dom,
|
||||
'blkif_handle' : 0,
|
||||
'vdevice' : dev.vdev })
|
||||
del self.devices[vdev]
|
||||
self.factory.writeRequest(msg)
|
||||
|
||||
Executable
+378
@@ -0,0 +1,378 @@
|
||||
import xen.ext.xc; xc = xen.ext.xc.new()
|
||||
from xen.ext import xu
|
||||
from messages import msgTypeName
|
||||
|
||||
VIRQ_MISDIRECT = 0 # Catch-all interrupt for unbound VIRQs.
|
||||
VIRQ_TIMER = 1 # Timebase update, and/or requested timeout.
|
||||
VIRQ_DEBUG = 2 # Request guest to dump debug info.
|
||||
VIRQ_CONSOLE = 3 # (DOM0) bytes received on emergency console.
|
||||
VIRQ_DOM_EXC = 4 # (DOM0) Exceptional event for some domain.
|
||||
|
||||
def eventChannel(dom1, dom2):
|
||||
return xc.evtchn_bind_interdomain(dom1=dom1, dom2=dom2)
|
||||
|
||||
class ChannelFactory:
|
||||
"""Factory for creating channels.
|
||||
Maintains a table of channels.
|
||||
"""
|
||||
|
||||
""" Channels indexed by index. """
|
||||
channels = {}
|
||||
|
||||
def __init__(self):
|
||||
"""Constructor - do not use. Use the channelFactory function."""
|
||||
self.notifier = xu.notifier()
|
||||
|
||||
def addChannel(self, channel):
|
||||
"""Add a channel.
|
||||
"""
|
||||
idx = channel.idx
|
||||
self.channels[idx] = channel
|
||||
self.notifier.bind(idx)
|
||||
# Try to wake it up
|
||||
#self.notifier.unmask(idx)
|
||||
#channel.notify()
|
||||
|
||||
def getChannel(self, idx):
|
||||
"""Get the channel with the given index (if any).
|
||||
"""
|
||||
return self.channels.get(idx)
|
||||
|
||||
def delChannel(self, idx):
|
||||
"""Remove the channel with the given index (if any).
|
||||
"""
|
||||
if idx in self.channels:
|
||||
del self.channels[idx]
|
||||
self.notifier.unbind(idx)
|
||||
|
||||
def domChannel(self, dom):
|
||||
"""Get the channel for the given domain.
|
||||
Construct if necessary.
|
||||
"""
|
||||
dom = int(dom)
|
||||
for chan in self.channels.values():
|
||||
if not isinstance(chan, Channel): continue
|
||||
if chan.dom == dom:
|
||||
return chan
|
||||
chan = Channel(self, dom)
|
||||
self.addChannel(chan)
|
||||
return chan
|
||||
|
||||
def virqChannel(self, virq):
|
||||
"""Get the channel for the given virq.
|
||||
Construct if necessary.
|
||||
"""
|
||||
for chan in self.channels.values():
|
||||
if not isinstance(chan, VirqChannel): continue
|
||||
if chan.virq == virq:
|
||||
return chan
|
||||
chan = VirqChannel(self, virq)
|
||||
self.addChannel(chan)
|
||||
return chan
|
||||
|
||||
def channelClosed(self, channel):
|
||||
"""The given channel has been closed - remove it.
|
||||
"""
|
||||
self.delChannel(channel.idx)
|
||||
|
||||
def createPort(self, dom):
|
||||
"""Create a port for a channel to the given domain.
|
||||
"""
|
||||
return xu.port(dom)
|
||||
|
||||
def channelFactory():
|
||||
"""Singleton constructor for the channel factory.
|
||||
Use this instead of the class constructor.
|
||||
"""
|
||||
global inst
|
||||
try:
|
||||
inst
|
||||
except:
|
||||
inst = ChannelFactory()
|
||||
return inst
|
||||
|
||||
class BaseChannel:
|
||||
"""Abstract superclass for channels.
|
||||
|
||||
The subclass constructor must set idx to the port to use.
|
||||
"""
|
||||
|
||||
def __init__(self, factory):
|
||||
self.factory = factory
|
||||
self.idx = -1
|
||||
self.closed = 0
|
||||
|
||||
def getIndex(self):
|
||||
"""Get the channel index.
|
||||
"""
|
||||
return self.idx
|
||||
|
||||
def notificationReceived(self, type):
|
||||
"""Called when a notification is received.
|
||||
Closes the channel on error, otherwise calls
|
||||
handleNotification(type), which should be defined
|
||||
in a subclass.
|
||||
"""
|
||||
#print 'notificationReceived> type=', type, self
|
||||
if self.closed: return
|
||||
if type == self.factory.notifier.EXCEPTION:
|
||||
print 'notificationReceived> EXCEPTION'
|
||||
info = xc.evtchn_status(self.idx)
|
||||
if info['status'] == 'unbound':
|
||||
print 'notificationReceived> EXCEPTION closing...'
|
||||
self.close()
|
||||
return
|
||||
self.handleNotification(type)
|
||||
|
||||
def close(self):
|
||||
"""Close the channel. Calls channelClosed() on the factory.
|
||||
Override in subclass.
|
||||
"""
|
||||
self.factory.channelClosed(self)
|
||||
|
||||
def handleNotification(self, type):
|
||||
"""Handle notification.
|
||||
Define in subclass.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class VirqChannel(BaseChannel):
|
||||
"""A channel for handling a virq.
|
||||
"""
|
||||
|
||||
def __init__(self, factory, virq):
|
||||
"""Create a channel for the given virq using the given factory.
|
||||
|
||||
Do not call directly, use virqChannel on the factory.
|
||||
"""
|
||||
BaseChannel.__init__(self, factory)
|
||||
self.virq = virq
|
||||
# Notification port (int).
|
||||
self.port = xc.evtchn_bind_virq(virq)
|
||||
self.idx = self.port
|
||||
# Clients to call when a virq arrives.
|
||||
self.clients = []
|
||||
|
||||
def __repr__(self):
|
||||
return ('<VirqChannel virq=%d port=%d>'
|
||||
% (self.virq, self.port))
|
||||
|
||||
def getVirq(self):
|
||||
"""Get the channel's virq.
|
||||
"""
|
||||
return self.virq
|
||||
|
||||
def close(self):
|
||||
"""Close the channel. Calls lostChannel(self) on all its clients and
|
||||
channelClosed() on the factory.
|
||||
"""
|
||||
for c in self.clients:
|
||||
c.lostChannel(self)
|
||||
del self.clients
|
||||
BaseChannel.close(self)
|
||||
|
||||
def registerClient(self, client):
|
||||
"""Register a client. The client will be called with
|
||||
client.virqReceived(virq) when a virq is received.
|
||||
The client will be called with client.lostChannel(self) if the
|
||||
channel is closed.
|
||||
"""
|
||||
self.clients.append(client)
|
||||
|
||||
def handleNotification(self, type):
|
||||
for c in self.clients:
|
||||
c.virqReceived(self.virq)
|
||||
|
||||
def notify(self):
|
||||
xc.evtchn_send(self.port)
|
||||
|
||||
|
||||
class Channel(BaseChannel):
|
||||
"""A control channel to a domain. Messages for the domain device controllers
|
||||
are multiplexed over the channel (console, block devs, net devs).
|
||||
"""
|
||||
|
||||
def __init__(self, factory, dom):
|
||||
"""Create a channel to the given domain using the given factory.
|
||||
|
||||
Do not call directly, use domChannel on the factory.
|
||||
"""
|
||||
BaseChannel.__init__(self, factory)
|
||||
# Domain.
|
||||
self.dom = int(dom)
|
||||
# Domain port (object).
|
||||
self.port = self.factory.createPort(dom)
|
||||
# Channel port (int).
|
||||
self.idx = self.port.local_port
|
||||
# Registered devices.
|
||||
self.devs = []
|
||||
# Devices indexed by the message types they handle.
|
||||
self.devs_by_type = {}
|
||||
# Output queue.
|
||||
self.queue = []
|
||||
self.closed = 0
|
||||
|
||||
def getLocalPort(self):
|
||||
"""Get the local port.
|
||||
"""
|
||||
return self.port.local_port
|
||||
|
||||
def getRemotePort(self):
|
||||
"""Get the remote port.
|
||||
"""
|
||||
return self.port.remote_port
|
||||
|
||||
def close(self):
|
||||
"""Close the channel. Calls lostChannel() on all its devices and
|
||||
channelClosed() on the factory.
|
||||
"""
|
||||
self.closed = 1
|
||||
for d in self.devs:
|
||||
d.lostChannel()
|
||||
self.factory.channelClosed(self)
|
||||
self.devs = []
|
||||
self.devs_by_type = {}
|
||||
|
||||
def registerDevice(self, types, dev):
|
||||
"""Register a device controller.
|
||||
|
||||
@param types message types the controller handles
|
||||
@param dev device controller
|
||||
"""
|
||||
if self.closed: return
|
||||
self.devs.append(dev)
|
||||
for ty in types:
|
||||
self.devs_by_type[ty] = dev
|
||||
|
||||
def deregisterDevice(self, dev):
|
||||
"""Remove the registration for a device controller.
|
||||
|
||||
@param dev device controller
|
||||
"""
|
||||
if dev in self.devs:
|
||||
self.devs.remove(dev)
|
||||
types = [ ty for (ty, d) in self.devs_by_type.items() if d == dev ]
|
||||
for ty in types:
|
||||
del self.devs_by_type[ty]
|
||||
|
||||
def getDevice(self, type):
|
||||
"""Get the device controller handling a message type.
|
||||
|
||||
@param type message type
|
||||
@returns controller or None
|
||||
"""
|
||||
return self.devs_by_type.get(type)
|
||||
|
||||
def getMessageType(self, msg):
|
||||
"""Get a 2-tuple of the message type and subtype.
|
||||
"""
|
||||
hdr = msg.get_header()
|
||||
return (hdr['type'], hdr.get('subtype'))
|
||||
|
||||
def __repr__(self):
|
||||
return ('<Channel dom=%d ports=%d:%d>'
|
||||
% (self.dom,
|
||||
self.port.local_port,
|
||||
self.port.remote_port))
|
||||
|
||||
def handleNotification(self, type):
|
||||
work = 0
|
||||
work += self.handleRequests()
|
||||
work += self.handleResponses()
|
||||
work += self.handleWrites()
|
||||
if work:
|
||||
self.notify()
|
||||
|
||||
def notify(self):
|
||||
self.port.notify()
|
||||
|
||||
def handleRequests(self):
|
||||
work = 0
|
||||
while 1:
|
||||
msg = self.readRequest()
|
||||
if not msg: break
|
||||
self.requestReceived(msg)
|
||||
work += 1
|
||||
return work
|
||||
|
||||
def requestReceived(self, msg):
|
||||
(ty, subty) = self.getMessageType(msg)
|
||||
#todo: Must respond before writing any more messages.
|
||||
#todo: Should automate this (respond on write)
|
||||
self.port.write_response(msg)
|
||||
dev = self.getDevice(ty)
|
||||
if dev:
|
||||
dev.requestReceived(msg, ty, subty)
|
||||
else:
|
||||
print ("requestReceived> No device: Message type %s %d:%d"
|
||||
% (msgTypeName(ty, subty), ty, subty)), self
|
||||
|
||||
def handleResponses(self):
|
||||
work = 0
|
||||
while 1:
|
||||
msg = self.readResponse()
|
||||
if not msg: break
|
||||
self.responseReceived(msg)
|
||||
work += 1
|
||||
return work
|
||||
|
||||
def responseReceived(self, msg):
|
||||
(ty, subty) = self.getMessageType(msg)
|
||||
dev = self.getDevice(ty)
|
||||
if dev:
|
||||
dev.responseReceived(msg, ty, subty)
|
||||
else:
|
||||
print ("responseReceived> No device: Message type %d:%d"
|
||||
% (msgTypeName(ty, subty), ty, subty)), self
|
||||
|
||||
def handleWrites(self):
|
||||
work = 0
|
||||
# Pull data from producers.
|
||||
for dev in self.devs:
|
||||
work += dev.produceRequests()
|
||||
# Flush the queue.
|
||||
while self.queue and self.port.space_to_write_request():
|
||||
msg = self.queue.pop(0)
|
||||
self.port.write_request(msg)
|
||||
work += 1
|
||||
return work
|
||||
|
||||
def writeRequest(self, msg, notify=1):
|
||||
if self.closed:
|
||||
val = -1
|
||||
elif self.writeReady():
|
||||
self.port.write_request(msg)
|
||||
if notify: self.notify()
|
||||
val = 1
|
||||
else:
|
||||
self.queue.append(msg)
|
||||
val = 0
|
||||
return val
|
||||
|
||||
def writeResponse(self, msg):
|
||||
if self.closed: return -1
|
||||
self.port.write_response(msg)
|
||||
return 1
|
||||
|
||||
def writeReady(self):
|
||||
if self.closed or self.queue: return 0
|
||||
return self.port.space_to_write_request()
|
||||
|
||||
def readRequest(self):
|
||||
if self.closed:
|
||||
return None
|
||||
if self.port.request_to_read():
|
||||
val = self.port.read_request()
|
||||
else:
|
||||
val = None
|
||||
return val
|
||||
|
||||
def readResponse(self):
|
||||
if self.closed:
|
||||
return None
|
||||
if self.port.response_to_read():
|
||||
val = self.port.read_response()
|
||||
else:
|
||||
val = None
|
||||
return val
|
||||
Executable
+232
@@ -0,0 +1,232 @@
|
||||
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet import protocol
|
||||
from twisted.protocols import telnet
|
||||
|
||||
from xen.ext import xu
|
||||
|
||||
from xen.xend import EventServer
|
||||
eserver = EventServer.instance()
|
||||
|
||||
import controller
|
||||
from messages import *
|
||||
from params import *
|
||||
|
||||
"""Telnet binary option."""
|
||||
TRANSMIT_BINARY = '0'
|
||||
WILL = chr(251)
|
||||
IAC = chr(255)
|
||||
|
||||
class ConsoleProtocol(protocol.Protocol):
|
||||
"""Asynchronous handler for a console TCP socket.
|
||||
"""
|
||||
|
||||
def __init__(self, controller, idx):
|
||||
self.controller = controller
|
||||
self.idx = idx
|
||||
self.addr = None
|
||||
self.binary = 0
|
||||
|
||||
def connectionMade(self):
|
||||
peer = self.transport.getPeer()
|
||||
self.addr = (peer.host, peer.port)
|
||||
if self.controller.connect(self.addr, self):
|
||||
self.transport.write("Cannot connect to console %d on domain %d\n"
|
||||
% (self.idx, self.controller.dom))
|
||||
self.loseConnection()
|
||||
return
|
||||
else:
|
||||
# KAF: A nice quiet successful connect. Don't bother with telnet
|
||||
# control sequence -- telnet is not the appropriate protocol here.
|
||||
#self.transport.write("Connected to console %d on domain %d\n"
|
||||
# % (self.idx, self.controller.dom))
|
||||
#self.setTelnetTransmitBinary()
|
||||
eserver.inject('xend.console.connect',
|
||||
[self.idx, self.addr[0], self.addr[1]])
|
||||
|
||||
def setTelnetTransmitBinary(self):
|
||||
"""Send the sequence to set the telnet TRANSMIT-BINARY option.
|
||||
"""
|
||||
self.write(IAC + WILL + TRANSMIT_BINARY)
|
||||
|
||||
def dataReceived(self, data):
|
||||
if self.controller.handleInput(self, data):
|
||||
self.loseConnection()
|
||||
|
||||
def write(self, data):
|
||||
#if not self.connected: return -1
|
||||
self.transport.write(data)
|
||||
return len(data)
|
||||
|
||||
def connectionLost(self, reason=None):
|
||||
eserver.inject('xend.console.disconnect',
|
||||
[self.idx, self.addr[0], self.addr[1]])
|
||||
self.controller.disconnect()
|
||||
|
||||
def loseConnection(self):
|
||||
self.transport.loseConnection()
|
||||
|
||||
class ConsoleFactory(protocol.ServerFactory):
|
||||
"""Asynchronous handler for a console server socket.
|
||||
"""
|
||||
protocol = ConsoleProtocol
|
||||
|
||||
def __init__(self, controller, idx):
|
||||
#protocol.ServerFactory.__init__(self)
|
||||
self.controller = controller
|
||||
self.idx = idx
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
proto = self.protocol(self.controller, self.idx)
|
||||
proto.factory = self
|
||||
return proto
|
||||
|
||||
class ConsoleControllerFactory(controller.ControllerFactory):
|
||||
"""Factory for creating console controllers.
|
||||
"""
|
||||
|
||||
def createInstance(self, dom, console_port=None):
|
||||
if console_port is None:
|
||||
console_port = CONSOLE_PORT_BASE + dom
|
||||
console = ConsoleController(self, dom, console_port)
|
||||
self.addInstance(console)
|
||||
eserver.inject('xend.console.create',
|
||||
[console.idx, console.dom, console.console_port])
|
||||
return console
|
||||
|
||||
def consoleClosed(self, console):
|
||||
eserver.inject('xend.console.close', console.idx)
|
||||
self.delInstance(console)
|
||||
|
||||
class ConsoleController(controller.Controller):
|
||||
"""Console controller for a domain.
|
||||
Does not poll for i/o itself, but relies on the notifier to post console
|
||||
output and the connected TCP sockets to post console input.
|
||||
"""
|
||||
|
||||
def __init__(self, factory, dom, console_port):
|
||||
#print 'ConsoleController> dom=', dom, type(dom)
|
||||
controller.Controller.__init__(self, factory, dom)
|
||||
self.majorTypes = [ CMSG_CONSOLE ]
|
||||
self.status = "new"
|
||||
self.addr = None
|
||||
self.conn = None
|
||||
self.rbuf = xu.buffer()
|
||||
self.wbuf = xu.buffer()
|
||||
self.console_port = console_port
|
||||
|
||||
self.registerChannel()
|
||||
self.listener = None
|
||||
self.listen()
|
||||
#print 'ConsoleController<', 'dom=', self.dom, 'idx=', self.idx
|
||||
|
||||
def sxpr(self):
|
||||
val =['console',
|
||||
['status', self.status ],
|
||||
['id', self.idx ],
|
||||
['domain', self.dom ],
|
||||
['local_port', self.channel.getLocalPort() ],
|
||||
['remote_port', self.channel.getRemotePort() ],
|
||||
['console_port', self.console_port ] ]
|
||||
if self.addr:
|
||||
val.append(['connected', self.addr[0], self.addr[1]])
|
||||
return val
|
||||
|
||||
def ready(self):
|
||||
return not (self.closed() or self.rbuf.empty())
|
||||
|
||||
def closed(self):
|
||||
return self.status == 'closed'
|
||||
|
||||
def connected(self):
|
||||
return self.status == 'connected'
|
||||
|
||||
def close(self):
|
||||
try:
|
||||
#print 'ConsoleController> close dom=', self.dom
|
||||
self.status = "closed"
|
||||
if self.conn:
|
||||
self.conn.loseConnection()
|
||||
self.listener.stopListening()
|
||||
self.deregisterChannel()
|
||||
self.lostChannel()
|
||||
except Exception, ex:
|
||||
print 'ConsoleController>close>', ex
|
||||
raise
|
||||
|
||||
def listen(self):
|
||||
"""Listen for TCP connections to the console port..
|
||||
"""
|
||||
if self.closed(): return
|
||||
self.status = "listening"
|
||||
if self.listener:
|
||||
#self.listener.startListening()
|
||||
pass
|
||||
else:
|
||||
f = ConsoleFactory(self, self.idx)
|
||||
self.listener = reactor.listenTCP(self.console_port, f)
|
||||
|
||||
def connect(self, addr, conn):
|
||||
if self.closed(): return -1
|
||||
if self.connected(): return -1
|
||||
self.addr = addr
|
||||
self.conn = conn
|
||||
self.status = "connected"
|
||||
self.handleOutput()
|
||||
return 0
|
||||
|
||||
def disconnect(self):
|
||||
if self.conn:
|
||||
self.conn.loseConnection()
|
||||
self.addr = None
|
||||
self.conn = None
|
||||
self.listen()
|
||||
|
||||
def requestReceived(self, msg, type, subtype):
|
||||
#print '***Console', self.dom, msg.get_payload()
|
||||
self.rbuf.write(msg.get_payload())
|
||||
self.handleOutput()
|
||||
|
||||
def responseReceived(self, msg, type, subtype):
|
||||
pass
|
||||
|
||||
def produceRequests(self):
|
||||
# Send as much pending console data as there is room for.
|
||||
work = 0
|
||||
while not self.wbuf.empty() and self.channel.writeReady():
|
||||
msg = xu.message(CMSG_CONSOLE, 0, 0)
|
||||
msg.append_payload(self.wbuf.read(msg.MAX_PAYLOAD))
|
||||
work += self.channel.writeRequest(msg, notify=0)
|
||||
return work
|
||||
|
||||
def handleInput(self, conn, data):
|
||||
"""Handle some external input aimed at the console.
|
||||
Called from a TCP connection (conn).
|
||||
"""
|
||||
if self.closed(): return -1
|
||||
if conn != self.conn: return 0
|
||||
self.wbuf.write(data)
|
||||
if self.produceRequests():
|
||||
self.channel.notify()
|
||||
return 0
|
||||
|
||||
def handleOutput(self):
|
||||
"""Handle buffered output from the console.
|
||||
Sends it to the connected console (if any).
|
||||
"""
|
||||
if self.closed():
|
||||
#print 'Console>handleOutput> closed'
|
||||
return -1
|
||||
if not self.conn:
|
||||
#print 'Console>handleOutput> not connected'
|
||||
return 0
|
||||
while not self.rbuf.empty():
|
||||
try:
|
||||
#print 'Console>handleOutput> writing...'
|
||||
bytes = self.conn.write(self.rbuf.peek())
|
||||
if bytes > 0:
|
||||
self.rbuf.discard(bytes)
|
||||
except socket.error, error:
|
||||
pass
|
||||
#print 'Console>handleOutput<'
|
||||
return 0
|
||||
Executable
+169
@@ -0,0 +1,169 @@
|
||||
from twisted.internet import defer
|
||||
|
||||
import channel
|
||||
from messages import msgTypeName
|
||||
|
||||
class CtrlMsgRcvr:
|
||||
"""Abstract class for things that deal with a control interface to a domain.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.channelFactory = channel.channelFactory()
|
||||
self.majorTypes = [ ]
|
||||
self.subTypes = {}
|
||||
self.dom = None
|
||||
self.channel = None
|
||||
self.idx = None
|
||||
|
||||
def requestReceived(self, msg, type, subtype):
|
||||
method = self.subTypes.get(subtype)
|
||||
if method:
|
||||
method(msg, 1)
|
||||
else:
|
||||
print ('requestReceived> No handler: Message type %s %d:%d'
|
||||
% (msgTypeName(type, subtype), type, subtype)), self
|
||||
|
||||
def responseReceived(self, msg, type, subtype):
|
||||
method = self.subTypes.get(subtype)
|
||||
if method:
|
||||
method(msg, 0)
|
||||
else:
|
||||
print ('responseReceived> No handler: Message type %s %d:%d'
|
||||
% (msgTypeName(type, subtype), type, subtype)), self
|
||||
|
||||
def lostChannel(self):
|
||||
pass
|
||||
|
||||
def registerChannel(self):
|
||||
#print 'CtrlMsgRcvr>registerChannel>', self
|
||||
self.channel = self.channelFactory.domChannel(self.dom)
|
||||
self.idx = self.channel.getIndex()
|
||||
if self.majorTypes:
|
||||
self.channel.registerDevice(self.majorTypes, self)
|
||||
|
||||
def deregisterChannel(self):
|
||||
#print 'CtrlMsgRcvr>deregisterChannel>', self
|
||||
if self.channel:
|
||||
self.channel.deregisterDevice(self)
|
||||
del self.channel
|
||||
|
||||
def produceRequests(self):
|
||||
return 0
|
||||
|
||||
def writeRequest(self, msg):
|
||||
if self.channel:
|
||||
self.channel.writeRequest(msg)
|
||||
else:
|
||||
print 'CtrlMsgRcvr>writeRequest>', 'no channel!', self
|
||||
|
||||
def writeResponse(self, msg):
|
||||
if self.channel:
|
||||
self.channel.writeResponse(msg)
|
||||
else:
|
||||
print 'CtrlMsgRcvr>writeResponse>', 'no channel!', self
|
||||
|
||||
class ControllerFactory(CtrlMsgRcvr):
|
||||
"""Abstract class for factories creating controllers.
|
||||
Maintains a table of instances.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
CtrlMsgRcvr.__init__(self)
|
||||
self.instances = {}
|
||||
self.dlist = []
|
||||
self.dom = 0
|
||||
# Timeout (in seconds) for deferreds.
|
||||
self.timeout = 10
|
||||
|
||||
def addInstance(self, instance):
|
||||
self.instances[instance.idx] = instance
|
||||
|
||||
def getInstance(self, idx):
|
||||
return self.instances.get(idx)
|
||||
|
||||
def getInstances(self):
|
||||
return self.instances.values()
|
||||
|
||||
def getInstanceByDom(self, dom):
|
||||
for inst in self.instances.values():
|
||||
if inst.dom == dom:
|
||||
return inst
|
||||
return None
|
||||
|
||||
def delInstance(self, instance):
|
||||
#print 'ControllerFactory>delInstance>', instance.idx
|
||||
if instance.idx in self.instances:
|
||||
#print 'ControllerFactory>delInstance> remove', instance.idx
|
||||
del self.instances[instance.idx]
|
||||
|
||||
def createInstance(self, dom, recreate=0):
|
||||
raise NotImplementedError()
|
||||
|
||||
def instanceClosed(self, instance):
|
||||
#print 'ControllerFactory>instanceClosed>', instance.idx, instance
|
||||
self.delInstance(instance)
|
||||
|
||||
def addDeferred(self):
|
||||
d = defer.Deferred()
|
||||
if self.timeout > 0:
|
||||
# The deferred will error if not called before timeout.
|
||||
d.setTimeout(self.timeout)
|
||||
self.dlist.append(d)
|
||||
return d
|
||||
|
||||
def callDeferred(self, *args):
|
||||
if self.dlist:
|
||||
d = self.dlist.pop(0)
|
||||
d.callback(*args)
|
||||
|
||||
def errDeferred(self, *args):
|
||||
if self.dlist:
|
||||
d = self.dlist.pop(0)
|
||||
d.errback(*args)
|
||||
|
||||
class Controller(CtrlMsgRcvr):
|
||||
"""Abstract class for a device controller attached to a domain.
|
||||
"""
|
||||
|
||||
def __init__(self, factory, dom):
|
||||
CtrlMsgRcvr.__init__(self)
|
||||
self.factory = factory
|
||||
self.dom = int(dom)
|
||||
self.channel = None
|
||||
self.idx = None
|
||||
|
||||
def close(self):
|
||||
self.deregisterChannel()
|
||||
self.lostChannel()
|
||||
|
||||
def lostChannel(self):
|
||||
#print 'Controller>lostChannel>', self, self.factory
|
||||
self.factory.instanceClosed(self)
|
||||
|
||||
class Dev:
|
||||
|
||||
def __init__(self, controller):
|
||||
self.controller = controller
|
||||
self.props = {}
|
||||
|
||||
def setprop(self, k, v):
|
||||
self.props[k] = v
|
||||
|
||||
def getprop(self, k, v=None):
|
||||
return self.props.get(k, v)
|
||||
|
||||
def hasprop(self, k):
|
||||
return k in self.props
|
||||
|
||||
def delprop(self, k):
|
||||
if k in self.props:
|
||||
del self.props[k]
|
||||
|
||||
#def __repr__(self):
|
||||
# return str(self.sxpr())
|
||||
|
||||
def sxpr(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
Executable
+269
@@ -0,0 +1,269 @@
|
||||
import struct
|
||||
|
||||
class Struct:
|
||||
|
||||
maxDepth = 10
|
||||
|
||||
base = ['x', 'B', 'H', 'I', 'L', 'Q', 'c', 'h', 'i', 'l', 'q', ]
|
||||
|
||||
sizes = {'B': 1,
|
||||
'H': 2,
|
||||
'I': 4,
|
||||
'L': 4,
|
||||
'Q': 8,
|
||||
'c': 1,
|
||||
'h': 2,
|
||||
'i': 4,
|
||||
'l': 4,
|
||||
'q': 8,
|
||||
'x': 1,
|
||||
}
|
||||
|
||||
formats = {
|
||||
'int8' : 'B',
|
||||
'int16' : 'H',
|
||||
'int32' : 'I',
|
||||
'int64' : 'Q',
|
||||
'u8' : 'B',
|
||||
'u16' : 'H',
|
||||
'u32' : 'I',
|
||||
'u64' : 'Q'
|
||||
}
|
||||
|
||||
def typedef(self, name, val):
|
||||
self.formats[name] = val
|
||||
|
||||
def struct(self, name, *f):
|
||||
self.typedef(name, StructInfo(self, f))
|
||||
|
||||
def getType(self, name):
|
||||
return self.formats[name]
|
||||
|
||||
def format(self, ty):
|
||||
d = 0
|
||||
f = ty
|
||||
while d < self.maxDepth:
|
||||
d += 1
|
||||
f = self.formats[f]
|
||||
if isinstance(f, StructInfo):
|
||||
return f.format()
|
||||
if f in self.base:
|
||||
return f
|
||||
return -1
|
||||
|
||||
def alignedformat(self, ty):
|
||||
fmt = self.format(ty)
|
||||
#print 'alignedformat> %s |%s|' %(ty, fmt)
|
||||
afmt = self.align(fmt)
|
||||
#print 'alignedformat< %s |%s| |%s|' % (ty, fmt, afmt)
|
||||
return afmt
|
||||
|
||||
def align(self, fmt):
|
||||
n1 = 0
|
||||
afmt = ''
|
||||
for a in fmt:
|
||||
n2 = self.getSize(a)
|
||||
m = n1 % n2
|
||||
if m:
|
||||
d = (n2 - m)
|
||||
afmt += 'x' * d
|
||||
n1 += d
|
||||
afmt += a
|
||||
n1 += n2
|
||||
return afmt
|
||||
|
||||
def fmtsize(self, fmt):
|
||||
s = 0
|
||||
for f in fmt:
|
||||
s += self.getSize(f)
|
||||
return s
|
||||
|
||||
def getSize(self, f):
|
||||
return self.sizes[f]
|
||||
|
||||
def pack(self, ty, data):
|
||||
return self.getType(ty).pack(data)
|
||||
|
||||
def unpack(self, ty, data):
|
||||
return self.getType(ty).unpack(data)
|
||||
|
||||
def show(self):
|
||||
l = self.formats.keys()
|
||||
l.sort()
|
||||
for v in l:
|
||||
print "%-35s %-10s %s" % (v, self.format(v), self.alignedformat(v))
|
||||
|
||||
|
||||
class StructInfo:
|
||||
|
||||
def __init__(self, s, f):
|
||||
self.fmt = None
|
||||
self.structs = s
|
||||
self.fields = f
|
||||
|
||||
def alignedformat(self):
|
||||
if self.afmt: return self.afmt
|
||||
self.afmt = self.structs.align(self.format())
|
||||
return self.afmt
|
||||
|
||||
def format(self):
|
||||
if self.fmt: return self.fmt
|
||||
fmt = ""
|
||||
for (ty, name) in self.fields:
|
||||
fmt += self.formatString(ty)
|
||||
self.fmt = fmt
|
||||
return fmt
|
||||
|
||||
def formatString(self, ty):
|
||||
if ty in self.fields:
|
||||
ty = self.fields[ty]
|
||||
return self.structs.format(ty)
|
||||
|
||||
def pack(self, *args):
|
||||
return struct.pack(self.alignedformat(), *args)
|
||||
|
||||
def unpack(self, data):
|
||||
return struct.unpack(self.alignedformat(), data)
|
||||
|
||||
types = Struct()
|
||||
|
||||
types.typedef('short' , 'h')
|
||||
types.typedef('int' , 'i')
|
||||
types.typedef('long' , 'l')
|
||||
types.typedef('unsigned short', 'H')
|
||||
types.typedef('unsigned int' , 'I')
|
||||
types.typedef('unsigned long' , 'L')
|
||||
types.typedef('domid_t' , 'u64')
|
||||
types.typedef('blkif_vdev_t' , 'u16')
|
||||
types.typedef('blkif_pdev_t' , 'u16')
|
||||
types.typedef('blkif_sector_t', 'u64')
|
||||
|
||||
types.struct('u8[6]',
|
||||
('u8', 'a1'),
|
||||
('u8', 'a2'),
|
||||
('u8', 'a3'),
|
||||
('u8', 'a4'),
|
||||
('u8', 'a5'),
|
||||
('u8', 'a6'))
|
||||
|
||||
types.struct('blkif_fe_interface_status_changed_t',
|
||||
('unsigned int', 'handle'),
|
||||
('unsigned int', 'status'),
|
||||
('unsigned int', 'evtchn'))
|
||||
|
||||
types.struct('blkif_fe_driver_status_changed_t',
|
||||
('unsigned int', 'status'),
|
||||
('unsigned int', 'nr_interfaces'))
|
||||
|
||||
types.struct('blkif_fe_interface_connect_t',
|
||||
('unsigned int' , 'handle'),
|
||||
('unsigned long', 'shmem_frame'))
|
||||
|
||||
types.struct('blkif_fe_interface_disconnect_t',
|
||||
('unsigned int', 'handle'))
|
||||
|
||||
types.struct('blkif_extent_t',
|
||||
('blkif_pdev_t' , 'device'),
|
||||
('blkif_sector_t', 'sector_start'),
|
||||
('blkif_sector_t', 'sector_length'))
|
||||
|
||||
types.struct('blkif_be_create_t',
|
||||
('domid_t' , 'domid'),
|
||||
('unsigned int', 'blkif_handle'),
|
||||
('unsigned int', 'status'))
|
||||
|
||||
types.struct('blkif_be_destroy_t',
|
||||
('domid_t' , 'domid'),
|
||||
('unsigned int', 'blkif_handle'),
|
||||
('unsigned int', 'status'))
|
||||
|
||||
types.struct('blkif_be_connect_t',
|
||||
('domid_t' , 'domid'),
|
||||
('unsigned int' , 'blkif_handle'),
|
||||
('unsigned int' , 'evtchn'),
|
||||
('unsigned long', 'shmem_frame'),
|
||||
('unsigned int' , 'status'))
|
||||
|
||||
types.struct('blkif_be_disconnect_t',
|
||||
('domid_t' , 'domid'),
|
||||
('unsigned int', 'blkif_handle'),
|
||||
('unsigned int', 'status'))
|
||||
|
||||
types.struct('blkif_be_vbd_create_t',
|
||||
('domid_t' , 'domid'), #Q
|
||||
('unsigned int', 'blkif_handle'), #I
|
||||
('blkif_vdev_t', 'vdevice'), #H
|
||||
('int' , 'readonly'), #i
|
||||
('unsigned int', 'status')) #I
|
||||
|
||||
types.struct('blkif_be_vbd_destroy_t',
|
||||
('domid_t' , 'domid'),
|
||||
('unsigned int', 'blkif_handle'),
|
||||
('blkif_vdev_t', 'vdevice'),
|
||||
('unsigned int', 'status'))
|
||||
|
||||
types.struct('blkif_be_vbd_grow_t',
|
||||
('domid_t' , 'domid'), #Q
|
||||
('unsigned int' , 'blkif_handle'), #I
|
||||
('blkif_vdev_t' , 'vdevice'), #H
|
||||
('blkif_extent_t', 'extent'), #HQQ
|
||||
('unsigned int' , 'status')) #I
|
||||
|
||||
types.struct('blkif_be_vbd_shrink_t',
|
||||
('domid_t' , 'domid'),
|
||||
('unsigned int', 'blkif_handle'),
|
||||
('blkif_vdev_t', 'vdevice'),
|
||||
('unsigned int', 'status'))
|
||||
|
||||
types.struct('blkif_be_driver_status_changed_t',
|
||||
('unsigned int', 'status'),
|
||||
('unsigned int', 'nr_interfaces'))
|
||||
|
||||
types.struct('netif_fe_interface_status_changed_t',
|
||||
('unsigned int', 'handle'),
|
||||
('unsigned int', 'status'),
|
||||
('unsigned int', 'evtchn'),
|
||||
('u8[6]', 'mac'))
|
||||
|
||||
types.struct('netif_fe_driver_status_changed_t',
|
||||
('unsigned int', 'status'),
|
||||
('unsigned int', 'nr_interfaces'))
|
||||
|
||||
types.struct('netif_fe_interface_connect_t',
|
||||
('unsigned int', 'handle'),
|
||||
('unsigned long', 'tx_shmem_frame'),
|
||||
('unsigned long', 'rx_shmem_frame'))
|
||||
|
||||
types.struct('netif_fe_interface_disconnect_t',
|
||||
('unsigned int', 'handle'))
|
||||
|
||||
types.struct('netif_be_create_t',
|
||||
('domid_t' , 'domid'),
|
||||
('unsigned int', 'netif_handle'),
|
||||
('u8[6]' , 'mac'),
|
||||
('unsigned int', 'status'))
|
||||
|
||||
types.struct('netif_be_destroy_t',
|
||||
('domid_t' , 'domid'),
|
||||
('unsigned int', 'netif_handle'),
|
||||
('unsigned int', 'status'))
|
||||
|
||||
types.struct('netif_be_connect_t',
|
||||
('domid_t' , 'domid'),
|
||||
('unsigned int' , 'netif_handle'),
|
||||
('unsigned int' , 'evtchn'),
|
||||
('unsigned long', 'tx_shmem_frame'),
|
||||
('unsigned long', 'rx_shmem_frame'),
|
||||
('unsigned int' , 'status'))
|
||||
|
||||
types.struct('netif_be_disconnect_t',
|
||||
('domid_t' , 'domid'),
|
||||
('unsigned int', 'netif_handle'),
|
||||
('unsigned int', 'status'))
|
||||
|
||||
types.struct('netif_be_driver_status_changed_t',
|
||||
('unsigned int', 'status'),
|
||||
('unsigned int', 'nr_interfaces'))
|
||||
|
||||
if 1 or __name__ == "__main__":
|
||||
types.show()
|
||||
@@ -0,0 +1,41 @@
|
||||
import channel
|
||||
import controller
|
||||
from messages import *
|
||||
|
||||
class DomainControllerFactory(controller.ControllerFactory):
|
||||
"""Factory for creating domain controllers.
|
||||
"""
|
||||
|
||||
def createInstance(self, dom):
|
||||
d = DomainController(self, dom)
|
||||
self.addInstance(d)
|
||||
return d
|
||||
|
||||
def getInstanceByDom(self, dom):
|
||||
for inst in self.instances.values():
|
||||
if inst.dom == dom:
|
||||
return inst
|
||||
inst = self.createInstance(dom)
|
||||
return inst
|
||||
|
||||
|
||||
class DomainController(controller.Controller):
|
||||
"""Generic controller for a domain.
|
||||
"""
|
||||
|
||||
reasons = {'poweroff' : 'shutdown_poweroff_t',
|
||||
'reboot' : 'shutdown_reboot_t',
|
||||
'suspend' : 'shutdown_suspend_t' }
|
||||
|
||||
def __init__(self, factory, dom):
|
||||
controller.Controller.__init__(self, factory, dom)
|
||||
self.majorTypes = [ CMSG_SHUTDOWN ]
|
||||
self.registerChannel()
|
||||
print 'DomainController>', self, self.channel, self.idx
|
||||
|
||||
def shutdown(self, reason):
|
||||
msgtype = self.reasons.get(reason)
|
||||
if not msgtype:
|
||||
raise ValueError('invalid reason:' + reason)
|
||||
msg = packMsg(msgtype, {})
|
||||
self.writeRequest(msg)
|
||||
@@ -0,0 +1,219 @@
|
||||
import struct
|
||||
|
||||
from xen.ext import xu
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
""" All message formats.
|
||||
Added to incrementally for the various message types.
|
||||
See below.
|
||||
"""
|
||||
msg_formats = {}
|
||||
|
||||
#============================================================================
|
||||
# Console message types.
|
||||
#============================================================================
|
||||
|
||||
CMSG_CONSOLE = 0
|
||||
|
||||
console_formats = { 'console_data': (CMSG_CONSOLE, 0) }
|
||||
|
||||
msg_formats.update(console_formats)
|
||||
|
||||
#============================================================================
|
||||
# Block interface message types.
|
||||
#============================================================================
|
||||
|
||||
CMSG_BLKIF_BE = 1
|
||||
CMSG_BLKIF_FE = 2
|
||||
|
||||
CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED = 0
|
||||
CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED = 32
|
||||
CMSG_BLKIF_FE_INTERFACE_CONNECT = 33
|
||||
CMSG_BLKIF_FE_INTERFACE_DISCONNECT = 34
|
||||
|
||||
CMSG_BLKIF_BE_CREATE = 0
|
||||
CMSG_BLKIF_BE_DESTROY = 1
|
||||
CMSG_BLKIF_BE_CONNECT = 2
|
||||
CMSG_BLKIF_BE_DISCONNECT = 3
|
||||
CMSG_BLKIF_BE_VBD_CREATE = 4
|
||||
CMSG_BLKIF_BE_VBD_DESTROY = 5
|
||||
CMSG_BLKIF_BE_VBD_GROW = 6
|
||||
CMSG_BLKIF_BE_VBD_SHRINK = 7
|
||||
CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED = 32
|
||||
|
||||
BLKIF_DRIVER_STATUS_DOWN = 0
|
||||
BLKIF_DRIVER_STATUS_UP = 1
|
||||
|
||||
BLKIF_INTERFACE_STATUS_DESTROYED = 0 #/* Interface doesn't exist. */
|
||||
BLKIF_INTERFACE_STATUS_DISCONNECTED = 1 #/* Exists but is disconnected. */
|
||||
BLKIF_INTERFACE_STATUS_CONNECTED = 2 #/* Exists and is connected. */
|
||||
|
||||
BLKIF_BE_STATUS_OKAY = 0
|
||||
BLKIF_BE_STATUS_ERROR = 1
|
||||
BLKIF_BE_STATUS_INTERFACE_EXISTS = 2
|
||||
BLKIF_BE_STATUS_INTERFACE_NOT_FOUND = 3
|
||||
BLKIF_BE_STATUS_INTERFACE_CONNECTED = 4
|
||||
BLKIF_BE_STATUS_VBD_EXISTS = 5
|
||||
BLKIF_BE_STATUS_VBD_NOT_FOUND = 6
|
||||
BLKIF_BE_STATUS_OUT_OF_MEMORY = 7
|
||||
BLKIF_BE_STATUS_EXTENT_NOT_FOUND = 8
|
||||
BLKIF_BE_STATUS_MAPPING_ERROR = 9
|
||||
|
||||
blkif_formats = {
|
||||
'blkif_be_connect_t':
|
||||
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CONNECT),
|
||||
|
||||
'blkif_be_create_t':
|
||||
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE),
|
||||
|
||||
'blkif_be_disconnect_t':
|
||||
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DISCONNECT),
|
||||
|
||||
'blkif_be_destroy_t':
|
||||
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY),
|
||||
|
||||
'blkif_be_vbd_create_t':
|
||||
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE),
|
||||
|
||||
'blkif_be_vbd_grow_t':
|
||||
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW),
|
||||
|
||||
'blkif_be_vbd_destroy_t':
|
||||
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_DESTROY),
|
||||
|
||||
'blkif_fe_interface_status_changed_t':
|
||||
(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED),
|
||||
|
||||
'blkif_fe_driver_status_changed_t':
|
||||
(CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED),
|
||||
|
||||
'blkif_fe_interface_connect_t':
|
||||
(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_CONNECT),
|
||||
}
|
||||
|
||||
msg_formats.update(blkif_formats)
|
||||
|
||||
#============================================================================
|
||||
# Network interface message types.
|
||||
#============================================================================
|
||||
|
||||
CMSG_NETIF_BE = 3
|
||||
CMSG_NETIF_FE = 4
|
||||
|
||||
CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED = 0
|
||||
CMSG_NETIF_FE_DRIVER_STATUS_CHANGED = 32
|
||||
CMSG_NETIF_FE_INTERFACE_CONNECT = 33
|
||||
CMSG_NETIF_FE_INTERFACE_DISCONNECT = 34
|
||||
|
||||
CMSG_NETIF_BE_CREATE = 0
|
||||
CMSG_NETIF_BE_DESTROY = 1
|
||||
CMSG_NETIF_BE_CONNECT = 2
|
||||
CMSG_NETIF_BE_DISCONNECT = 3
|
||||
CMSG_NETIF_BE_DRIVER_STATUS_CHANGED = 32
|
||||
|
||||
NETIF_INTERFACE_STATUS_DESTROYED = 0 #/* Interface doesn't exist. */
|
||||
NETIF_INTERFACE_STATUS_DISCONNECTED = 1 #/* Exists but is disconnected. */
|
||||
NETIF_INTERFACE_STATUS_CONNECTED = 2 #/* Exists and is connected. */
|
||||
|
||||
NETIF_DRIVER_STATUS_DOWN = 0
|
||||
NETIF_DRIVER_STATUS_UP = 1
|
||||
|
||||
netif_formats = {
|
||||
'netif_be_connect_t':
|
||||
(CMSG_NETIF_BE, CMSG_NETIF_BE_CONNECT),
|
||||
|
||||
'netif_be_create_t':
|
||||
(CMSG_NETIF_BE, CMSG_NETIF_BE_CREATE),
|
||||
|
||||
'netif_be_disconnect_t':
|
||||
(CMSG_NETIF_BE, CMSG_NETIF_BE_DISCONNECT),
|
||||
|
||||
'netif_be_destroy_t':
|
||||
(CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY),
|
||||
|
||||
'netif_be_driver_status_changed_t':
|
||||
(CMSG_NETIF_BE, CMSG_NETIF_BE_DRIVER_STATUS_CHANGED),
|
||||
|
||||
'netif_fe_driver_status_changed_t':
|
||||
(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED),
|
||||
|
||||
'netif_fe_interface_connect_t':
|
||||
(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_CONNECT),
|
||||
|
||||
'netif_fe_interface_status_changed_t':
|
||||
(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED),
|
||||
}
|
||||
|
||||
msg_formats.update(netif_formats)
|
||||
|
||||
#============================================================================
|
||||
CMSG_SHUTDOWN = 6
|
||||
|
||||
CMSG_SHUTDOWN_POWEROFF = 0
|
||||
CMSG_SHUTDOWN_REBOOT = 1
|
||||
CMSG_SHUTDOWN_SUSPEND = 2
|
||||
|
||||
STOPCODE_shutdown = 0
|
||||
STOPCODE_reboot = 1
|
||||
STOPCODE_suspend = 2
|
||||
|
||||
shutdown_formats = {
|
||||
'shutdown_poweroff_t':
|
||||
(CMSG_SHUTDOWN, CMSG_SHUTDOWN_POWEROFF),
|
||||
|
||||
'shutdown_reboot_t':
|
||||
(CMSG_SHUTDOWN, CMSG_SHUTDOWN_REBOOT),
|
||||
|
||||
'shutdown_suspend_t':
|
||||
(CMSG_SHUTDOWN, CMSG_SHUTDOWN_SUSPEND),
|
||||
}
|
||||
|
||||
msg_formats.update(shutdown_formats)
|
||||
|
||||
#============================================================================
|
||||
|
||||
class Msg:
|
||||
pass
|
||||
|
||||
def packMsg(ty, params):
|
||||
if DEBUG: print '>packMsg', ty, params
|
||||
(major, minor) = msg_formats[ty]
|
||||
args = {}
|
||||
for (k, v) in params.items():
|
||||
if k == 'mac':
|
||||
for i in range(0, 6):
|
||||
args['mac[%d]' % i] = v[i]
|
||||
else:
|
||||
args[k] = v
|
||||
if DEBUG:
|
||||
for (k, v) in args.items():
|
||||
print 'packMsg>', k, v, type(v)
|
||||
msgid = 0
|
||||
msg = xu.message(major, minor, msgid, args)
|
||||
return msg
|
||||
|
||||
def unpackMsg(ty, msg):
|
||||
args = msg.get_payload()
|
||||
mac = [0, 0, 0, 0, 0, 0]
|
||||
macs = []
|
||||
for (k, v) in args.items():
|
||||
if k.startswith('mac['):
|
||||
macs += k
|
||||
i = int(k[4:5])
|
||||
mac[i] = v
|
||||
else:
|
||||
pass
|
||||
if macs:
|
||||
args['mac'] = mac
|
||||
for k in macs:
|
||||
del args[k]
|
||||
if DEBUG: print '<unpackMsg', ty, args
|
||||
return args
|
||||
|
||||
def msgTypeName(ty, subty):
|
||||
for (name, info) in msg_formats.items():
|
||||
if info[0] == ty and info[1] == subty:
|
||||
return name
|
||||
return None
|
||||
|
||||
Executable
+316
@@ -0,0 +1,316 @@
|
||||
import random
|
||||
|
||||
from twisted.internet import defer
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import PrettyPrint
|
||||
from xen.xend import Vifctl
|
||||
|
||||
import channel
|
||||
import controller
|
||||
from messages import *
|
||||
|
||||
class NetifControllerFactory(controller.ControllerFactory):
|
||||
"""Factory for creating network interface controllers.
|
||||
Also handles the 'back-end' channel to the device driver domain.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
controller.ControllerFactory.__init__(self)
|
||||
|
||||
self.majorTypes = [ CMSG_NETIF_BE ]
|
||||
|
||||
self.subTypes = {
|
||||
CMSG_NETIF_BE_CREATE : self.recv_be_create,
|
||||
CMSG_NETIF_BE_CONNECT: self.recv_be_connect,
|
||||
CMSG_NETIF_BE_DRIVER_STATUS_CHANGED: self.recv_be_driver_status_changed,
|
||||
}
|
||||
self.attached = 1
|
||||
self.registerChannel()
|
||||
|
||||
def createInstance(self, dom, recreate=0):
|
||||
"""Create or find the network interface controller for a domain.
|
||||
"""
|
||||
#print 'netif>createInstance> dom=', dom
|
||||
netif = self.getInstanceByDom(dom)
|
||||
if netif is None:
|
||||
netif = NetifController(self, dom)
|
||||
self.addInstance(netif)
|
||||
return netif
|
||||
|
||||
def getDomainDevices(self, dom):
|
||||
netif = self.getInstanceByDom(dom)
|
||||
return (netif and netif.getDevices()) or []
|
||||
|
||||
def getDomainDevice(self, dom, vif):
|
||||
netif = self.getInstanceByDom(dom)
|
||||
return (netif and netif.getDevice(vif)) or None
|
||||
|
||||
def setControlDomain(self, dom, recreate=0):
|
||||
"""Set the 'back-end' device driver domain.
|
||||
"""
|
||||
if self.dom == dom: return
|
||||
self.deregisterChannel()
|
||||
if not recreate:
|
||||
self.attached = 0
|
||||
self.dom = dom
|
||||
self.registerChannel()
|
||||
#
|
||||
#if xend.netif.be_port.remote_dom != 0:
|
||||
# xend.netif.recovery = True
|
||||
# xend.netif.be_port = xend.main.port_from_dom(dom)
|
||||
#
|
||||
|
||||
def getControlDomain(self):
|
||||
return self.dom
|
||||
|
||||
def recv_be_create(self, msg, req):
|
||||
self.callDeferred(0)
|
||||
|
||||
def recv_be_connect(self, msg, req):
|
||||
val = unpackMsg('netif_be_connect_t', msg)
|
||||
dom = val['domid']
|
||||
vif = val['netif_handle']
|
||||
netif = self.getInstanceByDom(dom)
|
||||
if netif:
|
||||
netif.send_interface_connected(vif)
|
||||
else:
|
||||
print "recv_be_connect> unknown vif=", vif
|
||||
pass
|
||||
|
||||
def recv_be_driver_status_changed(self, msg, req):
|
||||
val = unpackMsg('netif_be_driver_status_changed_t', msg)
|
||||
status = val['status']
|
||||
if status == NETIF_DRIVER_STATUS_UP and not self.attached:
|
||||
# If we are not attached the driver domain was changed, and
|
||||
# this signals the new driver domain is ready.
|
||||
for netif in self.getInstances():
|
||||
netif.reattach_devices()
|
||||
self.attached = 1
|
||||
|
||||
## pl = msg.get_payload()
|
||||
## status = pl['status']
|
||||
## if status == NETIF_DRIVER_STATUS_UP:
|
||||
## if xend.netif.recovery:
|
||||
## print "New netif backend now UP, notifying guests:"
|
||||
## for netif_key in interface.list.keys():
|
||||
## netif = interface.list[netif_key]
|
||||
## netif.create()
|
||||
## print " Notifying %d" % netif.dom
|
||||
## msg = xu.message(
|
||||
## CMSG_NETIF_FE,
|
||||
## CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED, 0,
|
||||
## { 'handle' : 0, 'status' : 1 })
|
||||
## netif.ctrlif_tx_req(xend.main.port_from_dom(netif.dom),msg)
|
||||
## print "Done notifying guests"
|
||||
## recovery = False
|
||||
|
||||
class NetDev(controller.Dev):
|
||||
"""Info record for a network device.
|
||||
"""
|
||||
|
||||
def __init__(self, ctrl, vif, mac):
|
||||
controller.Dev.__init__(self, ctrl)
|
||||
self.vif = vif
|
||||
self.mac = mac
|
||||
self.evtchn = None
|
||||
self.bridge = None
|
||||
self.ipaddr = []
|
||||
|
||||
def sxpr(self):
|
||||
vif = str(self.vif)
|
||||
mac = self.get_mac()
|
||||
val = ['netdev', ['vif', vif], ['mac', mac]]
|
||||
if self.bridge:
|
||||
val.append(['bridge', self.bridge])
|
||||
if self.evtchn:
|
||||
val.append(['evtchn',
|
||||
self.evtchn['port1'],
|
||||
self.evtchn['port2']])
|
||||
return val
|
||||
|
||||
def get_vifname(self):
|
||||
return "vif%d.%d" % (self.controller.dom, self.vif)
|
||||
|
||||
def get_mac(self):
|
||||
return ':'.join(map(lambda x: "%x" % x, self.mac))
|
||||
|
||||
def vifctl_params(self):
|
||||
return { 'mac' : self.get_mac(),
|
||||
'bridge': self.bridge,
|
||||
'ipaddr': self.ipaddr }
|
||||
|
||||
def up(self, bridge=None, ipaddr=[]):
|
||||
self.bridge = bridge
|
||||
self.ipaddr = ipaddr
|
||||
Vifctl.up(self.get_vifname(), **self.vifctl_params())
|
||||
|
||||
def down(self):
|
||||
Vifctl.down(self.get_vifname(), **self.vifctl_params())
|
||||
|
||||
def destroy(self):
|
||||
def cb_destroy(val):
|
||||
self.controller.send_be_destroy(self.vif)
|
||||
print 'NetDev>destroy>', 'vif=', self.vif
|
||||
PrettyPrint.prettyprint(self.sxpr())
|
||||
self.down()
|
||||
d = self.controller.factory.addDeferred()
|
||||
d.addCallback(cb_destroy)
|
||||
self.controller.send_be_disconnect(self.vif)
|
||||
#self.controller.send_be_destroy(self.vif)
|
||||
|
||||
|
||||
class NetifController(controller.Controller):
|
||||
"""Network interface controller. Handles all network devices for a domain.
|
||||
"""
|
||||
|
||||
def __init__(self, factory, dom):
|
||||
#print 'NetifController> dom=', dom
|
||||
controller.Controller.__init__(self, factory, dom)
|
||||
self.devices = {}
|
||||
|
||||
self.majorTypes = [ CMSG_NETIF_FE ]
|
||||
|
||||
self.subTypes = {
|
||||
CMSG_NETIF_FE_DRIVER_STATUS_CHANGED:
|
||||
self.recv_fe_driver_status_changed,
|
||||
CMSG_NETIF_FE_INTERFACE_CONNECT :
|
||||
self.recv_fe_interface_connect,
|
||||
}
|
||||
self.registerChannel()
|
||||
#print 'NetifController<', 'dom=', self.dom, 'idx=', self.idx
|
||||
|
||||
def sxpr(self):
|
||||
val = ['netif', ['dom', self.dom]]
|
||||
return val
|
||||
|
||||
def randomMAC(self):
|
||||
# VIFs get a random MAC address with a "special" vendor id.
|
||||
#
|
||||
# NB. The vendor is currently an "obsolete" one that used to belong
|
||||
# to DEC (AA-00-00). Using it is probably a bit rude :-)
|
||||
#
|
||||
# NB2. The first bit of the first random octet is set to zero for
|
||||
# all dynamic MAC addresses. This may allow us to manually specify
|
||||
# MAC addresses for some VIFs with no fear of clashes.
|
||||
mac = [ 0xaa, 0x00, 0x00,
|
||||
random.randint(0x00, 0x7f),
|
||||
random.randint(0x00, 0xff),
|
||||
random.randint(0x00, 0xff) ]
|
||||
return mac
|
||||
|
||||
def lostChannel(self):
|
||||
print 'NetifController>lostChannel>', 'dom=', self.dom
|
||||
#self.destroyDevices()
|
||||
controller.Controller.lostChannel(self)
|
||||
|
||||
def getDevices(self):
|
||||
return self.devices.values()
|
||||
|
||||
def getDevice(self, vif):
|
||||
return self.devices.get(vif)
|
||||
|
||||
def addDevice(self, vif, vmac):
|
||||
if vmac is None:
|
||||
mac = self.randomMAC()
|
||||
else:
|
||||
mac = [ int(x, 16) for x in vmac.split(':') ]
|
||||
if len(mac) != 6: raise ValueError("invalid mac")
|
||||
#print "attach_device>", "vif=", vif, "mac=", mac
|
||||
dev = NetDev(self, vif, mac)
|
||||
self.devices[vif] = dev
|
||||
return dev
|
||||
|
||||
def destroy(self):
|
||||
print 'NetifController>destroy>', 'dom=', self.dom
|
||||
self.destroyDevices()
|
||||
|
||||
def destroyDevices(self):
|
||||
for dev in self.getDevices():
|
||||
dev.destroy()
|
||||
|
||||
def attachDevice(self, vif, vmac, recreate=0):
|
||||
"""Attach a network device.
|
||||
If vmac is None a random mac address is assigned.
|
||||
|
||||
@param vif interface index
|
||||
@param vmac mac address (string)
|
||||
"""
|
||||
self.addDevice(vif, vmac)
|
||||
if recreate:
|
||||
d = defer.Deferred()
|
||||
d.callback(self)
|
||||
else:
|
||||
d = self.factory.addDeferred()
|
||||
self.send_be_create(vif)
|
||||
return d
|
||||
|
||||
def reattach_devices(self):
|
||||
"""Reattach all devices when the back-end control domain has changed.
|
||||
"""
|
||||
d = self.factory.addDeferred()
|
||||
self.send_be_create(vif)
|
||||
self.attach_fe_devices(0)
|
||||
|
||||
def attach_fe_devices(self):
|
||||
for dev in self.devices.values():
|
||||
msg = packMsg('netif_fe_interface_status_changed_t',
|
||||
{ 'handle' : dev.vif,
|
||||
'status' : NETIF_INTERFACE_STATUS_DISCONNECTED,
|
||||
'evtchn' : 0,
|
||||
'mac' : dev.mac })
|
||||
self.writeRequest(msg)
|
||||
|
||||
def recv_fe_driver_status_changed(self, msg, req):
|
||||
if not req: return
|
||||
msg = packMsg('netif_fe_driver_status_changed_t',
|
||||
{ 'status' : NETIF_DRIVER_STATUS_UP,
|
||||
'nr_interfaces' : len(self.devices) })
|
||||
self.writeRequest(msg)
|
||||
self.attach_fe_devices()
|
||||
|
||||
def recv_fe_interface_connect(self, msg, req):
|
||||
val = unpackMsg('netif_fe_interface_connect_t', msg)
|
||||
dev = self.devices[val['handle']]
|
||||
dev.evtchn = channel.eventChannel(0, self.dom)
|
||||
msg = packMsg('netif_be_connect_t',
|
||||
{ 'domid' : self.dom,
|
||||
'netif_handle' : dev.vif,
|
||||
'evtchn' : dev.evtchn['port1'],
|
||||
'tx_shmem_frame' : val['tx_shmem_frame'],
|
||||
'rx_shmem_frame' : val['rx_shmem_frame'] })
|
||||
self.factory.writeRequest(msg)
|
||||
|
||||
def send_interface_connected(self, vif):
|
||||
dev = self.devices[vif]
|
||||
msg = packMsg('netif_fe_interface_status_changed_t',
|
||||
{ 'handle' : dev.vif,
|
||||
'status' : NETIF_INTERFACE_STATUS_CONNECTED,
|
||||
'evtchn' : dev.evtchn['port2'],
|
||||
'mac' : dev.mac })
|
||||
self.writeRequest(msg)
|
||||
|
||||
def send_be_create(self, vif):
|
||||
dev = self.devices[vif]
|
||||
msg = packMsg('netif_be_create_t',
|
||||
{ 'domid' : self.dom,
|
||||
'netif_handle' : dev.vif,
|
||||
'mac' : dev.mac })
|
||||
self.factory.writeRequest(msg)
|
||||
|
||||
def send_be_disconnect(self, vif):
|
||||
dev = self.devices[vif]
|
||||
msg = packMsg('netif_be_disconnect_t',
|
||||
{ 'domid' : self.dom,
|
||||
'netif_handle' : dev.vif })
|
||||
self.factory.writeRequest(msg)
|
||||
|
||||
def send_be_destroy(self, vif):
|
||||
print 'NetifController>send_be_destroy>', 'dom=', self.dom, 'vif=', vif
|
||||
PrettyPrint.prettyprint(self.sxpr())
|
||||
dev = self.devices[vif]
|
||||
del self.devices[vif]
|
||||
msg = packMsg('netif_be_destroy_t',
|
||||
{ 'domid' : self.dom,
|
||||
'netif_handle' : vif })
|
||||
self.factory.writeRequest(msg)
|
||||
@@ -0,0 +1,10 @@
|
||||
# The following parameters could be placed in a configuration file.
|
||||
PID_FILE = '/var/run/xend.pid'
|
||||
LOG_FILE = '/var/log/xend.log'
|
||||
USER = 'root'
|
||||
CONTROL_DIR = '/var/run/xend'
|
||||
MGMT_SOCK = 'xendsock' # relative to CONTROL_DIR
|
||||
EVENT_PORT = 8001
|
||||
|
||||
CONSOLE_PORT_BASE = 9600
|
||||
|
||||
@@ -0,0 +1,580 @@
|
||||
#!/usr/bin/python2
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
"""
|
||||
Input-driven parsing for s-expression (sxp) format.
|
||||
Create a parser: pin = Parser();
|
||||
Then call pin.input(buf) with your input.
|
||||
Call pin.input_eof() when done.
|
||||
Use pin.read() to see if a value has been parsed, pin.get_val()
|
||||
to get a parsed value. You can call ready and get_val at any time -
|
||||
you don't have to wait until after calling input_eof.
|
||||
|
||||
"""
|
||||
from __future__ import generators
|
||||
|
||||
import sys
|
||||
import types
|
||||
import errno
|
||||
import string
|
||||
from StringIO import StringIO
|
||||
|
||||
__all__ = [
|
||||
"mime_type",
|
||||
"ParseError",
|
||||
"Parser",
|
||||
"atomp",
|
||||
"show",
|
||||
"show_xml",
|
||||
"elementp",
|
||||
"name",
|
||||
"attributes",
|
||||
"attribute",
|
||||
"children",
|
||||
"child",
|
||||
"child_at",
|
||||
"child0",
|
||||
"child1",
|
||||
"child2",
|
||||
"child3",
|
||||
"child4",
|
||||
"child_value",
|
||||
"has_id",
|
||||
"with_id",
|
||||
"child_with_id",
|
||||
"elements",
|
||||
"parse",
|
||||
]
|
||||
|
||||
mime_type = "application/sxp"
|
||||
|
||||
escapes = {
|
||||
'a': '\a',
|
||||
'b': '\b',
|
||||
't': '\t',
|
||||
'n': '\n',
|
||||
'v': '\v',
|
||||
'f': '\f',
|
||||
'r': '\r',
|
||||
'\\': '\\',
|
||||
'\'': '\'',
|
||||
'\"': '\"'}
|
||||
|
||||
k_list_open = "("
|
||||
k_list_close = ")"
|
||||
k_attr_open = "@"
|
||||
k_eval = "!"
|
||||
|
||||
escapes_rev = {}
|
||||
for k in escapes:
|
||||
escapes_rev[escapes[k]] = k
|
||||
|
||||
class ParseError(StandardError):
|
||||
|
||||
def __init__(self, parser, value):
|
||||
self.parser = parser
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
class ParserState:
|
||||
|
||||
def __init__(self, fn, parent=None):
|
||||
self.parent = parent
|
||||
self.buf = ''
|
||||
self.val = []
|
||||
self.delim = None
|
||||
self.fn = fn
|
||||
|
||||
def push(self, fn):
|
||||
return ParserState(fn, parent=self)
|
||||
|
||||
class Parser:
|
||||
|
||||
def __init__(self):
|
||||
self.error = sys.stderr
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
self.val = []
|
||||
self.eof = 0
|
||||
self.err = 0
|
||||
self.line_no = 0
|
||||
self.char_no = 0
|
||||
self.state = None
|
||||
|
||||
def push_state(self, fn):
|
||||
self.state = self.state.push(fn)
|
||||
|
||||
def pop_state(self):
|
||||
val = self.state
|
||||
self.state = self.state.parent
|
||||
if self.state and self.state.fn == self.state_start:
|
||||
# Return to start state - produce the value.
|
||||
self.val += self.state.val
|
||||
self.state.val = []
|
||||
return val
|
||||
|
||||
def in_class(self, c, s):
|
||||
return s.find(c) >= 0
|
||||
|
||||
def in_space_class(self, c):
|
||||
return self.in_class(c, ' \t\n\v\f\r')
|
||||
|
||||
def is_separator(self, c):
|
||||
return self.in_class(c, '{}()<>[]!;')
|
||||
|
||||
def in_comment_class(self, c):
|
||||
return self.in_class(c, '#')
|
||||
|
||||
def in_string_quote_class(self, c):
|
||||
return self.in_class(c, '"\'')
|
||||
|
||||
def in_printable_class(self, c):
|
||||
return self.in_class(c, string.printable)
|
||||
|
||||
def set_error_stream(self, error):
|
||||
self.error = error
|
||||
|
||||
def has_error(self):
|
||||
return self.err > 0
|
||||
|
||||
def at_eof(self):
|
||||
return self.eof
|
||||
|
||||
def input_eof(self):
|
||||
self.eof = 1
|
||||
self.input_char(-1)
|
||||
|
||||
def input(self, buf):
|
||||
if not buf or len(buf) == 0:
|
||||
self.input_eof()
|
||||
else:
|
||||
for c in buf:
|
||||
self.input_char(c)
|
||||
|
||||
def input_char(self, c):
|
||||
if self.at_eof():
|
||||
pass
|
||||
elif c == '\n':
|
||||
self.line_no += 1
|
||||
self.char_no = 0
|
||||
else:
|
||||
self.char_no += 1
|
||||
|
||||
if self.state is None:
|
||||
self.begin_start(None)
|
||||
self.state.fn(c)
|
||||
|
||||
def ready(self):
|
||||
return len(self.val) > 0
|
||||
|
||||
def get_val(self):
|
||||
v = self.val[0]
|
||||
self.val = self.val[1:]
|
||||
return v
|
||||
|
||||
def get_all(self):
|
||||
return self.val
|
||||
|
||||
def begin_start(self, c):
|
||||
self.state = ParserState(self.state_start)
|
||||
|
||||
def end_start(self):
|
||||
self.val += self.state.val
|
||||
self.pop_state()
|
||||
|
||||
def state_start(self, c):
|
||||
if self.at_eof():
|
||||
self.end_start()
|
||||
elif self.in_space_class(c):
|
||||
pass
|
||||
elif self.in_comment_class(c):
|
||||
self.begin_comment(c)
|
||||
elif c == k_list_open:
|
||||
self.begin_list(c)
|
||||
elif c == k_list_close:
|
||||
raise ParseError(self, "syntax error: "+c)
|
||||
elif self.in_string_quote_class(c):
|
||||
self.begin_string(c)
|
||||
elif self.in_printable_class(c):
|
||||
self.begin_atom(c)
|
||||
elif c == chr(4):
|
||||
# ctrl-D, EOT: end-of-text.
|
||||
self.input_eof()
|
||||
else:
|
||||
raise ParseError(self, "invalid character: code %d" % ord(c))
|
||||
|
||||
def begin_comment(self, c):
|
||||
self.push_state(self.state_comment)
|
||||
self.state.buf += c
|
||||
|
||||
def end_comment(self):
|
||||
self.pop_state()
|
||||
|
||||
def state_comment(self, c):
|
||||
if c == '\n' or self.at_eof():
|
||||
self.end_comment()
|
||||
else:
|
||||
self.state.buf += c
|
||||
|
||||
def begin_string(self, c):
|
||||
self.push_state(self.state_string)
|
||||
self.state.delim = c
|
||||
|
||||
def end_string(self):
|
||||
val = self.state.buf
|
||||
self.state.parent.val.append(val)
|
||||
self.pop_state()
|
||||
|
||||
def state_string(self, c):
|
||||
if self.at_eof():
|
||||
raise ParseError(self, "unexpected EOF")
|
||||
elif c == self.state.delim:
|
||||
self.end_string()
|
||||
elif c == '\\':
|
||||
self.push_state(self.state_escape)
|
||||
else:
|
||||
self.state.buf += c
|
||||
|
||||
def state_escape(self, c):
|
||||
if self.at_eof():
|
||||
raise ParseError(self, "unexpected EOF")
|
||||
d = escapes.get(c)
|
||||
if d:
|
||||
self.state.parent.buf += d
|
||||
self.pop_state()
|
||||
elif c == 'x':
|
||||
self.state.fn = self.state_hex
|
||||
self.state.val = 0
|
||||
else:
|
||||
self.state.fn = self.state_octal
|
||||
self.state.val = 0
|
||||
self.input_char(c)
|
||||
|
||||
def state_octal(self, c):
|
||||
def octaldigit(c):
|
||||
self.state.val *= 8
|
||||
self.state.val += ord(c) - ord('0')
|
||||
self.state.buf += c
|
||||
if self.state.val < 0 or self.state.val > 0xff:
|
||||
raise ParseError(self, "invalid octal escape: out of range " + self.state.buf)
|
||||
if len(self.state.buf) == 3:
|
||||
octaldone()
|
||||
|
||||
def octaldone():
|
||||
d = chr(self.state.val)
|
||||
self.state.parent.buf += d
|
||||
self.pop_state()
|
||||
|
||||
if self.at_eof():
|
||||
raise ParseError(self, "unexpected EOF")
|
||||
elif '0' <= c <= '7':
|
||||
octaldigit(c)
|
||||
elif len(self.buf):
|
||||
octaldone()
|
||||
self.input_char(c)
|
||||
|
||||
def state_hex(self, c):
|
||||
def hexdone():
|
||||
d = chr(self.state.val)
|
||||
self.state.parent.buf += d
|
||||
self.pop_state()
|
||||
|
||||
def hexdigit(c, d):
|
||||
self.state.val *= 16
|
||||
self.state.val += ord(c) - ord(d)
|
||||
self.state.buf += c
|
||||
if self.state.val < 0 or self.state.val > 0xff:
|
||||
raise ParseError(self, "invalid hex escape: out of range " + self.state.buf)
|
||||
if len(self.state.buf) == 2:
|
||||
hexdone()
|
||||
|
||||
if self.at_eof():
|
||||
raise ParseError(self, "unexpected EOF")
|
||||
elif '0' <= c <= '9':
|
||||
hexdigit(c, '0')
|
||||
elif 'A' <= c <= 'F':
|
||||
hexdigit(c, 'A')
|
||||
elif 'a' <= c <= 'f':
|
||||
hexdigit(c, 'a')
|
||||
elif len(buf):
|
||||
hexdone()
|
||||
self.input_char(c)
|
||||
|
||||
def begin_atom(self, c):
|
||||
self.push_state(self.state_atom)
|
||||
self.state.buf = c
|
||||
|
||||
def end_atom(self):
|
||||
val = self.state.buf
|
||||
self.state.parent.val.append(val)
|
||||
self.pop_state()
|
||||
|
||||
def state_atom(self, c):
|
||||
if self.at_eof():
|
||||
self.end_atom()
|
||||
elif (self.is_separator(c) or
|
||||
self.in_space_class(c) or
|
||||
self.in_comment_class(c)):
|
||||
self.end_atom()
|
||||
self.input_char(c)
|
||||
else:
|
||||
self.state.buf += c
|
||||
|
||||
def begin_list(self, c):
|
||||
self.push_state(self.state_list)
|
||||
|
||||
def end_list(self):
|
||||
val = self.state.val
|
||||
self.state.parent.val.append(val)
|
||||
self.pop_state()
|
||||
|
||||
def state_list(self, c):
|
||||
if self.at_eof():
|
||||
raise ParseError(self, "unexpected EOF")
|
||||
elif c == k_list_close:
|
||||
self.end_list()
|
||||
else:
|
||||
self.state_start(c)
|
||||
|
||||
def atomp(sxpr):
|
||||
if sxpr.isalnum() or sxpr == '@':
|
||||
return 1
|
||||
for c in sxpr:
|
||||
if c in string.whitespace: return 0
|
||||
if c in '"\'\\(){}[]<>$#&%^': return 0
|
||||
if c in string.ascii_letters: continue
|
||||
if c in string.digits: continue
|
||||
if c in '.-_:/~': continue
|
||||
return 0
|
||||
return 1
|
||||
|
||||
def show(sxpr, out=sys.stdout):
|
||||
if isinstance(sxpr, types.ListType):
|
||||
out.write(k_list_open)
|
||||
i = 0
|
||||
for x in sxpr:
|
||||
if i: out.write(' ')
|
||||
show(x, out)
|
||||
i += 1
|
||||
out.write(k_list_close)
|
||||
elif isinstance(sxpr, types.StringType) and atomp(sxpr):
|
||||
out.write(sxpr)
|
||||
else:
|
||||
#out.write("'" + str(sxpr) + "'")
|
||||
out.write(repr(str(sxpr)))
|
||||
|
||||
def show_xml(sxpr, out=sys.stdout):
|
||||
if isinstance(sxpr, types.ListType):
|
||||
element = name(sxpr)
|
||||
out.write('<%s' % element)
|
||||
for attr in attributes(sxpr):
|
||||
out.write(' %s=%s' % (attr[0], attr[1]))
|
||||
out.write('>')
|
||||
i = 0
|
||||
for x in children(sxpr):
|
||||
if i: out.write(' ')
|
||||
show_xml(x, out)
|
||||
i += 1
|
||||
out.write('</%s>' % element)
|
||||
elif isinstance(sxpr, types.StringType) and atomp(sxpr):
|
||||
out.write(sxpr)
|
||||
else:
|
||||
out.write(str(sxpr))
|
||||
|
||||
def elementp(sxpr, elt=None):
|
||||
return (isinstance(sxpr, types.ListType)
|
||||
and len(sxpr)
|
||||
and (None == elt or sxpr[0] == elt))
|
||||
|
||||
def name(sxpr):
|
||||
val = None
|
||||
if isinstance(sxpr, types.StringType):
|
||||
val = sxpr
|
||||
elif isinstance(sxpr, types.ListType) and len(sxpr):
|
||||
val = sxpr[0]
|
||||
return val
|
||||
|
||||
def attributes(sxpr):
|
||||
val = []
|
||||
if isinstance(sxpr, types.ListType) and len(sxpr) > 1:
|
||||
attr = sxpr[1]
|
||||
if elementp(attr, k_attr_open):
|
||||
val = attr[1:]
|
||||
return val
|
||||
|
||||
def attribute(sxpr, key, val=None):
|
||||
for x in attributes(sxpr):
|
||||
if x[0] == key:
|
||||
val = x[1]
|
||||
break
|
||||
return val
|
||||
|
||||
def children(sxpr, elt=None):
|
||||
val = []
|
||||
if isinstance(sxpr, types.ListType) and len(sxpr) > 1:
|
||||
i = 1
|
||||
x = sxpr[i]
|
||||
if elementp(x, k_attr_open):
|
||||
i += 1
|
||||
val = sxpr[i : ]
|
||||
if elt:
|
||||
def iselt(x):
|
||||
return elementp(x, elt)
|
||||
val = filter(iselt, val)
|
||||
return val
|
||||
|
||||
def child(sxpr, elt, val=None):
|
||||
for x in children(sxpr):
|
||||
if elementp(x, elt):
|
||||
val = x
|
||||
break
|
||||
return val
|
||||
|
||||
def child_at(sxpr, index, val=None):
|
||||
kids = children(sxpr)
|
||||
if len(kids) > index:
|
||||
val = kids[index]
|
||||
return val
|
||||
|
||||
def child0(sxpr, val=None):
|
||||
return child_at(sxpr, 0, val)
|
||||
|
||||
def child1(sxpr, val=None):
|
||||
return child_at(sxpr, 1, val)
|
||||
|
||||
def child2(sxpr, val=None):
|
||||
return child_at(sxpr, 2, val)
|
||||
|
||||
def child3(sxpr, val=None):
|
||||
return child_at(sxpr, 3, val)
|
||||
|
||||
def child4(sxpr, val=None):
|
||||
return child_at(sxpr, 4, val)
|
||||
|
||||
def child_value(sxpr, elt, val=None):
|
||||
kid = child(sxpr, elt)
|
||||
if kid:
|
||||
val = child_at(kid, 0, val)
|
||||
return val
|
||||
|
||||
def has_id(sxpr, id):
|
||||
"""Test if an s-expression has a given id.
|
||||
"""
|
||||
return attribute(sxpr, 'id') == id
|
||||
|
||||
def with_id(sxpr, id, val=None):
|
||||
"""Find the first s-expression with a given id, at any depth.
|
||||
|
||||
sxpr s-exp or list
|
||||
id id
|
||||
val value if not found (default None)
|
||||
|
||||
return s-exp or val
|
||||
"""
|
||||
if isinstance(sxpr, types.ListType):
|
||||
for n in sxpr:
|
||||
if has_id(n, id):
|
||||
val = n
|
||||
break
|
||||
v = with_id(n, id)
|
||||
if v is None: continue
|
||||
val = v
|
||||
break
|
||||
return val
|
||||
|
||||
def child_with_id(sxpr, id, val=None):
|
||||
"""Find the first child with a given id.
|
||||
|
||||
sxpr s-exp or list
|
||||
id id
|
||||
val value if not found (default None)
|
||||
|
||||
return s-exp or val
|
||||
"""
|
||||
if isinstance(sxpr, types.ListType):
|
||||
for n in sxpr:
|
||||
if has_id(n, id):
|
||||
val = n
|
||||
break
|
||||
return val
|
||||
|
||||
def elements(sxpr, ctxt=None):
|
||||
"""Generate elements (at any depth).
|
||||
Visit elements in pre-order.
|
||||
Values generated are (node, context)
|
||||
The context is None if there is no parent, otherwise
|
||||
(index, parent, context) where index is the node's index w.r.t its parent,
|
||||
and context is the parent's context.
|
||||
|
||||
sxpr s-exp
|
||||
|
||||
returns generator
|
||||
"""
|
||||
yield (sxpr, ctxt)
|
||||
i = 0
|
||||
for n in children(sxpr):
|
||||
if isinstance(n, types.ListType):
|
||||
# Calling elements() recursively does not generate recursively,
|
||||
# it just returns a generator object. So we must iterate over it.
|
||||
for v in elements(n, (i, sxpr, ctxt)):
|
||||
yield v
|
||||
i += 1
|
||||
|
||||
def to_string(sxpr):
|
||||
"""Convert an sxpr to a string.
|
||||
|
||||
sxpr sxpr
|
||||
returns string
|
||||
"""
|
||||
io = StringIO()
|
||||
show(sxpr, io)
|
||||
io.seek(0)
|
||||
val = io.getvalue()
|
||||
io.close()
|
||||
return val
|
||||
|
||||
def from_string(str):
|
||||
"""Create an sxpr by parsing a string.
|
||||
|
||||
str string
|
||||
returns sxpr
|
||||
"""
|
||||
io = StringIO(str)
|
||||
return parse(io)
|
||||
|
||||
def parse(io):
|
||||
"""Completely parse all input from 'io'.
|
||||
|
||||
io input file object
|
||||
returns list of values, None if incomplete
|
||||
raises ParseError on parse error
|
||||
"""
|
||||
pin = Parser()
|
||||
while 1:
|
||||
buf = io.readline()
|
||||
pin.input(buf)
|
||||
if len(buf) == 0:
|
||||
break
|
||||
if pin.ready():
|
||||
val = pin.get_all()
|
||||
else:
|
||||
val = None
|
||||
return val
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print ">main"
|
||||
pin = Parser()
|
||||
while 1:
|
||||
buf = sys.stdin.read(1024)
|
||||
#buf = sys.stdin.readline()
|
||||
pin.input(buf)
|
||||
while pin.ready():
|
||||
val = pin.get_val()
|
||||
print
|
||||
print '****** val=', val
|
||||
if len(buf) == 0:
|
||||
break
|
||||
|
||||
Whitespace-only changes.
@@ -0,0 +1,366 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
"""Domain creation.
|
||||
"""
|
||||
import string
|
||||
import sys
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import PrettyPrint
|
||||
from xen.xend.XendClient import server
|
||||
|
||||
from xen.xm.opts import *
|
||||
|
||||
gopts = Opts(use="""[options]
|
||||
|
||||
Create a domain.
|
||||
""")
|
||||
|
||||
gopts.opt('help', short='h',
|
||||
fn=set_true, default=0,
|
||||
use="Print this help.")
|
||||
|
||||
gopts.opt('quiet', short='q',
|
||||
fn=set_true, default=0,
|
||||
use="Quiet.")
|
||||
|
||||
gopts.opt('path', val='PATH',
|
||||
fn=set_value, default='.:/etc/xen',
|
||||
use="Search path for default scripts.")
|
||||
|
||||
gopts.opt('defaults', short='f', val='FILE',
|
||||
fn=set_value, default='xmdefaults',
|
||||
use="Use the given default script.")
|
||||
|
||||
gopts.opt('config', short='F', val='FILE',
|
||||
fn=set_value, default=None,
|
||||
use='Domain configuration to use (SXP).')
|
||||
|
||||
gopts.opt('load', short='L', val='FILE',
|
||||
fn=set_value, default=None,
|
||||
use='Domain saved state to load.')
|
||||
|
||||
gopts.opt('define', short='D', val='VAR=VAL',
|
||||
fn=set_var, default=None,
|
||||
use="""Set a variable before loading defaults, e.g. '-D vmid=3'
|
||||
to set vmid. May be repeated to set more thanone variable.""")
|
||||
|
||||
gopts.opt('dryrun', short='n',
|
||||
fn=set_true, default=0,
|
||||
use="Dry run - print the config but don't create the domain.")
|
||||
|
||||
gopts.opt('name', short='N', val='NAME',
|
||||
fn=set_value, default=None,
|
||||
use="Domain name.")
|
||||
|
||||
gopts.opt('console', short='c',
|
||||
fn=set_true, default=0,
|
||||
use="Connect to console after domain is created.")
|
||||
|
||||
gopts.opt('kernel', short='k', val='FILE',
|
||||
fn=set_value, default=None,
|
||||
use="Path to kernel image.")
|
||||
|
||||
gopts.opt('ramdisk', short='r', val='FILE',
|
||||
fn=set_value, default='',
|
||||
use="Path to ramdisk.")
|
||||
|
||||
gopts.opt('builder', short='b', val='FUNCTION',
|
||||
fn=set_value, default='linux',
|
||||
use="Function to use to build the domain.")
|
||||
|
||||
gopts.opt('memory', short='m', val='MEMORY',
|
||||
fn=set_value, default=128,
|
||||
use="Domain memory in MB.")
|
||||
|
||||
gopts.opt('blkif',
|
||||
fn=set_true, default=0,
|
||||
use="Make the domain a block device backend.")
|
||||
|
||||
gopts.opt('netif',
|
||||
fn=set_true, default=0,
|
||||
use="Make the domain a network interface backend.")
|
||||
|
||||
gopts.opt('disk', short='d', val='phy:DEV,VDEV,MODE',
|
||||
fn=append_value, default=[],
|
||||
use="""Add a disk device to a domain. The physical device is DEV, which
|
||||
is exported to the domain as VDEV. The disk is read-only if MODE
|
||||
is 'r', read-write if MODE is 'w'.
|
||||
The option may be repeated to add more than one disk.
|
||||
""")
|
||||
|
||||
gopts.opt('pci', val='BUS,DEV,FUNC',
|
||||
fn=append_value, default=[],
|
||||
use="""Add a PCI device to a domain, using given params (in hex).
|
||||
For example '-pci c0,02,1a'.
|
||||
The option may be repeated to add more than one pci device.
|
||||
""")
|
||||
|
||||
gopts.opt('ipaddr', short='i', val="IPADDR",
|
||||
fn=append_value, default=[],
|
||||
use="Add an IP address to the domain.")
|
||||
|
||||
gopts.opt('vif', val="mac=MAC,bridge=BRIDGE",
|
||||
fn=append_value, default=[],
|
||||
use="""Add a network interface with the given MAC address and bridge.
|
||||
If mac is not specified a random MAC address is used.
|
||||
If bridge is not specified the default bridge is used.
|
||||
This option may be repeated to add more than one vif.
|
||||
Specifying vifs will increase the number of interfaces as needed.
|
||||
""")
|
||||
|
||||
gopts.opt('nics', val="NUM",
|
||||
fn=set_int, default=1,
|
||||
use="""Set the number of network interfaces.
|
||||
Use the vif option to define interface parameters, otherwise
|
||||
defaults are used. Specifying vifs will increase the
|
||||
number of interfaces as needed.
|
||||
""")
|
||||
|
||||
gopts.opt('root', short='R', val='DEVICE',
|
||||
fn=set_value, default='',
|
||||
use="""Set the root= parameter on the kernel command line.
|
||||
Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
|
||||
|
||||
gopts.opt('extra', short='E', val="ARGS",
|
||||
fn=set_value, default='',
|
||||
use="Set extra arguments to append to the kernel command line.")
|
||||
|
||||
gopts.opt('ip', short='I', val='IPADDR',
|
||||
fn=set_value, default='',
|
||||
use="Set the kernel IP interface address.")
|
||||
|
||||
gopts.opt('gateway', val="IPADDR",
|
||||
fn=set_value, default='',
|
||||
use="Set the kernel IP gateway.")
|
||||
|
||||
gopts.opt('netmask', val="MASK",
|
||||
fn=set_value, default = '',
|
||||
use="Set the kernel IP netmask.")
|
||||
|
||||
gopts.opt('hostname', val="NAME",
|
||||
fn=set_value, default='',
|
||||
use="Set the kernel IP hostname.")
|
||||
|
||||
gopts.opt('interface', val="INTF",
|
||||
fn=set_value, default="eth0",
|
||||
use="Set the kernel IP interface name.")
|
||||
|
||||
gopts.opt('dhcp', val="off|dhcp",
|
||||
fn=set_value, default='off',
|
||||
use="Set the kernel dhcp option.")
|
||||
|
||||
gopts.opt('nfs_server', val="IPADDR",
|
||||
fn=set_value, default=None,
|
||||
use="Set the address of the NFS server for NFS root.")
|
||||
|
||||
gopts.opt('nfs_root', val="PATH",
|
||||
fn=set_value, default=None,
|
||||
use="Set the path of the root NFS directory.")
|
||||
|
||||
def strip(pre, s):
|
||||
"""Strip prefix 'pre' if present.
|
||||
"""
|
||||
if s.startswith(pre):
|
||||
return s[len(pre):]
|
||||
else:
|
||||
return s
|
||||
|
||||
def configure_image(config, opts):
|
||||
"""Create the image config.
|
||||
"""
|
||||
config_image = [ opts.builder ]
|
||||
config_image.append([ 'kernel', os.path.abspath(opts.kernel) ])
|
||||
if opts.ramdisk:
|
||||
config_image.append([ 'ramdisk', os.path.abspath(opts.ramdisk) ])
|
||||
if opts.cmdline_ip:
|
||||
cmdline_ip = strip('ip=', opts.cmdline_ip)
|
||||
config_image.append(['ip', cmdline_ip])
|
||||
if opts.root:
|
||||
cmdline_root = strip('root=', opts.root)
|
||||
config_image.append(['root', cmdline_root])
|
||||
if opts.extra:
|
||||
config_image.append(['args', opts.extra])
|
||||
config.append(['image', config_image ])
|
||||
|
||||
def configure_disks(config_devs, opts):
|
||||
"""Create the config for disks (virtual block devices).
|
||||
"""
|
||||
for (uname, dev, mode) in opts.disk:
|
||||
config_vbd = ['vbd',
|
||||
['uname', uname],
|
||||
['dev', dev ],
|
||||
['mode', mode ] ]
|
||||
config_devs.append(['device', config_vbd])
|
||||
|
||||
def configure_pci(config_devs, opts):
|
||||
"""Create the config for pci devices.
|
||||
"""
|
||||
for (bus, dev, func) in opts.pci:
|
||||
config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]]
|
||||
config_devs.append(['device', config_pci])
|
||||
|
||||
def configure_vifs(config_devs, opts):
|
||||
"""Create the config for virtual network interfaces.
|
||||
"""
|
||||
vifs = opts.vif
|
||||
vifs_n = max(opts.nics, len(vifs))
|
||||
|
||||
for idx in range(0, vifs_n):
|
||||
if idx < len(vifs):
|
||||
d = vifs[idx]
|
||||
mac = d.get('mac')
|
||||
bridge = d.get('bridge')
|
||||
else:
|
||||
mac = None
|
||||
bridge = None
|
||||
config_vif = ['vif']
|
||||
if mac:
|
||||
config_vif.append(['mac', mac])
|
||||
if bridge:
|
||||
config_vif.append(['bridge', bridge])
|
||||
config_devs.append(['device', config_vif])
|
||||
|
||||
def configure_vfr(config, opts):
|
||||
if not opts.ipaddr: return
|
||||
config_vfr = ['vfr']
|
||||
idx = 0 # No way of saying which IP is for which vif?
|
||||
for ip in opts.ipaddr:
|
||||
config_vfr.append(['vif', ['id', idx], ['ip', ip]])
|
||||
config.append(config_vfr)
|
||||
|
||||
|
||||
def make_config(opts):
|
||||
"""Create the domain configuration.
|
||||
"""
|
||||
|
||||
config = ['vm',
|
||||
['name', opts.name ],
|
||||
['memory', opts.memory ] ]
|
||||
if opts.cpu:
|
||||
config.append(['cpu', opts.cpu])
|
||||
if opts.blkif:
|
||||
config.append(['backend', ['blkif']])
|
||||
if opts.netif:
|
||||
config.append(['backend', ['netif']])
|
||||
|
||||
configure_image(config, opts)
|
||||
config_devs = []
|
||||
configure_disks(config_devs, opts)
|
||||
configure_pci(config_devs, opts)
|
||||
configure_vifs(config_devs, opts)
|
||||
config += config_devs
|
||||
return config
|
||||
|
||||
def preprocess_disk(opts):
|
||||
if not opts.disk: return
|
||||
disk = []
|
||||
for v in opts.disk:
|
||||
d = v.split(',')
|
||||
if len(d) != 3:
|
||||
opts.err('Invalid disk specifier: ' + v)
|
||||
disk.append(d)
|
||||
opts.disk = disk
|
||||
|
||||
def preprocess_pci(opts):
|
||||
if not opts.pci: return
|
||||
pci = []
|
||||
for v in opts.pci:
|
||||
d = v.split(',')
|
||||
if len(d) != 3:
|
||||
opts.err('Invalid pci specifier: ' + v)
|
||||
# Components are in hex: add hex specifier.
|
||||
hexd = map(lambda v: '0x'+v, d)
|
||||
pci.append(hexd)
|
||||
opts.pci = pci
|
||||
|
||||
def preprocess_vifs(opts):
|
||||
if not opts.vif: return
|
||||
vifs = []
|
||||
for vif in opts.vif:
|
||||
d = {}
|
||||
a = vif.split(',')
|
||||
for b in a:
|
||||
(k, v) = b.strip().split('=')
|
||||
k = k.strip()
|
||||
v = v.strip()
|
||||
if k not in ['mac', 'bridge']:
|
||||
opts.err('Invalid vif specifier: ' + vif)
|
||||
d[k] = v
|
||||
vifs.append(d)
|
||||
opts.vif = vifs
|
||||
|
||||
def preprocess_ip(opts):
|
||||
setip = (opts.hostname or opts.netmask
|
||||
or opts.gateway or opts.dhcp or opts.interface)
|
||||
if not setip: return
|
||||
#if not opts
|
||||
ip = (opts.ip
|
||||
+ ':'
|
||||
+ ':' + opts.gateway
|
||||
+ ':' + opts.netmask
|
||||
+ ':' + opts.hostname
|
||||
+ ':' + opts.interface
|
||||
+ ':' + opts.dhcp)
|
||||
opts.cmdline_ip = ip
|
||||
|
||||
def preprocess_nfs(opts):
|
||||
if (opts.nfs_root or opts.nfs_server):
|
||||
if (not opts.nfs_root) or (not opts.nfs_server):
|
||||
opts.err('Must set nfs root and nfs server')
|
||||
else:
|
||||
return
|
||||
nfs = 'nfsroot=' + opts.nfs_server + ':' + opts.nfs_root
|
||||
opts.extra = nfs + ' ' + opts.extra
|
||||
|
||||
def preprocess(opts):
|
||||
if not opts.kernel:
|
||||
opts.err("No kernel specified")
|
||||
preprocess_disk(opts)
|
||||
preprocess_pci(opts)
|
||||
preprocess_vifs(opts)
|
||||
preprocess_ip(opts)
|
||||
preprocess_nfs(opts)
|
||||
|
||||
def make_domain(opts, config):
|
||||
"""Create, build and start a domain.
|
||||
Returns: [int] the ID of the new domain.
|
||||
"""
|
||||
if opts.vals.load:
|
||||
filename = os.path.abspath(opts.vals.load)
|
||||
dominfo = server.xend_domain_restore(filename, config)
|
||||
else:
|
||||
dominfo = server.xend_domain_create(config)
|
||||
|
||||
dom = int(sxp.child_value(dominfo, 'id'))
|
||||
console_info = sxp.child(dominfo, 'console')
|
||||
if console_info:
|
||||
console_port = int(sxp.child_value(console_info, 'port'))
|
||||
else:
|
||||
console_port = None
|
||||
|
||||
if server.xend_domain_unpause(dom) < 0:
|
||||
server.xend_domain_destroy(dom)
|
||||
opts.err("Failed to start domain %d" % dom)
|
||||
opts.info("Started domain %d, console on port %d"
|
||||
% (dom, console_port))
|
||||
return (dom, console_port)
|
||||
|
||||
def main(argv):
|
||||
opts = gopts
|
||||
args = opts.parse(argv)
|
||||
if opts.vals.help:
|
||||
opts.usage()
|
||||
return
|
||||
if opts.vals.config:
|
||||
pass
|
||||
else:
|
||||
opts.load_defaults()
|
||||
preprocess(opts.vals)
|
||||
config = make_config(opts.vals)
|
||||
if opts.vals.dryrun:
|
||||
PrettyPrint.prettyprint(config)
|
||||
else:
|
||||
make_domain(opts, config)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
||||
@@ -0,0 +1,448 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
"""Grand unified management application for Xen.
|
||||
"""
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
from getopt import getopt
|
||||
|
||||
from xen.xend import PrettyPrint
|
||||
from xen.xend import sxp
|
||||
from xen.xend.XendClient import server
|
||||
from xen.xm import create, shutdown
|
||||
|
||||
class Prog:
|
||||
"""Base class for sub-programs.
|
||||
"""
|
||||
|
||||
"""Program group it belongs to"""
|
||||
group = 'all'
|
||||
"""Program name."""
|
||||
name = '??'
|
||||
"""Short program info."""
|
||||
info = ''
|
||||
|
||||
def __init__(self, xm):
|
||||
self.xm = xm
|
||||
|
||||
def err(self, msg):
|
||||
self.xm.err(msg)
|
||||
|
||||
def help(self, args):
|
||||
self.shortHelp(args)
|
||||
|
||||
def shortHelp(self, args):
|
||||
print "%-14s %s" % (self.name, self.info)
|
||||
|
||||
def main(self, args):
|
||||
"""Program main entry point.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class ProgUnknown(Prog):
|
||||
|
||||
name = 'unknown'
|
||||
info = ''
|
||||
|
||||
def help(self, args):
|
||||
self.xm.err("Unknown command: %s\nTry '%s help' for more information."
|
||||
% (args[0], self.xm.name))
|
||||
|
||||
main = help
|
||||
|
||||
class Xm:
|
||||
"""Main application.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.name = 'xm'
|
||||
self.unknown = ProgUnknown(self)
|
||||
self.progs = {}
|
||||
|
||||
def err(self, msg):
|
||||
print >>sys.stderr, "Error:", msg
|
||||
sys.exit(1)
|
||||
|
||||
def main(self, args):
|
||||
"""Main entry point. Dispatches to the progs.
|
||||
"""
|
||||
self.name = args[0]
|
||||
if len(args) < 2:
|
||||
self.err("Missing command\nTry '%s help' for more information."
|
||||
% self.name)
|
||||
help = self.helparg(args)
|
||||
p = self.getprog(args[1], self.unknown)
|
||||
if help:
|
||||
p.help(args[1:])
|
||||
else:
|
||||
p.main(args[1:])
|
||||
|
||||
def helparg(self, args):
|
||||
for a in args:
|
||||
if a in ['-h', '--help']:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def prog(self, pklass):
|
||||
"""Add a sub-program.
|
||||
|
||||
pklass program class (Prog subclass)
|
||||
"""
|
||||
p = pklass(self)
|
||||
self.progs[p.name] = p
|
||||
return p
|
||||
|
||||
def getprog(self, name, val=None):
|
||||
"""Get a sub-program.
|
||||
"""
|
||||
return self.progs.get(name, val)
|
||||
|
||||
def proglist(self):
|
||||
"""Get a list of sub-programs, ordered by group.
|
||||
"""
|
||||
groups = {}
|
||||
for p in self.progs.values():
|
||||
l = groups.get(p.group, [])
|
||||
l.append(p)
|
||||
groups[p.group] = l
|
||||
kl = groups.keys()
|
||||
kl.sort()
|
||||
pl = []
|
||||
for k in kl:
|
||||
l = groups[k]
|
||||
l.sort()
|
||||
pl += l
|
||||
return pl
|
||||
|
||||
# Create the application object, then add the sub-program classes.
|
||||
xm = Xm()
|
||||
|
||||
class ProgHelp(Prog):
|
||||
|
||||
name = "help"
|
||||
info = "Print help."
|
||||
|
||||
def help(self, args):
|
||||
if len(args) == 2:
|
||||
name = args[1]
|
||||
p = self.xm.getprog(name)
|
||||
if p:
|
||||
p.help(args[1:])
|
||||
else:
|
||||
print '%s: Unknown command: %s' % (self.name, name)
|
||||
else:
|
||||
for p in self.xm.proglist():
|
||||
p.shortHelp(args)
|
||||
print "\nTry '%s help CMD' for help on CMD" % self.xm.name
|
||||
|
||||
main = help
|
||||
|
||||
xm.prog(ProgHelp)
|
||||
|
||||
class ProgCreate(Prog):
|
||||
|
||||
group = 'domain'
|
||||
name = "create"
|
||||
info = """Create a domain."""
|
||||
|
||||
def help(self, args):
|
||||
create.main([args[0], '-h'])
|
||||
|
||||
def main(self, args):
|
||||
create.main(args)
|
||||
|
||||
xm.prog(ProgCreate)
|
||||
|
||||
class ProgSave(Prog):
|
||||
group = 'domain'
|
||||
name = "save"
|
||||
info = """Save domain state (and config) to file."""
|
||||
|
||||
def help(self, args):
|
||||
print args[0], "DOM FILE"
|
||||
print """\nSave domain with id DOM to FILE."""
|
||||
|
||||
def main(self, args):
|
||||
if len(args) < 3: self.err("%s: Missing arguments" % args[0])
|
||||
dom = args[1]
|
||||
savefile = os.path.abspath(args[2])
|
||||
server.xend_domain_save(dom, savefile)
|
||||
|
||||
xm.prog(ProgSave)
|
||||
|
||||
class ProgRestore(Prog):
|
||||
group = 'domain'
|
||||
name = "restore"
|
||||
info = """Create a domain from a saved state."""
|
||||
|
||||
def help(self, args):
|
||||
print args[0], "FILE [CONFIG]"
|
||||
print "\nRestore a domain from FILE using configuration CONFIG."
|
||||
|
||||
def main(self, help, args):
|
||||
if len(args) < 2: self.err("%s: Missing arguments" % args[0])
|
||||
savefile = os.path.abspath(args[1])
|
||||
if len(args) >= 3:
|
||||
configfile = os.path.abspath(args[2])
|
||||
else:
|
||||
configfile = None
|
||||
info = server.xend_domain_restore(savefile, configfile)
|
||||
PrettyPrint.prettyprint(info)
|
||||
|
||||
xm.prog(ProgRestore)
|
||||
|
||||
class ProgList(Prog):
|
||||
group = 'domain'
|
||||
name = "list"
|
||||
info = """List info about domains."""
|
||||
|
||||
short_options = 'l'
|
||||
long_options = ['long']
|
||||
|
||||
def help(self, args):
|
||||
if help:
|
||||
print args[0], '[options] [DOM...]'
|
||||
print """\nGet information about domains.
|
||||
Either all domains or the domains given.
|
||||
|
||||
-l, --long Get more detailed information.
|
||||
"""
|
||||
return
|
||||
|
||||
def main(self, args):
|
||||
use_long = 0
|
||||
(options, params) = getopt(args[1:],
|
||||
self.short_options,
|
||||
self.long_options)
|
||||
n = len(params)
|
||||
for (k, v) in options:
|
||||
if k in ['-l', '--long']:
|
||||
use_long = 1
|
||||
|
||||
if n == 0:
|
||||
doms = map(int, server.xend_domains())
|
||||
doms.sort()
|
||||
else:
|
||||
doms = map(int, params)
|
||||
|
||||
if use_long:
|
||||
self.long_list(doms)
|
||||
else:
|
||||
self.brief_list(doms)
|
||||
|
||||
def brief_list(self, doms):
|
||||
print 'Dom Name Mem(MB) CPU State Time(s)'
|
||||
for dom in doms:
|
||||
info = server.xend_domain(dom)
|
||||
d = {}
|
||||
d['dom'] = int(dom)
|
||||
d['name'] = sxp.child_value(info, 'name', '??')
|
||||
d['mem'] = int(sxp.child_value(info, 'memory', '0'))
|
||||
d['cpu'] = int(sxp.child_value(info, 'cpu', '0'))
|
||||
d['state'] = sxp.child_value(info, 'state', '??')
|
||||
d['cpu_time'] = float(sxp.child_value(info, 'cpu_time', '0'))
|
||||
print ("%(dom)-4d %(name)-16s %(mem)7d %(cpu)3d %(state)5s %(cpu_time)7.1f" % d)
|
||||
|
||||
def long_list(self, doms):
|
||||
for dom in doms:
|
||||
info = server.xend_domain(dom)
|
||||
print '\nDomain %d' % dom
|
||||
PrettyPrint.prettyprint(info)
|
||||
|
||||
xm.prog(ProgList)
|
||||
|
||||
class ProgDestroy(Prog):
|
||||
group = 'domain'
|
||||
name = "destroy"
|
||||
info = """Terminate a domain immediately."""
|
||||
|
||||
def help(self, args):
|
||||
print args[0], 'DOM'
|
||||
print '\nTerminate domain DOM immediately.'
|
||||
|
||||
def main(self, args):
|
||||
if len(args) < 2: self.err("%s: Missing domain" % args[0])
|
||||
dom = args[1]
|
||||
server.xend_domain_destroy(dom)
|
||||
|
||||
xm.prog(ProgDestroy)
|
||||
|
||||
class ProgShutdown(Prog):
|
||||
group = 'domain'
|
||||
name = "shutdown"
|
||||
info = """Shutdown a domain."""
|
||||
|
||||
def help(self, args):
|
||||
shutdown.main([args[0], '-h'])
|
||||
|
||||
def main(self, args):
|
||||
shutdown.main(args)
|
||||
|
||||
xm.prog(ProgShutdown)
|
||||
|
||||
class ProgPause(Prog):
|
||||
group = 'domain'
|
||||
name = "pause"
|
||||
info = """Pause execution of a domain."""
|
||||
|
||||
def help(self, args):
|
||||
print args[0], 'DOM'
|
||||
print '\nPause execution of domain DOM.'
|
||||
|
||||
def main(self, args):
|
||||
if len(args) < 2: self.err("%s: Missing domain" % args[0])
|
||||
dom = args[1]
|
||||
server.xend_domain_pause(dom)
|
||||
|
||||
xm.prog(ProgPause)
|
||||
|
||||
class ProgUnpause(Prog):
|
||||
group = 'domain'
|
||||
name = "unpause"
|
||||
info = """Unpause a paused domain."""
|
||||
|
||||
def help(self, args):
|
||||
print args[0], 'DOM'
|
||||
print '\nUnpause execution of domain DOM.'
|
||||
|
||||
def main(self, args):
|
||||
if len(args) < 2: self.err("%s: Missing domain" % args[0])
|
||||
dom = args[1]
|
||||
server.xend_domain_unpause(dom)
|
||||
|
||||
xm.prog(ProgUnpause)
|
||||
|
||||
class ProgPincpu(Prog):
|
||||
group = 'domain'
|
||||
name = "pincpu"
|
||||
info = """Pin a domain to a cpu. """
|
||||
|
||||
def help(self, args):
|
||||
print args[0],'DOM CPU'
|
||||
print '\nPin domain DOM to cpu CPU.'
|
||||
|
||||
def main(self, args):
|
||||
if len(args) != 3: self.err("%s: Invalid argument(s)" % args[0])
|
||||
v = map(int, args[1:3])
|
||||
server.xend_domain_pincpu(*v)
|
||||
|
||||
xm.prog(ProgPincpu)
|
||||
|
||||
class ProgBvt(Prog):
|
||||
group = 'scheduler'
|
||||
name = "bvt"
|
||||
info = """Set BVT scheduler parameters."""
|
||||
|
||||
def help(self, args):
|
||||
print args[0], "DOM MCUADV WARP WARPL WARPU"
|
||||
print '\nSet Borrowed Virtual Time scheduler parameters.'
|
||||
|
||||
def main(self, args):
|
||||
if len(args) != 6: self.err("%s: Invalid argument(s)" % args[0])
|
||||
v = map(int, args[1:6])
|
||||
server.xend_domain_cpu_bvt_set(*v)
|
||||
|
||||
xm.prog(ProgBvt)
|
||||
|
||||
class ProgBvtslice(Prog):
|
||||
group = 'scheduler'
|
||||
name = "bvtslice"
|
||||
info = """Set the BVT scheduler slice."""
|
||||
|
||||
def help(self, args):
|
||||
print args[0], 'SLICE'
|
||||
print '\nSet Borrowed Virtual Time scheduler slice.'
|
||||
|
||||
def main(self, args):
|
||||
if len(args) < 2: self.err('%s: Missing slice' % args[0])
|
||||
server.xend_node_cpu_bvt_slice_set(slice)
|
||||
|
||||
xm.prog(ProgBvtslice)
|
||||
|
||||
class ProgAtropos(Prog):
|
||||
group = 'scheduler'
|
||||
name= "atropos"
|
||||
info = """Set atropos parameters."""
|
||||
|
||||
def help(self, args):
|
||||
print args[0], "DOM PERIOD SLICE LATENCY XTRATIME"
|
||||
print "\nSet atropos parameters."
|
||||
|
||||
def main(self, args):
|
||||
if len(args) != 5: self.err("%s: Invalid argument(s)" % args[0])
|
||||
v = map(int, args[1:5])
|
||||
server.xend_domain_cpu_atropos_set(*v)
|
||||
|
||||
xm.prog(ProgAtropos)
|
||||
|
||||
class ProgRrobin(Prog):
|
||||
group = 'scheduler'
|
||||
name = "rrobin"
|
||||
info = """Set round robin slice."""
|
||||
|
||||
def help(self, args):
|
||||
print args[0], "SLICE"
|
||||
print "\nSet round robin scheduler slice."
|
||||
|
||||
def main(self, args):
|
||||
if len(args) != 2: self.err("%s: Invalid argument(s)" % args[0])
|
||||
rrslice = int(args[1])
|
||||
server.xend_node_rrobin_set(rrslice)
|
||||
|
||||
xm.prog(ProgRrobin)
|
||||
|
||||
class ProgInfo(Prog):
|
||||
group = 'host'
|
||||
name = "info"
|
||||
info = """Get information about the xen host."""
|
||||
|
||||
def main(self, args):
|
||||
info = server.xend_node()
|
||||
for x in info[1:]:
|
||||
print "%-23s:" % x[0], x[1]
|
||||
|
||||
xm.prog(ProgInfo)
|
||||
|
||||
class ProgConsoles(Prog):
|
||||
group = 'console'
|
||||
name = "consoles"
|
||||
info = """Get information about domain consoles."""
|
||||
|
||||
def main(self, args):
|
||||
l = server.xend_consoles()
|
||||
print "Dom Port Id"
|
||||
for x in l:
|
||||
info = server.xend_console(x)
|
||||
d = {}
|
||||
d['dom'] = sxp.child(info, 'dst', ['dst', '?', '?'])[1]
|
||||
d['port'] = sxp.child_value(info, 'port', '?')
|
||||
d['id'] = sxp.child_value(info, 'id', '?')
|
||||
print "%(dom)3s %(port)4s %(id)3s" % d
|
||||
|
||||
xm.prog(ProgConsoles)
|
||||
|
||||
class ProgConsole(Prog):
|
||||
group = 'console'
|
||||
name = "console"
|
||||
info = """Open a console to a domain."""
|
||||
|
||||
def help(self, args):
|
||||
print "console DOM"
|
||||
print "\nOpen a console to domain DOM."
|
||||
|
||||
def main(self, args):
|
||||
if len(args) < 2: self.err("%s: Missing domain" % args[0])
|
||||
dom = args[1]
|
||||
info = server.xend_domain(dom)
|
||||
console = sxp.child(info, "console")
|
||||
if not console:
|
||||
self.err("No console information")
|
||||
port = sxp.child_value(console, "port")
|
||||
from xen.util import console_client
|
||||
console_client.connect("localhost", int(port))
|
||||
|
||||
xm.prog(ProgConsole)
|
||||
|
||||
def main(args):
|
||||
xm.main(args)
|
||||
@@ -0,0 +1,340 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
"""Object-oriented command-line option support.
|
||||
"""
|
||||
from getopt import getopt
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
import types
|
||||
|
||||
class Opt:
|
||||
"""An individual option.
|
||||
"""
|
||||
def __init__(self, opts, name, short=None, long=None,
|
||||
val=None, fn=None, use=None, default=None):
|
||||
"""Create an option.
|
||||
|
||||
opts parent options object
|
||||
name name of the field it controls
|
||||
short short (1-char) command line switch (optional)
|
||||
long long command-line switch. Defaults to option name.
|
||||
val string used to print option args in help.
|
||||
If val is not specified the option has no arg.
|
||||
fn function to call when the option is specified.
|
||||
use usage (help) string
|
||||
default default value if not specified on command-line
|
||||
"""
|
||||
self.opts = opts
|
||||
self.name = name
|
||||
self.short = short
|
||||
if long is None:
|
||||
long = name
|
||||
self.long = long
|
||||
self.val = val
|
||||
self.use = use
|
||||
self.default = default
|
||||
self.optkeys = []
|
||||
if self.short:
|
||||
self.optkeys.append('-' + self.short)
|
||||
if self.long:
|
||||
self.optkeys.append('--' + self.long)
|
||||
self.fn = fn
|
||||
self.specified_opt = None
|
||||
self.specified_val = None
|
||||
self.value = None
|
||||
self.set(default)
|
||||
|
||||
def __repr__(self):
|
||||
return self.name + '=' + str(self.specified_val)
|
||||
|
||||
__str__ = __repr__
|
||||
|
||||
def set(self, value):
|
||||
"""Set the option value.
|
||||
"""
|
||||
self.opts.setopt(self.name, value)
|
||||
|
||||
def get(self):
|
||||
"""Get the option value.
|
||||
"""
|
||||
return self.opts.getopt(self.name)
|
||||
|
||||
def append(self, value):
|
||||
"""Append a value to the option value.
|
||||
"""
|
||||
v = self.get() or []
|
||||
v.append(value)
|
||||
self.set(v)
|
||||
|
||||
def short_opt(self):
|
||||
"""Short option spec.
|
||||
"""
|
||||
if self.short:
|
||||
if self.val:
|
||||
return self.short + ':'
|
||||
else:
|
||||
return self.short
|
||||
else:
|
||||
return None
|
||||
|
||||
def long_opt(self):
|
||||
"""Long option spec.
|
||||
"""
|
||||
if self.long:
|
||||
if self.val:
|
||||
return self.long + '='
|
||||
else:
|
||||
return self.long
|
||||
else:
|
||||
return None
|
||||
|
||||
def show(self):
|
||||
sep = ''
|
||||
for x in self.optkeys:
|
||||
print sep, x,
|
||||
sep = ','
|
||||
if self.val:
|
||||
print self.val,
|
||||
print
|
||||
if self.use:
|
||||
print '\t',
|
||||
print self.use
|
||||
if self.val:
|
||||
print '\tDefault', self.default or 'None'
|
||||
|
||||
def specify(self, k, v):
|
||||
"""Specify the option. Called when the option is set
|
||||
from the command line.
|
||||
|
||||
k option switch used
|
||||
v optional value given (if any)
|
||||
"""
|
||||
if k in self.optkeys:
|
||||
if self.val is None and v:
|
||||
self.opts.err("Option '%s' does not take a value" % k)
|
||||
self.specified_opt = k
|
||||
self.specified_val = v
|
||||
if self.fn:
|
||||
self.fn(self, k, v)
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def specified(self):
|
||||
"""Test whether the option has been specified: set
|
||||
from the command line.
|
||||
"""
|
||||
return self.specified_opt
|
||||
|
||||
class OptVals:
|
||||
"""Class to hold option values.
|
||||
"""
|
||||
pass
|
||||
|
||||
class Opts:
|
||||
"""Container for options.
|
||||
"""
|
||||
def __init__(self, use=None):
|
||||
"""Options constructor.
|
||||
|
||||
use usage string
|
||||
"""
|
||||
self.use = use
|
||||
# List of options.
|
||||
self.options = []
|
||||
# Options indexed by name.
|
||||
self.options_map = {}
|
||||
# Command-line arguments.
|
||||
self.argv = []
|
||||
# Option values.
|
||||
self.vals = OptVals()
|
||||
self.vals.quiet = 0
|
||||
# Variables for default scripts.
|
||||
self.vars = {}
|
||||
|
||||
def __repr__(self):
|
||||
return '\n'.join(map(str, self.options))
|
||||
|
||||
__str__ = __repr__
|
||||
|
||||
def opt(self, name, **args):
|
||||
"""Add an option.
|
||||
|
||||
name option name
|
||||
**args keyword params for option constructor
|
||||
"""
|
||||
x = Opt(self, name, **args)
|
||||
self.options.append(x)
|
||||
self.options_map[name] = x
|
||||
return x
|
||||
|
||||
def setvar(self, var, val):
|
||||
"""Set a default script variable.
|
||||
"""
|
||||
self.vars[var] = val
|
||||
|
||||
def getvar(self, var):
|
||||
"""Get a default script variable.
|
||||
"""
|
||||
return self.vars.get(var)
|
||||
|
||||
def option(self, name):
|
||||
"""Get an option (object).
|
||||
"""
|
||||
return self.options_map.get(name)
|
||||
|
||||
def setopt(self, name, val):
|
||||
"""Set an option value.
|
||||
An option can also be set using 'opts.vals.name = val'.
|
||||
"""
|
||||
setattr(self.vals, name, val)
|
||||
|
||||
def getopt(self, name):
|
||||
"""Get an option value.
|
||||
An option value can also be got using 'opts.vals.name'.
|
||||
"""
|
||||
getattr(self.vals, name)
|
||||
|
||||
def specified(self, name):
|
||||
"""Test if an option has been specified.
|
||||
"""
|
||||
opt = self.option(name)
|
||||
return opt and opt.specified()
|
||||
|
||||
def err(self, msg):
|
||||
"""Print an error to stderr and exit.
|
||||
"""
|
||||
print >>sys.stderr, "Error:", msg
|
||||
sys.exit(1)
|
||||
|
||||
def info(self, msg):
|
||||
"""Print a message to stdout (unless quiet is set).
|
||||
"""
|
||||
if self.vals.quiet: return
|
||||
print msg
|
||||
|
||||
def warn(self, msg):
|
||||
"""Print a warning to stdout.
|
||||
"""
|
||||
print >>sys.stderr, "Warning:", msg
|
||||
|
||||
def parse(self, argv):
|
||||
"""Parse arguments argv using the options.
|
||||
|
||||
return remaining arguments
|
||||
"""
|
||||
self.argv = argv
|
||||
(vals, args) = getopt(argv[1:], self.short_opts(), self.long_opts())
|
||||
self.args = args
|
||||
for (k, v) in vals:
|
||||
for opt in self.options:
|
||||
if opt.specify(k, v): break
|
||||
else:
|
||||
print >>sys.stderr, "Error: Unknown option:", k
|
||||
self.usage()
|
||||
return args
|
||||
|
||||
def short_opts(self):
|
||||
"""Get short options specifier for getopt.
|
||||
"""
|
||||
l = []
|
||||
for x in self.options:
|
||||
y = x.short_opt()
|
||||
if not y: continue
|
||||
l.append(y)
|
||||
return ''.join(l)
|
||||
|
||||
def long_opts(self):
|
||||
"""Get long options specifier for getopt.
|
||||
"""
|
||||
l = []
|
||||
for x in self.options:
|
||||
y = x.long_opt()
|
||||
if not y: continue
|
||||
l.append(y)
|
||||
return l
|
||||
|
||||
def usage(self):
|
||||
print 'Usage: ', self.argv[0], self.use or 'OPTIONS'
|
||||
for opt in self.options:
|
||||
opt.show()
|
||||
|
||||
def load_defaults(self):
|
||||
"""Load a defaults script. Assumes these options set:
|
||||
'path' search path
|
||||
'default' script name
|
||||
"""
|
||||
for x in [ '' ] + self.vals.path.split(':'):
|
||||
if x:
|
||||
p = os.path.join(x, self.vals.defaults)
|
||||
else:
|
||||
p = self.vals.defaults
|
||||
if os.path.exists(p):
|
||||
self.load(p)
|
||||
break
|
||||
else:
|
||||
self.err("Cannot open defaults file %s" % self.vals.defaults)
|
||||
|
||||
def load(self, defaults, help=0):
|
||||
"""Load a defaults file. Local variables in the file
|
||||
are used to set options with the same names.
|
||||
Variables are not used to set options that are already specified.
|
||||
"""
|
||||
# Create global and lobal dicts for the file.
|
||||
# Initialize locals to the vars.
|
||||
# Use exec to do the standard imports and
|
||||
# define variables we are passing to the script.
|
||||
globals = {}
|
||||
locals = {}
|
||||
locals.update(self.vars)
|
||||
cmd = '\n'.join(["import sys",
|
||||
"import os",
|
||||
"import os.path",
|
||||
"import xen.util.ip",
|
||||
"xm_file = '%s'" % defaults,
|
||||
"xm_help = %d" % help ])
|
||||
exec cmd in globals, locals
|
||||
execfile(defaults, globals, locals)
|
||||
if help: return
|
||||
# Extract the values set by the script and set the corresponding
|
||||
# options, if not set on the command line.
|
||||
vtypes = [ types.StringType,
|
||||
types.ListType,
|
||||
types.IntType,
|
||||
types.FloatType
|
||||
]
|
||||
for (k, v) in locals.items():
|
||||
if self.specified(k): continue
|
||||
if not(type(v) in vtypes): continue
|
||||
self.setopt(k, v)
|
||||
|
||||
def set_true(opt, k, v):
|
||||
"""Set an option true."""
|
||||
opt.set(1)
|
||||
|
||||
def set_false(opt, k, v):
|
||||
"""Set an option false."""
|
||||
opt.set(0)
|
||||
|
||||
def set_value(opt, k, v):
|
||||
"""Set an option to a valoue."""
|
||||
opt.set(v)
|
||||
|
||||
def set_int(opt, k, v):
|
||||
"""Set an option to an integer value."""
|
||||
try:
|
||||
v = int(v)
|
||||
except:
|
||||
opt.opts.err('Invalid value: ' + str(v))
|
||||
opt.set(v)
|
||||
|
||||
def append_value(opt, k, v):
|
||||
"""Append a value to a list option."""
|
||||
opt.append(v)
|
||||
|
||||
def set_var(opt, k, v):
|
||||
"""Set a default script variable.
|
||||
"""
|
||||
(var, val) = v.strip().split('=')
|
||||
opt.opts.setvar(var.strip(), val.strip())
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
"""Domain shutdown.
|
||||
"""
|
||||
import string
|
||||
import sys
|
||||
import time
|
||||
|
||||
from xen.xend.XendClient import server
|
||||
from xen.xm.opts import *
|
||||
|
||||
gopts = Opts(use="""[options] [DOM]
|
||||
|
||||
Shutdown one or more domains gracefully.""")
|
||||
|
||||
gopts.opt('help', short='h',
|
||||
fn=set_true, default=0,
|
||||
use="Print this help.")
|
||||
|
||||
gopts.opt('all', short='a',
|
||||
fn=set_true, default=0,
|
||||
use="Shutdown all domains.")
|
||||
|
||||
gopts.opt('wait', short='w',
|
||||
fn=set_true, default=0,
|
||||
use='Wait for shutdown to complete.')
|
||||
|
||||
gopts.opt('norestart', short='n',
|
||||
fn=set_true, default=0,
|
||||
use='Prevent domain restart.')
|
||||
|
||||
def shutdown(opts, doms, wait):
|
||||
def domains():
|
||||
return [ int(a) for a in server.xend_domains() ]
|
||||
if doms == None: doms = domains()
|
||||
if 0 in doms:
|
||||
doms.remove(0)
|
||||
for d in doms:
|
||||
server.xend_domain_shutdown(d)
|
||||
if wait:
|
||||
while doms:
|
||||
alive = domains()
|
||||
dead = []
|
||||
for d in doms:
|
||||
if d in alive: continue
|
||||
dead.append(d)
|
||||
for d in dead:
|
||||
opts.info("Domain %d terminated" % d)
|
||||
doms.remove(d)
|
||||
time.sleep(1)
|
||||
opts.info("All domains terminated")
|
||||
|
||||
def main_all(opts, args):
|
||||
shutdown(opts, None, opts.vals.wait)
|
||||
|
||||
def main_dom(opts, args):
|
||||
if len(args) < 1: opts.err('Missing domain')
|
||||
dom = args[0]
|
||||
try:
|
||||
domid = int(dom)
|
||||
except:
|
||||
opts.err('Invalid domain: ' + dom)
|
||||
shutdown(opts, [ domid ], opts.vals.wait)
|
||||
|
||||
def main(argv):
|
||||
opts = gopts
|
||||
args = opts.parse(argv)
|
||||
if opts.vals.help:
|
||||
opts.usage()
|
||||
return
|
||||
print 'shutdown.main>', len(args), args
|
||||
if opts.vals.all:
|
||||
main_all(opts, args)
|
||||
else:
|
||||
main_dom(opts, args)
|
||||
|
||||