Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libstorage-ng for openSUSE:Factory checked in at 2023-04-22 21:57:22 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libstorage-ng (Old) and /work/SRC/openSUSE:Factory/.libstorage-ng.new.1533 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libstorage-ng" Sat Apr 22 21:57:22 2023 rev:215 rq:1081245 version:4.5.97 Changes: -------- --- /work/SRC/openSUSE:Factory/libstorage-ng/libstorage-ng.changes 2023-04-20 15:13:52.473792031 +0200 +++ /work/SRC/openSUSE:Factory/.libstorage-ng.new.1533/libstorage-ng.changes 2023-04-22 21:57:58.760326435 +0200 @@ -1,0 +2,8 @@ +Fri Apr 21 11:28:00 UTC 2023 - aschn...@suse.com + +- merge gh#openSUSE/libstorage-ng#926 +- reimplemented transport detection for NVMe (bsc#1210144) +- added tests for nvme parsers +- 4.5.97 + +-------------------------------------------------------------------- Old: ---- libstorage-ng-4.5.96.tar.xz New: ---- libstorage-ng-4.5.97.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libstorage-ng.spec ++++++ --- /var/tmp/diff_new_pack.PAdGiD/_old 2023-04-22 21:57:59.588331385 +0200 +++ /var/tmp/diff_new_pack.PAdGiD/_new 2023-04-22 21:57:59.596331434 +0200 @@ -18,7 +18,7 @@ %define libname %{name}1 Name: libstorage-ng -Version: 4.5.96 +Version: 4.5.97 Release: 0 Summary: Library for storage management License: GPL-2.0-only ++++++ libstorage-ng-4.5.96.tar.xz -> libstorage-ng-4.5.97.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libstorage-ng-4.5.96/LIBVERSION new/libstorage-ng-4.5.97/LIBVERSION --- old/libstorage-ng-4.5.96/LIBVERSION 2023-04-18 09:14:43.000000000 +0200 +++ new/libstorage-ng-4.5.97/LIBVERSION 2023-04-21 13:28:00.000000000 +0200 @@ -1 +1 @@ -1.86.0 +1.86.1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libstorage-ng-4.5.96/VERSION new/libstorage-ng-4.5.97/VERSION --- old/libstorage-ng-4.5.96/VERSION 2023-04-18 09:14:43.000000000 +0200 +++ new/libstorage-ng-4.5.97/VERSION 2023-04-21 13:28:00.000000000 +0200 @@ -1 +1 @@ -4.5.96 +4.5.97 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libstorage-ng-4.5.96/doc/nvme.md new/libstorage-ng-4.5.97/doc/nvme.md --- old/libstorage-ng-4.5.96/doc/nvme.md 1970-01-01 01:00:00.000000000 +0100 +++ new/libstorage-ng-4.5.97/doc/nvme.md 2023-04-21 13:28:00.000000000 +0200 @@ -0,0 +1,21 @@ + +Detection of Transport for NVMe Disks +------------------------------------- + +- Get output of 'nvme list-subsys --output=json'. + +- Check /sys/module/nvme_core/parameters/multipath to see whether + native or non-native multipath is used. + +- Native multipath: + + For the disk "/dev/nvmeXnY", look for the subsys with name + "nvme-subsysX" in the output of 'nvme list-subsys'. Take the + transport of any path of that subsys. + +- Non-native multipath: + + For the disk "/dev/nvmeXnY", look for the path (in any subsys) with + the name "nvmeX" in the output of 'nvme list-subsys' and take its + transport. + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libstorage-ng-4.5.96/storage/Devices/DiskImpl.cc new/libstorage-ng-4.5.97/storage/Devices/DiskImpl.cc --- old/libstorage-ng-4.5.96/storage/Devices/DiskImpl.cc 2023-04-18 09:14:43.000000000 +0200 +++ new/libstorage-ng-4.5.97/storage/Devices/DiskImpl.cc 2023-04-21 13:28:00.000000000 +0200 @@ -153,20 +153,6 @@ } - string - Disk::Impl::get_nvme_controller() const - { - static const regex nvme_name_rx(DEV_DIR "/nvme([0-9]+)n([0-9]+)", regex::extended); - - smatch match; - - if (!regex_match(get_name(), match, nvme_name_rx)) - ST_THROW(Exception("failed to extract nvme controller")); - - return "nvme" + match[1].str(); - } - - bool Disk::Impl::is_brd() const { @@ -228,25 +214,9 @@ if (is_nvme()) { system_info.getCmdNvmeList(); // so far just for logging + const CmdNvmeListSubsys& cmd_nvme_list_subsys = system_info.getCmdNvmeListSubsys(); -#if 0 - const File& transport_file = system_info.getFile(SYSFS_DIR "/class/nvme/" + get_nvme_controller() + - "/transport"); - string tmp = transport_file.get<string>(); - - if (tmp == "pcie") - transport = Transport::PCIE; - else if (tmp == "fc") - transport = Transport::FC; - else if (tmp == "tcp") - transport = Transport::TCP; - else if (tmp == "rdma") - transport = Transport::RDMA; - else if (tmp == "loop") - transport = Transport::LOOP; - else - y2err("unknown NVMe transport"); -#endif + transport = cmd_nvme_list_subsys.get_transport(get_name(), system_info); } else if (is_pmem() || is_brd()) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libstorage-ng-4.5.96/storage/Devices/DiskImpl.h new/libstorage-ng-4.5.97/storage/Devices/DiskImpl.h --- old/libstorage-ng-4.5.96/storage/Devices/DiskImpl.h 2023-04-18 09:14:43.000000000 +0200 +++ new/libstorage-ng-4.5.97/storage/Devices/DiskImpl.h 2023-04-21 13:28:00.000000000 +0200 @@ -89,10 +89,7 @@ void set_zone_model(ZoneModel zone_model) { Impl::zone_model = zone_model; } bool is_pmem() const; - bool is_nvme() const; - string get_nvme_controller() const; - bool is_brd() const; const string& get_image_filename() const { return image_filename; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libstorage-ng-4.5.96/storage/Devices/Multipath.h new/libstorage-ng-4.5.97/storage/Devices/Multipath.h --- old/libstorage-ng-4.5.96/storage/Devices/Multipath.h 2023-04-18 09:14:43.000000000 +0200 +++ new/libstorage-ng-4.5.97/storage/Devices/Multipath.h 2023-04-21 13:28:00.000000000 +0200 @@ -32,7 +32,7 @@ /** - * A multipath device. Not for NVMe multipath. + * A multipath device. Not for native NVMe multipath. */ class Multipath : public Partitionable { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libstorage-ng-4.5.96/storage/SystemInfo/CmdNvme.cc new/libstorage-ng-4.5.97/storage/SystemInfo/CmdNvme.cc --- old/libstorage-ng-4.5.96/storage/SystemInfo/CmdNvme.cc 2023-04-18 09:14:43.000000000 +0200 +++ new/libstorage-ng-4.5.97/storage/SystemInfo/CmdNvme.cc 2023-04-21 13:28:00.000000000 +0200 @@ -20,10 +20,17 @@ */ +#include <regex> + #include "storage/SystemInfo/CmdNvme.h" +#include "storage/SystemInfo/DevAndSys.h" +#include "storage/SystemInfo/SystemInfoImpl.h" #include "storage/Utils/StorageDefines.h" #include "storage/Utils/SystemCmd.h" #include "storage/Utils/JsonFile.h" +#include "storage/Utils/ExceptionImpl.h" +#include "storage/Utils/LoggerImpl.h" +#include "storage/Utils/StorageTmpl.h" namespace storage @@ -40,7 +47,205 @@ void CmdNvmeList::parse(const vector<string>& lines) { - JsonFile json_file(lines); + JsonFile json_file(lines); + } + + + CmdNvmeListSubsys::CmdNvmeListSubsys() + { + SystemCmd cmd(NVME_BIN " list-subsys --verbose --output json", SystemCmd::DoThrow); + + parse(cmd.stdout()); + } + + + void + CmdNvmeListSubsys::parse(const vector<string>& lines) + { + JsonFile json_file(lines); + + string tmp; + + if (!json_object_is_type(json_file.get_root(), json_type_array)) + ST_THROW(Exception("not an array in output of 'nvme list-subsys'")); + + for (size_t i = 0; i < json_object_array_length(json_file.get_root()); ++i) + { + json_object* tmp1 = json_object_array_get_idx(json_file.get_root(), i); + + Something something; + + if (!get_child_value(tmp1, "HostNQN", something.host_nqn)) + ST_THROW(Exception("\"HostNQN\" not found in json output of 'nvme list-subsys'")); + + if (!get_child_value(tmp1, "HostID", something.host_id)) + ST_THROW(Exception("\"HostID\" not found in json output of 'nvme list-subsys'")); + + vector<json_object*> tmp2; + if (!get_child_nodes(tmp1, "Subsystems", tmp2)) + ST_THROW(Exception("\"Controllers\" not found in json output of 'nvme list-subsys'")); + + for (json_object* tmp3 : tmp2) + { + Subsystem subsystem; + + if (!get_child_value(tmp3, "Name", subsystem.name)) + ST_THROW(Exception("\"Name\" not found in json output of 'nvme list-subsys'")); + + if (!get_child_value(tmp3, "NQN", subsystem.nqn)) + ST_THROW(Exception("\"NQN\" not found in json output of 'nvme list-subsys'")); + + vector<json_object*> tmp4; + if (!get_child_nodes(tmp3, "Paths", tmp4)) + ST_THROW(Exception("\"Paths\" not found in json output of 'nvme list-subsys'")); + + for (json_object* tmp5 : tmp4) + { + Path path; + + if (!get_child_value(tmp5, "Name", path.name)) + ST_THROW(Exception("\"Name\" not found in json output of 'nvme list-subsys'")); + + if (!get_child_value(tmp5, "Transport", tmp)) + ST_THROW(Exception("\"Transport\" not found in json output of 'nvme list'")); + + if (tmp == "pcie") + path.transport = Transport::PCIE; + else if (tmp == "fc") + path.transport = Transport::FC; + else if (tmp == "tcp") + path.transport = Transport::TCP; + else if (tmp == "rdma") + path.transport = Transport::RDMA; + else if (tmp == "loop") + path.transport = Transport::LOOP; + else + y2err("unknown NVMe transport"); + + subsystem.paths.push_back(path); + } + + something.subsystems.push_back(subsystem); + } + + somethings.push_back(something); + } + + y2mil(*this); + } + + + Transport + CmdNvmeListSubsys::get_transport(const string& device, SystemInfo::Impl& system_info) const + { + static const regex nvme_name_rx(DEV_DIR "/nvme([0-9]+)n([0-9]+)", regex::extended); + + smatch match; + + if (!regex_match(device, match, nvme_name_rx)) + ST_THROW(Exception("failed to extract nvme subsystem/path")); + + if (is_native_multipath(system_info)) + { + string name = "nvme-subsys" + match[1].str(); + const CmdNvmeListSubsys::Subsystem& subsystem = find_subsystem_by_name(name); + + if (subsystem.paths.empty()) + ST_THROW(Exception("nvme subsystem has no paths")); + + // TODO in theory there could be several paths with different transports + return subsystem.paths[0].transport; + } + else + { + string name = "nvme" + match[1].str(); + const CmdNvmeListSubsys::Path& path = find_path_by_name(name); + + return path.transport; + } + } + + + bool + CmdNvmeListSubsys::is_native_multipath(SystemInfo::Impl& system_info) const + { + const File& multipath_file = system_info.getFile(SYSFS_DIR "/module/nvme_core/parameters/multipath"); + return multipath_file.get<string>() == "Y"; + } + + + const CmdNvmeListSubsys::Subsystem& + CmdNvmeListSubsys::find_subsystem_by_name(const string& name) const + { + for (const Something& something : somethings) + { + for (const Subsystem& subsystem : something.subsystems) + { + if (subsystem.name == name) + return subsystem; + } + } + + ST_THROW(Exception("nvme subsystem not found")); + } + + + const CmdNvmeListSubsys::Path& + CmdNvmeListSubsys::find_path_by_name(const string& name) const + { + for (const Something& something : somethings) + { + for (const Subsystem& subsystem : something.subsystems) + { + for (const Path& path : subsystem.paths) + { + if (path.name == name) + return path; + } + } + } + + ST_THROW(Exception("nvme path not found")); + } + + + std::ostream& + operator<<(std::ostream& s, const CmdNvmeListSubsys& cmd_nvme_list_subsys) + { + for (const CmdNvmeListSubsys::Something& something : cmd_nvme_list_subsys.somethings) + s << something << '\n'; + + return s; + } + + + std::ostream& + operator<<(std::ostream& s, const CmdNvmeListSubsys::Something& something) + { + s << "host-nqn:" << something.host_nqn << ", host-id:" << something.host_id + << ", subsystems: " << something.subsystems; + + return s; + } + + + std::ostream& + operator<<(std::ostream& s, const CmdNvmeListSubsys::Subsystem& subsystem) + { + s << "name:" << subsystem.name << "nqn:" << subsystem.nqn + << ", paths:" << subsystem.paths; + + return s; + } + + + std::ostream& + operator<<(std::ostream& s, const CmdNvmeListSubsys::Path& path) + { + s << "name:" << path.name << ", transport: " + << get_transport_name(path.transport); + + return s; } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libstorage-ng-4.5.96/storage/SystemInfo/CmdNvme.h new/libstorage-ng-4.5.97/storage/SystemInfo/CmdNvme.h --- old/libstorage-ng-4.5.96/storage/SystemInfo/CmdNvme.h 2023-04-18 09:14:43.000000000 +0200 +++ new/libstorage-ng-4.5.97/storage/SystemInfo/CmdNvme.h 2023-04-21 13:28:00.000000000 +0200 @@ -27,6 +27,9 @@ #include <string> #include <vector> +#include "storage/Devices/Disk.h" +#include "storage/SystemInfo/SystemInfo.h" + namespace storage { @@ -47,7 +50,62 @@ private: - void parse(const vector<string>& lines); + void parse(const vector<string>& lines); + + }; + + + /** + * Class to run the command "nvme list-subsys". + */ + class CmdNvmeListSubsys + { + + public: + + CmdNvmeListSubsys(); + + struct Path + { + string name; + Transport transport; + }; + + struct Subsystem + { + string name; + string nqn; + vector<Path> paths; + }; + + struct Something + { + string host_nqn; + string host_id; + vector<Subsystem> subsystems; + }; + + /** + * Get the transport for a device. Checks whether the system is using native or + * non-native NVMe multipath. + */ + Transport get_transport(const string& device, SystemInfo::Impl& system_info) const; + + friend std::ostream& operator<<(std::ostream& s, const CmdNvmeListSubsys& cmd_nvme_list_subsys); + friend std::ostream& operator<<(std::ostream& s, const Path& path); + friend std::ostream& operator<<(std::ostream& s, const Subsystem& subsystem); + friend std::ostream& operator<<(std::ostream& s, const Something& somethings); + + private: + + void parse(const vector<string>& lines); + + bool is_native_multipath(SystemInfo::Impl& system_info) const; + + const Subsystem& find_subsystem_by_name(const string& name) const; + const Path& find_path_by_name(const string& name) const; + + vector<Something> somethings; }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libstorage-ng-4.5.96/storage/SystemInfo/SystemInfoImpl.h new/libstorage-ng-4.5.97/storage/SystemInfo/SystemInfoImpl.h --- old/libstorage-ng-4.5.96/storage/SystemInfo/SystemInfoImpl.h 2023-04-18 09:14:43.000000000 +0200 +++ new/libstorage-ng-4.5.97/storage/SystemInfo/SystemInfoImpl.h 2023-04-21 13:28:00.000000000 +0200 @@ -94,6 +94,7 @@ const Blkid& getBlkid() { return blkid.get(); } const Lsscsi& getLsscsi() { return lsscsi.get(); } const CmdNvmeList& getCmdNvmeList() { return cmd_nvme_list.get(); } + const CmdNvmeListSubsys& getCmdNvmeListSubsys() { return cmd_nvme_list_subsys.get(); } const Parted& getParted(const string& device) { return parteds.get(device); } const Dasdview& getDasdview(const string& device) { return dasdviews.get(device); } const CmdDmsetupInfo& getCmdDmsetupInfo() { return cmd_dmsetup_info.get(); } @@ -249,6 +250,7 @@ LazyObject<Blkid> blkid; LazyObject<Lsscsi> lsscsi; LazyObject<CmdNvmeList> cmd_nvme_list; + LazyObject<CmdNvmeListSubsys> cmd_nvme_list_subsys; LazyObjects<Parted> parteds; LazyObjects<Dasdview> dasdviews; LazyObject<CmdDmsetupInfo> cmd_dmsetup_info; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libstorage-ng-4.5.96/testsuite/SystemInfo/Makefile.am new/libstorage-ng-4.5.97/testsuite/SystemInfo/Makefile.am --- old/libstorage-ng-4.5.96/testsuite/SystemInfo/Makefile.am 2023-04-18 09:14:43.000000000 +0200 +++ new/libstorage-ng-4.5.97/testsuite/SystemInfo/Makefile.am 2023-04-21 13:28:00.000000000 +0200 @@ -19,7 +19,8 @@ mdadm-detail.test mdlinks.test \ parted-34.test parted-35.test \ proc-mdstat.test proc-mounts.test pvs.test systeminfo.test \ - udevadm-info.test vgs.test multipath.test + udevadm-info.test vgs.test multipath.test nvme-list.test \ + nvme-list-subsys.test AM_DEFAULT_SOURCE_EXT = .cc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libstorage-ng-4.5.96/testsuite/SystemInfo/nvme-list-subsys.cc new/libstorage-ng-4.5.97/testsuite/SystemInfo/nvme-list-subsys.cc --- old/libstorage-ng-4.5.96/testsuite/SystemInfo/nvme-list-subsys.cc 1970-01-01 01:00:00.000000000 +0100 +++ new/libstorage-ng-4.5.97/testsuite/SystemInfo/nvme-list-subsys.cc 2023-04-21 13:28:00.000000000 +0200 @@ -0,0 +1,70 @@ + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE libstorage + +#include <numeric> +#include <boost/test/unit_test.hpp> + +#include "storage/SystemInfo/CmdNvme.h" +#include "storage/Utils/Mockup.h" +#include "storage/Utils/SystemCmd.h" +#include "storage/Utils/StorageDefines.h" + + +using namespace std; +using namespace storage; + + +void +check(const vector<string>& input, const vector<string>& output) +{ + Mockup::set_mode(Mockup::Mode::PLAYBACK); + Mockup::set_command(NVME_BIN " list-subsys --verbose --output json", input); + + CmdNvmeListSubsys cmd_nvme_list_subsys; + + ostringstream parsed; + parsed.setf(std::ios::boolalpha); + parsed << cmd_nvme_list_subsys; + + string lhs = parsed.str(); + string rhs = accumulate(output.begin(), output.end(), ""s, + [](auto a, auto b) { return a + b + "\n"; }); + + BOOST_CHECK_EQUAL(lhs, rhs); +} + + +BOOST_AUTO_TEST_CASE(parse1) +{ + set_logger(get_stdout_logger()); + + vector<string> input = { + "[", + " {", + " \"HostNQN\":\"nqn.2014-08.org.nvmexpress:uuid:afb211cc-32bb-11b2-a85c-8b99b656b4d1\",", + " \"HostID\":\"8aa6c093-7e2f-4b59-93dd-9374345abed8\",", + " \"Subsystems\":[", + " {", + " \"Name\":\"nvme-subsys0\",", + " \"NQN\":\"nqn.2014.08.org.nvmexpress:17aa17aa1142267006586\",", + " \"Paths\":[", + " {", + " \"Name\":\"nvme0\",", + " \"Transport\":\"pcie\",", + " \"Address\":\"0000:3e:00.0\",", + " \"State\":\"live\"", + " }", + " ]", + " }", + " ]", + " }", + "]" + }; + + vector<string> output = { + "host-nqn:nqn.2014-08.org.nvmexpress:uuid:afb211cc-32bb-11b2-a85c-8b99b656b4d1, host-id:8aa6c093-7e2f-4b59-93dd-9374345abed8, subsystems: <name:nvme-subsys0nqn:nqn.2014.08.org.nvmexpress:17aa17aa1142267006586, paths:<name:nvme0, transport: PCIe>>" + }; + + check(input, output); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libstorage-ng-4.5.96/testsuite/SystemInfo/nvme-list.cc new/libstorage-ng-4.5.97/testsuite/SystemInfo/nvme-list.cc --- old/libstorage-ng-4.5.96/testsuite/SystemInfo/nvme-list.cc 1970-01-01 01:00:00.000000000 +0100 +++ new/libstorage-ng-4.5.97/testsuite/SystemInfo/nvme-list.cc 2023-04-21 13:28:00.000000000 +0200 @@ -0,0 +1,77 @@ + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE libstorage + +#include <numeric> +#include <boost/test/unit_test.hpp> + +#include "storage/SystemInfo/CmdNvme.h" +#include "storage/Utils/Mockup.h" +#include "storage/Utils/SystemCmd.h" +#include "storage/Utils/StorageDefines.h" + + +using namespace std; +using namespace storage; + + +void +check(const vector<string>& input, const vector<string>& output) +{ + Mockup::set_mode(Mockup::Mode::PLAYBACK); + Mockup::set_command(NVME_BIN " list --verbose --output json", input); + + CmdNvmeList cmd_nvme_list; +} + + +BOOST_AUTO_TEST_CASE(parse1) +{ + set_logger(get_stdout_logger()); + + vector<string> input = { + "{", + " \"Devices\":[", + " {", + " \"HostNQN\":\"nqn.2014-08.org.nvmexpress:uuid:afb211cc-32bb-11b2-a85c-8b99b656b4d1\",", + " \"HostID\":\"8aa6c093-7e2f-4b59-93dd-9374345abed8\",", + " \"Subsystems\":[", + " {", + " \"Subsystem\":\"nvme-subsys0\",", + " \"SubsystemNQN\":\"nqn.2014.08.org.nvmexpress:17aa17aa1142267006586\",", + " \"Controllers\":[", + " {", + " \"Controller\":\"nvme0\",", + " \"SerialNumber\":\"1142267006586\",", + " \"ModelNumber\":\"LENSE20512GMSP34MEAT2TA\",", + " \"Firmware\":\"2.8.8341\",", + " \"Transport\":\"pcie\",", + " \"Address\":\"0000:3e:00.0\",", + " \"Namespaces\":[", + " {", + " \"NameSpace\":\"nvme0n1\",", + " \"NSID\":1,", + " \"UsedBytes\":0,", + " \"MaximumLBA\":1000215216,", + " \"PhysicalSize\":-2147483648,", + " \"SectorSize\":512", + " }", + " ],", + " \"Paths\":[", + " ]", + " }", + " ],", + " \"Namespaces\":[", + " ]", + " }", + " ]", + " }", + " ]", + "}" + }; + + vector<string> output = { + }; + + check(input, output); +}