@@ -1244,6 +1244,110 @@ static int libxl__build_device_model_args_new(libxl__gc *gc,
nics[i].devid, ifname,
libxl_tapif_script(gc),
libxl_tapif_script(gc)));
+
+ /* Userspace COLO Proxy need this */
+ if (state->saved_state) {
+ /* secondary colo run */
+ } else {
+ /* primary colo run */
+ if (nics[i].sock_mirror_id &&
+ nics[i].sock_mirror_ip &&
+ nics[i].sock_mirror_port) {
+ flexarray_append(dm_args, "-chardev");
+ flexarray_append(dm_args,
+ GCSPRINTF("socket,id=%s,host=%s,port=%s,server,nowait",
+ nics[i].sock_mirror_id,
+ nics[i].sock_mirror_ip,
+ nics[i].sock_mirror_port));
+ }
+ if (nics[i].sock_compare_pri_in_id &&
+ nics[i].sock_compare_pri_in_ip &&
+ nics[i].sock_compare_pri_in_port) {
+ flexarray_append(dm_args, "-chardev");
+ flexarray_append(dm_args,
+ GCSPRINTF("socket,id=%s,host=%s,port=%s,server,nowait",
+ nics[i].sock_compare_pri_in_id,
+ nics[i].sock_compare_pri_in_ip,
+ nics[i].sock_compare_pri_in_port));
+ }
+ if (nics[i].sock_compare_sec_in_id &&
+ nics[i].sock_compare_sec_in_ip &&
+ nics[i].sock_compare_sec_in_port) {
+ flexarray_append(dm_args, "-chardev");
+ flexarray_append(dm_args,
+ GCSPRINTF("socket,id=%s,host=%s,port=%s,server,nowait",
+ nics[i].sock_compare_sec_in_id,
+ nics[i].sock_compare_sec_in_ip,
+ nics[i].sock_compare_sec_in_port));
+ }
+ if (nics[i].sock_redirector0_id &&
+ nics[i].sock_redirector0_ip &&
+ nics[i].sock_redirector0_port) {
+ flexarray_append(dm_args, "-chardev");
+ flexarray_append(dm_args,
+ GCSPRINTF("socket,id=%s,host=%s,port=%s,server,nowait",
+ nics[i].sock_redirector0_id,
+ nics[i].sock_redirector0_ip,
+ nics[i].sock_redirector0_port));
+ }
+ if (nics[i].sock_redirector1_id &&
+ nics[i].sock_redirector1_ip &&
+ nics[i].sock_redirector1_port) {
+ flexarray_append(dm_args, "-chardev");
+ flexarray_append(dm_args,
+ GCSPRINTF("socket,id=%s,host=%s,port=%s",
+ nics[i].sock_redirector1_id,
+ nics[i].sock_redirector1_ip,
+ nics[i].sock_redirector1_port));
+ }
+ if (nics[i].sock_redirector2_id &&
+ nics[i].sock_redirector2_ip &&
+ nics[i].sock_redirector2_port) {
+ flexarray_append(dm_args, "-chardev");
+ flexarray_append(dm_args,
+ GCSPRINTF("socket,id=%s,host=%s,port=%s",
+ nics[i].sock_redirector2_id,
+ nics[i].sock_redirector2_ip,
+ nics[i].sock_redirector2_port));
+ }
+ if (nics[i].filter_mirror_queue &&
+ nics[i].filter_mirror_outdev) {
+ flexarray_append(dm_args, "-object");
+ flexarray_append(dm_args,
+ GCSPRINTF("filter-mirror,id=m1,netdev=net%d,queue=%s,outdev=%s",
+ nics[i].devid,
+ nics[i].filter_mirror_queue,
+ nics[i].filter_mirror_outdev));
+ }
+ if (nics[i].filter_redirector0_queue &&
+ nics[i].filter_redirector0_indev) {
+ flexarray_append(dm_args, "-object");
+ flexarray_append(dm_args,
+ GCSPRINTF("filter-redirector,id=r1,netdev=net%d,queue=%s,indev=%s",
+ nics[i].devid,
+ nics[i].filter_redirector0_queue,
+ nics[i].filter_redirector0_indev));
+ }
+ if (nics[i].filter_redirector1_queue &&
+ nics[i].filter_redirector1_outdev) {
+ flexarray_append(dm_args, "-object");
+ flexarray_append(dm_args,
+ GCSPRINTF("filter-redirector,id=r2,netdev=net%d,queue=%s,outdev=%s",
+ nics[i].devid,
+ nics[i].filter_redirector1_queue,
+ nics[i].filter_redirector1_outdev));
+ }
+ if (nics[i].colo_compare_pri_in &&
+ nics[i].colo_compare_sec_in &&
+ nics[i].colo_compare_out) {
+ flexarray_append(dm_args, "-object");
+ flexarray_append(dm_args,
+ GCSPRINTF("colo-compare,id=c1,primary_in=%s,secondary_in=%s,outdev=%s",
+ nics[i].colo_compare_pri_in,
+ nics[i].colo_compare_sec_in,
+ nics[i].colo_compare_out));
+ }
+ }
ioemu_nics++;
}
}
@@ -196,6 +196,123 @@ static void libxl__device_nic_add(libxl__egc *egc, uint32_t domid,
flexarray_append(back, nic->coloft_forwarddev);
}
+ if (nic->sock_mirror_id) {
+ flexarray_append(back, "sock_mirror_id");
+ flexarray_append(back, nic->sock_mirror_id);
+ }
+ if (nic->sock_mirror_ip) {
+ flexarray_append(back, "sock_mirror_ip");
+ flexarray_append(back, nic->sock_mirror_ip);
+ }
+ if (nic->sock_mirror_port) {
+ flexarray_append(back, "sock_mirror_port");
+ flexarray_append(back, nic->sock_mirror_port);
+ }
+ if (nic->sock_compare_pri_in_id) {
+ flexarray_append(back, "sock_compare_pri_in_id");
+ flexarray_append(back, nic->sock_compare_pri_in_id);
+ }
+ if (nic->sock_compare_pri_in_ip) {
+ flexarray_append(back, "sock_compare_pri_in_ip");
+ flexarray_append(back, nic->sock_compare_pri_in_ip);
+ }
+ if (nic->sock_compare_pri_in_port) {
+ flexarray_append(back, "sock_compare_pri_in_port");
+ flexarray_append(back, nic->sock_compare_pri_in_port);
+ }
+ if (nic->sock_compare_sec_in_id) {
+ flexarray_append(back, "sock_compare_sec_in_id");
+ flexarray_append(back, nic->sock_compare_sec_in_id);
+ }
+ if (nic->sock_compare_sec_in_ip) {
+ flexarray_append(back, "sock_compare_sec_in_ip");
+ flexarray_append(back, nic->sock_compare_sec_in_ip);
+ }
+ if (nic->sock_compare_sec_in_port) {
+ flexarray_append(back, "sock_compare_sec_in_port");
+ flexarray_append(back, nic->sock_compare_sec_in_port);
+ }
+ if (nic->sock_redirector0_id) {
+ flexarray_append(back, "sock_redirector0_id");
+ flexarray_append(back, nic->sock_redirector0_id);
+ }
+ if (nic->sock_redirector0_ip) {
+ flexarray_append(back, "sock_redirector0_ip");
+ flexarray_append(back, nic->sock_redirector0_ip);
+ }
+ if (nic->sock_redirector0_port) {
+ flexarray_append(back, "sock_redirector0_port");
+ flexarray_append(back, nic->sock_redirector0_port);
+ }
+ if (nic->sock_redirector1_id) {
+ flexarray_append(back, "sock_redirector1_id");
+ flexarray_append(back, nic->sock_redirector1_id);
+ }
+ if (nic->sock_redirector1_ip) {
+ flexarray_append(back, "sock_redirector1_ip");
+ flexarray_append(back, nic->sock_redirector1_ip);
+ }
+ if (nic->sock_redirector1_port) {
+ flexarray_append(back, "sock_redirector1_port");
+ flexarray_append(back, nic->sock_redirector1_port);
+ }
+ if (nic->sock_redirector2_id) {
+ flexarray_append(back, "sock_redirector2_id");
+ flexarray_append(back, nic->sock_redirector2_id);
+ }
+ if (nic->sock_redirector2_ip) {
+ flexarray_append(back, "sock_redirector2_ip");
+ flexarray_append(back, nic->sock_redirector2_ip);
+ }
+ if (nic->sock_redirector2_port) {
+ flexarray_append(back, "sock_redirector2_port");
+ flexarray_append(back, nic->sock_redirector2_port);
+ }
+ if (nic->filter_mirror_queue) {
+ flexarray_append(back, "filter_mirror_queue");
+ flexarray_append(back, nic->filter_mirror_queue);
+ }
+ if (nic->filter_mirror_outdev) {
+ flexarray_append(back, "filter_mirror_outdev");
+ flexarray_append(back, nic->filter_mirror_outdev);
+ }
+ if (nic->filter_redirector0_queue) {
+ flexarray_append(back, "filter_redirector0_queue");
+ flexarray_append(back, nic->filter_redirector0_queue);
+ }
+ if (nic->filter_redirector0_indev) {
+ flexarray_append(back, "filter_redirector0_indev");
+ flexarray_append(back, nic->filter_redirector0_indev);
+ }
+ if (nic->filter_redirector0_outdev) {
+ flexarray_append(back, "filter_redirector0_outdev");
+ flexarray_append(back, nic->filter_redirector0_outdev);
+ }
+ if (nic->filter_redirector1_queue) {
+ flexarray_append(back, "filter_redirector1_queue");
+ flexarray_append(back, nic->filter_redirector1_queue);
+ }
+ if (nic->filter_redirector1_indev) {
+ flexarray_append(back, "filter_redirector1_indev");
+ flexarray_append(back, nic->filter_redirector1_indev);
+ }
+ if (nic->filter_redirector1_outdev) {
+ flexarray_append(back, "filter_redirector1_outdev");
+ flexarray_append(back, nic->filter_redirector1_outdev);
+ }
+ if (nic->colo_compare_pri_in) {
+ flexarray_append(back, "colo_compare_pri_in");
+ flexarray_append(back, nic->colo_compare_pri_in);
+ }
+ if (nic->colo_compare_sec_in) {
+ flexarray_append(back, "colo_compare_sec_in");
+ flexarray_append(back, nic->colo_compare_sec_in);
+ }
+ if (nic->colo_compare_out) {
+ flexarray_append(back, "colo_compare_out");
+ flexarray_append(back, nic->colo_compare_out);
+ }
+
flexarray_append(back, "mac");
flexarray_append(back,GCSPRINTF(LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac)));
if (nic->ip) {
@@ -348,6 +465,121 @@ static int libxl__device_nic_from_xenstore(libxl__gc *gc,
GCSPRINTF("%s/forwarddev", libxl_path),
(const char **)(&nic->coloft_forwarddev));
if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/sock_mirror_id", libxl_path),
+ (const char **)(&nic->sock_mirror_id));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/sock_mirror_ip", libxl_path),
+ (const char **)(&nic->sock_mirror_ip));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/sock_mirror_port", libxl_path),
+ (const char **)(&nic->sock_mirror_port));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/sock_compare_pri_in_id", libxl_path),
+ (const char **)(&nic->sock_compare_pri_in_id));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/sock_compare_pri_in_ip", libxl_path),
+ (const char **)(&nic->sock_compare_pri_in_ip));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/sock_compare_pri_in_port", libxl_path),
+ (const char **)(&nic->sock_compare_pri_in_port));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/sock_compare_sec_in_id", libxl_path),
+ (const char **)(&nic->sock_compare_sec_in_id));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/sock_compare_sec_in_ip", libxl_path),
+ (const char **)(&nic->sock_compare_sec_in_ip));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/sock_compare_sec_in_port", libxl_path),
+ (const char **)(&nic->sock_compare_sec_in_port));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/sock_redirector0_id", libxl_path),
+ (const char **)(&nic->sock_redirector0_id));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/sock_redirector0_ip", libxl_path),
+ (const char **)(&nic->sock_redirector0_ip));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/sock_redirector0_port", libxl_path),
+ (const char **)(&nic->sock_redirector0_port));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/sock_redirector1_id", libxl_path),
+ (const char **)(&nic->sock_redirector1_id));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/sock_redirector1_ip", libxl_path),
+ (const char **)(&nic->sock_redirector1_ip));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/sock_redirector1_port", libxl_path),
+ (const char **)(&nic->sock_redirector1_port));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/sock_redirector2_id", libxl_path),
+ (const char **)(&nic->sock_redirector2_id));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/sock_redirector2_ip", libxl_path),
+ (const char **)(&nic->sock_redirector2_ip));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/sock_redirector2_port", libxl_path),
+ (const char **)(&nic->sock_redirector2_port));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/filter_mirror_queue", libxl_path),
+ (const char **)(&nic->filter_mirror_queue));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/filter_mirror_outdev", libxl_path),
+ (const char **)(&nic->filter_mirror_outdev));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/filter_redirector0_queue", libxl_path),
+ (const char **)(&nic->filter_redirector0_queue));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/filter_redirector0_indev", libxl_path),
+ (const char **)(&nic->filter_redirector0_indev));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/filter_redirector0_outdev", libxl_path),
+ (const char **)(&nic->filter_redirector0_outdev));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/filter_redirector1_queue", libxl_path),
+ (const char **)(&nic->filter_redirector1_queue));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/filter_redirector1_indev", libxl_path),
+ (const char **)(&nic->filter_redirector1_indev));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/filter_redirector1_outdev", libxl_path),
+ (const char **)(&nic->filter_redirector1_outdev));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/colo_compare_pri_in", libxl_path),
+ (const char **)(&nic->colo_compare_pri_in));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/colo_compare_sec_in", libxl_path),
+ (const char **)(&nic->colo_compare_sec_in));
+ if (rc) goto out;
+ rc = libxl__xs_read_checked(NOGC, XBT_NULL,
+ GCSPRINTF("%s/colo_compare_out", libxl_path),
+ (const char **)(&nic->colo_compare_out));
/* vif_ioemu nics use the same xenstore entries as vif interfaces */
rc = libxl__xs_read_checked(gc, XBT_NULL,
@@ -629,7 +629,36 @@ libxl_device_nic = Struct("device_nic", [
("gatewaydev", string),
# Note that the COLO configuration settings should be considered unstable.
# They may change incompatibly in future versions of Xen.
- ("coloft_forwarddev", string)
+ ("coloft_forwarddev", string),
+ ("sock_mirror_id", string),
+ ("sock_mirror_ip", string),
+ ("sock_mirror_port", string),
+ ("sock_compare_pri_in_id", string),
+ ("sock_compare_pri_in_ip", string),
+ ("sock_compare_pri_in_port", string),
+ ("sock_compare_sec_in_id", string),
+ ("sock_compare_sec_in_ip", string),
+ ("sock_compare_sec_in_port", string),
+ ("sock_redirector0_id", string),
+ ("sock_redirector0_ip", string),
+ ("sock_redirector0_port", string),
+ ("sock_redirector1_id", string),
+ ("sock_redirector1_ip", string),
+ ("sock_redirector1_port", string),
+ ("sock_redirector2_id", string),
+ ("sock_redirector2_ip", string),
+ ("sock_redirector2_port", string),
+ ("filter_mirror_queue", string),
+ ("filter_mirror_outdev", string),
+ ("filter_redirector0_queue", string),
+ ("filter_redirector0_indev", string),
+ ("filter_redirector0_outdev", string),
+ ("filter_redirector1_queue", string),
+ ("filter_redirector1_indev", string),
+ ("filter_redirector1_outdev", string),
+ ("colo_compare_pri_in", string),
+ ("colo_compare_sec_in", string),
+ ("colo_compare_out", string)
])
libxl_device_pci = Struct("device_pci", [
@@ -1060,6 +1060,64 @@ static int parse_nic_config(libxl_device_nic *nic, XLU_Config **config, char *to
parse_vif_rate(config, oparg, nic);
} else if (MATCH_OPTION("forwarddev", token, oparg)) {
replace_string(&nic->coloft_forwarddev, oparg);
+ } else if (MATCH_OPTION("sock_mirror_id", token, oparg)) {
+ replace_string(&nic->sock_mirror_id, oparg);
+ } else if (MATCH_OPTION("sock_mirror_ip", token, oparg)) {
+ replace_string(&nic->sock_mirror_ip, oparg);
+ } else if (MATCH_OPTION("sock_mirror_port", token, oparg)) {
+ replace_string(&nic->sock_mirror_port, oparg);
+ } else if (MATCH_OPTION("sock_compare_sec_in_id", token, oparg)) {
+ replace_string(&nic->sock_compare_sec_in_id, oparg);
+ } else if (MATCH_OPTION("sock_compare_sec_in_ip", token, oparg)) {
+ replace_string(&nic->sock_compare_sec_in_ip, oparg);
+ } else if (MATCH_OPTION("sock_compare_sec_in_port", token, oparg)) {
+ replace_string(&nic->sock_compare_sec_in_port, oparg);
+ } else if (MATCH_OPTION("sock_redirector0_id", token, oparg)) {
+ replace_string(&nic->sock_redirector0_id, oparg);
+ } else if (MATCH_OPTION("sock_redirector0_ip", token, oparg)) {
+ replace_string(&nic->sock_redirector0_ip, oparg);
+ } else if (MATCH_OPTION("sock_redirector0_port", token, oparg)) {
+ replace_string(&nic->sock_redirector0_port, oparg);
+ } else if (MATCH_OPTION("sock_redirector1_id", token, oparg)) {
+ replace_string(&nic->sock_redirector1_id, oparg);
+ } else if (MATCH_OPTION("sock_redirector1_ip", token, oparg)) {
+ replace_string(&nic->sock_redirector1_ip, oparg);
+ } else if (MATCH_OPTION("sock_redirector1_port", token, oparg)) {
+ replace_string(&nic->sock_redirector1_port, oparg);
+ } else if (MATCH_OPTION("sock_redirector2_id", token, oparg)) {
+ replace_string(&nic->sock_redirector2_id, oparg);
+ } else if (MATCH_OPTION("sock_redirector2_ip", token, oparg)) {
+ replace_string(&nic->sock_redirector2_ip, oparg);
+ } else if (MATCH_OPTION("sock_redirector2_port", token, oparg)) {
+ replace_string(&nic->sock_redirector2_port, oparg);
+ } else if (MATCH_OPTION("sock_compare_pri_in_id", token, oparg)) {
+ replace_string(&nic->sock_compare_pri_in_id, oparg);
+ } else if (MATCH_OPTION("sock_compare_pri_in_ip", token, oparg)) {
+ replace_string(&nic->sock_compare_pri_in_ip, oparg);
+ } else if (MATCH_OPTION("sock_compare_pri_in_port", token, oparg)) {
+ replace_string(&nic->sock_compare_pri_in_port, oparg);
+ } else if (MATCH_OPTION("filter_mirror_queue", token, oparg)) {
+ replace_string(&nic->filter_mirror_queue, oparg);
+ } else if (MATCH_OPTION("filter_mirror_outdev", token, oparg)) {
+ replace_string(&nic->filter_mirror_outdev, oparg);
+ } else if (MATCH_OPTION("filter_redirector0_queue", token, oparg)) {
+ replace_string(&nic->filter_redirector0_queue, oparg);
+ } else if (MATCH_OPTION("filter_redirector0_indev", token, oparg)) {
+ replace_string(&nic->filter_redirector0_indev, oparg);
+ } else if (MATCH_OPTION("filter_redirector0_outdev", token, oparg)) {
+ replace_string(&nic->filter_redirector0_outdev, oparg);
+ } else if (MATCH_OPTION("filter_redirector1_queue", token, oparg)) {
+ replace_string(&nic->filter_redirector1_queue, oparg);
+ } else if (MATCH_OPTION("filter_redirector1_indev", token, oparg)) {
+ replace_string(&nic->filter_redirector1_indev, oparg);
+ } else if (MATCH_OPTION("filter_redirector1_outdev", token, oparg)) {
+ replace_string(&nic->filter_redirector1_outdev, oparg);
+ } else if (MATCH_OPTION("colo_compare_pri_in", token, oparg)) {
+ replace_string(&nic->colo_compare_pri_in, oparg);
+ } else if (MATCH_OPTION("colo_compare_sec_in", token, oparg)) {
+ replace_string(&nic->colo_compare_sec_in, oparg);
+ } else if (MATCH_OPTION("colo_compare_out", token, oparg)) {
+ replace_string(&nic->colo_compare_out, oparg);
} else if (MATCH_OPTION("accel", token, oparg)) {
fprintf(stderr, "the accel parameter for vifs is currently not supported\n");
} else {
Qemu need this args to start userspace colo-proxy. Signed-off-by: Zhang Chen <zhangchen.fnst@cn.fujitsu.com> --- tools/libxl/libxl_dm.c | 104 ++++++++++++++++++++ tools/libxl/libxl_nic.c | 232 ++++++++++++++++++++++++++++++++++++++++++++ tools/libxl/libxl_types.idl | 31 +++++- tools/libxl/xl_cmdimpl.c | 58 +++++++++++ 4 files changed, 424 insertions(+), 1 deletion(-)