[PATCHv2 0/7] Support for full function consoles

Motivation: the current libvirt-cim support for consoles is very limited in that it doesn't allow to specify the target and source types supported by libvirt. Having full support of the libvirt console feature is however mandatory for architectures like s390. The current implementation using a Graphics_RASD to represent consoles cannot easily be extended without breaking the existing implementation (or without being entirely unusable). Therefore a new RASD class is added to represent consoles in the model. Since there's no SVPC resource type for a console we use the combination of ResourceType = 1 (Other) and OtherResourceType = "console" for the new RASD instances. In order to be backward compatible, the only console type supported by older libvirt-cim versions (target type 'pty') will still be returned as Graphics_RASD. In the long run, this should be deprecated. V2 Changes: - Squashed old patches 2 and 5 and moved that one to position 4 - Fix double frees - Fix NULL pointer accesses - Improve error handling - Improve error messages - Some cosmetic changes Thilo Boehm (5): schema: New SVPC types for chardev/consoles libxkutil: Console Support RASD: Schema and Provider Support for Console RASDs VSMS: Support for domains with console devices VSMS: add default console Viktor Mihajlovski (2): VSMS: Set resource types for default devices Device: CIM_LogicalDevice for consoles libxkutil/device_parsing.c | 314 ++++++++++++++++++- libxkutil/device_parsing.h | 43 ++- libxkutil/xmlgen.c | 191 ++++++++++- schema/ResourceAllocationSettingData.mof | 246 ++++++++++++++- schema/ResourceAllocationSettingData.registration | 5 +- src/Virt_Device.c | 33 ++ src/Virt_RASD.c | 149 ++++++++- src/Virt_VirtualSystemManagementService.c | 348 +++++++++++++++++++-- src/svpc_types.h | 102 +++++- 9 files changed, 1392 insertions(+), 39 deletions(-) -- 1.7.9.5

