From nobody Sat Dec 21 14:34:58 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1726646151656911.5053319045401; Wed, 18 Sep 2024 00:55:51 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 39AA31AF4; Wed, 18 Sep 2024 03:55:50 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id D37891406; Wed, 18 Sep 2024 03:55:29 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 2749B1408; Wed, 18 Sep 2024 03:55:27 -0400 (EDT) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id A657B13FB for ; Wed, 18 Sep 2024 03:55:26 -0400 (EDT) Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-624-cEoctXlxP0We6t7NPyN-BQ-1; Wed, 18 Sep 2024 03:55:23 -0400 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id B7162195395E for ; Wed, 18 Sep 2024 07:55:22 +0000 (UTC) Received: from speedmetal.lan (unknown [10.45.242.5]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id AC9C419560A3 for ; Wed, 18 Sep 2024 07:55:21 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-0.5 required=5.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=3.4.4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1726646126; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=PadyQkU2DHnE2FHOIlV8MxC3BEfkva/3Z7qS0jTNDtE=; b=PFngVQ7UW/Q0ZQ+EpvvMoGbT+vVFgUZWknJ3xsCnhFEM0xMO45/i/4BOFX7wmyvjqENBK9 YyAgIrakG5EQkxe5NRNQ5GhAD1t+mLD1Ly3SNQ4zn5z9qp3WzkalD0O0dgBeUTJdKhhP1G FI7UD4uNCPDi8Kwrwh60RM6XdsvUfwQ= X-MC-Unique: cEoctXlxP0We6t7NPyN-BQ-1 From: Peter Krempa To: devel@lists.libvirt.org Subject: [PATCH 1/8] qemu: capabilities: Explain that QEMU_CAPS_CHARDEV_JSON will be used in tests only Date: Wed, 18 Sep 2024 09:55:11 +0200 Message-ID: <8671a6c98bf490658b1d59f3cf46ecd3f08de77c.1726646064.git.pkrempa@redhat.com> In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: K73KYMB5UHIBF4HNOQZD6XYAUDEGBNH3 X-Message-ID-Hash: K73KYMB5UHIBF4HNOQZD6XYAUDEGBNH3 X-MailFrom: pkrempa@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-config-1; header-match-config-2; header-match-config-3; header-match-devel.lists.libvirt.org-0; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header X-Mailman-Version: 3.2.2 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1726646152719116600 Content-Type: text/plain; charset="utf-8" I've added that capability a long time ago when I was converting various stuff to use JSON but the support in '-chardev' didn't yet materialize. Fix the comment to make that clear and also that it'll be used in tests for the upcoming refactor of the chardev code (so that we can validate generator against the schema even if that doesn't yet work). Signed-off-by: Peter Krempa Reviewed-by: J=C3=A1n Tomko --- src/qemu/qemu_capabilities.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 5036d49aab..736d34179e 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -623,7 +623,7 @@ typedef enum { /* virQEMUCapsFlags grouping marker for = syntax-check */ QEMU_CAPS_MEMORY_BACKEND_RESERVE, /* -object memory-backend-*.reserve= =3D */ QEMU_CAPS_PIIX4_ACPI_ROOT_PCI_HOTPLUG, /* -M pc PIIX4_PM.acpi-root-pci= -hotplug */ QEMU_CAPS_NETDEV_JSON, /* -netdev accepts JSON */ - QEMU_CAPS_CHARDEV_JSON, /* -chardev accepts JSON */ + QEMU_CAPS_CHARDEV_JSON, /* Reserved for '-chardev' JSON support. For n= ow used only in tests. */ /* 415 */ X_QEMU_CAPS_DEVICE_JSON_BROKEN_HOTPLUG, /* -device accepts JSON (must = not be used - users are filtering the capbility) */ --=20 2.46.0 From nobody Sat Dec 21 14:34:58 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1726646227359494.61296206857355; Wed, 18 Sep 2024 00:57:07 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 4A90C197E; Wed, 18 Sep 2024 03:57:06 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 7BABB15A2; Wed, 18 Sep 2024 03:55:34 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id C8FD813FC; Wed, 18 Sep 2024 03:55:27 -0400 (EDT) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id AF72613FC for ; Wed, 18 Sep 2024 03:55:26 -0400 (EDT) Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-58-dp1AkYrmMWCPJ4tPefkgUw-1; Wed, 18 Sep 2024 03:55:24 -0400 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id E6CA619560B4 for ; Wed, 18 Sep 2024 07:55:23 +0000 (UTC) Received: from speedmetal.lan (unknown [10.45.242.5]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id F0B0119560A3 for ; Wed, 18 Sep 2024 07:55:22 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-0.5 required=5.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=3.4.4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1726646126; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=2jBEpvYxHxt+ha1VkqbyK245fosJVDph2Nn3hRc3mHQ=; b=B3udacadFsptDiq6VfbwiaORH6IZSOVY1414qC3sMkXCU7TKFkSXMvwKqmDkGheZEMbRIr vwd2Gb87tFAhCV9+O8TQ+8Urd3pQ2rWTbGpkKiV9HxX/aTLanmDORAI571mf7nLXh3CtkO AZYJ9qFc6uVPcN0lq2WoQ6yY9phUNN0= X-MC-Unique: dp1AkYrmMWCPJ4tPefkgUw-1 From: Peter Krempa To: devel@lists.libvirt.org Subject: [PATCH 2/8] qemuxmlconftest: Add 'chardev-backends' test case Date: Wed, 18 Sep 2024 09:55:12 +0200 Message-ID: In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: V3XQVWKMFBNSKSQAUR3WUQIUXI4FN6PF X-Message-ID-Hash: V3XQVWKMFBNSKSQAUR3WUQIUXI4FN6PF X-MailFrom: pkrempa@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-config-1; header-match-config-2; header-match-config-3; header-match-devel.lists.libvirt.org-0; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header X-Mailman-Version: 3.2.2 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1726646228727116600 Content-Type: text/plain; charset="utf-8" The test case attempts to test as many of the chardev backends as possible by adding channels with various configs. The idea is to have a representative sample which will later be used also for QMP schema testing. Signed-off-by: Peter Krempa Reviewed-by: J=C3=A1n Tomko --- .../chardev-backends.x86_64-latest.args | 79 ++++++++++ .../chardev-backends.x86_64-latest.xml | 149 ++++++++++++++++++ tests/qemuxmlconfdata/chardev-backends.xml | 111 +++++++++++++ tests/qemuxmlconftest.c | 2 + 4 files changed, 341 insertions(+) create mode 100644 tests/qemuxmlconfdata/chardev-backends.x86_64-latest.ar= gs create mode 100644 tests/qemuxmlconfdata/chardev-backends.x86_64-latest.xml create mode 100644 tests/qemuxmlconfdata/chardev-backends.xml diff --git a/tests/qemuxmlconfdata/chardev-backends.x86_64-latest.args b/te= sts/qemuxmlconfdata/chardev-backends.x86_64-latest.args new file mode 100644 index 0000000000..4e6e17587e --- /dev/null +++ b/tests/qemuxmlconfdata/chardev-backends.x86_64-latest.args @@ -0,0 +1,79 @@ +LC_ALL=3DC \ +PATH=3D/bin \ +HOME=3D/var/lib/libvirt/qemu/domain--1-QEMUGuest1 \ +USER=3Dtest \ +LOGNAME=3Dtest \ +XDG_DATA_HOME=3D/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.local/share \ +XDG_CACHE_HOME=3D/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.cache \ +XDG_CONFIG_HOME=3D/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.config \ +/usr/bin/qemu-system-x86_64 \ +-name guest=3DQEMUGuest1,debug-threads=3Don \ +-S \ +-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/va= r/lib/libvirt/qemu/domain--1-QEMUGuest1/master-key.aes"}' \ +-machine pc,usb=3Doff,dump-guest-core=3Doff,memory-backend=3Dpc.ram,acpi= =3Doff \ +-accel tcg \ +-cpu qemu64 \ +-m size=3D219136k \ +-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}'= \ +-overcommit mem-lock=3Doff \ +-smp 1,sockets=3D1,cores=3D1,threads=3D1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-no-user-config \ +-nodefaults \ +-chardev socket,id=3Dcharmonitor,fd=3D1729,server=3Don,wait=3Doff \ +-mon chardev=3Dcharmonitor,id=3Dmonitor,mode=3Dcontrol \ +-rtc base=3Dutc \ +-no-shutdown \ +-boot strict=3Don \ +-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0= x2"}' \ +-device '{"driver":"virtio-serial-pci","id":"virtio-serial0","bus":"pci.0"= ,"addr":"0x3"}' \ +-chardev parallel,id=3Dcharparallel0,path=3D/dev/parport0 \ +-device '{"driver":"isa-parallel","chardev":"charparallel0","id":"parallel= 0"}' \ +-add-fd set=3D0,fd=3D1751,opaque=3Dchannel0-log \ +-chardev null,id=3Dcharchannel0,logfile=3D/dev/fdset/0,logappend=3Don \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":1,"chard= ev":"charchannel0","id":"channel0","name":"chardev-null"}' \ +-add-fd set=3D1,fd=3D1751,opaque=3Dchannel1-log \ +-chardev vc,id=3Dcharchannel1,logfile=3D/dev/fdset/1,logappend=3Don \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":2,"chard= ev":"charchannel1","id":"channel1","name":"chardev-vc"}' \ +-chardev pty,id=3Dcharchannel2 \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":3,"chard= ev":"charchannel2","id":"channel2","name":"chardev-pty"}' \ +-chardev stdio,id=3Dcharchannel3 \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":4,"chard= ev":"charchannel3","id":"channel3","name":"chardev-stdio"}' \ +-add-fd set=3D2,fd=3D1750,opaque=3Dchannel4-source \ +-chardev file,id=3Dcharchannel4,path=3D/dev/fdset/2,append=3Don \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":5,"chard= ev":"charchannel4","id":"channel4","name":"chardev-file"}' \ +-chardev pipe,id=3Dcharchannel5,path=3D/path/to/pipe \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":6,"chard= ev":"charchannel5","id":"channel5","name":"chardev-pipe"}' \ +-chardev serial,id=3Dcharchannel6,path=3D/path/to/device \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":7,"chard= ev":"charchannel6","id":"channel6","name":"chardev-dev"}' \ +-chardev socket,id=3Dcharchannel7,fd=3D1729,server=3Don,wait=3Doff \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":8,"chard= ev":"charchannel7","id":"channel7","name":"chardev-unix-listen"}' \ +-chardev socket,id=3Dcharchannel8,path=3D/path/to/unix-listen,reconnect=3D= 2 \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":9,"chard= ev":"charchannel8","id":"channel8","name":"chardev-unix-connect"}' \ +-chardev socket,id=3Dcharchannel9,host=3D1.2.3.4,port=3D5678,server=3Don,w= ait=3Doff \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":10,"char= dev":"charchannel9","id":"channel9","name":"chardev-tcp-listen-raw"}' \ +-chardev socket,id=3Dcharchannel10,host=3D1.2.3.4,port=3D5679,telnet=3Don,= server=3Don,wait=3Doff \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":11,"char= dev":"charchannel10","id":"channel10","name":"chardev-tcp-listen-telnet"}' \ +-object '{"qom-type":"tls-creds-x509","id":"objcharchannel11_tls0","dir":"= /etc/pki/libvirt-chardev","endpoint":"client","verify-peer":true}' \ +-chardev socket,id=3Dcharchannel11,host=3D1.2.3.4,port=3D5678,reconnect=3D= 2,tls-creds=3Dobjcharchannel11_tls0 \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":12,"char= dev":"charchannel11","id":"channel11","name":"chardev-tcp-connect-raw"}' \ +-object '{"qom-type":"tls-creds-x509","id":"objcharchannel12_tls0","dir":"= /etc/pki/libvirt-chardev","endpoint":"client","verify-peer":true}' \ +-chardev socket,id=3Dcharchannel12,host=3Dhostname.global.,port=3D5679,tel= net=3Don,reconnect=3D2,tls-creds=3Dobjcharchannel12_tls0 \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":13,"char= dev":"charchannel12","id":"channel12","name":"chardev-tcp-connect-telnet"}'= \ +-chardev udp,id=3Dcharchannel13,host=3D127.0.0.1,port=3D2222,localaddr=3D,= localport=3D0 \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":14,"char= dev":"charchannel13","id":"channel13","name":"chardev-udp-nobind"}' \ +-chardev udp,id=3Dcharchannel14,host=3D127.0.0.1,port=3D2222,localaddr=3D1= 27.0.0.1,localport=3D1111 \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":15,"char= dev":"charchannel14","id":"channel14","name":"chardev-udp-bind"}' \ +-chardev spicevmc,id=3Dcharchannel15,name=3Dvdagent \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":16,"char= dev":"charchannel15","id":"channel15","name":"com.redhat.spice.0"}' \ +-chardev qemu-vdagent,id=3Dcharchannel16,name=3Dvdagent,clipboard=3Doff,mo= use=3Doff \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":17,"char= dev":"charchannel16","id":"channel16","name":"chardev-vdagent"}' \ +-chardev dbus,id=3Dcharchannel17,name=3Dtest.channel.0 \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":18,"char= dev":"charchannel17","id":"channel17","name":"chardev-dbus"}' \ +-chardev spiceport,id=3Dcharchannel18,name=3Dtest.channel.0 \ +-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":19,"char= dev":"charchannel18","id":"channel18","name":"chardev-spiceport"}' \ +-audiodev '{"id":"audio1","driver":"spice"}' \ +-spice port=3D5901,addr=3D0.0.0.0,seamless-migration=3Don \ +-device '{"driver":"cirrus-vga","id":"video0","bus":"pci.0","addr":"0x2"}'= \ +-sandbox on,obsolete=3Ddeny,elevateprivileges=3Ddeny,spawn=3Ddeny,resource= control=3Ddeny \ +-msg timestamp=3Don diff --git a/tests/qemuxmlconfdata/chardev-backends.x86_64-latest.xml b/tes= ts/qemuxmlconfdata/chardev-backends.x86_64-latest.xml new file mode 100644 index 0000000000..57a16b5b7c --- /dev/null +++ b/tests/qemuxmlconfdata/chardev-backends.x86_64-latest.xml @@ -0,0 +1,149 @@ + + QEMUGuest1 + c7a5fdbd-edaf-9455-926a-d65c16db1809 + 219100 + 219100 + 1 + + hvm + + + + qemu64 + + + destroy + restart + destroy + + /usr/bin/qemu-system-x86_64 + +
+ + + +
+ + + + + + + + +
+ + + + +
+ + + +
+ + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + + + +
+ + + + + +
+ + + + + +
+ + + + + + + +
+ + + + + + + +
+ + + + +
+ + + + + +
+ + + +
+ + + + + + + +
+ + + + +
+ + + + +
+ + + + + + +