The default graphics and input devices were built without their resource types being set correctly. This has not hurted yet. Future changes will however require that the device resource type is matching the actual device type. Signed-off-by: Viktor Mihajlovski <mihajlov@linux.vnet.ibm.com> Reviewed-by: Boris Fiuczynski <fiuczy@linux.vnet.ibm.com> --- src/Virt_VirtualSystemManagementService.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Virt_VirtualSystemManagementService.c b/src/Virt_VirtualSystemManagementService.c index 79dec73..6629b35 100644 --- a/src/Virt_VirtualSystemManagementService.c +++ b/src/Virt_VirtualSystemManagementService.c @@ -581,6 +581,7 @@ static bool default_graphics_device(struct domain *domain) return false; } + domain->dev_graphics->type = CIM_RES_TYPE_GRAPHICS; domain->dev_graphics->dev.graphics.type = strdup("vnc"); domain->dev_graphics->dev.graphics.dev.vnc.port = strdup("-1"); domain->dev_graphics->dev.graphics.dev.vnc.host = strdup("127.0.0.1"); @@ -609,6 +610,7 @@ static bool default_input_device(struct domain *domain) return false; } + domain->dev_input->type = CIM_RES_TYPE_INPUT; domain->dev_input->dev.input.type = strdup("mouse"); if (domain->type == DOMAIN_XENPV) { -- 1.7.9.5

From: Thilo Boehm <tboehm@linux.vnet.ibm.com> C definitions for the console source types, defining the representation of the consoles in the hypervisor host. Includes mapping from and to string representations. Signed-off-by: Thilo Boehm <tboehm@linux.vnet.ibm.com> Signed-off-by: Viktor Mihajlovski <mihajlov@linux.vnet.ibm.com> Reviewed-by: Boris Fiuczynski <fiuczy@linux.vnet.ibm.com> --- src/svpc_types.h | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/src/svpc_types.h b/src/svpc_types.h index 99dd56f..2e4d73f 100644 --- a/src/svpc_types.h +++ b/src/svpc_types.h @@ -1,5 +1,5 @@ /* - * Copyright IBM Corp. 2007 + * Copyright IBM Corp. 2007, 2013 * * Authors: * Dan Smith <danms@us.ibm.com> @@ -167,5 +167,100 @@ enum CIM_op_status { CIM_OP_STATUS_POWER_MODE = 18, }; +/* emum for the Character device Source resource types */ +enum CIM_chardev_source_type { + CIM_CHARDEV_SOURCE_TYPE_NULL = 0, + CIM_CHARDEV_SOURCE_TYPE_VC = 1, + CIM_CHARDEV_SOURCE_TYPE_PTY = 2, + CIM_CHARDEV_SOURCE_TYPE_DEV = 3, + CIM_CHARDEV_SOURCE_TYPE_FILE = 4, + CIM_CHARDEV_SOURCE_TYPE_PIPE = 5, + CIM_CHARDEV_SOURCE_TYPE_STDIO = 6, + CIM_CHARDEV_SOURCE_TYPE_UDP = 7, + CIM_CHARDEV_SOURCE_TYPE_TCP = 8, + CIM_CHARDEV_SOURCE_TYPE_UNIXSOCK = 9, + CIM_CHARDEV_SOURCE_TYPE_SPICEVMC = 10, + /* please insert new source types above */ + CIM_CHARDEV_SOURCE_TYPE_INVALIDTYPE, + CIM_CHARDEV_SOURCE_TYPE_UNKNOWN = 32768, +}; + +static inline int chardev_source_type_StrToID(const char *type_str) +{ + int rc = CIM_CHARDEV_SOURCE_TYPE_UNKNOWN; + + if (type_str == NULL) + return rc; + + if (STREQC(type_str, "null")) + rc = CIM_CHARDEV_SOURCE_TYPE_NULL; + else if (STREQC(type_str, "vc")) + rc = CIM_CHARDEV_SOURCE_TYPE_VC; + else if (STREQC(type_str, "pty")) + rc = CIM_CHARDEV_SOURCE_TYPE_PTY; + else if (STREQC(type_str, "dev")) + rc = CIM_CHARDEV_SOURCE_TYPE_DEV; + else if (STREQC(type_str, "file")) + rc = CIM_CHARDEV_SOURCE_TYPE_FILE; + else if (STREQC(type_str, "pipe")) + rc = CIM_CHARDEV_SOURCE_TYPE_PIPE; + else if (STREQC(type_str, "stdio")) + rc = CIM_CHARDEV_SOURCE_TYPE_STDIO; + else if (STREQC(type_str, "udp")) + rc = CIM_CHARDEV_SOURCE_TYPE_UDP; + else if (STREQC(type_str, "tcp")) + rc = CIM_CHARDEV_SOURCE_TYPE_TCP; + else if (STREQC(type_str, "unix")) + rc = CIM_CHARDEV_SOURCE_TYPE_UNIXSOCK; + else if (STREQC(type_str, "spicevmc")) + rc = CIM_CHARDEV_SOURCE_TYPE_SPICEVMC; + + return rc; +} + +static inline const char* chardev_source_type_IDToStr(int type) +{ + char *type_str = NULL; + + switch (type) + { + case CIM_CHARDEV_SOURCE_TYPE_NULL: + type_str = "null"; + break; + case CIM_CHARDEV_SOURCE_TYPE_VC: + type_str = "vc"; + break; + case CIM_CHARDEV_SOURCE_TYPE_PTY: + type_str = "pty"; + break; + case CIM_CHARDEV_SOURCE_TYPE_DEV: + type_str = "dev"; + break; + case CIM_CHARDEV_SOURCE_TYPE_FILE: + type_str = "file"; + break; + case CIM_CHARDEV_SOURCE_TYPE_PIPE: + type_str = "pipe"; + break; + case CIM_CHARDEV_SOURCE_TYPE_STDIO: + type_str = "stdio"; + break; + case CIM_CHARDEV_SOURCE_TYPE_UDP: + type_str = "udp"; + break; + case CIM_CHARDEV_SOURCE_TYPE_TCP: + type_str = "tcp"; + break; + case CIM_CHARDEV_SOURCE_TYPE_UNIXSOCK: + type_str = "unix"; + break; + case CIM_CHARDEV_SOURCE_TYPE_SPICEVMC: + type_str = "spicevmc"; + break; + default: + break; + } + return type_str; +} #endif -- 1.7.9.5

From: Thilo Boehm <tboehm@linux.vnet.ibm.com> Added data types for the representation of console devices and their source type specific properties. Further, implemented libvirt XML parsing and generation of console device XML. Signed-off-by: Thilo Boehm <tboehm@linux.vnet.ibm.com> Signed-off-by: Viktor Mihajlovski <mihajlov@linux.vnet.ibm.com> Reviewed-by: Boris Fiuczynski <fiuczy@linux.vnet.ibm.com> --- V2 Changes: - avoid double free in the error path - check NULL in CU_DEBUG libxkutil/device_parsing.c | 314 ++++++++++++++++++++++++++++++++++++++++++-- libxkutil/device_parsing.h | 43 +++++- libxkutil/xmlgen.c | 191 ++++++++++++++++++++++++++- src/svpc_types.h | 5 +- 4 files changed, 541 insertions(+), 12 deletions(-) diff --git a/libxkutil/device_parsing.c b/libxkutil/device_parsing.c index fa9f998..ddd7a30 100644 --- a/libxkutil/device_parsing.c +++ b/libxkutil/device_parsing.c @@ -1,5 +1,5 @@ /* - * Copyright IBM Corp. 2007 + * Copyright IBM Corp. 2007, 2013 * * Authors: * Dan Smith <danms@us.ibm.com> @@ -41,6 +41,11 @@ #define NET_XPATH (xmlChar *)"/domain/devices/interface" #define EMU_XPATH (xmlChar *)"/domain/devices/emulator" #define MEM_XPATH (xmlChar *)"/domain/memory | /domain/currentMemory" +#define CONSOLE_XPATH (xmlChar *)"/domain/devices/console" +/* + * To be backward compatible, serial and console is + * still part of the graphics. + */ #define GRAPHICS_XPATH (xmlChar *)"/domain/devices/graphics | "\ "/domain/devices/console | /domain/devices/serial" #define INPUT_XPATH (xmlChar *)"/domain/devices/input" @@ -50,6 +55,11 @@ #define MAX(a,b) (((a)>(b))?(a):(b)) +#define DUP_FIELD(d, s, f) do { \ + if ((s)->f != NULL) \ + (d)->f = strdup((s)->f); \ + } while (0); + /* Device parse function */ typedef int (*dev_parse_func_t)(xmlNode *, struct virt_device **); @@ -133,6 +143,140 @@ static void cleanup_graphics_device(struct graphics_device *dev) free(dev->type); } +static void cleanup_path_device(struct path_device *dev) +{ + if (dev == NULL) + return; + + free(dev->path); + +} + +static void cleanup_unixsock_device(struct unixsock_device *dev) +{ + if (dev == NULL) + return; + + free(dev->path); + free(dev->mode); + +} + +static void cleanup_tcp_device(struct tcp_device *dev) +{ + if (dev == NULL) + return; + + free(dev->mode); + free(dev->protocol); + free(dev->host); + free(dev->service); + +} + +static void cleanup_udp_device(struct udp_device *dev) +{ + if (dev == NULL) + return; + + free(dev->bind_host); + free(dev->bind_service); + free(dev->connect_host); + free(dev->connect_service); +}; + +static void cleanup_console_device(struct console_device *dev) +{ + if (dev == NULL) + return; + + switch (dev->source_type) + { + case CIM_CHARDEV_SOURCE_TYPE_PTY: + cleanup_path_device(&dev->source_dev.pty); + break; + case CIM_CHARDEV_SOURCE_TYPE_DEV: + cleanup_path_device(&dev->source_dev.dev); + break; + case CIM_CHARDEV_SOURCE_TYPE_FILE: + cleanup_path_device(&dev->source_dev.file); + break; + case CIM_CHARDEV_SOURCE_TYPE_PIPE: + cleanup_path_device(&dev->source_dev.pipe); + break; + case CIM_CHARDEV_SOURCE_TYPE_UNIXSOCK: + cleanup_unixsock_device(&dev->source_dev.unixsock); + break; + case CIM_CHARDEV_SOURCE_TYPE_UDP: + cleanup_udp_device(&dev->source_dev.udp); + break; + case CIM_CHARDEV_SOURCE_TYPE_TCP: + cleanup_tcp_device(&dev->source_dev.tcp); + break; + default: + /* Nothing to do for : + CIM_CHARDEV_SOURCE_TYPE_STDIO + CIM_CHARDEV_SOURCE_TYPE_NULL + CIM_CHARDEV_SOURCE_TYPE_VC + CIM_CHARDEV_SOURCE_TYPE_SPICEVMC + */ + break; + } + + dev->source_type = 0; + free(dev->target_type); + memset(&dev->source_dev, 0, sizeof(dev->source_dev)); +}; + +static void console_device_dup(struct console_device *t, + struct console_device *s) +{ + cleanup_console_device(t); + + t->source_type = s->source_type; + DUP_FIELD(t, s, target_type); + + switch (s->source_type) + { + case CIM_CHARDEV_SOURCE_TYPE_PTY: + DUP_FIELD(t, s, source_dev.pty.path); + break; + case CIM_CHARDEV_SOURCE_TYPE_DEV: + DUP_FIELD(t, s, source_dev.dev.path); + break; + case CIM_CHARDEV_SOURCE_TYPE_FILE: + DUP_FIELD(t, s, source_dev.file.path); + break; + case CIM_CHARDEV_SOURCE_TYPE_PIPE: + DUP_FIELD(t, s, source_dev.pipe.path); + break; + case CIM_CHARDEV_SOURCE_TYPE_UNIXSOCK: + DUP_FIELD(t, s, source_dev.unixsock.path); + DUP_FIELD(t, s, source_dev.unixsock.mode); + break; + case CIM_CHARDEV_SOURCE_TYPE_UDP: + DUP_FIELD(t, s, source_dev.udp.bind_host); + DUP_FIELD(t, s, source_dev.udp.bind_service); + DUP_FIELD(t, s, source_dev.udp.connect_host); + DUP_FIELD(t, s, source_dev.udp.connect_service); + break; + case CIM_CHARDEV_SOURCE_TYPE_TCP: + DUP_FIELD(t, s, source_dev.tcp.mode); + DUP_FIELD(t, s, source_dev.tcp.protocol); + DUP_FIELD(t, s, source_dev.tcp.host); + DUP_FIELD(t, s, source_dev.tcp.service); + break; + default: + /* Nothing to do for : + CIM_CHARDEV_SOURCE_TYPE_STDIO + CIM_CHARDEV_SOURCE_TYPE_NULL + CIM_CHARDEV_SOURCE_TYPE_VC + CIM_CHARDEV_SOURCE_TYPE_SPICEVMC + */ + break; + } +} + static void cleanup_input_device(struct input_device *dev) { if (dev == NULL) @@ -157,6 +301,8 @@ void cleanup_virt_device(struct virt_device *dev) cleanup_graphics_device(&dev->dev.graphics); else if (dev->type == CIM_RES_TYPE_INPUT) cleanup_input_device(&dev->dev.input); + else if (dev->type == CIM_RES_TYPE_CONSOLE) + cleanup_console_device(&dev->dev.console); free(dev->id); @@ -613,6 +759,138 @@ static char *get_attr_value_default(xmlNode *node, char *attrname, return ret; } +static int parse_console_device(xmlNode *node, struct virt_device **vdevs) +{ + struct virt_device *vdev = NULL; + struct console_device *cdev = NULL; + char *source_type_str = NULL; + char *target_port_ID = NULL; + char *udp_source_mode = NULL; + + xmlNode *child = NULL; + + vdev = calloc(1, sizeof(*vdev)); + if (vdev == NULL) + goto err; + + cdev = &(vdev->dev.console); + + source_type_str = get_attr_value(node, "type"); + if (source_type_str == NULL) + goto err; + CU_DEBUG("console device type = %s", source_type_str ? : "NULL"); + + cdev->source_type = chardev_source_type_StrToID(source_type_str); + if (cdev->source_type == CIM_CHARDEV_SOURCE_TYPE_UNKNOWN) + goto err; + + CU_DEBUG("console device type ID = %d", cdev->source_type); + + for (child = node->children; child != NULL; child = child->next) { + if (XSTREQ(child->name, "target")) { + cdev->target_type = get_attr_value(child, "type"); + CU_DEBUG("Console device target type = '%s'", + cdev->target_type ? : "NULL"); + target_port_ID = get_attr_value(child, "port"); + if (target_port_ID == NULL) + goto err; + } + + if (XSTREQ(child->name, "source")) { + switch (cdev->source_type) + { + case CIM_CHARDEV_SOURCE_TYPE_PTY: + cdev->source_dev.pty.path = + get_attr_value(child, "path"); + break; + case CIM_CHARDEV_SOURCE_TYPE_DEV: + cdev->source_dev.dev.path = + get_attr_value(child, "path"); + break; + case CIM_CHARDEV_SOURCE_TYPE_FILE: + cdev->source_dev.file.path = + get_attr_value(child, "path"); + break; + case CIM_CHARDEV_SOURCE_TYPE_PIPE: + cdev->source_dev.pipe.path = + get_attr_value(child, "path"); + break; + case CIM_CHARDEV_SOURCE_TYPE_UNIXSOCK: + cdev->source_dev.unixsock.mode = + get_attr_value(child, "mode"); + cdev->source_dev.unixsock.path = + get_attr_value(child, "path"); + break; + case CIM_CHARDEV_SOURCE_TYPE_UDP: + udp_source_mode = get_attr_value(child, "mode"); + if (udp_source_mode == NULL) + goto err; + if (STREQC(udp_source_mode, "bind")) { + cdev->source_dev.udp.bind_host = + get_attr_value(child, "host"); + cdev->source_dev.udp.bind_service = + get_attr_value(child, "service"); + } else if (STREQC(udp_source_mode, "connect")) { + cdev->source_dev.udp.connect_host = + get_attr_value(child, "host"); + cdev->source_dev.udp.connect_service = + get_attr_value(child, "service"); + } else { + CU_DEBUG("unknown udp mode: %s", + udp_source_mode ? : "NULL"); + goto err; + } + break; + case CIM_CHARDEV_SOURCE_TYPE_TCP: + cdev->source_dev.tcp.mode = + get_attr_value(child, "mode"); + cdev->source_dev.tcp.host = + get_attr_value(child, "host"); + cdev->source_dev.tcp.service = + get_attr_value(child, "service"); + break; + + default: + /* Nothing to do for : + CIM_CHARDEV_SOURCE_TYPE_STDIO + CIM_CHARDEV_SOURCE_TYPE_NULL + CIM_CHARDEV_SOURCE_TYPE_VC + CIM_CHARDEV_SOURCE_TYPE_SPICEVMC + */ + break; + } + } + if ((cdev->source_type == CIM_CHARDEV_SOURCE_TYPE_TCP) + && XSTREQ(child->name, "protocol")) { + cdev->source_dev.tcp.protocol = + get_attr_value(child, "type"); + } + } + + vdev->type = CIM_RES_TYPE_CONSOLE; + + if (asprintf(&vdev->id, "charconsole:%s", target_port_ID) == -1) { + CU_DEBUG("Failed to create charconsole id string"); + goto err; + } + + *vdevs = vdev; + free(source_type_str); + free(target_port_ID); + free(udp_source_mode); + + return 1; + + err: + free(source_type_str); + free(target_port_ID); + free(udp_source_mode); + cleanup_console_device(cdev); + free(vdev); + + return 0; +} + static int parse_graphics_device(xmlNode *node, struct virt_device **vdevs) { struct virt_device *vdev = NULL; @@ -664,8 +942,20 @@ static int parse_graphics_device(xmlNode *node, struct virt_device **vdevs) child = child->next) { if (XSTREQ(child->name, "source")) gdev->dev.vnc.host = get_attr_value(child, "path"); - else if (XSTREQ(child->name, "target")) - gdev->dev.vnc.port = get_attr_value(child, "port"); + else if (XSTREQ(child->name, "target")) { + gdev->dev.vnc.port = + get_attr_value(child, "port"); + /* The graphics pty console can only be a + virtio console. If 'type' is not set in the + xml, the default of libvirt is virtio.*/ + char *t_type = get_attr_value(child, "type"); + if (t_type != NULL && !STREQC(t_type, "virtio")) { + CU_DEBUG("Not a pty-virtio graphics console"); + free(t_type); + goto err; + } + free(t_type); + } } } else { @@ -841,6 +1131,11 @@ static int parse_devices(const char *xml, struct virt_device **_list, int type) func = &parse_graphics_device; break; + case CIM_RES_TYPE_CONSOLE: + xpathstr = CONSOLE_XPATH; + func = &parse_console_device; + break; + case CIM_RES_TYPE_INPUT: xpathstr = INPUT_XPATH; func = &parse_input_device; @@ -876,11 +1171,6 @@ static int parse_devices(const char *xml, struct virt_device **_list, int type) return count; } -#define DUP_FIELD(d, s, f) do { \ - if ((s)->f != NULL) \ - (d)->f = strdup((s)->f); \ - } while (0); - struct virt_device *virt_device_dup(struct virt_device *_dev) { struct virt_device *dev; @@ -939,8 +1229,10 @@ struct virt_device *virt_device_dup(struct virt_device *_dev) } else if (dev->type == CIM_RES_TYPE_INPUT) { DUP_FIELD(dev, _dev, dev.input.type); DUP_FIELD(dev, _dev, dev.input.bus); + } else if (dev->type == CIM_RES_TYPE_CONSOLE) { + console_device_dup(&dev->dev.console, + &_dev->dev.console); } - return dev; } @@ -1299,6 +1591,9 @@ int get_dominfo_from_xml(const char *xml, struct domain **dominfo) (*dominfo)->dev_graphics_ct = parse_devices(xml, &(*dominfo)->dev_graphics, CIM_RES_TYPE_GRAPHICS); + (*dominfo)->dev_console_ct = parse_devices(xml, + &(*dominfo)->dev_console, + CIM_RES_TYPE_CONSOLE); (*dominfo)->dev_input_ct = parse_devices(xml, &(*dominfo)->dev_input, CIM_RES_TYPE_INPUT); @@ -1396,6 +1691,7 @@ void cleanup_dominfo(struct domain **dominfo) cleanup_virt_devices(&dom->dev_vcpu, dom->dev_vcpu_ct); cleanup_virt_devices(&dom->dev_graphics, dom->dev_graphics_ct); cleanup_virt_devices(&dom->dev_input, dom->dev_input_ct); + cleanup_virt_devices(&dom->dev_console, dom->dev_console_ct); free(dom); diff --git a/libxkutil/device_parsing.h b/libxkutil/device_parsing.h index 14e49b8..2803d6a 100644 --- a/libxkutil/device_parsing.h +++ b/libxkutil/device_parsing.h @@ -1,5 +1,5 @@ /* - * Copyright IBM Corp. 2007 + * Copyright IBM Corp. 2007, 2013 * * Authors: * Dan Smith <danms@us.ibm.com> @@ -111,6 +111,43 @@ struct graphics_device { } dev; }; +struct path_device { + char *path; +}; + +struct unixsock_device { + char *path; + char *mode; +}; + +struct tcp_device { + char *mode; + char *protocol; + char *host; + char *service; +}; + +struct udp_device { + char *bind_host; + char *bind_service; + char *connect_host; + char *connect_service; +}; + +struct console_device { + uint16_t source_type; + union { + struct path_device file; + struct path_device pty; + struct path_device dev; + struct path_device pipe; + struct unixsock_device unixsock; + struct tcp_device tcp; + struct udp_device udp; + } source_dev; + char *target_type; +}; + struct input_device { char *type; char *bus; @@ -125,6 +162,7 @@ struct virt_device { struct vcpu_device vcpu; struct emu_device emu; struct graphics_device graphics; + struct console_device console; struct input_device input; } dev; char *id; @@ -182,6 +220,9 @@ struct domain { struct virt_device *dev_graphics; int dev_graphics_ct; + struct virt_device *dev_console; + int dev_console_ct; + struct virt_device *dev_emu; struct virt_device *dev_input; diff --git a/libxkutil/xmlgen.c b/libxkutil/xmlgen.c index 2ca2341..45bfb04 100644 --- a/libxkutil/xmlgen.c +++ b/libxkutil/xmlgen.c @@ -1,5 +1,5 @@ /* - * Copyright IBM Corp. 2007 + * Copyright IBM Corp. 2007, 2013 * * Authors: * Dan Smith <danms@us.ibm.com> @@ -42,6 +42,189 @@ typedef const char *(*devfn_t)(xmlNodePtr node, struct domain *dominfo); typedef const char *(*poolfn_t)(xmlNodePtr node, struct virt_pool *pool); typedef const char *(*resfn_t)(xmlNodePtr node, struct virt_pool_res *res); +static int _count_graphics_console_definitions(struct domain *dominfo) +{ + int i; + int num = 0; + + for (i = 0; i < dominfo->dev_graphics_ct; i++) { + struct virt_device *_dev = &dominfo->dev_graphics[i]; + if (_dev->type == CIM_RES_TYPE_UNKNOWN) + continue; + + struct graphics_device *dev = &_dev->dev.graphics; + + if (STREQC(dev->type, "console")) { + num++; + } + } + CU_DEBUG("Found %d console defintions in graphics devices.",num); + return num; + +} + +static const char *console_xml(xmlNodePtr root, struct domain *dominfo) +{ + int i; + xmlNodePtr console; + xmlNodePtr tmp; + int num_graphics_consol_def = 0; + int num_suppressed_console_def = 0; + + num_graphics_consol_def = _count_graphics_console_definitions(dominfo); + + for (i = 0; i < dominfo->dev_console_ct; i++) { + struct virt_device *_dev = &dominfo->dev_console[i]; + if (_dev->type == CIM_RES_TYPE_UNKNOWN) + continue; + + struct console_device *cdev = &_dev->dev.console; + + /* Due to backward compatibility, the graphics device handling + is still parsing consoles: + source = pty, target = virtio (which is the default target) + But the console device handling processes these kind of + consoles too. This would lead to a duplication of these + default consoles in the domain xml definition. + This code prevents the console handling of writing xml for + duplicate pty/virtio consoles which are written by the + graphics device handling. */ + if (cdev->source_type == CIM_CHARDEV_SOURCE_TYPE_PTY && + (cdev->target_type == NULL || + STREQC(cdev->target_type, "virtio"))) { + if (num_suppressed_console_def < + num_graphics_consol_def) { + num_suppressed_console_def++; + continue; + } + } + + console = xmlNewChild(root, NULL, BAD_CAST "console", NULL); + if (console == NULL) + return XML_ERROR; + + xmlNewProp(console, BAD_CAST "type", + BAD_CAST + chardev_source_type_IDToStr(cdev->source_type)); + + switch (cdev->source_type) { + case CIM_CHARDEV_SOURCE_TYPE_PTY: + /* The path property is not mandatory */ + if (cdev->source_dev.pty.path) { + tmp = xmlNewChild(console, NULL, + BAD_CAST "source", NULL); + if (tmp == NULL) + return XML_ERROR; + xmlNewProp(tmp, BAD_CAST "path", + BAD_CAST cdev->source_dev.pty.path); + } + break; + case CIM_CHARDEV_SOURCE_TYPE_DEV: + tmp = xmlNewChild(console, NULL, + BAD_CAST "source", NULL); + if (tmp == NULL) + return XML_ERROR; + xmlNewProp(tmp, BAD_CAST "path", + BAD_CAST cdev->source_dev.dev.path); + break; + case CIM_CHARDEV_SOURCE_TYPE_FILE: + tmp = xmlNewChild(console, NULL, + BAD_CAST "source", NULL); + if (tmp == NULL) + return XML_ERROR; + xmlNewProp(tmp, BAD_CAST "path", + BAD_CAST cdev->source_dev.file.path); + break; + case CIM_CHARDEV_SOURCE_TYPE_PIPE: + tmp = xmlNewChild(console, NULL, + BAD_CAST "source", NULL); + if (tmp == NULL) + return XML_ERROR; + xmlNewProp(tmp, BAD_CAST "path", + BAD_CAST cdev->source_dev.pipe.path); + break; + case CIM_CHARDEV_SOURCE_TYPE_UNIXSOCK: + tmp = xmlNewChild(console, NULL, + BAD_CAST "source", NULL); + if (tmp == NULL) + return XML_ERROR; + xmlNewProp(tmp, BAD_CAST "mode", + BAD_CAST cdev->source_dev.unixsock.mode); + xmlNewProp(tmp, BAD_CAST "path", + BAD_CAST cdev->source_dev.unixsock.path); + break; + case CIM_CHARDEV_SOURCE_TYPE_UDP: + tmp = xmlNewChild(console, NULL, + BAD_CAST "source", NULL); + if (tmp == NULL) + return XML_ERROR; + xmlNewProp(tmp, BAD_CAST "mode", BAD_CAST "bind"); + xmlNewProp(tmp, BAD_CAST "host", + BAD_CAST cdev->source_dev.udp.bind_host); + /* The service property is not mandatory */ + if (cdev->source_dev.udp.bind_service) + xmlNewProp(tmp, BAD_CAST "service", + BAD_CAST + cdev->source_dev.udp.bind_service); + + tmp = xmlNewChild(console, NULL, + BAD_CAST "source", NULL); + if (tmp == NULL) + return XML_ERROR; + xmlNewProp(tmp, BAD_CAST "mode", BAD_CAST "connect"); + xmlNewProp(tmp, BAD_CAST "host", + BAD_CAST cdev->source_dev.udp.connect_host); + /* The service property is not mandatory */ + if (cdev->source_dev.udp.connect_service) + xmlNewProp(tmp, BAD_CAST "service", + BAD_CAST + cdev->source_dev.udp.connect_service); + + break; + case CIM_CHARDEV_SOURCE_TYPE_TCP: + tmp = xmlNewChild(console, NULL, + BAD_CAST "source", NULL); + if (tmp == NULL) + return XML_ERROR; + xmlNewProp(tmp, BAD_CAST "mode", + BAD_CAST cdev->source_dev.tcp.mode); + xmlNewProp(tmp, BAD_CAST "host", + BAD_CAST cdev->source_dev.tcp.host); + if (cdev->source_dev.tcp.service) + xmlNewProp(tmp, BAD_CAST "service", + BAD_CAST + cdev->source_dev.tcp.service); + if (cdev->source_dev.tcp.protocol) { + tmp = xmlNewChild(console, NULL, + BAD_CAST "protocol", NULL); + if (tmp == NULL) + return XML_ERROR; + xmlNewProp(tmp, BAD_CAST "type", + BAD_CAST cdev->source_dev.tcp.protocol); + } + break; + default: + /* Nothing to do for : + CIM_CHARDEV_SOURCE_TYPE_STDIO + CIM_CHARDEV_SOURCE_TYPE_NULL + CIM_CHARDEV_SOURCE_TYPE_VC + CIM_CHARDEV_SOURCE_TYPE_SPICEVMC + */ + break; + } + + if (cdev->target_type) { + tmp = xmlNewChild(console, NULL, + BAD_CAST "target", NULL); + if (tmp == NULL) + return XML_ERROR; + xmlNewProp(tmp, BAD_CAST "type", + BAD_CAST cdev->target_type); + } + } + return NULL; +} + static char *disk_block_xml(xmlNodePtr root, struct disk_device *dev) { xmlNodePtr disk; @@ -977,6 +1160,11 @@ char *device_to_xml(struct virt_device *_dev) dominfo->dev_graphics_ct = 1; dominfo->dev_graphics = dev; break; + case CIM_RES_TYPE_CONSOLE: + func = console_xml; + dominfo->dev_console_ct = 1; + dominfo->dev_console = dev; + break; case CIM_RES_TYPE_INPUT: func = input_xml; dominfo->dev_input_ct = 1; @@ -1017,6 +1205,7 @@ char *system_to_xml(struct domain *dominfo) &disk_xml, &net_xml, &input_xml, + &console_xml, &graphics_xml, &emu_xml, NULL diff --git a/src/svpc_types.h b/src/svpc_types.h index 2e4d73f..0f46a86 100644 --- a/src/svpc_types.h +++ b/src/svpc_types.h @@ -25,6 +25,7 @@ #define CIM_OPERATIONAL_STATUS 2 #define CIM_RES_TYPE_ALL 0 +#define CIM_RES_TYPE_OTHER 1 #define CIM_RES_TYPE_PROC 3 #define CIM_RES_TYPE_MEM 4 #define CIM_RES_TYPE_NET 10 @@ -34,8 +35,9 @@ #define CIM_RES_TYPE_INPUT 13 #define CIM_RES_TYPE_UNKNOWN 1000 #define CIM_RES_TYPE_IMAGE 32768 +#define CIM_RES_TYPE_CONSOLE 32769 -#define CIM_RES_TYPE_COUNT 6 +#define CIM_RES_TYPE_COUNT 7 const static int cim_res_types[CIM_RES_TYPE_COUNT] = {CIM_RES_TYPE_NET, CIM_RES_TYPE_DISK, @@ -43,6 +45,7 @@ const static int cim_res_types[CIM_RES_TYPE_COUNT] = CIM_RES_TYPE_PROC, CIM_RES_TYPE_GRAPHICS, CIM_RES_TYPE_INPUT, + CIM_RES_TYPE_CONSOLE, }; #define CIM_VSSD_RECOVERY_NONE 2 -- 1.7.9.5

From: Thilo Boehm <tboehm@linux.vnet.ibm.com> Representing console devices was very limited with the previous approach using a special graphical display. For instance it was not possible to define different target types (serial, virtio, sclp) nor to exploit all source types (pty, file, tcp, ...) available in libvirt. With the following new RASD classes it is possible to define real console resources: KVM_ConsoleResouceAllocationSettingData Xen_ConsoleResouceAllocationSettingData LXC_ConsoleResouceAllocationSettingData The ResourceType of the returned instances is is '1' (Other) and the OtherResourceType is 'console'. Implemented CIM operations: enumerate instances enumerate instance names get instance Signed-off-by: Thilo Boehm <tboehm@linux.vnet.ibm.com> Signed-off-by: Viktor Mihajlovski <mihajlov@linux.vnet.ibm.com> Reviewed-by: Boris Fiuczynski <fiuczy@linux.vnet.ibm.com> --- V2 Changes - squash schema and provider patch into one - check for NULL strings before using STREQC - reformat comparisons where the constant is on the left hand side schema/ResourceAllocationSettingData.mof | 246 ++++++++++++++++++++- schema/ResourceAllocationSettingData.registration | 5 +- src/Virt_RASD.c | 149 ++++++++++++- 3 files changed, 397 insertions(+), 3 deletions(-) diff --git a/schema/ResourceAllocationSettingData.mof b/schema/ResourceAllocationSettingData.mof index 871ab04..ebc4806 100644 --- a/schema/ResourceAllocationSettingData.mof +++ b/schema/ResourceAllocationSettingData.mof @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2007 +// Copyright IBM Corp. 2007, 2013 [Description ("Xen virtual disk configuration"), Provider("cmpi::Virt_RASD") @@ -445,3 +445,247 @@ class KVM_StorageVolumeResourceAllocationSettingData : KVM_ResourceAllocationSet string AllocationUnits; }; +[Description ("KVM virtual character device"), + Provider("cmpi::Virt_RASD") +] +class KVM_CharacterResourceAllocationSettingData : KVM_ResourceAllocationSettingData +{ + [Description ("The type of resource in the source/host environment."), + ValueMap {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}, + Values {"null", "vc", "pty", "dev", "file", "pipe", + "stdio", "udp", "tcp", "unix", "spicevmc"}, + ModelCorrespondence {"KVM_CharacterResourceAllocationSettingData.SourcePath", + "KVM_CharacterResourceAllocationSettingData.ConnectURL", + "KVM_CharacterResourceAllocationSettingData.BindURL"}] + uint16 SourceType; + + [Description ("If SourceType=4 ('file'),this is the full qualified file path. " + "The file is opened and all data sent to the character device " + "is written to the file. " + "If SourceType=2 ('pty'),this is the full qualified Pseudo TTY path. " + "A Pseudo TTY is allocated using /dev/ptmx. " + "If SourceType=3 ('dev'), this is the full qualified file path " + "to the underlying phsical character device. The device types must " + "match, eg the emulated serial port should only be connected to a " + "host serial port - don't connect a serial port to a parallel port. " + "If SourceType=5 ('pipe'), this is the full qualified file path " + "of a named pipe."), + ModelCorrespondence {"KVM_CharacterResourceAllocationSettingData.SourceType"}] + string SourcePath; + + [Description ("This URL describes the connection to a remote or local location " + "where the character devices acts as a client." + "To use a literal IPv6 address in the URI, the literal address should be " + "enclosed in '[' and ']' characters. " + "If SourceType=7 ('udp'), this is defines a udp remote host and port connection " + "to send packages. That the character device acts as a UDP netconsole service, " + "sending and receiving packets, the BindURL property must also be defined. " + "This is a lossy service. " + "Only 'udp' is valid for the protocol part of the URI. " + "Format of the URL: <protocol>://<host>:<port>. e.g. udp://0.0.0.0:2245 " + "If SourceType=8 ('tcp'), this is defines a remote host and port connection. " + "The protocol part of the URI can be: 'raw', 'telnet', 'telnets', 'tls'. " + "Format of the URL: <protocol>://<host>:<port>. e.g. raw://[3ffe:2a00:100:7031::1]:2245 " + "If SourceType=9 ('unix'), this is defined the full qualified file path " + "of a Unix domain socket. Only 'file' is valid for the protocol part of the URI. " + "Format of the URL: file://<full qualified file path>. " + "e.g. file:///tmp/console-out"), + ModelCorrespondence {"KVM_CharacterResourceAllocationSettingData.SourceType"}] + string ConnectURL; + + [Description ("This URL describes the connection to a remote or local location " + "where the character devices acts as a server. " + "To use a literal IPv6 address in the URI, the literal address should be " + "enclosed in '[' and ']' characters. " + "If SourceType=7 ('udp'), this is defines a udp remote host and port connection " + "to receive packages. That the character device acts as a UDP netconsole service, " + "sending and receiving packets, the ConnectURL property must also be defined. " + "This is a lossy service. " + "Only 'udp' is valid for the protocol part of the URI. " + "Format of the URL: <protocol>://<host>:<port>. e.g. udp://0.0.0.0:2245 " + "If SourceType=8 ('tcp'), this is defines a remote host and port connection. " + "The protocol part of the URI can be: 'raw', 'telnet', 'telnets', 'tls'. " + "Format of the URL: <protocol>://<host>:<port>. e.g. raw://[3ffe:2a00:100:7031::1]:2245 " + "If SourceType=9 ('unix'), this is defined the full qualified file path " + "of a Unix domain socket. Only 'file' is valid for the protocol part of the URI. " + "Format of the URL: file://<full qualified file path>. " + "e.g. file:///tmp/console-in"), + ModelCorrespondence {"KVM_CharacterResourceAllocationSettingData.SourceType"}] + string BindURL; +}; + + +[Description ("KVM virtual console device. It is identified by: " + "CIM_ResourceAllocationSettingData.ResourceType=1 ( 'Other' ) and " + "CIM_ResourceAllocationSettingData.OtherResourceType='console'"), + Provider("cmpi::Virt_RASD") +] +class KVM_ConsoleResourceAllocationSettingData : KVM_CharacterResourceAllocationSettingData +{ + [Description ("The type of the console in the target/guest environment.")] + string TargetType; +}; + + + +[Description ("Xen virtual character device"), + Provider("cmpi::Virt_RASD") +] +class Xen_CharacterResourceAllocationSettingData : Xen_ResourceAllocationSettingData +{ + [Description ("The type of resource in the source/host environment."), + ValueMap {"0","1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}, + Values {"null", "vc", "pty", "dev", "file", "pipe", + "stdio", "udp", "tcp", "unix", "spicevmc"}, + ModelCorrespondence {"Xen_CharacterResourceAllocationSettingData.SourcePath", + "Xen_CharacterResourceAllocationSettingData.ConnectURL", + "Xen_CharacterResourceAllocationSettingData.BindURL"}] + uint16 SourceType; + + [Description ("If SourceType=4 ('file'),this is the full qualified file path. " + "The file is opened and all data sent to the character device " + "is written to the file. " + "If SourceType=2 ('pty'),this is the full qualified Pseudo TTY path. " + "A Pseudo TTY is allocated using /dev/ptmx. " + "If SourceType=3 ('dev'), this is the full qualified file path " + "to the underlying phsical character device. The device types must " + "match, eg the emulated serial port should only be connected to a " + "host serial port - don't connect a serial port to a parallel port. " + "If SourceType=5 ('pipe'), this is the full qualified file path " + "of a named pipe."), + ModelCorrespondence {"Xen_CharacterResourceAllocationSettingData.SourceType"}] + string SourcePath; + + [Description ("This URL describes the connection to a remote or local location " + "where the character devices acts as a client." + "To use a literal IPv6 address in the URI, the literal address should be " + "enclosed in '[' and ']' characters. " + "If SourceType=7 ('udp'), this is defines a udp remote host and port connection " + "to send packages. That the character device acts as a UDP netconsole service, " + "sending and receiving packets, the BindURL property must also be defined. " + "This is a lossy service. " + "Only 'udp' is valid for the protocol part of the URI. " + "Format of the URL: <protocol>://<host>:<port>. e.g. udp://0.0.0.0:2245 " + "If SourceType=8 ('tcp'), this is defines a remote host and port connection. " + "The protocol part of the URI can be: 'raw', 'telnet', 'telnets', 'tls'. " + "Format of the URL: <protocol>://<host>:<port>. e.g. raw://[3ffe:2a00:100:7031::1]:2245 " + "If SourceType=9 ('unix'), this is defined the full qualified file path " + "of a Unix domain socket. Only 'file' is valid for the protocol part of the URI. " + "Format of the URL: file://<full qualified file path>. " + "e.g. file:///tmp/console-out"), + ModelCorrespondence {"Xen_CharacterResourceAllocationSettingData.SourceType"}] + string ConnectURL; + + [Description ("This URL describes the connection to a remote or local location " + "where the character devices acts as a server. " + "To use a literal IPv6 address in the URI, the literal address should be " + "enclosed in '[' and ']' characters. " + "If SourceType=7 ('udp'), this is defines a udp remote host and port connection " + "to receive packages. That the character device acts as a UDP netconsole service, " + "sending and receiving packets, the ConnectURL property must also be defined. " + "This is a lossy service. " + "Only 'udp' is valid for the protocol part of the URI. " + "Format of the URL: <protocol>://<host>:<port>. e.g. udp://0.0.0.0:2245 " + "If SourceType=8 ('tcp'), this is defines a remote host and port connection. " + "The protocol part of the URI can be: 'raw', 'telnet', 'telnets', 'tls'. " + "Format of the URL: <protocol>://<host>:<port>. e.g. raw://[3ffe:2a00:100:7031::1]:2245 " + "If SourceType=9 ('unix'), this is defined the full qualified file path " + "of a Unix domain socket. Only 'file' is valid for the protocol part of the URI. " + "Format of the URL: file://<full qualified file path>. " + "e.g. file:///tmp/console-in"), + ModelCorrespondence {"Xen_CharacterResourceAllocationSettingData.SourceType"}] + string BindURL; +}; + + +[Description ("Xen virtual console device. It is identified by: " + "CIM_ResourceAllocationSettingData.ResourceType=1 ( 'Other' ) and " + "CIM_ResourceAllocationSettingData.OtherResourceType='console'"), + Provider("cmpi::Virt_RASD") +] +class Xen_ConsoleResourceAllocationSettingData : Xen_CharacterResourceAllocationSettingData +{ + [Description ( "The type of the console in the target/guest environment.")] + string TargetType; +}; + +[Description ("LXC virtual character device"), + Provider("cmpi::Virt_RASD") +] +class LXC_CharacterResourceAllocationSettingData : LXC_ResourceAllocationSettingData +{ + [Description ("The type of resource in the source/host environment."), + ValueMap {"0","1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}, + Values {"null", "vc", "pty", "dev", "file", "pipe", + "stdio", "udp", "tcp", "unix", "spicevmc"}, + ModelCorrespondence {"LXC_CharacterResourceAllocationSettingData.SourcePath", + "LXC_CharacterResourceAllocationSettingData.ConnectURL", + "LXC_CharacterResourceAllocationSettingData.BindURL"}] + uint16 SourceType; + + [Description ("If SourceType=4 ('file'),this is the full qualified file path. " + "The file is opened and all data sent to the character device " + "is written to the file. " + "If SourceType=2 ('pty'),this is the full qualified Pseudo TTY path. " + "A Pseudo TTY is allocated using /dev/ptmx. " + "If SourceType=3 ('dev'), this is the full qualified file path " + "to the underlying phsical character device. The device types must " + "match, eg the emulated serial port should only be connected to a " + "host serial port - don't connect a serial port to a parallel port. " + "If SourceType=5 ('pipe'), this is the full qualified file path " + "of a named pipe."), + ModelCorrespondence {"LXC_CharacterResourceAllocationSettingData.SourceType"}] + string SourcePath; + + [Description ("This URL describes the connection to a remote or local location " + "where the character devices acts as a client." + "To use a literal IPv6 address in the URI, the literal address should be " + "enclosed in '[' and ']' characters. " + "If SourceType=7 ('udp'), this is defines a udp remote host and port connection " + "to send packages. That the character device acts as a UDP netconsole service, " + "sending and receiving packets, the BindURL property must also be defined. " + "This is a lossy service. " + "Only 'udp' is valid for the protocol part of the URI. " + "Format of the URL: <protocol>://<host>:<port>. e.g. udp://0.0.0.0:2245 " + "If SourceType=8 ('tcp'), this is defines a remote host and port connection. " + "The protocol part of the URI can be: 'raw', 'telnet', 'telnets', 'tls'. " + "Format of the URL: <protocol>://<host>:<port>. e.g. raw://[3ffe:2a00:100:7031::1]:2245 " + "If SourceType=9 ('unix'), this is defined the full qualified file path " + "of a Unix domain socket. Only 'file' is valid for the protocol part of the URI. " + "Format of the URL: file://<full qualified file path>. " + "e.g. file:///tmp/console-out"), + ModelCorrespondence {"LXC_CharacterResourceAllocationSettingData.SourceType"}] + string ConnectURL; + + [Description ("This URL describes the connection to a remote or local location " + "where the character devices acts as a server. " + "To use a literal IPv6 address in the URI, the literal address should be " + "enclosed in '[' and ']' characters. " + "If SourceType=7 ('udp'), this is defines a udp remote host and port connection " + "to receive packages. That the character device acts as a UDP netconsole service, " + "sending and receiving packets, the ConnectURL property must also be defined. " + "This is a lossy service. " + "Only 'udp' is valid for the protocol part of the URI. " + "Format of the URL: <protocol>://<host>:<port>. e.g. udp://0.0.0.0:2245 " + "If SourceType=8 ('tcp'), this is defines a remote host and port connection. " + "The protocol part of the URI can be: 'raw', 'telnet', 'telnets', 'tls'. " + "Format of the URL: <protocol>://<host>:<port>. e.g. raw://[3ffe:2a00:100:7031::1]:2245 " + "If SourceType=9 ('unix'), this is defined the full qualified file path " + "of a Unix domain socket. Only 'file' is valid for the protocol part of the URI. " + "Format of the URL: file://<full qualified file path>. " + "e.g. file:///tmp/console-in"), + ModelCorrespondence {"LXC_CharacterResourceAllocationSettingData.SourceType"}] + string BindURL; +}; + + +[Description ("LXC virtual console device. It is identified by: " + "CIM_ResourceAllocationSettingData.ResourceType=1 ( 'Other' ) and " + "CIM_ResourceAllocationSettingData.OtherResourceType='console'"), + Provider("cmpi::Virt_RASD") +] +class LXC_ConsoleResourceAllocationSettingData : LXC_CharacterResourceAllocationSettingData +{ + [Description ("The type of the console in the target/guest environment.")] + string TargetType; +}; diff --git a/schema/ResourceAllocationSettingData.registration b/schema/ResourceAllocationSettingData.registration index 2747f91..b969bfe 100644 --- a/schema/ResourceAllocationSettingData.registration +++ b/schema/ResourceAllocationSettingData.registration @@ -1,4 +1,4 @@ -# Copyright IBM Corp. 2007 +# Copyright IBM Corp. 2007, 2013 # Classname Namespace ProviderName ProviderModule ProviderTypes Xen_DiskResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance Xen_NetResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance @@ -6,14 +6,17 @@ Xen_ProcResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance Xen_MemResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance Xen_GraphicsResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance Xen_InputResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance +Xen_ConsoleResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance KVM_DiskResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance KVM_NetResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance KVM_ProcResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance KVM_MemResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance KVM_GraphicsResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance KVM_InputResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance +KVM_ConsoleResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance LXC_MemResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance LXC_DiskResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance LXC_ProcResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance LXC_GraphicsResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance LXC_InputResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance +LXC_ConsoleResourceAllocationSettingData root/virt Virt_RASD Virt_RASD instance diff --git a/src/Virt_RASD.c b/src/Virt_RASD.c index 150ccd3..e28d4e6 100644 --- a/src/Virt_RASD.c +++ b/src/Virt_RASD.c @@ -1,5 +1,5 @@ /* - * Copyright IBM Corp. 2007 + * Copyright IBM Corp. 2007, 2013 * * Authors: * Dan Smith <danms@us.ibm.com> @@ -662,6 +662,143 @@ static CMPIStatus set_graphics_rasd_params(const struct virt_device *dev, return s; } +static char* _build_console_url(const char *protocol, + const char *host, + const char *port) +{ + char* result = NULL; + + if (host == NULL) + goto out; + + if (protocol != NULL && STREQC("file", protocol)) { + /* The host string contains the file name. + Even if the file name does not start with a '/' + it is treated by libvirt as a full qualified path. + */ + if (host[0] == '/') { + if (asprintf(&result, "file://%s", host) < 0) + result = NULL; + goto out; + } else { + if (asprintf(&result, "file:///%s", host) < 0) + result = NULL; + goto out; + } + } + /* The assumption is that the host does not contain a port. + If the host string contains a ':', + the host is treated as an IPv6 address. + */ + if (strchr(host, ':') == NULL) { + if (port == NULL) { + if (asprintf(&result,"%s://%s", protocol, host) < 0) + result = NULL; + goto out; + } else { + if (asprintf(&result,"%s://%s:%s", protocol, + host,port) < 0) + result = NULL; + goto out; + } + } + out: + return result; +} + + +static CMPIStatus set_console_rasd_params(const struct virt_device *vdev, + CMPIInstance *inst) +{ + CMPIStatus s = {CMPI_RC_OK, NULL}; + const struct console_device *cdev = NULL; + char* tmp = NULL; + + cdev = &vdev->dev.console; + + CMSetProperty(inst, "OtherResourceType", "console", CMPI_chars); + CMSetProperty(inst, "SourceType", + (CMPIValue *)&cdev->source_type, CMPI_uint16); + CMSetProperty(inst, "TargetType", + (CMPIValue *)cdev->target_type, CMPI_chars); + + switch (cdev->source_type) { + case CIM_CHARDEV_SOURCE_TYPE_PTY: + CMSetProperty(inst, "SourcePath", + (CMPIValue *)cdev->source_dev.pty.path, + CMPI_chars); + break; + case CIM_CHARDEV_SOURCE_TYPE_DEV: + CMSetProperty(inst, "SourcePath", + (CMPIValue *)cdev->source_dev.dev.path, + CMPI_chars); + break; + case CIM_CHARDEV_SOURCE_TYPE_FILE: + CMSetProperty(inst, "SourcePath", + (CMPIValue *)cdev->source_dev.file.path, + CMPI_chars); + break; + case CIM_CHARDEV_SOURCE_TYPE_PIPE: + CMSetProperty(inst, "SourcePath", + (CMPIValue *)cdev->source_dev.pipe.path, + CMPI_chars); + break; + case CIM_CHARDEV_SOURCE_TYPE_UNIXSOCK: + tmp = _build_console_url("file", + cdev->source_dev.unixsock.path, NULL); + if (cdev->source_dev.unixsock.mode != NULL) { + if (STREQC(cdev->source_dev.unixsock.mode, "bind")) + CMSetProperty(inst, "BindURL", + (CMPIValue *)tmp, CMPI_chars); + else if (STREQC(cdev->source_dev.unixsock.mode, + "connect")) + CMSetProperty(inst, "ConnectURL", + (CMPIValue *)tmp, CMPI_chars); + } + free(tmp); + break; + case CIM_CHARDEV_SOURCE_TYPE_UDP: + tmp = _build_console_url("udp", + cdev->source_dev.udp.bind_host, + cdev->source_dev.udp.bind_service); + CMSetProperty(inst, "BindURL", + (CMPIValue *)tmp, CMPI_chars); + free(tmp); + + tmp = _build_console_url("udp", + cdev->source_dev.udp.connect_host, + cdev->source_dev.udp.connect_service); + CMSetProperty(inst, "ConnectURL", (CMPIValue *)tmp, CMPI_chars); + free(tmp); + break; + case CIM_CHARDEV_SOURCE_TYPE_TCP: + tmp = _build_console_url(cdev->source_dev.tcp.protocol, + cdev->source_dev.tcp.host, + cdev->source_dev.tcp.service); + if (cdev->source_dev.tcp.mode != NULL) { + if (STREQC(cdev->source_dev.tcp.mode, "bind")) + CMSetProperty(inst, "BindURL", + (CMPIValue *)tmp, CMPI_chars); + else if (STREQC(cdev->source_dev.tcp.mode, "connect")) + CMSetProperty(inst, "ConnectURL", + (CMPIValue *)tmp, CMPI_chars); + } + free(tmp); + break; + + default: + /* Nothing to do for : + CIM_CHARDEV_SOURCE_TYPE_STDIO + CIM_CHARDEV_SOURCE_TYPE_NULL + CIM_CHARDEV_SOURCE_TYPE_VC + CIM_CHARDEV_SOURCE_TYPE_SPICEVMC + */ + break; + } + + return s; +} + static CMPIStatus set_input_rasd_params(const struct virt_device *dev, CMPIInstance *inst) { @@ -721,6 +858,9 @@ CMPIInstance *rasd_from_vdev(const CMPIBroker *broker, } else if (dev->type == CIM_RES_TYPE_GRAPHICS) { type = CIM_RES_TYPE_GRAPHICS; base = "GraphicsResourceAllocationSettingData"; + } else if (dev->type == CIM_RES_TYPE_CONSOLE) { + type = CIM_RES_TYPE_OTHER; + base = "ConsoleResourceAllocationSettingData"; } else if (dev->type == CIM_RES_TYPE_INPUT) { type = CIM_RES_TYPE_INPUT; base = "InputResourceAllocationSettingData"; @@ -777,6 +917,8 @@ CMPIInstance *rasd_from_vdev(const CMPIBroker *broker, s = set_graphics_rasd_params(dev, inst, host, CLASSNAME(ref)); } else if (dev->type == CIM_RES_TYPE_INPUT) { s = set_input_rasd_params(dev, inst); + } else if (dev->type == CIM_RES_TYPE_CONSOLE) { + s = set_console_rasd_params(dev, inst); } /* FIXME: Put the HostResource in place */ @@ -909,6 +1051,8 @@ CMPIrc res_type_from_rasd_classname(const char *cn, uint16_t *type) *type = CIM_RES_TYPE_INPUT; else if (STREQ(base, "StorageVolumeResourceAllocationSettingData")) *type = CIM_RES_TYPE_IMAGE; + else if (STREQ(base, "ConsoleResourceAllocationSettingData")) + *type = CIM_RES_TYPE_CONSOLE; else goto out; @@ -940,6 +1084,9 @@ CMPIrc rasd_classname_from_type(uint16_t type, const char **classname) case CIM_RES_TYPE_GRAPHICS: *classname = "GraphicsResourceAllocationSettingData"; break; + case CIM_RES_TYPE_CONSOLE: + *classname = "ConsoleResourceAllocationSettingData"; + break; case CIM_RES_TYPE_INPUT: *classname = "InputResourceAllocationSettingData"; break; -- 1.7.9.5

Extended the Virt_Device provider to return DisplayController devices for the consoles found. This mainly for the purpose of consistency with the other RASD types. Signed-off-by: Viktor Mihajlovski <mihajlov@linux.vnet.ibm.com> Reviewed-by: Boris Fiuczynski <fiuczy@linux.vnet.ibm.com> --- src/Virt_Device.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/Virt_Device.c b/src/Virt_Device.c index c3b515c..aa47276 100644 --- a/src/Virt_Device.c +++ b/src/Virt_Device.c @@ -251,6 +251,34 @@ static CMPIInstance *graphics_instance(const CMPIBroker *broker, return inst; } +static CMPIInstance *console_instance(const CMPIBroker *broker, + struct console_device *dev, + const virDomainPtr dom, + const char *ns) +{ + CMPIInstance *inst; + virConnectPtr conn; + const char *ctype; + + conn = virDomainGetConnect(dom); + inst = get_typed_instance(broker, + pfx_from_conn(conn), + "DisplayController", + ns, + true); + + if (inst == NULL) { + CU_DEBUG("Failed to get instance for DisplayController"); + return NULL; + } + + ctype = chardev_source_type_IDToStr(dev->source_type); + CMSetProperty(inst, "VideoProcessor", + (CMPIValue *)ctype, CMPI_chars); + + return inst; +} + int get_input_dev_caption(const char *type, const char *bus, char **cap) @@ -483,6 +511,11 @@ static bool device_instances(const CMPIBroker *broker, &dev->dev.graphics, dom, ns); + else if (dev->type == CIM_RES_TYPE_CONSOLE) + instance = console_instance(broker, + &dev->dev.console, + dom, + ns); else if (dev->type == CIM_RES_TYPE_INPUT) instance = input_instance(broker, &dev->dev.input, -- 1.7.9.5

From: Thilo Boehm <tboehm@linux.vnet.ibm.com> An instance of KVM_ConsoleResourceAllocationSettingData can be added to domain specification for VSMS DefineSystem() to define a console for a domain. A console definition can not be modified or deleted. It only can be added at system definition and deleted at system deletion. If a KVM_ConsoleRASD is specified on a system definition, no default graphics adapter definition is done. Signed-off-by: Thilo Boehm <tboehm@linux.vnet.ibm.com> Signed-off-by: Viktor Mihajlovski <mihajlov@linux.vnet.ibm.com> Reviewed-by: Boris Fiuczynski <fiuczy@linux.vnet.ibm.com> --- V2 Changes - Fix realloc error handling - Improve error message - Check for NULL pointers before passing to STREQC and CU_DEBUG - Fix uin16_t usage - Fix a few whitespace issues src/Virt_VirtualSystemManagementService.c | 312 ++++++++++++++++++++++++++--- 1 file changed, 289 insertions(+), 23 deletions(-) diff --git a/src/Virt_VirtualSystemManagementService.c b/src/Virt_VirtualSystemManagementService.c index 6629b35..67dd3f2 100644 --- a/src/Virt_VirtualSystemManagementService.c +++ b/src/Virt_VirtualSystemManagementService.c @@ -1,5 +1,5 @@ /* - * Copyright IBM Corp. 2007 + * Copyright IBM Corp. 2007, 2013 * * Authors: * Dan Smith <danms@us.ibm.com> @@ -626,7 +626,8 @@ static bool default_input_device(struct domain *domain) static bool add_default_devs(struct domain *domain) { - if (domain->dev_graphics_ct < 1) { + if (domain->dev_graphics_ct < 1 && + domain->dev_console_ct < 1) { if (!default_graphics_device(domain)) return false; } @@ -1339,47 +1340,292 @@ static int parse_sdl_address(const char *id, return ret; } -static int parse_vnc_address(const char *id, - char **ip, - char **port) +static int parse_ip_address(const char *id, + char **ip, + char **port) { int ret; char *tmp_ip = NULL; char *tmp_port = NULL; - CU_DEBUG("Entering parse_vnc_address, address is %s", id); + CU_DEBUG("Entering parse_ip_address, address is %s", id); if (strstr(id, "[") != NULL) { /* its an ipv6 address */ ret = sscanf(id, "%a[^]]]:%as", &tmp_ip, &tmp_port); - strcat(tmp_ip, "]"); + if (tmp_ip != NULL) { + tmp_ip = realloc(tmp_ip, strlen(tmp_ip) + 2); + if (tmp_ip != NULL) { + strcat(tmp_ip, "]"); + } + } } else { ret = sscanf(id, "%a[^:]:%as", &tmp_ip, &tmp_port); } - if (ret != 2) { + /* ret == 2: address and port, ret == 1: address only */ + if (ret < 1) { ret = 0; goto out; } - if (ip) + if (ip) { *ip = strdup(tmp_ip); + CU_DEBUG("IP = '%s'",*ip); + } - if (port) + if (port && tmp_port) { *port = strdup(tmp_port); - - ret = 1; + CU_DEBUG("Port = '%s'",*port); + } out: - if (ip && port) - CU_DEBUG("Exiting parse_vnc_address, ip is %s, port is %s", - *ip, *port); - free(tmp_ip); free(tmp_port); return ret; } +static bool parse_console_url(const char *url, + char **protocol, + char **host, + char **port) +{ + bool success = false; + char *tmp_protocol = NULL; + char *tmp_address = NULL; + + CU_DEBUG("Entering parse_console_url:'%s'", url); + + if (sscanf(url,"%a[^:]://%as", &tmp_protocol, &tmp_address) != 2) + goto out; + + if (parse_ip_address(tmp_address, host, port) < 1) + goto out; + + if (protocol) { + *protocol = strdup(tmp_protocol); + CU_DEBUG("Protocol = '%s'", *protocol); + } + + success = true; + + out: + free(tmp_protocol); + free(tmp_address); + + return success; +} + +static const char *_unixsock_console_rasd_to_vdev(CMPIInstance *inst, + struct console_device *cdev) +{ + const char *val = NULL; + const char *val2 = NULL; + char* protocol = NULL; + + cdev->source_dev.unixsock.mode = NULL; + if (cu_get_str_prop(inst,"ConnectURL", &val) == CMPI_RC_OK) { + CU_DEBUG("ConnectURL = '%s'", val); + cdev->source_dev.unixsock.mode = strdup("connect"); + } + + if (cu_get_str_prop(inst, "BindURL", &val2) == CMPI_RC_OK) { + if (cdev->source_dev.unixsock.mode != NULL) + return "ConsoleRASD: Only one of ConnectURL or BindURL " + "is allowed for UNIX domain sockets."; + CU_DEBUG("BindURL = '%s'", val2); + cdev->source_dev.unixsock.mode = strdup("bind"); + val = val2; + } + + if (val) { + if (!parse_console_url(val, &protocol, &cdev->source_dev.unixsock.path, NULL)) + return "ConsoleRASD: Invalid ConnectURL or BindURL for " + "UNIX domain socket client/server."; + + if (protocol != NULL && !STREQC("file", protocol)) { + CU_DEBUG("Wrong ConsoleRASD protocol specified: '%s'", protocol); + free(protocol); + return "ConsoleRASD: Protocol 'file' was not specified for " + "ConnectURL or BindURL for UNIX domain socket client/server."; + } + free(protocol); + } else { + return "ConsoleRASD: ConnectURL or BindURL not specified for " + "UNIX domain socket client/server."; + } + + return NULL; +} + +static const char *_udp_console_rasd_to_vdev(CMPIInstance *inst, + struct console_device *cdev) +{ + const char *val = NULL; + char* protocol = NULL; + + if (cu_get_str_prop(inst, "ConnectURL", &val) != CMPI_RC_OK) + return "ConsoleRASD: ConnectURL not specified for UDP network console."; + + if (!parse_console_url(val, &protocol, + &cdev->source_dev.udp.connect_host, + &cdev->source_dev.udp.connect_service)) + return "ConsoleRASD: Invalid ConnectURL specified for UDP network console."; + + if (protocol != NULL && !STREQC("udp", protocol)) { + CU_DEBUG("Wrong ConsoleRASD protocol specified: '%s'", protocol); + free(protocol); + return "ConsoleRASD: Protocol 'udp' was not specified at " + "ConnectURL for UDP network console."; + } + + free(protocol); + + if (cu_get_str_prop(inst, "BindURL", &val) != CMPI_RC_OK) + return "ConsoleRASD: BindURL not specified for UDP network console."; + + if (!parse_console_url(val, &protocol, + &cdev->source_dev.udp.bind_host, + &cdev->source_dev.udp.bind_service)) + return "ConsoleRASD: Invalid BindURL specified for UDP network console."; + + if (protocol != NULL && !STREQC("udp", protocol)) { + CU_DEBUG("Wrong ConsoleRASD protocol specified: '%s'", protocol); + free(protocol); + return "ConsoleRASD: Protocol 'udp' was not specified at BindURL " + "for UDP network console."; + } + + free(protocol); + return NULL; +} + +static const char *_tcp_console_rasd_to_vdev(CMPIInstance *inst, + struct console_device *cdev) +{ + const char *val = NULL; + const char *val2 = NULL; + + cdev->source_dev.tcp.mode = NULL; + if (cu_get_str_prop(inst, "ConnectURL", &val) == CMPI_RC_OK) { + CU_DEBUG("ConnectURL = '%s'", val); + cdev->source_dev.tcp.mode = strdup("connect"); + } + + if (cu_get_str_prop(inst, "BindURL", &val2) == CMPI_RC_OK) { + if (cdev->source_dev.tcp.mode != NULL) + return "ConsoleRASD: Only one of ConnectURL or BindURL " + "is allowed for TCP sockets."; + CU_DEBUG("BindURL = '%s'", val2); + cdev->source_dev.tcp.mode = strdup("bind"); + val = val2; + } + + if (val) { + if (!parse_console_url(val, + &cdev->source_dev.tcp.protocol, + &cdev->source_dev.tcp.host, + &cdev->source_dev.tcp.service)) + return "ConsoleRASD: Invalid ConnectURL or BindURL for " + "TCP client/server console."; + if (cdev->source_dev.tcp.service == NULL) + return "ConsoleRASD: Missing TCP port for TCP client/server console."; + } else { + return "ConsoleRASD: ConnectURL or BindURL not specified for " + "TCP client/server console."; + } + + if (cdev->source_dev.tcp.protocol != NULL) { + if (STREQC("udp", cdev->source_dev.tcp.protocol)) { + CU_DEBUG("Wrong ConsoleRASD protocol specified: '%s'", + cdev->source_dev.tcp.protocol); + return "ConsoleRASD: Invalid protocol 'udp' was specified at " + "TCP client/server console."; + } else if (STREQC("file", cdev->source_dev.tcp.protocol)) { + CU_DEBUG("Wrong ConsoleRASD protocol specified: '%s'", + cdev->source_dev.tcp.protocol); + return "ConsoleRASD: Invalid protocol 'file' was specified at " + "TCP client/server console."; + } + } + + return NULL; +} + +static const char *console_rasd_to_vdev(CMPIInstance *inst, + struct virt_device *dev) +{ + int rc = 0; + const char *msg = NULL; + const char *val = NULL; + struct console_device *cdev = &dev->dev.console; + uint16_t tmp; + + rc = cu_get_u16_prop(inst, "SourceType", &tmp); + if (rc != CMPI_RC_OK) + return "ConsoleRASD: SourceType field not specified."; + + if (tmp >= CIM_CHARDEV_SOURCE_TYPE_INVALIDTYPE) + return "ConsoleRASD: Invalid SourceType value"; + + cdev->source_type = tmp; + CU_DEBUG("Processing SourceType: %d", cdev->source_type); + + /* property not required */ + if (cu_get_str_prop(inst, "TargetType", &val) == CMPI_RC_OK) + cdev->target_type = strdup(val); + CU_DEBUG("TargetType is '%s'", cdev->target_type ? : "NULL" ); + + switch (cdev->source_type) { + case CIM_CHARDEV_SOURCE_TYPE_PTY: + /* property not required */ + if (cu_get_str_prop(inst, "SourcePath", &val) == CMPI_RC_OK) + cdev->source_dev.pty.path = strdup(val); + break; + case CIM_CHARDEV_SOURCE_TYPE_DEV: + if (cu_get_str_prop(inst, "SourcePath", &val) != CMPI_RC_OK) + return "ConsoleRASD: SourcePath not specified for Host device proxy."; + cdev->source_dev.dev.path = strdup(val); + break; + case CIM_CHARDEV_SOURCE_TYPE_FILE: + if (cu_get_str_prop(inst, "SourcePath", &val) != CMPI_RC_OK) + return "ConsoleRASD: SourcePath not specified for Device logfile."; + cdev->source_dev.file.path = strdup(val); + break; + case CIM_CHARDEV_SOURCE_TYPE_PIPE: + if (cu_get_str_prop(inst, "SourcePath", &val) != CMPI_RC_OK) + return "ConsoleRASD: SourcePath not specified for Named pipe."; + cdev->source_dev.pipe.path = strdup(val); + break; + case CIM_CHARDEV_SOURCE_TYPE_UNIXSOCK: + msg = _unixsock_console_rasd_to_vdev(inst, cdev); + if (msg != NULL) + return msg; + break; + case CIM_CHARDEV_SOURCE_TYPE_UDP: + msg = _udp_console_rasd_to_vdev(inst, cdev); + if (msg != NULL) + return msg; + break; + case CIM_CHARDEV_SOURCE_TYPE_TCP: + msg = _tcp_console_rasd_to_vdev(inst, cdev); + if (msg != NULL) + return msg; + break; + + default: + /* Nothing to do for : + CIM_CHARDEV_SOURCE_TYPE_STDIO + CIM_CHARDEV_SOURCE_TYPE_NULL + CIM_CHARDEV_SOURCE_TYPE_VC + CIM_CHARDEV_SOURCE_TYPE_SPICEVMC + */ + break; + } + + return NULL; +} + static const char *graphics_rasd_to_vdev(CMPIInstance *inst, struct virt_device *dev) { @@ -1394,7 +1640,7 @@ static const char *graphics_rasd_to_vdev(CMPIInstance *inst, } dev->dev.graphics.type = strdup(val); - CU_DEBUG("graphics type = %s", dev->dev.graphics.type); + CU_DEBUG("graphics type = %s", dev->dev.graphics.type ? : "NULL"); /* FIXME: Add logic to prevent address:port collisions */ if (STREQC(dev->dev.graphics.type, "vnc")) { @@ -1411,10 +1657,10 @@ static const char *graphics_rasd_to_vdev(CMPIInstance *inst, val = "127.0.0.1:-1"; } - ret = parse_vnc_address(val, + ret = parse_ip_address(val, &dev->dev.graphics.dev.vnc.host, &dev->dev.graphics.dev.vnc.port); - if (ret != 1) { + if (ret != 2) { msg = "GraphicsRASD field Address not valid"; goto out; } @@ -1540,6 +1786,8 @@ static const char *_sysvirt_rasd_to_vdev(CMPIInstance *inst, return proc_rasd_to_vdev(inst, dev); } else if (type == CIM_RES_TYPE_GRAPHICS) { return graphics_rasd_to_vdev(inst, dev); + } else if (type == CIM_RES_TYPE_CONSOLE) { + return console_rasd_to_vdev(inst, dev); } else if (type == CIM_RES_TYPE_INPUT) { return input_rasd_to_vdev(inst, dev); } @@ -1601,7 +1849,7 @@ static const char *rasd_to_vdev(CMPIInstance *inst, return msg; } -static char *add_device_nodup(struct virt_device *dev, +static const char *add_device_nodup(struct virt_device *dev, struct virt_device *list, int max, int *index) @@ -1663,6 +1911,9 @@ static const char *classify_resources(CMPIArray *resources, if (!make_space(&domain->dev_graphics, domain->dev_graphics_ct, count)) return "Failed to alloc graphics list"; + if (!make_space(&domain->dev_console, domain->dev_console_ct, count)) + return "Failed to alloc console list"; + if (!make_space(&domain->dev_input, domain->dev_input_ct, count)) return "Failed to alloc input list"; @@ -1765,6 +2016,14 @@ static const char *classify_resources(CMPIArray *resources, domain->dev_graphics, gcount, &domain->dev_graphics_ct); + } else if (type == CIM_RES_TYPE_CONSOLE) { + msg = rasd_to_vdev(inst, + domain, + &domain->dev_console[domain->dev_console_ct], + ns, + p_error); + if (msg == NULL) + domain->dev_console_ct+=1; } else if (type == CIM_RES_TYPE_INPUT) { domain->dev_input_ct = 1; msg = rasd_to_vdev(inst, @@ -2570,6 +2829,9 @@ static struct virt_device **find_list(struct domain *dominfo, } else if (type == CIM_RES_TYPE_GRAPHICS) { list = &dominfo->dev_graphics; *count = &dominfo->dev_graphics_ct; + } else if (type == CIM_RES_TYPE_CONSOLE) { + list = &dominfo->dev_console; + *count = &dominfo->dev_console_ct; } else if (type == CIM_RES_TYPE_INPUT) { list = &dominfo->dev_input; *count = &dominfo->dev_input_ct; @@ -2693,7 +2955,8 @@ static CMPIStatus resource_del(struct domain *dominfo, if (STREQ(dev->id, devid)) { if ((type == CIM_RES_TYPE_GRAPHICS) || - (type == CIM_RES_TYPE_INPUT)) + (type == CIM_RES_TYPE_CONSOLE) || + (type == CIM_RES_TYPE_INPUT)) cu_statusf(_BROKER, &s, CMPI_RC_OK, ""); else { s = _resource_dynamic(dominfo, @@ -2774,7 +3037,9 @@ static CMPIStatus resource_add(struct domain *dominfo, goto out; } - if ((type == CIM_RES_TYPE_GRAPHICS) || (type == CIM_RES_TYPE_INPUT)) { + if ((type == CIM_RES_TYPE_GRAPHICS) || + (type == CIM_RES_TYPE_INPUT) || + (type == CIM_RES_TYPE_CONSOLE)) { (*count)++; cu_statusf(_BROKER, &s, CMPI_RC_OK, ""); goto out; @@ -2850,7 +3115,8 @@ static CMPIStatus resource_mod(struct domain *dominfo, } if ((type == CIM_RES_TYPE_GRAPHICS) || - (type == CIM_RES_TYPE_INPUT)) + (type == CIM_RES_TYPE_INPUT) || + (type == CIM_RES_TYPE_CONSOLE)) cu_statusf(_BROKER, &s, CMPI_RC_OK, ""); else { #if LIBVIR_VERSION_NUMBER < 9000 -- 1.7.9.5

On 09/11/2013 10:45 AM, Viktor Mihajlovski wrote:
From: Thilo Boehm <tboehm@linux.vnet.ibm.com>
An instance of KVM_ConsoleResourceAllocationSettingData can be added to domain specification for VSMS DefineSystem() to define a console for a domain. A console definition can not be modified or deleted. It only can be added at system definition and deleted at system deletion. If a KVM_ConsoleRASD is specified on a system definition, no default graphics adapter definition is done.
Signed-off-by: Thilo Boehm <tboehm@linux.vnet.ibm.com> Signed-off-by: Viktor Mihajlovski <mihajlov@linux.vnet.ibm.com> Reviewed-by: Boris Fiuczynski <fiuczy@linux.vnet.ibm.com> --- V2 Changes - Fix realloc error handling - Improve error message - Check for NULL pointers before passing to STREQC and CU_DEBUG - Fix uin16_t usage - Fix a few whitespace issues
src/Virt_VirtualSystemManagementService.c | 312 ++++++++++++++++++++++++++--- 1 file changed, 289 insertions(+), 23 deletions(-)
diff --git a/src/Virt_VirtualSystemManagementService.c b/src/Virt_VirtualSystemManagementService.c index 6629b35..67dd3f2 100644 --- a/src/Virt_VirtualSystemManagementService.c +++ b/src/Virt_VirtualSystemManagementService.c @@ -1,5 +1,5 @@ /* - * Copyright IBM Corp. 2007 + * Copyright IBM Corp. 2007, 2013 * * Authors: * Dan Smith <danms@us.ibm.com> @@ -626,7 +626,8 @@ static bool default_input_device(struct domain *domain)
static bool add_default_devs(struct domain *domain) { - if (domain->dev_graphics_ct < 1) { + if (domain->dev_graphics_ct < 1 && + domain->dev_console_ct < 1) { if (!default_graphics_device(domain)) return false; } @@ -1339,47 +1340,292 @@ static int parse_sdl_address(const char *id, return ret; }
-static int parse_vnc_address(const char *id, - char **ip, - char **port) +static int parse_ip_address(const char *id, + char **ip, + char **port) { int ret; char *tmp_ip = NULL; char *tmp_port = NULL;
- CU_DEBUG("Entering parse_vnc_address, address is %s", id); + CU_DEBUG("Entering parse_ip_address, address is %s", id); if (strstr(id, "[") != NULL) { /* its an ipv6 address */ ret = sscanf(id, "%a[^]]]:%as", &tmp_ip, &tmp_port); - strcat(tmp_ip, "]"); + if (tmp_ip != NULL) { + tmp_ip = realloc(tmp_ip, strlen(tmp_ip) + 2); + if (tmp_ip != NULL) { + strcat(tmp_ip, "]"); + }
It's possible that tmp_ip == NULL, so how about this instead: if (tmp_ip == NULL) { ret = 0; goto out; } strcat(tmp_ip, "]"); You won't be getting too far if realloc() fails anyway, but better than core in a few lines on strdup(tmp_ip); failure.
+ } } else { ret = sscanf(id, "%a[^:]:%as", &tmp_ip, &tmp_port); }
- if (ret != 2) { + /* ret == 2: address and port, ret == 1: address only */ + if (ret < 1) { ret = 0; goto out; }
- if (ip) + if (ip) { *ip = strdup(tmp_ip);
If 'tmp_ip == NULL' then this is problematic Everything else seems fine - so if you want me to sqaush in the above I will do so and then just push. John
+ CU_DEBUG("IP = '%s'",*ip); + }
- if (port) + if (port && tmp_port) { *port = strdup(tmp_port); - - ret = 1; + CU_DEBUG("Port = '%s'",*port); + }
out: - if (ip && port) - CU_DEBUG("Exiting parse_vnc_address, ip is %s, port is %s", - *ip, *port); - free(tmp_ip); free(tmp_port);
return ret; }
+static bool parse_console_url(const char *url, + char **protocol, + char **host, + char **port) +{ + bool success = false; + char *tmp_protocol = NULL; + char *tmp_address = NULL; + + CU_DEBUG("Entering parse_console_url:'%s'", url); + + if (sscanf(url,"%a[^:]://%as", &tmp_protocol, &tmp_address) != 2) + goto out; + + if (parse_ip_address(tmp_address, host, port) < 1) + goto out; + + if (protocol) { + *protocol = strdup(tmp_protocol); + CU_DEBUG("Protocol = '%s'", *protocol); + } + + success = true; + + out: + free(tmp_protocol); + free(tmp_address); + + return success; +} + +static const char *_unixsock_console_rasd_to_vdev(CMPIInstance *inst, + struct console_device *cdev) +{ + const char *val = NULL; + const char *val2 = NULL; + char* protocol = NULL; + + cdev->source_dev.unixsock.mode = NULL; + if (cu_get_str_prop(inst,"ConnectURL", &val) == CMPI_RC_OK) { + CU_DEBUG("ConnectURL = '%s'", val); + cdev->source_dev.unixsock.mode = strdup("connect"); + } + + if (cu_get_str_prop(inst, "BindURL", &val2) == CMPI_RC_OK) { + if (cdev->source_dev.unixsock.mode != NULL) + return "ConsoleRASD: Only one of ConnectURL or BindURL " + "is allowed for UNIX domain sockets."; + CU_DEBUG("BindURL = '%s'", val2); + cdev->source_dev.unixsock.mode = strdup("bind"); + val = val2; + } + + if (val) { + if (!parse_console_url(val, &protocol, &cdev->source_dev.unixsock.path, NULL)) + return "ConsoleRASD: Invalid ConnectURL or BindURL for " + "UNIX domain socket client/server."; + + if (protocol != NULL && !STREQC("file", protocol)) { + CU_DEBUG("Wrong ConsoleRASD protocol specified: '%s'", protocol); + free(protocol); + return "ConsoleRASD: Protocol 'file' was not specified for " + "ConnectURL or BindURL for UNIX domain socket client/server."; + } + free(protocol); + } else { + return "ConsoleRASD: ConnectURL or BindURL not specified for " + "UNIX domain socket client/server."; + } + + return NULL; +} + +static const char *_udp_console_rasd_to_vdev(CMPIInstance *inst, + struct console_device *cdev) +{ + const char *val = NULL; + char* protocol = NULL; + + if (cu_get_str_prop(inst, "ConnectURL", &val) != CMPI_RC_OK) + return "ConsoleRASD: ConnectURL not specified for UDP network console."; + + if (!parse_console_url(val, &protocol, + &cdev->source_dev.udp.connect_host, + &cdev->source_dev.udp.connect_service)) + return "ConsoleRASD: Invalid ConnectURL specified for UDP network console."; + + if (protocol != NULL && !STREQC("udp", protocol)) { + CU_DEBUG("Wrong ConsoleRASD protocol specified: '%s'", protocol); + free(protocol); + return "ConsoleRASD: Protocol 'udp' was not specified at " + "ConnectURL for UDP network console."; + } + + free(protocol); + + if (cu_get_str_prop(inst, "BindURL", &val) != CMPI_RC_OK) + return "ConsoleRASD: BindURL not specified for UDP network console."; + + if (!parse_console_url(val, &protocol, + &cdev->source_dev.udp.bind_host, + &cdev->source_dev.udp.bind_service)) + return "ConsoleRASD: Invalid BindURL specified for UDP network console."; + + if (protocol != NULL && !STREQC("udp", protocol)) { + CU_DEBUG("Wrong ConsoleRASD protocol specified: '%s'", protocol); + free(protocol); + return "ConsoleRASD: Protocol 'udp' was not specified at BindURL " + "for UDP network console."; + } + + free(protocol); + return NULL; +} + +static const char *_tcp_console_rasd_to_vdev(CMPIInstance *inst, + struct console_device *cdev) +{ + const char *val = NULL; + const char *val2 = NULL; + + cdev->source_dev.tcp.mode = NULL; + if (cu_get_str_prop(inst, "ConnectURL", &val) == CMPI_RC_OK) { + CU_DEBUG("ConnectURL = '%s'", val); + cdev->source_dev.tcp.mode = strdup("connect"); + } + + if (cu_get_str_prop(inst, "BindURL", &val2) == CMPI_RC_OK) { + if (cdev->source_dev.tcp.mode != NULL) + return "ConsoleRASD: Only one of ConnectURL or BindURL " + "is allowed for TCP sockets."; + CU_DEBUG("BindURL = '%s'", val2); + cdev->source_dev.tcp.mode = strdup("bind"); + val = val2; + } + + if (val) { + if (!parse_console_url(val, + &cdev->source_dev.tcp.protocol, + &cdev->source_dev.tcp.host, + &cdev->source_dev.tcp.service)) + return "ConsoleRASD: Invalid ConnectURL or BindURL for " + "TCP client/server console."; + if (cdev->source_dev.tcp.service == NULL) + return "ConsoleRASD: Missing TCP port for TCP client/server console."; + } else { + return "ConsoleRASD: ConnectURL or BindURL not specified for " + "TCP client/server console."; + } + + if (cdev->source_dev.tcp.protocol != NULL) { + if (STREQC("udp", cdev->source_dev.tcp.protocol)) { + CU_DEBUG("Wrong ConsoleRASD protocol specified: '%s'", + cdev->source_dev.tcp.protocol); + return "ConsoleRASD: Invalid protocol 'udp' was specified at " + "TCP client/server console."; + } else if (STREQC("file", cdev->source_dev.tcp.protocol)) { + CU_DEBUG("Wrong ConsoleRASD protocol specified: '%s'", + cdev->source_dev.tcp.protocol); + return "ConsoleRASD: Invalid protocol 'file' was specified at " + "TCP client/server console."; + } + } + + return NULL; +} + +static const char *console_rasd_to_vdev(CMPIInstance *inst, + struct virt_device *dev) +{ + int rc = 0; + const char *msg = NULL; + const char *val = NULL; + struct console_device *cdev = &dev->dev.console; + uint16_t tmp; + + rc = cu_get_u16_prop(inst, "SourceType", &tmp); + if (rc != CMPI_RC_OK) + return "ConsoleRASD: SourceType field not specified."; + + if (tmp >= CIM_CHARDEV_SOURCE_TYPE_INVALIDTYPE) + return "ConsoleRASD: Invalid SourceType value"; + + cdev->source_type = tmp; + CU_DEBUG("Processing SourceType: %d", cdev->source_type); + + /* property not required */ + if (cu_get_str_prop(inst, "TargetType", &val) == CMPI_RC_OK) + cdev->target_type = strdup(val); + CU_DEBUG("TargetType is '%s'", cdev->target_type ? : "NULL" ); + + switch (cdev->source_type) { + case CIM_CHARDEV_SOURCE_TYPE_PTY: + /* property not required */ + if (cu_get_str_prop(inst, "SourcePath", &val) == CMPI_RC_OK) + cdev->source_dev.pty.path = strdup(val); + break; + case CIM_CHARDEV_SOURCE_TYPE_DEV: + if (cu_get_str_prop(inst, "SourcePath", &val) != CMPI_RC_OK) + return "ConsoleRASD: SourcePath not specified for Host device proxy."; + cdev->source_dev.dev.path = strdup(val); + break; + case CIM_CHARDEV_SOURCE_TYPE_FILE: + if (cu_get_str_prop(inst, "SourcePath", &val) != CMPI_RC_OK) + return "ConsoleRASD: SourcePath not specified for Device logfile."; + cdev->source_dev.file.path = strdup(val); + break; + case CIM_CHARDEV_SOURCE_TYPE_PIPE: + if (cu_get_str_prop(inst, "SourcePath", &val) != CMPI_RC_OK) + return "ConsoleRASD: SourcePath not specified for Named pipe."; + cdev->source_dev.pipe.path = strdup(val); + break; + case CIM_CHARDEV_SOURCE_TYPE_UNIXSOCK: + msg = _unixsock_console_rasd_to_vdev(inst, cdev); + if (msg != NULL) + return msg; + break; + case CIM_CHARDEV_SOURCE_TYPE_UDP: + msg = _udp_console_rasd_to_vdev(inst, cdev); + if (msg != NULL) + return msg; + break; + case CIM_CHARDEV_SOURCE_TYPE_TCP: + msg = _tcp_console_rasd_to_vdev(inst, cdev); + if (msg != NULL) + return msg; + break; + + default: + /* Nothing to do for : + CIM_CHARDEV_SOURCE_TYPE_STDIO + CIM_CHARDEV_SOURCE_TYPE_NULL + CIM_CHARDEV_SOURCE_TYPE_VC + CIM_CHARDEV_SOURCE_TYPE_SPICEVMC + */ + break; + } + + return NULL; +} + static const char *graphics_rasd_to_vdev(CMPIInstance *inst, struct virt_device *dev) { @@ -1394,7 +1640,7 @@ static const char *graphics_rasd_to_vdev(CMPIInstance *inst, } dev->dev.graphics.type = strdup(val);
- CU_DEBUG("graphics type = %s", dev->dev.graphics.type); + CU_DEBUG("graphics type = %s", dev->dev.graphics.type ? : "NULL");
/* FIXME: Add logic to prevent address:port collisions */ if (STREQC(dev->dev.graphics.type, "vnc")) { @@ -1411,10 +1657,10 @@ static const char *graphics_rasd_to_vdev(CMPIInstance *inst, val = "127.0.0.1:-1"; }
- ret = parse_vnc_address(val, + ret = parse_ip_address(val, &dev->dev.graphics.dev.vnc.host, &dev->dev.graphics.dev.vnc.port); - if (ret != 1) { + if (ret != 2) { msg = "GraphicsRASD field Address not valid"; goto out; } @@ -1540,6 +1786,8 @@ static const char *_sysvirt_rasd_to_vdev(CMPIInstance *inst, return proc_rasd_to_vdev(inst, dev); } else if (type == CIM_RES_TYPE_GRAPHICS) { return graphics_rasd_to_vdev(inst, dev); + } else if (type == CIM_RES_TYPE_CONSOLE) { + return console_rasd_to_vdev(inst, dev); } else if (type == CIM_RES_TYPE_INPUT) { return input_rasd_to_vdev(inst, dev); } @@ -1601,7 +1849,7 @@ static const char *rasd_to_vdev(CMPIInstance *inst, return msg; }
-static char *add_device_nodup(struct virt_device *dev, +static const char *add_device_nodup(struct virt_device *dev, struct virt_device *list, int max, int *index) @@ -1663,6 +1911,9 @@ static const char *classify_resources(CMPIArray *resources, if (!make_space(&domain->dev_graphics, domain->dev_graphics_ct, count)) return "Failed to alloc graphics list";
+ if (!make_space(&domain->dev_console, domain->dev_console_ct, count)) + return "Failed to alloc console list"; + if (!make_space(&domain->dev_input, domain->dev_input_ct, count)) return "Failed to alloc input list";
@@ -1765,6 +2016,14 @@ static const char *classify_resources(CMPIArray *resources, domain->dev_graphics, gcount, &domain->dev_graphics_ct); + } else if (type == CIM_RES_TYPE_CONSOLE) { + msg = rasd_to_vdev(inst, + domain, + &domain->dev_console[domain->dev_console_ct], + ns, + p_error); + if (msg == NULL) + domain->dev_console_ct+=1; } else if (type == CIM_RES_TYPE_INPUT) { domain->dev_input_ct = 1; msg = rasd_to_vdev(inst, @@ -2570,6 +2829,9 @@ static struct virt_device **find_list(struct domain *dominfo, } else if (type == CIM_RES_TYPE_GRAPHICS) { list = &dominfo->dev_graphics; *count = &dominfo->dev_graphics_ct; + } else if (type == CIM_RES_TYPE_CONSOLE) { + list = &dominfo->dev_console; + *count = &dominfo->dev_console_ct; } else if (type == CIM_RES_TYPE_INPUT) { list = &dominfo->dev_input; *count = &dominfo->dev_input_ct; @@ -2693,7 +2955,8 @@ static CMPIStatus resource_del(struct domain *dominfo,
if (STREQ(dev->id, devid)) { if ((type == CIM_RES_TYPE_GRAPHICS) || - (type == CIM_RES_TYPE_INPUT)) + (type == CIM_RES_TYPE_CONSOLE) || + (type == CIM_RES_TYPE_INPUT)) cu_statusf(_BROKER, &s, CMPI_RC_OK, ""); else { s = _resource_dynamic(dominfo, @@ -2774,7 +3037,9 @@ static CMPIStatus resource_add(struct domain *dominfo, goto out; }
- if ((type == CIM_RES_TYPE_GRAPHICS) || (type == CIM_RES_TYPE_INPUT)) { + if ((type == CIM_RES_TYPE_GRAPHICS) || + (type == CIM_RES_TYPE_INPUT) || + (type == CIM_RES_TYPE_CONSOLE)) { (*count)++; cu_statusf(_BROKER, &s, CMPI_RC_OK, ""); goto out; @@ -2850,7 +3115,8 @@ static CMPIStatus resource_mod(struct domain *dominfo, }
if ((type == CIM_RES_TYPE_GRAPHICS) || - (type == CIM_RES_TYPE_INPUT)) + (type == CIM_RES_TYPE_INPUT) || + (type == CIM_RES_TYPE_CONSOLE)) cu_statusf(_BROKER, &s, CMPI_RC_OK, ""); else { #if LIBVIR_VERSION_NUMBER < 9000

On 09/12/2013 05:31 PM, John Ferlan wrote: [...]
It's possible that tmp_ip == NULL, so how about this instead:
if (tmp_ip == NULL) { ret = 0; goto out; } strcat(tmp_ip, "]");
You won't be getting too far if realloc() fails anyway, but better than core in a few lines on strdup(tmp_ip); failure.
right ... Boris pointed that out to me today, I was too eager to get over it, and sent V2 before I got his feedback
+ } } else { ret = sscanf(id, "%a[^:]:%as", &tmp_ip, &tmp_port); }
- if (ret != 2) { + /* ret == 2: address and port, ret == 1: address only */ + if (ret < 1) { ret = 0; goto out; }
- if (ip) + if (ip) { *ip = strdup(tmp_ip);
If 'tmp_ip == NULL' then this is problematic
Everything else seems fine - so if you want me to sqaush in the above I will do so and then just push.
That would be really nice ... thanks! -- Mit freundlichen Grüßen/Kind Regards Viktor Mihajlovski IBM Deutschland Research & Development GmbH Vorsitzender des Aufsichtsrats: Martina Köderitz Geschäftsführung: Dirk Wittkopp Sitz der Gesellschaft: Böblingen Registergericht: Amtsgericht Stuttgart, HRB 243294

From: Thilo Boehm <tboehm@linux.vnet.ibm.com> A new function to add a default console has been added. As with the default graphics and default input device, the default device generation is optional and depends on the domain type and architecture. Initially, we only create an s390 console, which is necessary to define a runnable guest. Signed-off-by: Thilo Boehm <tboehm@linux.vnet.ibm.com> Signed-off-by: Viktor Mihajlovski <mihajlov@linux.vnet.ibm.com> Reviewed-by: Boris Fiuczynski <fiuczy@linux.vnet.ibm.com> --- src/Virt_VirtualSystemManagementService.c | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/Virt_VirtualSystemManagementService.c b/src/Virt_VirtualSystemManagementService.c index 67dd3f2..d556086 100644 --- a/src/Virt_VirtualSystemManagementService.c +++ b/src/Virt_VirtualSystemManagementService.c @@ -592,6 +592,38 @@ static bool default_graphics_device(struct domain *domain) return true; } +static bool default_console_device(struct domain *domain) +{ + /* currently only taking care for s390 guests */ + if ((domain->type == DOMAIN_KVM || domain->type == DOMAIN_QEMU) && + domain->os_info.fv.arch != NULL && + (XSTREQ(domain->os_info.fv.arch, "s390") || + XSTREQ(domain->os_info.fv.arch, "s390x" ))) { + char * consoletype = NULL; + if (domain->os_info.fv.machine != NULL && + XSTREQ(domain->os_info.fv.machine, "s390-ccw-virtio")) { + consoletype = "sclp"; + } else { + consoletype = "virtio"; + } + free(domain->dev_console); + domain->dev_console = calloc(1, sizeof(*domain->dev_console)); + if (domain->dev_console == NULL) { + CU_DEBUG("Failed to allocate default console device."); + return false; + } + CU_DEBUG("Defining default console device for s390."); + domain->dev_console->type = CIM_RES_TYPE_CONSOLE; + domain->dev_console->dev.console.source_type = + CIM_CHARDEV_SOURCE_TYPE_PTY; + domain->dev_console->dev.console.target_type = + strdup(consoletype); + domain->dev_console_ct = 1; + } + + return true; +} + static bool default_input_device(struct domain *domain) { if (domain->type == DOMAIN_LXC) @@ -628,6 +660,8 @@ static bool add_default_devs(struct domain *domain) { if (domain->dev_graphics_ct < 1 && domain->dev_console_ct < 1) { + if (!default_console_device(domain)) + return false; if (!default_graphics_device(domain)) return false; } -- 1.7.9.5

On 09/11/2013 10:45 AM, Viktor Mihajlovski wrote:
Motivation: the current libvirt-cim support for consoles is very limited in that it doesn't allow to specify the target and source types supported by libvirt. Having full support of the libvirt console feature is however mandatory for architectures like s390.
The current implementation using a Graphics_RASD to represent consoles cannot easily be extended without breaking the existing implementation (or without being entirely unusable).
Therefore a new RASD class is added to represent consoles in the model. Since there's no SVPC resource type for a console we use the combination of ResourceType = 1 (Other) and OtherResourceType = "console" for the new RASD instances.
In order to be backward compatible, the only console type supported by older libvirt-cim versions (target type 'pty') will still be returned as Graphics_RASD. In the long run, this should be deprecated.
V2 Changes: - Squashed old patches 2 and 5 and moved that one to position 4 - Fix double frees - Fix NULL pointer accesses - Improve error handling - Improve error messages - Some cosmetic changes
Thilo Boehm (5): schema: New SVPC types for chardev/consoles libxkutil: Console Support RASD: Schema and Provider Support for Console RASDs VSMS: Support for domains with console devices VSMS: add default console
Viktor Mihajlovski (2): VSMS: Set resource types for default devices Device: CIM_LogicalDevice for consoles
libxkutil/device_parsing.c | 314 ++++++++++++++++++- libxkutil/device_parsing.h | 43 ++- libxkutil/xmlgen.c | 191 ++++++++++- schema/ResourceAllocationSettingData.mof | 246 ++++++++++++++- schema/ResourceAllocationSettingData.registration | 5 +- src/Virt_Device.c | 33 ++ src/Virt_RASD.c | 149 ++++++++- src/Virt_VirtualSystemManagementService.c | 348 +++++++++++++++++++-- src/svpc_types.h | 102 +++++- 9 files changed, 1392 insertions(+), 39 deletions(-)
Series ACK and pushed with minor adjustment to 6/7 to check for NULL return on realloc() John
participants (2)
-
John Ferlan
-
Viktor Mihajlovski