diff --git a/README.md b/README.md index 2dd607c62a2..80c98f94fbb 100644 --- a/README.md +++ b/README.md @@ -63,3 +63,11 @@ Copyright 2002-2024, OpenNebula Project, OpenNebula Systems (formerly C12G Labs) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +## Acknowledgements + +Some of the software features included in this repository have been made possible through the funding of the following innovation projects: + +- [SovereignEdge.Cognit](https://cognit.sovereignedge.eu/) (Grant Agreement 101092711), through the European Union’s Horizon Europe Research and Innovation Programme. +- [ONEedge5G](https://opennebula.io/innovation/oneedge5g/) (Grant Agreement TSI-064200-2023-1), supported by the Spanish Ministry for Digital Transformation and Civil Service through the UNICO I+D 6G Program, co-funded by the European Union – NextGenerationEU through the Recovery and Resilience Facility (RRF). +- [ONEnextgen](http://onenextgen.eu/) (Grant Agreement UNICO IPCEI-2023-003), supported by the Spanish Ministry for Digital Transformation and Civil Service through the UNICO IPCEI Program, co-funded by the European Union – NextGenerationEU through the Recovery and Resilience Facility (RRF). diff --git a/include/Backups.h b/include/Backups.h index 138700ad9c8..48471caa83c 100644 --- a/include/Backups.h +++ b/include/Backups.h @@ -175,6 +175,11 @@ class Backups config.replace("LAST_BACKUP_SIZE", size); } + void last_backup_format(const std::string& format) + { + config.replace("LAST_BACKUP_FORMAT", format); + } + void last_increment_id(int id) { config.replace("LAST_INCREMENT_ID", id); @@ -224,6 +229,15 @@ class Backups return sz; } + std::string last_backup_format() const + { + std::string fmt; + + config.get("LAST_BACKUP_FORMAT", fmt); + + return fmt; + } + int last_increment_id() const { int id; @@ -283,6 +297,8 @@ class Backups config.erase("LAST_BACKUP_ID"); config.erase("LAST_BACKUP_SIZE"); + + config.erase("LAST_BACKUP_FORMAT"); } /** diff --git a/include/DocumentPool.h b/include/DocumentPool.h index d08111713ae..32b33b7c37c 100644 --- a/include/DocumentPool.h +++ b/include/DocumentPool.h @@ -61,10 +61,10 @@ class DocumentPool : public PoolSQL int * oid, std::string& error_str) { - *oid = PoolSQL::allocate( - new Document(-1, uid, gid, uname, gname, umask, type, - std::move(template_contents)), - error_str); + Document doc {-1, uid, gid, uname, gname, umask, type, + std::move(template_contents)}; + + *oid = PoolSQL::allocate(doc, error_str); return *oid; } diff --git a/include/Driver.h b/include/Driver.h index 733c6750a53..ac8b9c06238 100644 --- a/include/Driver.h +++ b/include/Driver.h @@ -115,6 +115,14 @@ class Driver streamer.register_action(t, a); }; + /** + * Set a callback to be called when the driver is restarted and reconnects + */ + void set_reconnect_callback(std::function callback) + { + reconnect_callback = callback; + } + protected: Driver() = default; @@ -161,6 +169,11 @@ class Driver */ std::atomic terminate = {false}; + /** + * Reconnect callback, called when the driver restarts + */ + std::function reconnect_callback; + /** * Starts the driver. This function creates a new process and sets up the * communication pipes. @@ -314,6 +327,12 @@ ::start_driver(std::string& error) rc = read(from_drv, (void *) buffer, sizeof(char) * 31); + if ( rc < 0 ) + { + error = "Driver initialization failed, unable to read from driver\n"; + return -1; + } + buffer[rc]='\0'; std::istringstream iss(buffer); @@ -325,7 +344,7 @@ ::start_driver(std::string& error) if ( action != "INIT" || result != "SUCCESS" ) { - error = "Driver initialization failed\n"; + error = "Driver initialization failed, expected INIT SUCCESS message\n"; return -1; } @@ -352,6 +371,8 @@ ::start_listener() start_driver(error); streamer.fd(from_drv); + + if (reconnect_callback) reconnect_callback(); } }); } diff --git a/include/DriverManager.h b/include/DriverManager.h index 86ef8d9ad2d..8702bf890a2 100644 --- a/include/DriverManager.h +++ b/include/DriverManager.h @@ -91,6 +91,12 @@ class DriverManager static Log::MessageType log_type(char type); + /** + * Callback called when the driver is reconnected. Override this function + * to perform any actions when the driver is reconnected + */ + virtual void reconnected() {}; + private: std::map> drivers; @@ -207,7 +213,10 @@ int DriverManager::start(std::string& error) { for (auto& driver : drivers) { + driver.second->set_reconnect_callback(std::bind(&DriverManager::reconnected, this)); + auto rc = driver.second->start(error); + if (rc != 0) { NebulaLog::error("DrM", "Unable to start driver '" + driver.first diff --git a/include/GroupPool.h b/include/GroupPool.h index 06c72362c60..88550373d9e 100644 --- a/include/GroupPool.h +++ b/include/GroupPool.h @@ -42,7 +42,7 @@ class GroupPool : public PoolSQL /** * Identifier for the oneadmin group */ - static const int ONEADMIN_ID; + static constexpr int ONEADMIN_ID = 0; /** * Default name for the users group @@ -52,7 +52,7 @@ class GroupPool : public PoolSQL /** * Identifier for the user group */ - static const int USERS_ID; + static constexpr int USERS_ID = 1; /* ---------------------------------------------------------------------- */ /* Methods for DB management */ diff --git a/include/HostShareCapacity.h b/include/HostShareCapacity.h index fd2c66d2222..7656147e0f9 100644 --- a/include/HostShareCapacity.h +++ b/include/HostShareCapacity.h @@ -17,6 +17,7 @@ #ifndef HOST_SHARE_CAPACITY_H_ #define HOST_SHARE_CAPACITY_H_ +#include "Template.h" #include "Attribute.h" /* ------------------------------------------------------------------------ */ @@ -53,6 +54,48 @@ struct HostShareCapacity VectorAttribute * topology; std::vector nodes; + + /** + * Get the VM capacity from the template + * @param vid the VM ID + * @param tmpl the VM template. Warning: the HostShareCapacity use pointers to + * the tmpl, so it must exist for the lifetime of the HostareCapacity + */ + void set(int vid, Template& tmpl) + { + float fcpu; + + pci.clear(); + nodes.clear(); + + vmid = vid; + + if ((tmpl.get("MEMORY", mem) == false) || + (tmpl.get("CPU", fcpu) == false)) + { + cpu = 0; + mem = 0; + disk = 0; + + vcpu = 0; + + return; + } + + cpu = (int) (fcpu * 100); //% + mem = mem * 1024; //Kb + disk = 0; + + tmpl.get("VCPU", vcpu); + + tmpl.get("PCI", pci); + + tmpl.get("NUMA_NODE", nodes); + + topology = tmpl.get("TOPOLOGY"); + + return; + } }; #endif /*HOST_SHARE_CAPACITY_H_*/ diff --git a/include/InformationManager.h b/include/InformationManager.h index 0b0641fe9b1..68abf786f62 100644 --- a/include/InformationManager.h +++ b/include/InformationManager.h @@ -87,6 +87,11 @@ class InformationManager : public DriverManager> */ void raft_status(RaftManager::State raft); + /** + * Called when the driver is reconnected + */ + void reconnected() override; + protected: /** * Received undefined message -> print error diff --git a/include/LibVirtDriver.h b/include/LibVirtDriver.h index 044a262aee5..a6b6f1ffc19 100644 --- a/include/LibVirtDriver.h +++ b/include/LibVirtDriver.h @@ -46,15 +46,15 @@ class LibVirtDriver : public VirtualMachineManagerDriver std::string& error) const override; private: - static const int CEPH_DEFAULT_PORT; + static constexpr int CEPH_DEFAULT_PORT = 6789; - static const int GLUSTER_DEFAULT_PORT; + static constexpr int GLUSTER_DEFAULT_PORT = 24007; - static const int ISCSI_DEFAULT_PORT; + static constexpr int ISCSI_DEFAULT_PORT = 3260; - static const int Q35_ROOT_DEFAULT_PORTS; + static constexpr int Q35_ROOT_DEFAULT_PORTS = 16; - static const char * XML_DOMAIN_RNG_PATH; + static constexpr auto XML_DOMAIN_RNG_PATH = "/schemas/libvirt/domain.rng"; int deployment_description(const VirtualMachine * vm, const std::string& fn) const override diff --git a/include/NebulaLog.h b/include/NebulaLog.h index 3a29bfdcc2c..8f202d2f539 100644 --- a/include/NebulaLog.h +++ b/include/NebulaLog.h @@ -156,6 +156,11 @@ class NebulaLog return _log_type; }; + static bool initialized() + { + return (logger != 0); + } + private: NebulaLog() {}; diff --git a/include/NebulaService.h b/include/NebulaService.h index 7638249d974..7975cd61642 100644 --- a/include/NebulaService.h +++ b/include/NebulaService.h @@ -150,7 +150,7 @@ class NebulaService */ static std::string code_version() { - return "6.10.0"; // bump version + return "6.10.2"; // bump version } /** diff --git a/include/NebulaUtil.h b/include/NebulaUtil.h index 4c91c034547..118bb7c27e1 100644 --- a/include/NebulaUtil.h +++ b/include/NebulaUtil.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -141,12 +142,8 @@ namespace one_util * * @param st string to split * @param delim delimiter character - * @param clean_empty true to clean empty split parts. - * Example for st "a::b:c" - * clean_empty true will return ["a", "b", "c"] - * clean_empty fase will return ["a", "", "b", "c"] + * @param parts where the result will be saved * - * @return a vector containing the resulting substrings */ template void split(const std::string &st, char delim, std::vector &parts) @@ -176,6 +173,19 @@ namespace one_util } } + /** + * Splits a string, using the given delimiter + * + * @param st string to split + * @param delim delimiter character + * @param clean_empty true to clean empty split parts. + * Example for st "a::b:c" + * clean_empty true will return ["a", "b", "c"] + * clean_empty fase will return ["a", "", "b", "c"] + * + * @return a vector containing the resulting substrings + */ + std::vector split(const std::string& st, char delim, bool clean_empty = true); @@ -364,6 +374,34 @@ namespace one_util template <> bool str_cast(const std::string& str, std::string& value); + /** + * Converts string into unsigned integer type + * @param str Input string + * + * @return Unsigned integer value on success, 0 on failure. + * @note If value in string is greater than typename T can hold, + * maximum possible value will be returned + */ + template + T string_to_unsigned(const std::string& str) + { + T value; + + if (std::regex_search(str, std::regex("[^0-9]"))) + { + return 0; + } + + std::istringstream iss(str); + iss >> value; + + if (iss.fail() || !iss.eof()) + { + value = std::numeric_limits::max(); + } + + return value; + } } // namespace one_util #endif /* _NEBULA_UTIL_H_ */ diff --git a/include/PoolSQL.h b/include/PoolSQL.h index 96993b6deab..7038f382980 100644 --- a/include/PoolSQL.h +++ b/include/PoolSQL.h @@ -43,7 +43,7 @@ class PoolSQL */ PoolSQL(SqlDB * _db, const char * _table); - virtual ~PoolSQL(); + virtual ~PoolSQL() = default; /** * Allocates a new object, writting it in the pool database. No memory is @@ -52,7 +52,7 @@ class PoolSQL * @return the oid assigned to the object or -1 in case of failure */ virtual int allocate( - PoolObjectSQL *objsql, + PoolObjectSQL &objsql, std::string& error_str); /** diff --git a/include/RequestManagerAllocateDB.h b/include/RequestManagerAllocateDB.h index 3ddf46094fe..86783935526 100644 --- a/include/RequestManagerAllocateDB.h +++ b/include/RequestManagerAllocateDB.h @@ -47,9 +47,9 @@ class RequestManagerAllocateDB: public Request return; } - PoolObjectSQL * obj = create(xml); + auto obj = std::unique_ptr(create(xml)); - int rc = pool->allocate(obj, att.resp_msg); + int rc = pool->allocate(*obj, att.resp_msg); if ( rc == -1 ) { diff --git a/include/VirtualMachine.h b/include/VirtualMachine.h index 42a54925488..eaa0a814a30 100644 --- a/include/VirtualMachine.h +++ b/include/VirtualMachine.h @@ -413,6 +413,11 @@ class VirtualMachine : public PoolObjectSQL // ------------------------------------------------------------------------ // History // ------------------------------------------------------------------------ + /* + * Loads all VM history recordsfrom the database + */ + int load_history(SqlDB * db); + /** * Adds a new history record an writes it in the database. */ @@ -1034,10 +1039,19 @@ class VirtualMachine : public PoolObjectSQL /** * Get the VM physical capacity requirements for the host. - * @param sr the HostShareCapacity to store the capacity request. + * @param sr the HostShareCapacity to store the capacity request. The sr + * use pointers to VM template, do not destroy the VM object before sr. */ void get_capacity(HostShareCapacity &sr) const; + /** + * Get the VM physical capacity from the previous history + * @param sr the HostShareCapacity to store the capacity request. + * @param tmpl temporary object, to hold pointers, do not release the tmpl + * before the HostShareCapacity + */ + void get_previous_capacity(HostShareCapacity &sr, Template &tmpl) const; + /** * Adds automatic placement requirements: Datastore and Cluster * @param cluster_ids set of viable clusters for this VM @@ -1826,17 +1840,12 @@ class VirtualMachine : public PoolObjectSQL /** * History record, for the current host */ - History * history; + std::unique_ptr history; /** * History record, for the previous host */ - History * previous_history; - - /** - * Complete set of history records for the VM - */ - std::vector history_records; + std::unique_ptr previous_history; /** * VirtualMachine disks diff --git a/include/VirtualMachinePool.h b/include/VirtualMachinePool.h index 7262f44c6c7..eeb70af656d 100644 --- a/include/VirtualMachinePool.h +++ b/include/VirtualMachinePool.h @@ -374,6 +374,16 @@ class VirtualMachinePool : public PoolSQL */ VirtualMachineMonitorInfo get_monitoring(int vmid); + /** + * Dumps the VM history records in XML format + * + * @param oss the output stream to dump the pool contents + * @param vid the Virtual Machine ID + * + * @return 0 on success + */ + int dump_history(std::string& oss, int vid); + /** * Processes all the history records, and stores the monthly cost for each * VM diff --git a/include/VirtualRouterPool.h b/include/VirtualRouterPool.h index 85e1a7c2c67..3ba43f19dfd 100644 --- a/include/VirtualRouterPool.h +++ b/include/VirtualRouterPool.h @@ -55,9 +55,8 @@ class VirtualRouterPool : public PoolSQL int * oid, std::string& error_str) { - *oid = PoolSQL::allocate( - new VirtualRouter(-1, uid, gid, uname, gname, umask, move(template_contents)), - error_str); + VirtualRouter vr{-1, uid, gid, uname, gname, umask, move(template_contents)}; + *oid = PoolSQL::allocate(vr, error_str); return *oid; } diff --git a/install.sh b/install.sh index c8b3b3ef50d..ce5ebb68eef 100755 --- a/install.sh +++ b/install.sh @@ -1589,7 +1589,10 @@ NETWORK_FILES="src/vnm_mad/remotes/lib/vnm_driver.rb \ src/vnm_mad/remotes/lib/no_vlan.rb \ src/vnm_mad/remotes/lib/security_groups.rb \ src/vnm_mad/remotes/lib/security_groups_iptables.rb \ - src/vnm_mad/remotes/lib/nic.rb" + src/vnm_mad/remotes/lib/nic.rb \ + src/vnm_mad/remotes/lib/tproxy \ + src/vnm_mad/remotes/lib/tproxy.rb \ + src/vnm_mad/remotes/lib/ip_netns_exec" NETWORK_8021Q_FILES="src/vnm_mad/remotes/802.1Q/clean \ src/vnm_mad/remotes/802.1Q/post \ diff --git a/share/doc/xsd/api_info.xsd b/share/doc/xsd/api_info.xsd index b25687364eb..b96e5c181c6 100644 --- a/share/doc/xsd/api_info.xsd +++ b/share/doc/xsd/api_info.xsd @@ -13,7 +13,7 @@ - + @@ -21,7 +21,7 @@ - + diff --git a/share/doc/xsd/hook_message_api.xsd b/share/doc/xsd/hook_message_api.xsd index 0d5cc223098..644862c49f3 100644 --- a/share/doc/xsd/hook_message_api.xsd +++ b/share/doc/xsd/hook_message_api.xsd @@ -1,6 +1,7 @@ + diff --git a/share/doc/xsd/hook_message_retry.xsd b/share/doc/xsd/hook_message_retry.xsd index 82a4f66b42c..1d3479ed5d0 100644 --- a/share/doc/xsd/hook_message_retry.xsd +++ b/share/doc/xsd/hook_message_retry.xsd @@ -1,5 +1,5 @@ - + diff --git a/share/doc/xsd/hook_message_state.xsd b/share/doc/xsd/hook_message_state.xsd index 54ab82d241d..cfc1630475f 100644 --- a/share/doc/xsd/hook_message_state.xsd +++ b/share/doc/xsd/hook_message_state.xsd @@ -1,11 +1,15 @@ - + + + + + - + @@ -17,6 +21,7 @@ + diff --git a/share/doc/xsd/zone_pool.xsd b/share/doc/xsd/zone_pool.xsd index 2781210b813..900c04dbe0e 100644 --- a/share/doc/xsd/zone_pool.xsd +++ b/share/doc/xsd/zone_pool.xsd @@ -2,36 +2,40 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/install_gems/AlmaLinux8/Gemfile.lock b/share/install_gems/AlmaLinux8/Gemfile.lock index f2238a2ac60..8dd43efe8a3 100644 --- a/share/install_gems/AlmaLinux8/Gemfile.lock +++ b/share/install_gems/AlmaLinux8/Gemfile.lock @@ -17,26 +17,26 @@ GEM async (~> 1.14) awrence (1.2.1) aws-eventstream (1.3.0) - aws-partitions (1.956.0) - aws-sdk-cloudwatch (1.96.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-partitions (1.1001.0) + aws-sdk-cloudwatch (1.104.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-core (3.201.1) + aws-sdk-core (3.211.0) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-ec2 (1.465.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-ec2 (1.486.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-kms (1.95.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.156.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-s3 (1.169.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sigv4 (1.8.0) + aws-sigv4 (1.10.1) aws-eventstream (~> 1, >= 1.0.2) azure_mgmt_compute (0.22.0) ms_rest_azure (~> 0.12.0) @@ -54,21 +54,20 @@ GEM builder (3.3.0) cbor (0.5.9.8) chunky_png (1.4.0) - concurrent-ruby (1.3.3) + concurrent-ruby (1.3.4) configparser (0.1.7) console (1.15.3) fiber-local - cose (1.3.0) + cose (1.3.1) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) - curb (1.0.5) + curb (1.0.6) daemons (1.4.1) dalli (2.7.11) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - etc (1.3.0) eventmachine (1.2.7) - faraday (1.10.3) + faraday (1.10.4) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -89,12 +88,12 @@ GEM faraday-httpclient (1.0.1) faraday-multipart (1.0.4) multipart-post (~> 2) - faraday-net_http (1.0.1) + faraday-net_http (1.0.2) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) faraday-retry (1.0.3) - faraday_middleware (1.2.0) + faraday_middleware (1.2.1) faraday (~> 1.0) ffi (1.16.3) ffi-rzmq (2.0.7) @@ -108,16 +107,16 @@ GEM gnuplot (2.6.2) hashie (5.0.0) highline (1.7.10) - http-cookie (1.0.6) + http-cookie (1.0.7) domain_name (~> 0.5) i18n (0.9.5) concurrent-ruby (~> 1.0) inflection (1.0.0) - ipaddr (1.2.6) + ipaddr (1.2.7) ipaddress (0.8.3) jmespath (1.6.2) - json (2.7.2) - jwt (2.8.2) + json (2.7.5) + jwt (2.9.3) base64 memcache-client (1.8.5) mini_mime (1.1.2) @@ -153,12 +152,10 @@ GEM prometheus-client (4.2.3) base64 public_suffix (4.0.7) - racc (1.8.0) - rack (2.2.9) + racc (1.8.1) + rack (2.2.10) rack-protection (2.2.4) rack - rb-inotify (0.11.1) - ffi (~> 1.0) rbvmomi2 (3.7.1) builder (~> 3.2) json (~> 2.3) @@ -173,7 +170,7 @@ GEM ruby2_keywords (0.0.5) safety_net_attestation (0.4.0) jwt (~> 2.0) - sequel (5.82.0) + sequel (5.86.0) bigdecimal sinatra (2.2.4) mustermann (~> 2.0) @@ -189,7 +186,7 @@ GEM tilt (2.4.0) timeliness (0.3.10) timers (4.3.5) - tpm-key_attestation (0.12.0) + tpm-key_attestation (0.12.1) bindata (~> 2.4) openssl (> 2.0) openssl-signature_algorithm (~> 1.0) @@ -216,7 +213,7 @@ GEM openssl (>= 2.2) safety_net_attestation (~> 0.4.0) tpm-key_attestation (~> 0.12.0) - webrick (1.8.1) + webrick (1.9.0) xmlrpc (0.3.3) webrick zendesk_api (1.37.0) @@ -245,7 +242,6 @@ DEPENDENCIES console (= 1.15.3) curb dalli (< 3.0) - etc faraday_middleware (~> 1.2.0) ffi (< 1.17.0) ffi-rzmq (~> 2.0.7) @@ -267,7 +263,6 @@ DEPENDENCIES prometheus-client public_suffix rack - rb-inotify rbvmomi2 (~> 3.7.0) rotp rqrcode @@ -276,6 +271,7 @@ DEPENDENCIES sqlite3 (<= 1.6.9) thin treetop (>= 1.6.3) + unf (< 0.2.0) uuidtools vsphere-automation-cis (~> 0.4.6) vsphere-automation-vcenter (~> 0.4.6) diff --git a/share/install_gems/AlmaLinux9/Gemfile.lock b/share/install_gems/AlmaLinux9/Gemfile.lock index 3f60886b254..97a68bcc392 100644 --- a/share/install_gems/AlmaLinux9/Gemfile.lock +++ b/share/install_gems/AlmaLinux9/Gemfile.lock @@ -17,26 +17,26 @@ GEM async (~> 1.14) awrence (1.2.1) aws-eventstream (1.3.0) - aws-partitions (1.956.0) - aws-sdk-cloudwatch (1.96.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-partitions (1.1001.0) + aws-sdk-cloudwatch (1.104.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-core (3.201.1) + aws-sdk-core (3.211.0) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-ec2 (1.465.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-ec2 (1.486.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-kms (1.95.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.156.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-s3 (1.169.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sigv4 (1.8.0) + aws-sigv4 (1.10.1) aws-eventstream (~> 1, >= 1.0.2) azure_mgmt_compute (0.22.0) ms_rest_azure (~> 0.12.0) @@ -54,20 +54,19 @@ GEM builder (3.3.0) cbor (0.5.9.8) chunky_png (1.4.0) - concurrent-ruby (1.3.3) + concurrent-ruby (1.3.4) configparser (0.1.7) console (1.15.3) fiber-local - cose (1.3.0) + cose (1.3.1) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) - curb (1.0.5) + curb (1.0.6) daemons (1.4.1) dalli (2.7.11) domain_name (0.6.20240107) - etc (1.4.3) eventmachine (1.2.7) - faraday (1.10.3) + faraday (1.10.4) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -88,12 +87,12 @@ GEM faraday-httpclient (1.0.1) faraday-multipart (1.0.4) multipart-post (~> 2) - faraday-net_http (1.0.1) + faraday-net_http (1.0.2) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) faraday-retry (1.0.3) - faraday_middleware (1.2.0) + faraday_middleware (1.2.1) faraday (~> 1.0) ffi (1.16.3) ffi-rzmq (2.0.7) @@ -107,20 +106,20 @@ GEM gnuplot (2.6.2) hashie (5.0.0) highline (1.7.10) - http-cookie (1.0.6) + http-cookie (1.0.7) domain_name (~> 0.5) i18n (0.9.5) concurrent-ruby (~> 1.0) inflection (1.0.0) ipaddress (0.8.3) jmespath (1.6.2) - json (2.7.2) - jwt (2.8.2) + json (2.7.5) + jwt (2.9.3) base64 memcache-client (1.8.5) mini_mime (1.1.5) mini_portile2 (2.8.7) - minitest (5.24.1) + minitest (5.25.1) ms_rest (0.7.6) concurrent-ruby (~> 1.0) faraday (>= 0.9, < 2.0.0) @@ -131,12 +130,12 @@ GEM faraday-cookie_jar (~> 0.0.6) ms_rest (~> 0.7.6) multipart-post (2.4.1) - mustermann (3.0.0) + mustermann (3.0.3) ruby2_keywords (~> 0.0.1) mysql2 (0.5.6) net-ldap (0.19.0) - nio4r (2.7.3) - nokogiri (1.16.6) + nio4r (2.7.4) + nokogiri (1.16.7) mini_portile2 (~> 2.8.2) racc (~> 1.4) opennebula-augeas (0.6.6) @@ -149,22 +148,19 @@ GEM polyglot (0.3.5) prometheus-client (4.2.3) base64 - public_suffix (6.0.0) - racc (1.8.0) - rack (2.2.9) + public_suffix (6.0.1) + racc (1.8.1) + rack (2.2.10) rack-protection (3.2.0) base64 (>= 0.1.0) rack (~> 2.2, >= 2.2.4) - rb-inotify (0.11.1) - ffi (~> 1.0) rbvmomi2 (3.7.1) builder (~> 3.2) json (~> 2.3) nokogiri (~> 1.12, >= 1.12.5) optimist (~> 3.0) rchardet (1.8.0) - rexml (3.3.2) - strscan + rexml (3.3.9) rotp (6.3.0) rqrcode (2.2.0) chunky_png (~> 1.0) @@ -173,7 +169,7 @@ GEM ruby2_keywords (0.0.5) safety_net_attestation (0.4.0) jwt (~> 2.0) - sequel (5.82.0) + sequel (5.86.0) bigdecimal sinatra (3.2.0) mustermann (~> 3.0) @@ -182,7 +178,6 @@ GEM tilt (~> 2.0) sqlite3 (1.7.3) mini_portile2 (~> 2.8.0) - strscan (3.1.0) thin (1.8.2) daemons (~> 1.0, >= 1.0.9) eventmachine (~> 1.0, >= 1.0.4) @@ -191,7 +186,7 @@ GEM tilt (2.4.0) timeliness (0.3.10) timers (4.3.5) - tpm-key_attestation (0.12.0) + tpm-key_attestation (0.12.1) bindata (~> 2.4) openssl (> 2.0) openssl-signature_algorithm (~> 1.0) @@ -215,7 +210,7 @@ GEM openssl (>= 2.2) safety_net_attestation (~> 0.4.0) tpm-key_attestation (~> 0.12.0) - webrick (1.8.1) + webrick (1.9.0) xmlrpc (0.3.3) webrick zendesk_api (1.37.0) @@ -244,7 +239,6 @@ DEPENDENCIES console (= 1.15.3) curb dalli (< 3.0) - etc faraday_middleware (~> 1.2.0) ffi (< 1.17.0) ffi-rzmq (~> 2.0.7) @@ -266,7 +260,6 @@ DEPENDENCIES prometheus-client public_suffix rack - rb-inotify rbvmomi2 (~> 3.7.0) rexml rotp diff --git a/share/install_gems/Debian10/Gemfile.lock b/share/install_gems/Debian10/Gemfile.lock deleted file mode 100644 index e198ed166f5..00000000000 --- a/share/install_gems/Debian10/Gemfile.lock +++ /dev/null @@ -1,290 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - activesupport (4.2.11.3) - i18n (~> 0.7) - minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) - tzinfo (~> 1.1) - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) - android_key_attestation (0.3.0) - async (1.32.1) - console (~> 1.10) - nio4r (~> 2.3) - timers (~> 4.1) - async-io (1.32.1) - async (~> 1.14) - awrence (1.2.1) - aws-eventstream (1.3.0) - aws-partitions (1.956.0) - aws-sdk-cloudwatch (1.96.0) - aws-sdk-core (~> 3, >= 3.201.0) - aws-sigv4 (~> 1.5) - aws-sdk-core (3.201.1) - aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) - jmespath (~> 1, >= 1.6.1) - aws-sdk-ec2 (1.465.0) - aws-sdk-core (~> 3, >= 3.201.0) - aws-sigv4 (~> 1.5) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) - aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.156.0) - aws-sdk-core (~> 3, >= 3.201.0) - aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.5) - aws-sigv4 (1.8.0) - aws-eventstream (~> 1, >= 1.0.2) - azure_mgmt_compute (0.22.0) - ms_rest_azure (~> 0.12.0) - azure_mgmt_monitor (0.19.0) - ms_rest_azure (~> 0.12.0) - azure_mgmt_network (0.26.1) - ms_rest_azure (~> 0.12.0) - azure_mgmt_resources (0.18.2) - ms_rest_azure (~> 0.12.0) - azure_mgmt_storage (0.23.0) - ms_rest_azure (~> 0.12.0) - base64 (0.2.0) - bigdecimal (3.1.8) - bindata (2.5.0) - builder (3.3.0) - cbor (0.5.9.8) - chunky_png (1.4.0) - concurrent-ruby (1.3.3) - configparser (0.1.7) - console (1.15.3) - fiber-local - cose (1.3.0) - cbor (~> 0.5.9) - openssl-signature_algorithm (~> 1.0) - curb (1.0.5) - daemons (1.4.1) - dalli (2.7.11) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) - etc (1.3.0) - eventmachine (1.2.7) - faraday (1.10.3) - faraday-em_http (~> 1.0) - faraday-em_synchrony (~> 1.0) - faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0) - faraday-multipart (~> 1.0) - faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.0) - faraday-patron (~> 1.0) - faraday-rack (~> 1.0) - faraday-retry (~> 1.0) - ruby2_keywords (>= 0.0.4) - faraday-cookie_jar (0.0.7) - faraday (>= 0.8.0) - http-cookie (~> 1.0.0) - faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) - faraday-excon (1.1.0) - faraday-httpclient (1.0.1) - faraday-multipart (1.0.4) - multipart-post (~> 2) - faraday-net_http (1.0.1) - faraday-net_http_persistent (1.2.0) - faraday-patron (1.0.0) - faraday-rack (1.0.0) - faraday-retry (1.0.3) - faraday_middleware (1.2.0) - faraday (~> 1.0) - ffi (1.16.3) - ffi-rzmq (2.0.7) - ffi-rzmq-core (>= 1.0.7) - ffi-rzmq-core (1.0.7) - ffi - fiber-local (1.0.0) - git (1.19.1) - addressable (~> 2.8) - rchardet (~> 1.8) - gnuplot (2.6.2) - hashie (5.0.0) - highline (1.7.10) - http-cookie (1.0.6) - domain_name (~> 0.5) - i18n (0.9.5) - concurrent-ruby (~> 1.0) - inflection (1.0.0) - ipaddr (1.2.6) - ipaddress (0.8.3) - jmespath (1.6.2) - json (2.7.2) - jwt (2.8.2) - base64 - memcache-client (1.8.5) - mini_mime (1.1.2) - mini_portile2 (2.6.1) - minitest (5.15.0) - ms_rest (0.7.6) - concurrent-ruby (~> 1.0) - faraday (>= 0.9, < 2.0.0) - timeliness (~> 0.3.10) - ms_rest_azure (0.12.0) - concurrent-ruby (~> 1.0) - faraday (>= 0.9, < 2.0.0) - faraday-cookie_jar (~> 0.0.6) - ms_rest (~> 0.7.6) - multipart-post (2.4.1) - mustermann (2.0.2) - ruby2_keywords (~> 0.0.1) - mysql2 (0.5.6) - net-ldap (0.19.0) - nio4r (2.7.3) - nokogiri (1.12.5) - mini_portile2 (~> 2.6.1) - racc (~> 1.4) - opennebula-augeas (0.6.6) - openssl (2.2.3) - ipaddr - openssl-signature_algorithm (1.3.0) - openssl (> 2.0) - optimist (3.1.0) - ox (2.14.14) - parse-cron (0.1.4) - polyglot (0.3.5) - prometheus-client (4.2.3) - base64 - public_suffix (4.0.7) - racc (1.8.0) - rack (2.2.9) - rack-protection (2.2.4) - rack - rb-inotify (0.11.1) - ffi (~> 1.0) - rbvmomi2 (3.7.1) - builder (~> 3.2) - json (~> 2.3) - nokogiri (~> 1.12, >= 1.12.5) - optimist (~> 3.0) - rchardet (1.8.0) - rotp (6.3.0) - rqrcode (2.1.2) - chunky_png (~> 1.0) - rqrcode_core (~> 1.0) - rqrcode_core (1.2.0) - ruby2_keywords (0.0.5) - safety_net_attestation (0.4.0) - jwt (~> 2.0) - sequel (5.82.0) - bigdecimal - sinatra (2.2.4) - mustermann (~> 2.0) - rack (~> 2.2) - rack-protection (= 2.2.4) - tilt (~> 2.0) - sqlite3 (1.4.4) - thin (1.8.2) - daemons (~> 1.0, >= 1.0.9) - eventmachine (~> 1.0, >= 1.0.4) - rack (>= 1, < 3) - thread_safe (0.3.6) - tilt (2.4.0) - timeliness (0.3.10) - timers (4.3.5) - tpm-key_attestation (0.12.0) - bindata (~> 2.4) - openssl (> 2.0) - openssl-signature_algorithm (~> 1.0) - treetop (1.6.12) - polyglot (~> 0.3) - tzinfo (1.2.11) - thread_safe (~> 0.1) - unf (0.1.4) - unf_ext - unf_ext (0.0.9.1) - uuidtools (2.2.0) - vsphere-automation-cis (0.4.7) - vsphere-automation-runtime (~> 0.4.6) - vsphere-automation-runtime (0.4.7) - vsphere-automation-vcenter (0.4.7) - vsphere-automation-cis (~> 0.4.6) - vsphere-automation-runtime (~> 0.4.6) - webauthn (3.1.0) - android_key_attestation (~> 0.3.0) - awrence (~> 1.1) - bindata (~> 2.4) - cbor (~> 0.5.9) - cose (~> 1.1) - openssl (>= 2.2) - safety_net_attestation (~> 0.4.0) - tpm-key_attestation (~> 0.12.0) - webrick (1.8.1) - xmlrpc (0.3.3) - webrick - zendesk_api (1.37.0) - faraday (>= 0.9.0, < 2.0.0) - hashie (>= 3.5.2, < 6.0.0) - inflection - mini_mime - multipart-post (~> 2.0) - -PLATFORMS - ruby - -DEPENDENCIES - activesupport (~> 4.2) - addressable - async-io (= 1.32.1) - aws-sdk-cloudwatch - aws-sdk-ec2 (>= 1.151) - aws-sdk-s3 - azure_mgmt_compute - azure_mgmt_monitor - azure_mgmt_network - azure_mgmt_resources - azure_mgmt_storage - configparser - console (= 1.15.3) - curb - dalli (< 3.0) - etc - faraday_middleware (~> 1.2.0) - ffi (< 1.17.0) - ffi-rzmq (~> 2.0.7) - git (~> 1.5) - gnuplot - highline (~> 1.7) - i18n (~> 0.9) - ipaddress (~> 0.8.3) - json (>= 2.0) - memcache-client - mini_portile2 - minitest - mysql2 - net-ldap - nokogiri - opennebula-augeas (>= 0.6.6.pre) - ox - parse-cron - prometheus-client - public_suffix - rack - rb-inotify - rbvmomi2 (~> 3.7.0) - rotp - rqrcode - sequel - sinatra (< 4.0.0) - sqlite3 (<= 1.6.9) - thin - treetop (>= 1.6.3) - uuidtools - vsphere-automation-cis (~> 0.4.6) - vsphere-automation-vcenter (~> 0.4.6) - webauthn - xmlrpc - zendesk_api - -RUBY VERSION - ruby 2.5.5p157 - -BUNDLED WITH - 1.17.3 diff --git a/share/install_gems/Debian11/Gemfile.lock b/share/install_gems/Debian11/Gemfile.lock index 78c7d01b4bd..8748d65d75f 100644 --- a/share/install_gems/Debian11/Gemfile.lock +++ b/share/install_gems/Debian11/Gemfile.lock @@ -17,26 +17,26 @@ GEM async (~> 1.14) awrence (1.2.1) aws-eventstream (1.3.0) - aws-partitions (1.956.0) - aws-sdk-cloudwatch (1.96.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-partitions (1.1001.0) + aws-sdk-cloudwatch (1.104.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-core (3.201.1) + aws-sdk-core (3.211.0) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-ec2 (1.465.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-ec2 (1.486.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-kms (1.95.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.156.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-s3 (1.169.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sigv4 (1.8.0) + aws-sigv4 (1.10.1) aws-eventstream (~> 1, >= 1.0.2) azure_mgmt_compute (0.22.0) ms_rest_azure (~> 0.12.0) @@ -54,20 +54,19 @@ GEM builder (3.3.0) cbor (0.5.9.8) chunky_png (1.4.0) - concurrent-ruby (1.3.3) + concurrent-ruby (1.3.4) configparser (0.1.7) console (1.15.3) fiber-local - cose (1.3.0) + cose (1.3.1) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) - curb (1.0.5) + curb (1.0.6) daemons (1.4.1) dalli (2.7.11) domain_name (0.6.20240107) - etc (1.4.3) eventmachine (1.2.7) - faraday (1.10.3) + faraday (1.10.4) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -88,12 +87,12 @@ GEM faraday-httpclient (1.0.1) faraday-multipart (1.0.4) multipart-post (~> 2) - faraday-net_http (1.0.1) + faraday-net_http (1.0.2) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) faraday-retry (1.0.3) - faraday_middleware (1.2.0) + faraday_middleware (1.2.1) faraday (~> 1.0) ffi (1.16.3) ffi-rzmq (2.0.7) @@ -107,20 +106,20 @@ GEM gnuplot (2.6.2) hashie (5.0.0) highline (1.7.10) - http-cookie (1.0.6) + http-cookie (1.0.7) domain_name (~> 0.5) i18n (0.9.5) concurrent-ruby (~> 1.0) inflection (1.0.0) ipaddress (0.8.3) jmespath (1.6.2) - json (2.7.2) - jwt (2.8.2) + json (2.7.5) + jwt (2.9.3) base64 memcache-client (1.8.5) mini_mime (1.1.5) mini_portile2 (2.8.7) - minitest (5.24.1) + minitest (5.25.1) ms_rest (0.7.6) concurrent-ruby (~> 1.0) faraday (>= 0.9, < 2.0.0) @@ -131,11 +130,11 @@ GEM faraday-cookie_jar (~> 0.0.6) ms_rest (~> 0.7.6) multipart-post (2.4.1) - mustermann (3.0.0) + mustermann (3.0.3) ruby2_keywords (~> 0.0.1) mysql2 (0.5.6) net-ldap (0.19.0) - nio4r (2.7.3) + nio4r (2.7.4) nokogiri (1.15.6) mini_portile2 (~> 2.8.2) racc (~> 1.4) @@ -150,13 +149,11 @@ GEM prometheus-client (4.2.3) base64 public_suffix (5.1.1) - racc (1.8.0) - rack (2.2.9) + racc (1.8.1) + rack (2.2.10) rack-protection (3.2.0) base64 (>= 0.1.0) rack (~> 2.2, >= 2.2.4) - rb-inotify (0.11.1) - ffi (~> 1.0) rbvmomi2 (3.7.1) builder (~> 3.2) json (~> 2.3) @@ -171,7 +168,7 @@ GEM ruby2_keywords (0.0.5) safety_net_attestation (0.4.0) jwt (~> 2.0) - sequel (5.82.0) + sequel (5.86.0) bigdecimal sinatra (3.2.0) mustermann (~> 3.0) @@ -188,7 +185,7 @@ GEM tilt (2.4.0) timeliness (0.3.10) timers (4.3.5) - tpm-key_attestation (0.12.0) + tpm-key_attestation (0.12.1) bindata (~> 2.4) openssl (> 2.0) openssl-signature_algorithm (~> 1.0) @@ -212,7 +209,7 @@ GEM openssl (>= 2.2) safety_net_attestation (~> 0.4.0) tpm-key_attestation (~> 0.12.0) - webrick (1.8.1) + webrick (1.9.0) xmlrpc (0.3.3) webrick zendesk_api (1.37.0) @@ -241,7 +238,6 @@ DEPENDENCIES console (= 1.15.3) curb dalli (< 3.0) - etc faraday_middleware (~> 1.2.0) ffi (< 1.17.0) ffi-rzmq (~> 2.0.7) @@ -263,7 +259,6 @@ DEPENDENCIES prometheus-client public_suffix rack - rb-inotify rbvmomi2 (~> 3.7.0) rotp rqrcode diff --git a/share/install_gems/Debian12/Gemfile.lock b/share/install_gems/Debian12/Gemfile.lock index 9e8b8087795..8a5a30df97b 100644 --- a/share/install_gems/Debian12/Gemfile.lock +++ b/share/install_gems/Debian12/Gemfile.lock @@ -17,26 +17,26 @@ GEM async (~> 1.14) awrence (1.2.1) aws-eventstream (1.3.0) - aws-partitions (1.956.0) - aws-sdk-cloudwatch (1.96.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-partitions (1.1001.0) + aws-sdk-cloudwatch (1.104.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-core (3.201.1) + aws-sdk-core (3.211.0) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-ec2 (1.465.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-ec2 (1.486.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-kms (1.95.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.156.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-s3 (1.169.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sigv4 (1.8.0) + aws-sigv4 (1.10.1) aws-eventstream (~> 1, >= 1.0.2) azure_mgmt_compute (0.22.0) ms_rest_azure (~> 0.12.0) @@ -54,20 +54,19 @@ GEM builder (3.3.0) cbor (0.5.9.8) chunky_png (1.4.0) - concurrent-ruby (1.3.3) + concurrent-ruby (1.3.4) configparser (0.1.7) console (1.15.3) fiber-local - cose (1.3.0) + cose (1.3.1) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) - curb (1.0.5) + curb (1.0.6) daemons (1.4.1) dalli (2.7.11) domain_name (0.6.20240107) - etc (1.4.3) eventmachine (1.2.7) - faraday (1.10.3) + faraday (1.10.4) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -88,12 +87,12 @@ GEM faraday-httpclient (1.0.1) faraday-multipart (1.0.4) multipart-post (~> 2) - faraday-net_http (1.0.1) + faraday-net_http (1.0.2) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) faraday-retry (1.0.3) - faraday_middleware (1.2.0) + faraday_middleware (1.2.1) faraday (~> 1.0) ffi (1.16.3) ffi-rzmq (2.0.7) @@ -102,27 +101,27 @@ GEM ffi fiber-local (1.1.0) fiber-storage - fiber-storage (0.1.2) + fiber-storage (1.0.0) git (1.19.1) addressable (~> 2.8) rchardet (~> 1.8) gnuplot (2.6.2) hashie (5.0.0) highline (1.7.10) - http-cookie (1.0.6) + http-cookie (1.0.7) domain_name (~> 0.5) i18n (0.9.5) concurrent-ruby (~> 1.0) inflection (1.0.0) ipaddress (0.8.3) jmespath (1.6.2) - json (2.7.2) - jwt (2.8.2) + json (2.7.5) + jwt (2.9.3) base64 memcache-client (1.8.5) mini_mime (1.1.5) mini_portile2 (2.8.7) - minitest (5.24.1) + minitest (5.25.1) ms_rest (0.7.6) concurrent-ruby (~> 1.0) faraday (>= 0.9, < 2.0.0) @@ -133,12 +132,12 @@ GEM faraday-cookie_jar (~> 0.0.6) ms_rest (~> 0.7.6) multipart-post (2.4.1) - mustermann (3.0.0) + mustermann (3.0.3) ruby2_keywords (~> 0.0.1) mysql2 (0.5.6) net-ldap (0.19.0) - nio4r (2.7.3) - nokogiri (1.16.6) + nio4r (2.7.4) + nokogiri (1.16.7) mini_portile2 (~> 2.8.2) racc (~> 1.4) opennebula-augeas (0.6.6) @@ -151,22 +150,19 @@ GEM polyglot (0.3.5) prometheus-client (4.2.3) base64 - public_suffix (6.0.0) - racc (1.8.0) - rack (2.2.9) + public_suffix (6.0.1) + racc (1.8.1) + rack (2.2.10) rack-protection (3.2.0) base64 (>= 0.1.0) rack (~> 2.2, >= 2.2.4) - rb-inotify (0.11.1) - ffi (~> 1.0) rbvmomi2 (3.7.1) builder (~> 3.2) json (~> 2.3) nokogiri (~> 1.12, >= 1.12.5) optimist (~> 3.0) rchardet (1.8.0) - rexml (3.3.2) - strscan + rexml (3.3.9) rotp (6.3.0) rqrcode (2.2.0) chunky_png (~> 1.0) @@ -175,7 +171,7 @@ GEM ruby2_keywords (0.0.5) safety_net_attestation (0.4.0) jwt (~> 2.0) - sequel (5.82.0) + sequel (5.86.0) bigdecimal sinatra (3.2.0) mustermann (~> 3.0) @@ -184,7 +180,6 @@ GEM tilt (~> 2.0) sqlite3 (1.7.3) mini_portile2 (~> 2.8.0) - strscan (3.1.0) thin (1.8.2) daemons (~> 1.0, >= 1.0.9) eventmachine (~> 1.0, >= 1.0.4) @@ -193,7 +188,7 @@ GEM tilt (2.4.0) timeliness (0.3.10) timers (4.3.5) - tpm-key_attestation (0.12.0) + tpm-key_attestation (0.12.1) bindata (~> 2.4) openssl (> 2.0) openssl-signature_algorithm (~> 1.0) @@ -217,7 +212,7 @@ GEM openssl (>= 2.2) safety_net_attestation (~> 0.4.0) tpm-key_attestation (~> 0.12.0) - webrick (1.8.1) + webrick (1.9.0) xmlrpc (0.3.3) webrick zendesk_api (1.37.0) @@ -246,7 +241,6 @@ DEPENDENCIES console (= 1.15.3) curb dalli (< 3.0) - etc faraday_middleware (~> 1.2.0) ffi (< 1.17.0) ffi-rzmq (~> 2.0.7) @@ -268,7 +262,6 @@ DEPENDENCIES prometheus-client public_suffix rack - rb-inotify rbvmomi2 (~> 3.7.0) rexml rotp diff --git a/share/install_gems/Gemfile b/share/install_gems/Gemfile index 9990a6dcc2d..990363f01ae 100644 --- a/share/install_gems/Gemfile +++ b/share/install_gems/Gemfile @@ -39,10 +39,9 @@ if ruby_version >= Gem::Version.new('3.0.0') gem 'rexml' end -gem 'async-io', '= 1.32.1' -gem 'console', '= 1.15.3' -gem 'etc' -gem 'rb-inotify' +if ruby_version < Gem::Version.new('2.6.0') + gem 'unf', '< 0.2.0' +end ################################################################################ # gems groups @@ -180,3 +179,8 @@ end group :prometheus do gem 'prometheus-client' end + +group :tproxy do + gem 'async-io', '= 1.32.1' + gem 'console', '= 1.15.3' +end diff --git a/share/install_gems/RedHat8/Gemfile.lock b/share/install_gems/RedHat8/Gemfile.lock index f2238a2ac60..d1f60df6604 100644 --- a/share/install_gems/RedHat8/Gemfile.lock +++ b/share/install_gems/RedHat8/Gemfile.lock @@ -17,26 +17,26 @@ GEM async (~> 1.14) awrence (1.2.1) aws-eventstream (1.3.0) - aws-partitions (1.956.0) - aws-sdk-cloudwatch (1.96.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-partitions (1.1001.0) + aws-sdk-cloudwatch (1.104.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-core (3.201.1) + aws-sdk-core (3.211.0) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-ec2 (1.465.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-ec2 (1.486.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-kms (1.95.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.156.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-s3 (1.169.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sigv4 (1.8.0) + aws-sigv4 (1.10.1) aws-eventstream (~> 1, >= 1.0.2) azure_mgmt_compute (0.22.0) ms_rest_azure (~> 0.12.0) @@ -54,21 +54,20 @@ GEM builder (3.3.0) cbor (0.5.9.8) chunky_png (1.4.0) - concurrent-ruby (1.3.3) + concurrent-ruby (1.3.4) configparser (0.1.7) console (1.15.3) fiber-local - cose (1.3.0) + cose (1.3.1) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) - curb (1.0.5) + curb (1.0.6) daemons (1.4.1) dalli (2.7.11) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - etc (1.3.0) eventmachine (1.2.7) - faraday (1.10.3) + faraday (1.10.4) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -89,12 +88,12 @@ GEM faraday-httpclient (1.0.1) faraday-multipart (1.0.4) multipart-post (~> 2) - faraday-net_http (1.0.1) + faraday-net_http (1.0.2) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) faraday-retry (1.0.3) - faraday_middleware (1.2.0) + faraday_middleware (1.2.1) faraday (~> 1.0) ffi (1.16.3) ffi-rzmq (2.0.7) @@ -108,16 +107,16 @@ GEM gnuplot (2.6.2) hashie (5.0.0) highline (1.7.10) - http-cookie (1.0.6) + http-cookie (1.0.7) domain_name (~> 0.5) i18n (0.9.5) concurrent-ruby (~> 1.0) inflection (1.0.0) - ipaddr (1.2.6) + ipaddr (1.2.7) ipaddress (0.8.3) jmespath (1.6.2) - json (2.7.2) - jwt (2.8.2) + json (2.7.6) + jwt (2.9.3) base64 memcache-client (1.8.5) mini_mime (1.1.2) @@ -153,12 +152,10 @@ GEM prometheus-client (4.2.3) base64 public_suffix (4.0.7) - racc (1.8.0) - rack (2.2.9) + racc (1.8.1) + rack (2.2.10) rack-protection (2.2.4) rack - rb-inotify (0.11.1) - ffi (~> 1.0) rbvmomi2 (3.7.1) builder (~> 3.2) json (~> 2.3) @@ -173,7 +170,7 @@ GEM ruby2_keywords (0.0.5) safety_net_attestation (0.4.0) jwt (~> 2.0) - sequel (5.82.0) + sequel (5.86.0) bigdecimal sinatra (2.2.4) mustermann (~> 2.0) @@ -189,7 +186,7 @@ GEM tilt (2.4.0) timeliness (0.3.10) timers (4.3.5) - tpm-key_attestation (0.12.0) + tpm-key_attestation (0.12.1) bindata (~> 2.4) openssl (> 2.0) openssl-signature_algorithm (~> 1.0) @@ -216,7 +213,7 @@ GEM openssl (>= 2.2) safety_net_attestation (~> 0.4.0) tpm-key_attestation (~> 0.12.0) - webrick (1.8.1) + webrick (1.9.0) xmlrpc (0.3.3) webrick zendesk_api (1.37.0) @@ -245,7 +242,6 @@ DEPENDENCIES console (= 1.15.3) curb dalli (< 3.0) - etc faraday_middleware (~> 1.2.0) ffi (< 1.17.0) ffi-rzmq (~> 2.0.7) @@ -267,7 +263,6 @@ DEPENDENCIES prometheus-client public_suffix rack - rb-inotify rbvmomi2 (~> 3.7.0) rotp rqrcode @@ -276,6 +271,7 @@ DEPENDENCIES sqlite3 (<= 1.6.9) thin treetop (>= 1.6.3) + unf (< 0.2.0) uuidtools vsphere-automation-cis (~> 0.4.6) vsphere-automation-vcenter (~> 0.4.6) diff --git a/share/install_gems/RedHat8/TAG b/share/install_gems/RedHat8/TAG index 43b15847696..22ec1e64943 100644 --- a/share/install_gems/RedHat8/TAG +++ b/share/install_gems/RedHat8/TAG @@ -1 +1 @@ -8.8 +8.10 diff --git a/share/install_gems/RedHat9/Gemfile.lock b/share/install_gems/RedHat9/Gemfile.lock index 3f60886b254..a41a21223e7 100644 --- a/share/install_gems/RedHat9/Gemfile.lock +++ b/share/install_gems/RedHat9/Gemfile.lock @@ -17,26 +17,26 @@ GEM async (~> 1.14) awrence (1.2.1) aws-eventstream (1.3.0) - aws-partitions (1.956.0) - aws-sdk-cloudwatch (1.96.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-partitions (1.1001.0) + aws-sdk-cloudwatch (1.104.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-core (3.201.1) + aws-sdk-core (3.211.0) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-ec2 (1.465.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-ec2 (1.486.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-kms (1.95.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.156.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-s3 (1.169.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sigv4 (1.8.0) + aws-sigv4 (1.10.1) aws-eventstream (~> 1, >= 1.0.2) azure_mgmt_compute (0.22.0) ms_rest_azure (~> 0.12.0) @@ -54,20 +54,19 @@ GEM builder (3.3.0) cbor (0.5.9.8) chunky_png (1.4.0) - concurrent-ruby (1.3.3) + concurrent-ruby (1.3.4) configparser (0.1.7) console (1.15.3) fiber-local - cose (1.3.0) + cose (1.3.1) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) - curb (1.0.5) + curb (1.0.6) daemons (1.4.1) dalli (2.7.11) domain_name (0.6.20240107) - etc (1.4.3) eventmachine (1.2.7) - faraday (1.10.3) + faraday (1.10.4) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -88,12 +87,12 @@ GEM faraday-httpclient (1.0.1) faraday-multipart (1.0.4) multipart-post (~> 2) - faraday-net_http (1.0.1) + faraday-net_http (1.0.2) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) faraday-retry (1.0.3) - faraday_middleware (1.2.0) + faraday_middleware (1.2.1) faraday (~> 1.0) ffi (1.16.3) ffi-rzmq (2.0.7) @@ -107,20 +106,20 @@ GEM gnuplot (2.6.2) hashie (5.0.0) highline (1.7.10) - http-cookie (1.0.6) + http-cookie (1.0.7) domain_name (~> 0.5) i18n (0.9.5) concurrent-ruby (~> 1.0) inflection (1.0.0) ipaddress (0.8.3) jmespath (1.6.2) - json (2.7.2) - jwt (2.8.2) + json (2.7.6) + jwt (2.9.3) base64 memcache-client (1.8.5) mini_mime (1.1.5) mini_portile2 (2.8.7) - minitest (5.24.1) + minitest (5.25.1) ms_rest (0.7.6) concurrent-ruby (~> 1.0) faraday (>= 0.9, < 2.0.0) @@ -131,12 +130,12 @@ GEM faraday-cookie_jar (~> 0.0.6) ms_rest (~> 0.7.6) multipart-post (2.4.1) - mustermann (3.0.0) + mustermann (3.0.3) ruby2_keywords (~> 0.0.1) mysql2 (0.5.6) net-ldap (0.19.0) - nio4r (2.7.3) - nokogiri (1.16.6) + nio4r (2.7.4) + nokogiri (1.16.7) mini_portile2 (~> 2.8.2) racc (~> 1.4) opennebula-augeas (0.6.6) @@ -149,22 +148,19 @@ GEM polyglot (0.3.5) prometheus-client (4.2.3) base64 - public_suffix (6.0.0) - racc (1.8.0) - rack (2.2.9) + public_suffix (6.0.1) + racc (1.8.1) + rack (2.2.10) rack-protection (3.2.0) base64 (>= 0.1.0) rack (~> 2.2, >= 2.2.4) - rb-inotify (0.11.1) - ffi (~> 1.0) rbvmomi2 (3.7.1) builder (~> 3.2) json (~> 2.3) nokogiri (~> 1.12, >= 1.12.5) optimist (~> 3.0) rchardet (1.8.0) - rexml (3.3.2) - strscan + rexml (3.3.9) rotp (6.3.0) rqrcode (2.2.0) chunky_png (~> 1.0) @@ -173,7 +169,7 @@ GEM ruby2_keywords (0.0.5) safety_net_attestation (0.4.0) jwt (~> 2.0) - sequel (5.82.0) + sequel (5.86.0) bigdecimal sinatra (3.2.0) mustermann (~> 3.0) @@ -182,7 +178,6 @@ GEM tilt (~> 2.0) sqlite3 (1.7.3) mini_portile2 (~> 2.8.0) - strscan (3.1.0) thin (1.8.2) daemons (~> 1.0, >= 1.0.9) eventmachine (~> 1.0, >= 1.0.4) @@ -191,7 +186,7 @@ GEM tilt (2.4.0) timeliness (0.3.10) timers (4.3.5) - tpm-key_attestation (0.12.0) + tpm-key_attestation (0.12.1) bindata (~> 2.4) openssl (> 2.0) openssl-signature_algorithm (~> 1.0) @@ -215,7 +210,7 @@ GEM openssl (>= 2.2) safety_net_attestation (~> 0.4.0) tpm-key_attestation (~> 0.12.0) - webrick (1.8.1) + webrick (1.9.0) xmlrpc (0.3.3) webrick zendesk_api (1.37.0) @@ -244,7 +239,6 @@ DEPENDENCIES console (= 1.15.3) curb dalli (< 3.0) - etc faraday_middleware (~> 1.2.0) ffi (< 1.17.0) ffi-rzmq (~> 2.0.7) @@ -266,7 +260,6 @@ DEPENDENCIES prometheus-client public_suffix rack - rb-inotify rbvmomi2 (~> 3.7.0) rexml rotp diff --git a/share/install_gems/RedHat9/TAG b/share/install_gems/RedHat9/TAG index 1a2c3557ba4..0359f243283 100644 --- a/share/install_gems/RedHat9/TAG +++ b/share/install_gems/RedHat9/TAG @@ -1 +1 @@ -9.2 +9.4 diff --git a/share/install_gems/Ubuntu1804/Gemfile.lock b/share/install_gems/Ubuntu1804/Gemfile.lock deleted file mode 100644 index 31d1e55750e..00000000000 --- a/share/install_gems/Ubuntu1804/Gemfile.lock +++ /dev/null @@ -1,290 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - activesupport (4.2.11.3) - i18n (~> 0.7) - minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) - tzinfo (~> 1.1) - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) - android_key_attestation (0.3.0) - async (1.32.1) - console (~> 1.10) - nio4r (~> 2.3) - timers (~> 4.1) - async-io (1.32.1) - async (~> 1.14) - awrence (1.2.1) - aws-eventstream (1.3.0) - aws-partitions (1.956.0) - aws-sdk-cloudwatch (1.96.0) - aws-sdk-core (~> 3, >= 3.201.0) - aws-sigv4 (~> 1.5) - aws-sdk-core (3.201.1) - aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) - jmespath (~> 1, >= 1.6.1) - aws-sdk-ec2 (1.465.0) - aws-sdk-core (~> 3, >= 3.201.0) - aws-sigv4 (~> 1.5) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) - aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.156.0) - aws-sdk-core (~> 3, >= 3.201.0) - aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.5) - aws-sigv4 (1.8.0) - aws-eventstream (~> 1, >= 1.0.2) - azure_mgmt_compute (0.22.0) - ms_rest_azure (~> 0.12.0) - azure_mgmt_monitor (0.19.0) - ms_rest_azure (~> 0.12.0) - azure_mgmt_network (0.26.1) - ms_rest_azure (~> 0.12.0) - azure_mgmt_resources (0.18.2) - ms_rest_azure (~> 0.12.0) - azure_mgmt_storage (0.23.0) - ms_rest_azure (~> 0.12.0) - base64 (0.2.0) - bigdecimal (3.1.8) - bindata (2.5.0) - builder (3.3.0) - cbor (0.5.9.8) - chunky_png (1.4.0) - concurrent-ruby (1.3.3) - configparser (0.1.7) - console (1.15.3) - fiber-local - cose (1.3.0) - cbor (~> 0.5.9) - openssl-signature_algorithm (~> 1.0) - curb (1.0.5) - daemons (1.4.1) - dalli (2.7.11) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) - etc (1.3.0) - eventmachine (1.2.7) - faraday (1.10.3) - faraday-em_http (~> 1.0) - faraday-em_synchrony (~> 1.0) - faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0) - faraday-multipart (~> 1.0) - faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.0) - faraday-patron (~> 1.0) - faraday-rack (~> 1.0) - faraday-retry (~> 1.0) - ruby2_keywords (>= 0.0.4) - faraday-cookie_jar (0.0.7) - faraday (>= 0.8.0) - http-cookie (~> 1.0.0) - faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) - faraday-excon (1.1.0) - faraday-httpclient (1.0.1) - faraday-multipart (1.0.4) - multipart-post (~> 2) - faraday-net_http (1.0.1) - faraday-net_http_persistent (1.2.0) - faraday-patron (1.0.0) - faraday-rack (1.0.0) - faraday-retry (1.0.3) - faraday_middleware (1.2.0) - faraday (~> 1.0) - ffi (1.16.3) - ffi-rzmq (2.0.7) - ffi-rzmq-core (>= 1.0.7) - ffi-rzmq-core (1.0.7) - ffi - fiber-local (1.0.0) - git (1.19.1) - addressable (~> 2.8) - rchardet (~> 1.8) - gnuplot (2.6.2) - hashie (5.0.0) - highline (1.7.10) - http-cookie (1.0.6) - domain_name (~> 0.5) - i18n (0.9.5) - concurrent-ruby (~> 1.0) - inflection (1.0.0) - ipaddr (1.2.6) - ipaddress (0.8.3) - jmespath (1.6.2) - json (2.7.2) - jwt (2.8.2) - base64 - memcache-client (1.8.5) - mini_mime (1.1.2) - mini_portile2 (2.6.1) - minitest (5.15.0) - ms_rest (0.7.6) - concurrent-ruby (~> 1.0) - faraday (>= 0.9, < 2.0.0) - timeliness (~> 0.3.10) - ms_rest_azure (0.12.0) - concurrent-ruby (~> 1.0) - faraday (>= 0.9, < 2.0.0) - faraday-cookie_jar (~> 0.0.6) - ms_rest (~> 0.7.6) - multipart-post (2.4.1) - mustermann (2.0.2) - ruby2_keywords (~> 0.0.1) - mysql2 (0.5.6) - net-ldap (0.19.0) - nio4r (2.7.3) - nokogiri (1.12.5) - mini_portile2 (~> 2.6.1) - racc (~> 1.4) - opennebula-augeas (0.6.6) - openssl (2.2.3) - ipaddr - openssl-signature_algorithm (1.3.0) - openssl (> 2.0) - optimist (3.1.0) - ox (2.14.14) - parse-cron (0.1.4) - polyglot (0.3.5) - prometheus-client (4.2.3) - base64 - public_suffix (4.0.7) - racc (1.8.0) - rack (2.2.9) - rack-protection (2.2.4) - rack - rb-inotify (0.11.1) - ffi (~> 1.0) - rbvmomi2 (3.7.1) - builder (~> 3.2) - json (~> 2.3) - nokogiri (~> 1.12, >= 1.12.5) - optimist (~> 3.0) - rchardet (1.8.0) - rotp (6.3.0) - rqrcode (2.1.2) - chunky_png (~> 1.0) - rqrcode_core (~> 1.0) - rqrcode_core (1.2.0) - ruby2_keywords (0.0.5) - safety_net_attestation (0.4.0) - jwt (~> 2.0) - sequel (5.82.0) - bigdecimal - sinatra (2.2.4) - mustermann (~> 2.0) - rack (~> 2.2) - rack-protection (= 2.2.4) - tilt (~> 2.0) - sqlite3 (1.4.4) - thin (1.8.2) - daemons (~> 1.0, >= 1.0.9) - eventmachine (~> 1.0, >= 1.0.4) - rack (>= 1, < 3) - thread_safe (0.3.6) - tilt (2.4.0) - timeliness (0.3.10) - timers (4.3.5) - tpm-key_attestation (0.12.0) - bindata (~> 2.4) - openssl (> 2.0) - openssl-signature_algorithm (~> 1.0) - treetop (1.6.12) - polyglot (~> 0.3) - tzinfo (1.2.11) - thread_safe (~> 0.1) - unf (0.1.4) - unf_ext - unf_ext (0.0.9.1) - uuidtools (2.2.0) - vsphere-automation-cis (0.4.7) - vsphere-automation-runtime (~> 0.4.6) - vsphere-automation-runtime (0.4.7) - vsphere-automation-vcenter (0.4.7) - vsphere-automation-cis (~> 0.4.6) - vsphere-automation-runtime (~> 0.4.6) - webauthn (3.1.0) - android_key_attestation (~> 0.3.0) - awrence (~> 1.1) - bindata (~> 2.4) - cbor (~> 0.5.9) - cose (~> 1.1) - openssl (>= 2.2) - safety_net_attestation (~> 0.4.0) - tpm-key_attestation (~> 0.12.0) - webrick (1.8.1) - xmlrpc (0.3.3) - webrick - zendesk_api (1.37.0) - faraday (>= 0.9.0, < 2.0.0) - hashie (>= 3.5.2, < 6.0.0) - inflection - mini_mime - multipart-post (~> 2.0) - -PLATFORMS - ruby - -DEPENDENCIES - activesupport (~> 4.2) - addressable - async-io (= 1.32.1) - aws-sdk-cloudwatch - aws-sdk-ec2 (>= 1.151) - aws-sdk-s3 - azure_mgmt_compute - azure_mgmt_monitor - azure_mgmt_network - azure_mgmt_resources - azure_mgmt_storage - configparser - console (= 1.15.3) - curb - dalli (< 3.0) - etc - faraday_middleware (~> 1.2.0) - ffi (< 1.17.0) - ffi-rzmq (~> 2.0.7) - git (~> 1.5) - gnuplot - highline (~> 1.7) - i18n (~> 0.9) - ipaddress (~> 0.8.3) - json (>= 2.0) - memcache-client - mini_portile2 - minitest - mysql2 - net-ldap - nokogiri - opennebula-augeas (>= 0.6.6.pre) - ox - parse-cron - prometheus-client - public_suffix - rack - rb-inotify - rbvmomi2 (~> 3.7.0) - rotp - rqrcode - sequel - sinatra (< 4.0.0) - sqlite3 (<= 1.6.9) - thin - treetop (>= 1.6.3) - uuidtools - vsphere-automation-cis (~> 0.4.6) - vsphere-automation-vcenter (~> 0.4.6) - webauthn - xmlrpc - zendesk_api - -RUBY VERSION - ruby 2.5.1p57 - -BUNDLED WITH - 1.17.3 diff --git a/share/install_gems/Ubuntu2004/Gemfile.lock b/share/install_gems/Ubuntu2004/Gemfile.lock deleted file mode 100644 index 70084acf000..00000000000 --- a/share/install_gems/Ubuntu2004/Gemfile.lock +++ /dev/null @@ -1,286 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - activesupport (4.2.11.3) - i18n (~> 0.7) - minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) - tzinfo (~> 1.1) - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) - android_key_attestation (0.3.0) - async (1.32.1) - console (~> 1.10) - nio4r (~> 2.3) - timers (~> 4.1) - async-io (1.32.1) - async (~> 1.14) - awrence (1.2.1) - aws-eventstream (1.3.0) - aws-partitions (1.956.0) - aws-sdk-cloudwatch (1.96.0) - aws-sdk-core (~> 3, >= 3.201.0) - aws-sigv4 (~> 1.5) - aws-sdk-core (3.201.1) - aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) - jmespath (~> 1, >= 1.6.1) - aws-sdk-ec2 (1.465.0) - aws-sdk-core (~> 3, >= 3.201.0) - aws-sigv4 (~> 1.5) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) - aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.156.0) - aws-sdk-core (~> 3, >= 3.201.0) - aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.5) - aws-sigv4 (1.8.0) - aws-eventstream (~> 1, >= 1.0.2) - azure_mgmt_compute (0.22.0) - ms_rest_azure (~> 0.12.0) - azure_mgmt_monitor (0.19.0) - ms_rest_azure (~> 0.12.0) - azure_mgmt_network (0.26.1) - ms_rest_azure (~> 0.12.0) - azure_mgmt_resources (0.18.2) - ms_rest_azure (~> 0.12.0) - azure_mgmt_storage (0.23.0) - ms_rest_azure (~> 0.12.0) - base64 (0.2.0) - bigdecimal (3.1.8) - bindata (2.5.0) - builder (3.3.0) - cbor (0.5.9.8) - chunky_png (1.4.0) - concurrent-ruby (1.3.3) - configparser (0.1.7) - console (1.15.3) - fiber-local - cose (1.3.0) - cbor (~> 0.5.9) - openssl-signature_algorithm (~> 1.0) - curb (1.0.5) - daemons (1.4.1) - dalli (2.7.11) - domain_name (0.6.20240107) - etc (1.4.3) - eventmachine (1.2.7) - faraday (1.10.3) - faraday-em_http (~> 1.0) - faraday-em_synchrony (~> 1.0) - faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0) - faraday-multipart (~> 1.0) - faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.0) - faraday-patron (~> 1.0) - faraday-rack (~> 1.0) - faraday-retry (~> 1.0) - ruby2_keywords (>= 0.0.4) - faraday-cookie_jar (0.0.7) - faraday (>= 0.8.0) - http-cookie (~> 1.0.0) - faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) - faraday-excon (1.1.0) - faraday-httpclient (1.0.1) - faraday-multipart (1.0.4) - multipart-post (~> 2) - faraday-net_http (1.0.1) - faraday-net_http_persistent (1.2.0) - faraday-patron (1.0.0) - faraday-rack (1.0.0) - faraday-retry (1.0.3) - faraday_middleware (1.2.0) - faraday (~> 1.0) - ffi (1.16.3) - ffi-rzmq (2.0.7) - ffi-rzmq-core (>= 1.0.7) - ffi-rzmq-core (1.0.7) - ffi - fiber-local (1.0.0) - git (1.19.1) - addressable (~> 2.8) - rchardet (~> 1.8) - gnuplot (2.6.2) - hashie (5.0.0) - highline (1.7.10) - http-cookie (1.0.6) - domain_name (~> 0.5) - i18n (0.9.5) - concurrent-ruby (~> 1.0) - inflection (1.0.0) - ipaddress (0.8.3) - jmespath (1.6.2) - json (2.7.2) - jwt (2.8.2) - base64 - memcache-client (1.8.5) - mini_mime (1.1.5) - mini_portile2 (2.8.7) - minitest (5.24.1) - ms_rest (0.7.6) - concurrent-ruby (~> 1.0) - faraday (>= 0.9, < 2.0.0) - timeliness (~> 0.3.10) - ms_rest_azure (0.12.0) - concurrent-ruby (~> 1.0) - faraday (>= 0.9, < 2.0.0) - faraday-cookie_jar (~> 0.0.6) - ms_rest (~> 0.7.6) - multipart-post (2.4.1) - mustermann (3.0.0) - ruby2_keywords (~> 0.0.1) - mysql2 (0.5.6) - net-ldap (0.19.0) - nio4r (2.7.3) - nokogiri (1.15.6) - mini_portile2 (~> 2.8.2) - racc (~> 1.4) - opennebula-augeas (0.6.6) - openssl (3.2.0) - openssl-signature_algorithm (1.3.0) - openssl (> 2.0) - optimist (3.1.0) - ox (2.14.18) - parse-cron (0.1.4) - polyglot (0.3.5) - prometheus-client (4.2.3) - base64 - public_suffix (5.1.1) - racc (1.8.0) - rack (2.2.9) - rack-protection (3.2.0) - base64 (>= 0.1.0) - rack (~> 2.2, >= 2.2.4) - rb-inotify (0.11.1) - ffi (~> 1.0) - rbvmomi2 (3.7.1) - builder (~> 3.2) - json (~> 2.3) - nokogiri (~> 1.12, >= 1.12.5) - optimist (~> 3.0) - rchardet (1.8.0) - rotp (6.3.0) - rqrcode (2.2.0) - chunky_png (~> 1.0) - rqrcode_core (~> 1.0) - rqrcode_core (1.2.0) - ruby2_keywords (0.0.5) - safety_net_attestation (0.4.0) - jwt (~> 2.0) - sequel (5.82.0) - bigdecimal - sinatra (3.2.0) - mustermann (~> 3.0) - rack (~> 2.2, >= 2.2.4) - rack-protection (= 3.2.0) - tilt (~> 2.0) - sqlite3 (1.6.9) - mini_portile2 (~> 2.8.0) - thin (1.8.2) - daemons (~> 1.0, >= 1.0.9) - eventmachine (~> 1.0, >= 1.0.4) - rack (>= 1, < 3) - thread_safe (0.3.6) - tilt (2.4.0) - timeliness (0.3.10) - timers (4.3.5) - tpm-key_attestation (0.12.0) - bindata (~> 2.4) - openssl (> 2.0) - openssl-signature_algorithm (~> 1.0) - treetop (1.6.12) - polyglot (~> 0.3) - tzinfo (1.2.11) - thread_safe (~> 0.1) - uuidtools (2.2.0) - vsphere-automation-cis (0.4.7) - vsphere-automation-runtime (~> 0.4.6) - vsphere-automation-runtime (0.4.7) - vsphere-automation-vcenter (0.4.7) - vsphere-automation-cis (~> 0.4.6) - vsphere-automation-runtime (~> 0.4.6) - webauthn (3.1.0) - android_key_attestation (~> 0.3.0) - awrence (~> 1.1) - bindata (~> 2.4) - cbor (~> 0.5.9) - cose (~> 1.1) - openssl (>= 2.2) - safety_net_attestation (~> 0.4.0) - tpm-key_attestation (~> 0.12.0) - webrick (1.8.1) - xmlrpc (0.3.3) - webrick - zendesk_api (1.37.0) - faraday (>= 0.9.0, < 2.0.0) - hashie (>= 3.5.2, < 6.0.0) - inflection - mini_mime - multipart-post (~> 2.0) - -PLATFORMS - ruby - -DEPENDENCIES - activesupport (~> 4.2) - addressable - async-io (= 1.32.1) - aws-sdk-cloudwatch - aws-sdk-ec2 (>= 1.151) - aws-sdk-s3 - azure_mgmt_compute - azure_mgmt_monitor - azure_mgmt_network - azure_mgmt_resources - azure_mgmt_storage - configparser - console (= 1.15.3) - curb - dalli (< 3.0) - etc - faraday_middleware (~> 1.2.0) - ffi (< 1.17.0) - ffi-rzmq (~> 2.0.7) - git (~> 1.5) - gnuplot - highline (~> 1.7) - i18n (~> 0.9) - ipaddress (~> 0.8.3) - json (>= 2.0) - memcache-client - mini_portile2 - minitest - mysql2 - net-ldap - nokogiri - opennebula-augeas (>= 0.6.6.pre) - ox - parse-cron - prometheus-client - public_suffix - rack - rb-inotify - rbvmomi2 (~> 3.7.0) - rotp - rqrcode - sequel - sinatra (< 4.0.0) - sqlite3 (<= 1.6.9) - thin - treetop (>= 1.6.3) - uuidtools - vsphere-automation-cis (~> 0.4.6) - vsphere-automation-vcenter (~> 0.4.6) - webauthn - xmlrpc - zendesk_api - -RUBY VERSION - ruby 2.7.0p0 - -BUNDLED WITH - 1.17.3 diff --git a/share/install_gems/Ubuntu2204/Gemfile.lock b/share/install_gems/Ubuntu2204/Gemfile.lock index b84e7926618..2aa46fa49d8 100644 --- a/share/install_gems/Ubuntu2204/Gemfile.lock +++ b/share/install_gems/Ubuntu2204/Gemfile.lock @@ -17,26 +17,26 @@ GEM async (~> 1.14) awrence (1.2.1) aws-eventstream (1.3.0) - aws-partitions (1.956.0) - aws-sdk-cloudwatch (1.96.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-partitions (1.1001.0) + aws-sdk-cloudwatch (1.104.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-core (3.201.1) + aws-sdk-core (3.211.0) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-ec2 (1.465.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-ec2 (1.486.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-kms (1.95.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.156.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-s3 (1.169.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sigv4 (1.8.0) + aws-sigv4 (1.10.1) aws-eventstream (~> 1, >= 1.0.2) azure_mgmt_compute (0.22.0) ms_rest_azure (~> 0.12.0) @@ -54,20 +54,19 @@ GEM builder (3.3.0) cbor (0.5.9.8) chunky_png (1.4.0) - concurrent-ruby (1.3.3) + concurrent-ruby (1.3.4) configparser (0.1.7) console (1.15.3) fiber-local - cose (1.3.0) + cose (1.3.1) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) - curb (1.0.5) + curb (1.0.6) daemons (1.4.1) dalli (2.7.11) domain_name (0.6.20240107) - etc (1.4.3) eventmachine (1.2.7) - faraday (1.10.3) + faraday (1.10.4) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -88,12 +87,12 @@ GEM faraday-httpclient (1.0.1) faraday-multipart (1.0.4) multipart-post (~> 2) - faraday-net_http (1.0.1) + faraday-net_http (1.0.2) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) faraday-retry (1.0.3) - faraday_middleware (1.2.0) + faraday_middleware (1.2.1) faraday (~> 1.0) ffi (1.16.3) ffi-rzmq (2.0.7) @@ -107,20 +106,20 @@ GEM gnuplot (2.6.2) hashie (5.0.0) highline (1.7.10) - http-cookie (1.0.6) + http-cookie (1.0.7) domain_name (~> 0.5) i18n (0.9.5) concurrent-ruby (~> 1.0) inflection (1.0.0) ipaddress (0.8.3) jmespath (1.6.2) - json (2.7.2) - jwt (2.8.2) + json (2.7.5) + jwt (2.9.3) base64 memcache-client (1.8.5) mini_mime (1.1.5) mini_portile2 (2.8.7) - minitest (5.24.1) + minitest (5.25.1) ms_rest (0.7.6) concurrent-ruby (~> 1.0) faraday (>= 0.9, < 2.0.0) @@ -131,12 +130,12 @@ GEM faraday-cookie_jar (~> 0.0.6) ms_rest (~> 0.7.6) multipart-post (2.4.1) - mustermann (3.0.0) + mustermann (3.0.3) ruby2_keywords (~> 0.0.1) mysql2 (0.5.6) net-ldap (0.19.0) - nio4r (2.7.3) - nokogiri (1.16.6) + nio4r (2.7.4) + nokogiri (1.16.7) mini_portile2 (~> 2.8.2) racc (~> 1.4) opennebula-augeas (0.6.6) @@ -149,22 +148,19 @@ GEM polyglot (0.3.5) prometheus-client (4.2.3) base64 - public_suffix (6.0.0) - racc (1.8.0) - rack (2.2.9) + public_suffix (6.0.1) + racc (1.8.1) + rack (2.2.10) rack-protection (3.2.0) base64 (>= 0.1.0) rack (~> 2.2, >= 2.2.4) - rb-inotify (0.11.1) - ffi (~> 1.0) rbvmomi2 (3.7.1) builder (~> 3.2) json (~> 2.3) nokogiri (~> 1.12, >= 1.12.5) optimist (~> 3.0) rchardet (1.8.0) - rexml (3.3.2) - strscan + rexml (3.3.9) rotp (6.3.0) rqrcode (2.2.0) chunky_png (~> 1.0) @@ -173,7 +169,7 @@ GEM ruby2_keywords (0.0.5) safety_net_attestation (0.4.0) jwt (~> 2.0) - sequel (5.82.0) + sequel (5.86.0) bigdecimal sinatra (3.2.0) mustermann (~> 3.0) @@ -182,7 +178,6 @@ GEM tilt (~> 2.0) sqlite3 (1.7.3) mini_portile2 (~> 2.8.0) - strscan (3.1.0) thin (1.8.2) daemons (~> 1.0, >= 1.0.9) eventmachine (~> 1.0, >= 1.0.4) @@ -191,7 +186,7 @@ GEM tilt (2.4.0) timeliness (0.3.10) timers (4.3.5) - tpm-key_attestation (0.12.0) + tpm-key_attestation (0.12.1) bindata (~> 2.4) openssl (> 2.0) openssl-signature_algorithm (~> 1.0) @@ -215,7 +210,7 @@ GEM openssl (>= 2.2) safety_net_attestation (~> 0.4.0) tpm-key_attestation (~> 0.12.0) - webrick (1.8.1) + webrick (1.9.0) xmlrpc (0.3.3) webrick zendesk_api (1.37.0) @@ -244,7 +239,6 @@ DEPENDENCIES console (= 1.15.3) curb dalli (< 3.0) - etc faraday_middleware (~> 1.2.0) ffi (< 1.17.0) ffi-rzmq (~> 2.0.7) @@ -266,7 +260,6 @@ DEPENDENCIES prometheus-client public_suffix rack - rb-inotify rbvmomi2 (~> 3.7.0) rexml rotp diff --git a/share/install_gems/Ubuntu2404/Gemfile.lock b/share/install_gems/Ubuntu2404/Gemfile.lock index 421d2352267..fe9741ff38e 100644 --- a/share/install_gems/Ubuntu2404/Gemfile.lock +++ b/share/install_gems/Ubuntu2404/Gemfile.lock @@ -17,26 +17,26 @@ GEM async (~> 1.14) awrence (1.2.1) aws-eventstream (1.3.0) - aws-partitions (1.956.0) - aws-sdk-cloudwatch (1.96.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-partitions (1.1001.0) + aws-sdk-cloudwatch (1.104.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-core (3.201.1) + aws-sdk-core (3.211.0) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-ec2 (1.465.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-ec2 (1.486.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-kms (1.95.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.156.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-s3 (1.169.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sigv4 (1.8.0) + aws-sigv4 (1.10.1) aws-eventstream (~> 1, >= 1.0.2) azure_mgmt_compute (0.22.0) ms_rest_azure (~> 0.12.0) @@ -54,20 +54,19 @@ GEM builder (3.3.0) cbor (0.5.9.8) chunky_png (1.4.0) - concurrent-ruby (1.3.3) + concurrent-ruby (1.3.4) configparser (0.1.7) console (1.15.3) fiber-local - cose (1.3.0) + cose (1.3.1) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) - curb (1.0.5) + curb (1.0.6) daemons (1.4.1) dalli (2.7.11) domain_name (0.6.20240107) - etc (1.4.3) eventmachine (1.2.7) - faraday (1.10.3) + faraday (1.10.4) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -88,12 +87,12 @@ GEM faraday-httpclient (1.0.1) faraday-multipart (1.0.4) multipart-post (~> 2) - faraday-net_http (1.0.1) + faraday-net_http (1.0.2) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) faraday-retry (1.0.3) - faraday_middleware (1.2.0) + faraday_middleware (1.2.1) faraday (~> 1.0) ffi (1.17.0) ffi (1.17.0-aarch64-linux-gnu) @@ -112,27 +111,27 @@ GEM ffi fiber-local (1.1.0) fiber-storage - fiber-storage (0.1.2) + fiber-storage (1.0.0) git (1.19.1) addressable (~> 2.8) rchardet (~> 1.8) gnuplot (2.6.2) hashie (5.0.0) highline (1.7.10) - http-cookie (1.0.6) + http-cookie (1.0.7) domain_name (~> 0.5) i18n (0.9.5) concurrent-ruby (~> 1.0) inflection (1.0.0) ipaddress (0.8.3) jmespath (1.6.2) - json (2.7.2) - jwt (2.8.2) + json (2.7.5) + jwt (2.9.3) base64 memcache-client (1.8.5) mini_mime (1.1.5) mini_portile2 (2.8.7) - minitest (5.24.1) + minitest (5.25.1) ms_rest (0.7.6) concurrent-ruby (~> 1.0) faraday (>= 0.9, < 2.0.0) @@ -143,25 +142,25 @@ GEM faraday-cookie_jar (~> 0.0.6) ms_rest (~> 0.7.6) multipart-post (2.4.1) - mustermann (3.0.0) + mustermann (3.0.3) ruby2_keywords (~> 0.0.1) mysql2 (0.5.6) net-ldap (0.19.0) - nio4r (2.7.3) - nokogiri (1.16.6) + nio4r (2.7.4) + nokogiri (1.16.7) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.16.6-aarch64-linux) + nokogiri (1.16.7-aarch64-linux) racc (~> 1.4) - nokogiri (1.16.6-arm-linux) + nokogiri (1.16.7-arm-linux) racc (~> 1.4) - nokogiri (1.16.6-arm64-darwin) + nokogiri (1.16.7-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.6-x86-linux) + nokogiri (1.16.7-x86-linux) racc (~> 1.4) - nokogiri (1.16.6-x86_64-darwin) + nokogiri (1.16.7-x86_64-darwin) racc (~> 1.4) - nokogiri (1.16.6-x86_64-linux) + nokogiri (1.16.7-x86_64-linux) racc (~> 1.4) opennebula-augeas (0.6.6) openssl (3.2.0) @@ -173,22 +172,19 @@ GEM polyglot (0.3.5) prometheus-client (4.2.3) base64 - public_suffix (6.0.0) - racc (1.8.0) - rack (2.2.9) + public_suffix (6.0.1) + racc (1.8.1) + rack (2.2.10) rack-protection (3.2.0) base64 (>= 0.1.0) rack (~> 2.2, >= 2.2.4) - rb-inotify (0.11.1) - ffi (~> 1.0) rbvmomi2 (3.7.1) builder (~> 3.2) json (~> 2.3) nokogiri (~> 1.12, >= 1.12.5) optimist (~> 3.0) rchardet (1.8.0) - rexml (3.3.2) - strscan + rexml (3.3.9) rotp (6.3.0) rqrcode (2.2.0) chunky_png (~> 1.0) @@ -197,26 +193,25 @@ GEM ruby2_keywords (0.0.5) safety_net_attestation (0.4.0) jwt (~> 2.0) - sequel (5.82.0) + sequel (5.86.0) bigdecimal sinatra (3.2.0) mustermann (~> 3.0) rack (~> 2.2, >= 2.2.4) rack-protection (= 3.2.0) tilt (~> 2.0) - sqlite3 (2.0.2) + sqlite3 (2.2.0) mini_portile2 (~> 2.8.0) - sqlite3 (2.0.2-aarch64-linux-gnu) - sqlite3 (2.0.2-aarch64-linux-musl) - sqlite3 (2.0.2-arm-linux-gnu) - sqlite3 (2.0.2-arm-linux-musl) - sqlite3 (2.0.2-arm64-darwin) - sqlite3 (2.0.2-x86-linux-gnu) - sqlite3 (2.0.2-x86-linux-musl) - sqlite3 (2.0.2-x86_64-darwin) - sqlite3 (2.0.2-x86_64-linux-gnu) - sqlite3 (2.0.2-x86_64-linux-musl) - strscan (3.1.0) + sqlite3 (2.2.0-aarch64-linux-gnu) + sqlite3 (2.2.0-aarch64-linux-musl) + sqlite3 (2.2.0-arm-linux-gnu) + sqlite3 (2.2.0-arm-linux-musl) + sqlite3 (2.2.0-arm64-darwin) + sqlite3 (2.2.0-x86-linux-gnu) + sqlite3 (2.2.0-x86-linux-musl) + sqlite3 (2.2.0-x86_64-darwin) + sqlite3 (2.2.0-x86_64-linux-gnu) + sqlite3 (2.2.0-x86_64-linux-musl) thin (1.8.2) daemons (~> 1.0, >= 1.0.9) eventmachine (~> 1.0, >= 1.0.4) @@ -225,7 +220,7 @@ GEM tilt (2.4.0) timeliness (0.3.10) timers (4.3.5) - tpm-key_attestation (0.12.0) + tpm-key_attestation (0.12.1) bindata (~> 2.4) openssl (> 2.0) openssl-signature_algorithm (~> 1.0) @@ -249,7 +244,7 @@ GEM openssl (>= 2.2) safety_net_attestation (~> 0.4.0) tpm-key_attestation (~> 0.12.0) - webrick (1.8.1) + webrick (1.9.0) xmlrpc (0.3.3) webrick zendesk_api (1.37.0) @@ -291,7 +286,6 @@ DEPENDENCIES console (= 1.15.3) curb dalli (< 3.0) - etc faraday_middleware (~> 1.2.0) ffi-rzmq (~> 2.0.7) git (~> 1.5) @@ -312,7 +306,6 @@ DEPENDENCIES prometheus-client public_suffix rack - rb-inotify rbvmomi2 (~> 3.7.0) rexml rotp @@ -333,4 +326,4 @@ RUBY VERSION ruby 3.2.3p157 BUNDLED WITH - 2.5.15 + 2.5.22 diff --git a/share/linters/.rubocop.yml b/share/linters/.rubocop.yml index bea0dc83571..9bcf8eb490c 100644 --- a/share/linters/.rubocop.yml +++ b/share/linters/.rubocop.yml @@ -492,6 +492,10 @@ AllCops: - src/oneprometheus/prometheus/share/patch_datasources.rb - src/oneprometheus/opennebula-libvirt-exporter/src/libvirt_exporter.rb - src/oneprometheus/opennebula-libvirt-exporter/src/libvirt_collector.rb + - src/oneprovision/lib/provision/provision_config.rb + - src/vmm_mad/remotes/lib/vcenter_driver/datastore.rb + - src/vmm_mad/remotes/lib/vcenter_driver/virtual_machine.rb + - src/vmm_mad/remotes/lib/vcenter_driver/virtual_machine.rb NewCops: enable ######## diff --git a/share/onegate/onegate b/share/onegate/onegate index 415a7f20217..85489195383 100755 --- a/share/onegate/onegate +++ b/share/onegate/onegate @@ -29,7 +29,7 @@ require 'pp' module CloudClient # OpenNebula version - VERSION = '6.10.0' + VERSION = '6.10.2' # ######################################################################### # Default location for the authentication file diff --git a/share/pkgs/sudoers/centos/opennebula b/share/pkgs/sudoers/centos/opennebula index 54a01e83bc4..8d4b8ccfb9f 100644 --- a/share/pkgs/sudoers/centos/opennebula +++ b/share/pkgs/sudoers/centos/opennebula @@ -6,7 +6,8 @@ Cmnd_Alias ONE_HA = /usr/bin/systemctl start opennebula-flow, /usr/bin/systemctl Cmnd_Alias ONE_LVM = /usr/sbin/lvcreate, /usr/sbin/lvremove, /usr/sbin/lvs, /usr/sbin/vgdisplay, /usr/sbin/lvchange, /usr/sbin/lvscan, /usr/sbin/lvextend Cmnd_Alias ONE_LXC = /usr/bin/mount, /usr/bin/umount, /usr/bin/bindfs, /usr/sbin/losetup, /usr/bin/qemu-nbd, /usr/bin/lxc-attach, /usr/bin/lxc-config, /usr/bin/lxc-create, /usr/bin/lxc-destroy, /usr/bin/lxc-info, /usr/bin/lxc-ls, /usr/bin/lxc-start, /usr/bin/lxc-stop, /usr/bin/lxc-console, /usr/sbin/e2fsck, /usr/sbin/resize2fs, /usr/sbin/xfs_growfs, /usr/bin/rbd-nbd Cmnd_Alias ONE_MARKET = /usr/lib/one/sh/create_container_image.sh -Cmnd_Alias ONE_NET = /usr/sbin/ebtables, /usr/sbin/iptables, /usr/sbin/ip6tables, /usr/sbin/ipset, /usr/sbin/ip link *, /usr/sbin/ip neighbour *, /usr/sbin/ip route *, /usr/sbin/ip rule *, /usr/sbin/ip tuntap * +Cmnd_Alias ONE_NET = /usr/sbin/ebtables, /usr/sbin/iptables, /usr/sbin/ip6tables, /usr/sbin/ipset, /usr/sbin/ip link *, /usr/sbin/ip neighbour *, /usr/sbin/ip route *, /usr/sbin/ip rule *, /usr/sbin/ip tuntap *, /usr/sbin/nft, /var/tmp/one/vnm/tproxy, /usr/sbin/bridge vlan * +Cmnd_Alias ONE_NETNS = /usr/sbin/ip netns add *, /usr/sbin/ip netns delete *, /usr/sbin/ip netns pids *, /var/tmp/one/vnm/ip_netns_exec ip address *, /var/tmp/one/vnm/ip_netns_exec ip link *, /var/tmp/one/vnm/ip_netns_exec ip -j link show *, /var/tmp/one/vnm/ip_netns_exec ip route * Cmnd_Alias ONE_OVS = /usr/bin/ovs-ofctl, /usr/bin/ovs-vsctl, /usr/bin/ovs-appctl Cmnd_Alias ONE_MEM = /usr/sbin/sysctl vm.drop_caches=3 vm.compact_memory=1 Cmnd_Alias ONE_VGPU = /var/tmp/one/vgpu @@ -14,3 +15,4 @@ Cmnd_Alias ONE_VGPU = /var/tmp/one/vgpu ## Command aliases are enabled individually in dedicated ## sudoers files by each OpenNebula component (server, node). # oneadmin ALL=(ALL) NOPASSWD: ONE_CEPH, ONE_HA, ONE_LVM, ONE_LXC, ONE_MARKET, ONE_NET, ONE_OVS, ONE_MEM +# oneadmin ALL=(ALL) NOPASSWD:SETENV: ONE_NETNS diff --git a/share/pkgs/sudoers/debian/opennebula b/share/pkgs/sudoers/debian/opennebula index 29f0fa97985..41323b57bda 100644 --- a/share/pkgs/sudoers/debian/opennebula +++ b/share/pkgs/sudoers/debian/opennebula @@ -6,7 +6,8 @@ Cmnd_Alias ONE_HA = /usr/bin/systemctl start opennebula-flow, /usr/bin/systemctl Cmnd_Alias ONE_LVM = /usr/sbin/lvcreate, /usr/sbin/lvremove, /usr/sbin/lvs, /usr/sbin/vgdisplay, /usr/sbin/lvchange, /usr/sbin/lvscan, /usr/sbin/lvextend Cmnd_Alias ONE_LXC = /usr/bin/mount, /usr/bin/umount, /usr/bin/bindfs, /usr/sbin/losetup, /usr/bin/qemu-nbd, /usr/bin/lxc-attach, /usr/bin/lxc-config, /usr/bin/lxc-create, /usr/bin/lxc-destroy, /usr/bin/lxc-info, /usr/bin/lxc-ls, /usr/bin/lxc-start, /usr/bin/lxc-stop, /usr/bin/lxc-console, /usr/sbin/e2fsck, /usr/sbin/resize2fs, /usr/sbin/xfs_growfs, /usr/bin/rbd-nbd Cmnd_Alias ONE_MARKET = /usr/lib/one/sh/create_container_image.sh -Cmnd_Alias ONE_NET = /usr/sbin/ebtables, /usr/sbin/iptables, /usr/sbin/ip6tables, /usr/sbin/ipset, /usr/sbin/ip link *, /usr/sbin/ip neighbour *, /usr/sbin/ip route *, /usr/sbin/ip rule *, /usr/sbin/ip tuntap * +Cmnd_Alias ONE_NET = /usr/sbin/ebtables, /usr/sbin/iptables, /usr/sbin/ip6tables, /usr/sbin/ipset, /usr/sbin/ip link *, /usr/sbin/ip neighbour *, /usr/sbin/ip route *, /usr/sbin/ip rule *, /usr/sbin/ip tuntap *, /usr/sbin/nft, /var/tmp/one/vnm/tproxy, /usr/sbin/bridge vlan * +Cmnd_Alias ONE_NETNS = /usr/sbin/ip netns add *, /usr/sbin/ip netns delete *, /usr/sbin/ip netns pids *, /var/tmp/one/vnm/ip_netns_exec ip address *, /var/tmp/one/vnm/ip_netns_exec ip link *, /var/tmp/one/vnm/ip_netns_exec ip -j link show *, /var/tmp/one/vnm/ip_netns_exec ip route * Cmnd_Alias ONE_OVS = /usr/bin/ovs-ofctl, /usr/bin/ovs-vsctl, /usr/bin/ovs-appctl Cmnd_Alias ONE_MEM = /usr/sbin/sysctl vm.drop_caches=3 vm.compact_memory=1 Cmnd_Alias ONE_VGPU = /var/tmp/one/vgpu @@ -14,3 +15,4 @@ Cmnd_Alias ONE_VGPU = /var/tmp/one/vgpu ## Command aliases are enabled individually in dedicated ## sudoers files by each OpenNebula component (server, node). # oneadmin ALL=(ALL) NOPASSWD: ONE_CEPH, ONE_HA, ONE_LVM, ONE_LXC, ONE_MARKET, ONE_NET, ONE_OVS, ONE_MEM +# oneadmin ALL=(ALL) NOPASSWD:SETENV: ONE_NETNS diff --git a/share/pkgs/sudoers/opennebula-node-kvm b/share/pkgs/sudoers/opennebula-node-kvm index 86f9b016cea..f1a0c1b0225 100644 --- a/share/pkgs/sudoers/opennebula-node-kvm +++ b/share/pkgs/sudoers/opennebula-node-kvm @@ -1 +1,2 @@ oneadmin ALL=(ALL:ALL) NOPASSWD: ONE_CEPH, ONE_NET, ONE_OVS, ONE_LVM, ONE_MEM, ONE_VGPU +oneadmin ALL=(ALL:ALL) NOPASSWD:SETENV: ONE_NETNS diff --git a/share/pkgs/sudoers/opennebula-node-lxc b/share/pkgs/sudoers/opennebula-node-lxc index bbb87079c20..fc2c5a6eea3 100644 --- a/share/pkgs/sudoers/opennebula-node-lxc +++ b/share/pkgs/sudoers/opennebula-node-lxc @@ -1 +1,2 @@ oneadmin ALL=(ALL:ALL) NOPASSWD: ONE_LXC, ONE_NET, ONE_OVS, ONE_CEPH, ONE_LVM +oneadmin ALL=(ALL:ALL) NOPASSWD:SETENV: ONE_NETNS diff --git a/share/rubygems/generate b/share/rubygems/generate index 7156a80856f..8298e4c8ff0 100755 --- a/share/rubygems/generate +++ b/share/rubygems/generate @@ -19,7 +19,7 @@ require 'fileutils' require 'tmpdir' -VERSION = '6.10.0' +VERSION = '6.10.2' def version v = VERSION diff --git a/share/scripts/one b/share/scripts/one index 9363d9252ba..f516a760ba7 100755 --- a/share/scripts/one +++ b/share/scripts/one @@ -19,7 +19,6 @@ if [ -z "$ONE_LOCATION" ]; then ONE_PID=/var/run/one/oned.pid ONE_SCHEDPID=/var/run/one/sched.pid - ONE_HEMPID=/var/run/one/hem.pid ONE_CONF=/etc/one/oned.conf ONE_DB=/var/lib/one/one.db ONE_LOG=/var/log/one/oned.log @@ -36,7 +35,6 @@ if [ -z "$ONE_LOCATION" ]; then else ONE_PID=$ONE_LOCATION/var/oned.pid ONE_SCHEDPID=$ONE_LOCATION/var/sched.pid - ONE_HEMPID=$ONE_LOCATION/var/hem.pid ONE_CONF=$ONE_LOCATION/etc/oned.conf ONE_DB=$ONE_LOCATION/var/one.db ONE_LOG=$ONE_LOCATION/var/oned.log @@ -246,14 +244,19 @@ start_hem() [ -f "$ONE_HEM_LOG" ] && mv $ONE_HEM_LOG{,.$(date '+%Y%m%d%H%M%S')} fi - onehem-server start > /dev/null 2>&1 + HEM_ERROR=$(mktemp /tmp/hem-error.XXXXXX) + + onehem-server start > $HEM_ERROR 2>&1 LASTRC=$? if [ $LASTRC -ne 0 ]; then - echo "Error starting onehem-server" + echo "Error starting onehem-server: $(cat $HEM_ERROR)" + rm -f $HEM_ERROR exit 1 fi + + rm -f $HEM_ERROR } #------------------------------------------------------------------------------ diff --git a/share/sudoers/sudoers.rb b/share/sudoers/sudoers.rb index 1c0fde13827..a431b86277a 100644 --- a/share/sudoers/sudoers.rb +++ b/share/sudoers/sudoers.rb @@ -17,7 +17,7 @@ # Holds configuration about sudoers requirements for OpeNebula class Sudoers - NODECMDS = [:NET, :OVS, :LVM, :LXC, :MEM, :VGPU] + NODECMDS = [:NET, :NETNS, :OVS, :LVM, :LXC, :MEM, :VGPU] attr_accessor :cmds @@ -33,13 +33,25 @@ def initialize(lib_location) 'ip neighbour *', 'ip route *', 'ip rule *', - 'ip tuntap *' + 'ip tuntap *', + 'nft', + '/var/tmp/one/vnm/tproxy', + 'bridge' ], - :LVM => [ + :NETNS => [ + 'ip netns add *', + 'ip netns delete *', + 'ip netns pids *', + '/var/tmp/one/vnm/ip_netns_exec ip address *', + '/var/tmp/one/vnm/ip_netns_exec ip link *', + '/var/tmp/one/vnm/ip_netns_exec ip -j link show *', + '/var/tmp/one/vnm/ip_netns_exec ip route *' + ], + :LVM => [ 'lvcreate', 'lvremove', 'lvs', 'vgdisplay', 'lvchange', 'lvscan', 'lvextend' ], - :OVS => ['ovs-ofctl', 'ovs-vsctl'], - :CEPH => ['rbd'], + :OVS => ['ovs-ofctl', 'ovs-vsctl'], + :CEPH => ['rbd'], :HA => [ 'systemctl start opennebula-flow', 'systemctl stop opennebula-flow', @@ -64,8 +76,8 @@ def initialize(lib_location) 'lxc-console', 'e2fsck', 'resize2fs', 'xfs_growfs', 'rbd-nbd' ], :MARKET => ["#{lib_location}/sh/create_container_image.sh"], - :MEM => ['sysctl vm.drop_caches=3 vm.compact_memory=1'], - :VGPU => ['sudo', '/var/tmp/one/vgpu'] + :MEM => ['sysctl vm.drop_caches=3 vm.compact_memory=1'], + :VGPU => ['sudo', '/var/tmp/one/vgpu'] } end diff --git a/src/cli/command_parser.rb b/src/cli/command_parser.rb index ce1bdbea8d5..164a1e702ca 100644 --- a/src/cli/command_parser.rb +++ b/src/cli/command_parser.rb @@ -828,7 +828,7 @@ def format_text(arg) end def format_int(arg) - arg.match(/^\d+$/) ? [0,arg] : [-1] + arg.match(/^\d+$/) ? [0,arg] : [-1, "Argument '#{arg}' is not a valid ID"] end def format_file(arg) diff --git a/src/cli/one_helper/oneimage_helper.rb b/src/cli/one_helper/oneimage_helper.rb index c3de8b1fd1f..aba9a80a871 100644 --- a/src/cli/one_helper/oneimage_helper.rb +++ b/src/cli/one_helper/oneimage_helper.rb @@ -201,6 +201,22 @@ def self.type_to_str(id) Image::SHORT_IMAGE_TYPES[type_str] end + def retrieve_snapshot_id(image_id, id) + return [0, id.to_i] if id =~ /\A\d+\z/ + + image = retrieve_resource(image_id) + image.info + + ids = image.retrieve_elements( + "/IMAGE/SNAPSHOTS/SNAPSHOT[NAME='#{id}']/ID" + ) + + return [-1, "#{id} not found or duplicated"] \ + if ids.nil? || ids.size > 1 + + [0, ids[0].to_i] + end + def format_pool(options) config_file = self.class.table_conf diff --git a/src/cli/one_helper/onevm_helper.rb b/src/cli/one_helper/onevm_helper.rb index 7ccfabfd50e..6ad9d75bf89 100644 --- a/src/cli/one_helper/onevm_helper.rb +++ b/src/cli/one_helper/onevm_helper.rb @@ -252,6 +252,19 @@ def retrieve_disk_snapshot_id(vm_id, id) [0, ids[0].to_i] end + def retrieve_nic_id(vm_id, id) + return [0, id.to_i] if id =~ /\A\d+\z/ + + vm = retrieve_resource(vm_id) + vm.info + ids = vm.retrieve_elements("/VM/TEMPLATE/NIC[NAME='#{id}']/NIC_ID") + + return [-1, "NIC #{id} not found or duplicated"] \ + if ids.nil? || ids.size > 1 + + [0, ids[0].to_i] + end + def format_pool(options) config_file = self.class.table_conf @@ -1398,6 +1411,8 @@ def format_history(vm) table.show(history) end + public + def format_snapshots(vm) table = CLIHelper::ShowTable.new(nil, self) do column :AC, 'Is active', :left, :size => 2 do |d| diff --git a/src/cli/one_helper/onezone_helper.rb b/src/cli/one_helper/onezone_helper.rb index b1125fc2407..8cee96ad015 100644 --- a/src/cli/one_helper/onezone_helper.rb +++ b/src/cli/one_helper/onezone_helper.rb @@ -506,6 +506,22 @@ def self.state_to_str(id) Zone::SHORT_ZONE_STATES[state_str] end + def retrieve_server_id(zone_id, id) + return [0, id.to_i] if id =~ /\A\d+\z/ + + zone = retrieve_resource(zone_id) + zone.info + + ids = zone.retrieve_elements( + "/ZONE/SERVER_POOL/SERVER[NAME='#{id}']/ID" + ) + + return [-1, "#{id} not found or duplicated"] \ + if ids.nil? || ids.size > 1 + + [0, ids[0].to_i] + end + def format_pool(options) config_file = self.class.table_conf diff --git a/src/cli/onebackupjob b/src/cli/onebackupjob index c9509417b76..155c3c062d7 100755 --- a/src/cli/onebackupjob +++ b/src/cli/onebackupjob @@ -120,6 +120,10 @@ CommandParser::CmdParser.new(ARGV) do helper.filterflag_to_i(arg) end + set :format, :schedid, 'Scheduled Action id' do |arg| + format_int(arg) + end + ######################################################################## # Commands ######################################################################## diff --git a/src/cli/onehook b/src/cli/onehook index 124e20eb216..935415d2b7a 100755 --- a/src/cli/onehook +++ b/src/cli/onehook @@ -103,6 +103,10 @@ CommandParser::CmdParser.new(ARGV) do helper.list_to_id(arg) end + set :format, :execid, 'Hook execution id' do |arg| + format_int(arg) + end + ######################################################################## # Hook log Options ######################################################################## diff --git a/src/cli/onehost b/src/cli/onehost index 669e8d7ca18..6b6903728c3 100755 --- a/src/cli/onehost +++ b/src/cli/onehost @@ -355,7 +355,8 @@ CommandParser::CmdParser.new(ARGV) do end importvm_desc = <<-EOT.unindent - Import VM to OpenNebula + Import wild VM to OpenNebula. This method is deprecated and will be \ + removed in a future release. EOT command :importvm, diff --git a/src/cli/oneimage b/src/cli/oneimage index 5aa7e4cfc5b..5800122d4fe 100755 --- a/src/cli/oneimage +++ b/src/cli/oneimage @@ -149,7 +149,9 @@ CommandParser::CmdParser.new(ARGV) do end set :format, :imageid, OneImageHelper.to_id_desc do |arg| - helper.to_id(arg) + tmp = helper.to_id(arg) + @current_image = tmp[1] + tmp end set :format, :imageid_list, OneImageHelper.list_to_id_desc do |arg| @@ -170,6 +172,10 @@ CommandParser::CmdParser.new(ARGV) do end end + set :format, :snapshot_id, 'Snapshot name or id' do |arg| + helper.retrieve_snapshot_id(@current_image, arg) + end + ######################################################################## # Commands ######################################################################## diff --git a/src/cli/onevm b/src/cli/onevm index 79e49eaacbb..1e8a21a269b 100755 --- a/src/cli/onevm +++ b/src/cli/onevm @@ -320,7 +320,7 @@ CommandParser::CmdParser.new(ARGV) do helper.filterflag_to_i(arg) end - set :format, :diskid, 'Integer' do |arg| + set :format, :diskid, 'Disk id' do |arg| format_int(arg) end @@ -328,14 +328,30 @@ CommandParser::CmdParser.new(ARGV) do OpenNebulaHelper.size_in_mb(arg) end - format :snapshot_id, 'Snapshot identifier' do |arg| + set :format, :snapshot_id, 'Snapshot name or id' do |arg| helper.retrieve_snapshot_id(@current_vm, arg) end - format :disk_snapshot_id, 'Disk_snapshot identifier' do |arg| + set :format, :disk_snapshot_id, 'Disk_snapshot name or id' do |arg| helper.retrieve_disk_snapshot_id(@current_vm, arg) end + set :format, :nicid, 'NIC name or id' do |arg| + helper.retrieve_nic_id(@current_vm, arg) + end + + set :format, :pciid, 'PCI id' do |arg| + format_int(arg) + end + + set :format, :sched_id, 'Scheduled Action id' do |arg| + format_int(arg) + end + + set :format, :sgid, 'Security Group id' do |arg| + format_int(arg) + end + ######################################################################## # Commands ######################################################################## @@ -647,7 +663,8 @@ CommandParser::CmdParser.new(ARGV) do migrate_desc = <<-EOT.unindent Migrates the given running VM to another Host. If used with --live - parameter the miration is done without downtime. + parameter the migration is done without downtime. Datastore migration is + not supported for --live flag. States: RUNNING EOT @@ -1282,6 +1299,35 @@ CommandParser::CmdParser.new(ARGV) do helper.show_resource(args[0], options) end + disk_snapshot_list_desc = <<-EOT.unindent + Lists the snapshots of a disk + EOT + + command :"disk-snapshot-list", disk_snapshot_list_desc, :vmid, :diskid do + vm = helper.retrieve_resource(args[0]) + vm.info + + if vm.has_elements?('/VM/SNAPSHOTS') + filtered = [] + vm_hash = vm.to_hash + vm_snapshots = [vm_hash['VM']['SNAPSHOTS']].flatten + + vm_snapshots.each do |snap| + if snap['DISK_ID'] == args[1] + filtered << snap + end + end + + vm_hash['VM']['SNAPSHOTS'] = filtered + + CLIHelper.print_header('VM DISK SNAPSHOTS'.ljust(80), false) + helper.format_snapshots(vm_hash) + else + puts 'No snapshots available in this disk' + end + 0 + end + top_desc = <<-EOT.unindent Lists Images continuously EOT diff --git a/src/cli/onevmgroup b/src/cli/onevmgroup index 26560472367..6076af338f2 100755 --- a/src/cli/onevmgroup +++ b/src/cli/onevmgroup @@ -121,7 +121,7 @@ CommandParser::CmdParser.new(ARGV) do helper.filterflag_to_i(arg) end - format :roleid, 'Role identifier' do |arg| + set :format, :roleid, 'Role identifier' do |arg| helper.retrieve_role_id(@current_vmg, arg) end diff --git a/src/cli/onevnet b/src/cli/onevnet index 36cb5dc62ab..816192206f0 100755 --- a/src/cli/onevnet +++ b/src/cli/onevnet @@ -152,7 +152,7 @@ CommandParser::CmdParser.new(ARGV) do helper.filterflag_to_i(arg) end - set :format, :ar_id, 'Integer' do |arg| + set :format, :ar_id, 'Address Range id' do |arg| format_int(arg) end diff --git a/src/cli/onevrouter b/src/cli/onevrouter index 6755b8a463f..f62b5cb6a49 100755 --- a/src/cli/onevrouter +++ b/src/cli/onevrouter @@ -128,6 +128,10 @@ CommandParser::CmdParser.new(ARGV) do OpenNebulaHelper.rname_to_id(arg, 'VMTEMPLATE') end + set :format, :nicid, 'NIC id' do |arg| + format_int(arg) + end + ######################################################################## # Commands ######################################################################## diff --git a/src/cli/onezone b/src/cli/onezone index 9794b2a884e..8c61e1a33a5 100755 --- a/src/cli/onezone +++ b/src/cli/onezone @@ -91,13 +91,19 @@ CommandParser::CmdParser.new(ARGV) do # Formatters for arguments ######################################################################## set :format, :zoneid, OneZoneHelper.to_id_desc do |arg| - helper.to_id(arg) + tmp = helper.to_id(arg) + @current_zone = tmp[1] + tmp end set :format, :zoneid_list, OneZoneHelper.list_to_id_desc do |arg| helper.list_to_id(arg) end + set :format, :serverid, 'Server name or id' do |arg| + helper.retrieve_server_id(@current_zone, arg) + end + ######################################################################## # Commands ######################################################################## diff --git a/src/cloud/common/CloudClient.rb b/src/cloud/common/CloudClient.rb index c5d63921599..5d856336214 100644 --- a/src/cloud/common/CloudClient.rb +++ b/src/cloud/common/CloudClient.rb @@ -51,7 +51,7 @@ module CloudClient # OpenNebula version - VERSION = '6.10.0' + VERSION = '6.10.2' # ######################################################################### # Default location for the authentication file diff --git a/src/cluster/ClusterPool.cc b/src/cluster/ClusterPool.cc index 5f1f1194dc0..408f63d3118 100644 --- a/src/cluster/ClusterPool.cc +++ b/src/cluster/ClusterPool.cc @@ -98,43 +98,34 @@ ClusterPool::ClusterPool(SqlDB * db, int ClusterPool::allocate(string name, int * oid, string& error_str) { - Cluster * cluster; - - ostringstream oss; - - int db_oid; + *oid = -1; // Check name if ( !PoolObjectSQL::name_is_valid(name, error_str) ) { - goto error_name; + return *oid; } // Check for duplicates - db_oid = exist(name); + const auto db_oid = exist(name); if( db_oid != -1 ) { - goto error_duplicated; + ostringstream oss; + + oss << "NAME is already taken by CLUSTER " << db_oid << "."; + error_str = oss.str(); + + return *oid; } // Build a new Cluster object - cluster = new Cluster(-1, name, 0, vnc_conf); + Cluster cluster {-1, name, 0, vnc_conf}; // Insert the Object in the pool *oid = PoolSQL::allocate(cluster, error_str); return *oid; - - -error_duplicated: - oss << "NAME is already taken by CLUSTER " << db_oid << "."; - error_str = oss.str(); - -error_name: - *oid = -1; - - return *oid; } /* -------------------------------------------------------------------------- */ diff --git a/src/common/SSLUtil.cc b/src/common/SSLUtil.cc index 2c9f35b6c95..2b8f02b0d76 100644 --- a/src/common/SSLUtil.cc +++ b/src/common/SSLUtil.cc @@ -254,12 +254,12 @@ namespace ssl_util int rsa_public_encrypt(const std::string& in, std::string& out) { - static RSA * rsa = nullptr; + static EVP_PKEY_CTX *ctx = nullptr; static std::mutex m; std::lock_guard lock(m); - if ( rsa == nullptr) //initialize RSA structure + if ( ctx == nullptr) //initialize RSA structure { FILE * fp = fopen(pubk_path.c_str(), "r"); @@ -268,30 +268,54 @@ namespace ssl_util return -1; } - rsa = PEM_read_RSAPublicKey(fp, &rsa, nullptr, nullptr); + EVP_PKEY* pub_key = PEM_read_PUBKEY(fp, nullptr, nullptr, nullptr); - if ( rsa == nullptr ) + fclose(fp); + + if ( pub_key == nullptr ) { return -1; } - fclose(fp); - } + if (EVP_PKEY_base_id(pub_key) != EVP_PKEY_RSA) + { + EVP_PKEY_free(pub_key); + return -1; + } - char * out_c = (char *) malloc(sizeof(char) * RSA_size(rsa)); + ctx = EVP_PKEY_CTX_new(pub_key, nullptr); + if (!ctx) + return -1; - int rc = RSA_public_encrypt(in.length(), (const unsigned char *) in.c_str(), - (unsigned char *) out_c, rsa, RSA_PKCS1_PADDING); + if (EVP_PKEY_encrypt_init(ctx) < 1 || + EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) < 1) + { + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(pub_key); + ctx = nullptr; + return -1; + } - if ( rc != -1 ) - { - out.assign(out_c, rc); - rc = 0; } - free(out_c); + size_t out_len; + if (EVP_PKEY_encrypt(ctx, nullptr, &out_len, (const unsigned char *)in.c_str(), in.size()) <= 0) + return -1; + + // OpenSSL documentation states that if EVP_PKEY_encrypt is called with out as nullptr, + // it will write maximum size of the output buffer to out_len. + // If it's not nullptr it will write the actual size of buffer, so we call resize here twice. + std::string result; + result.resize(out_len); + + if (EVP_PKEY_encrypt(ctx, (unsigned char*) result.data(), &out_len, (const unsigned char *)in.c_str(), in.size()) <= 0) + return -1; + + result.resize(out_len); - return rc; + out = std::move(result); + + return 0; } /* -------------------------------------------------------------------------- */ @@ -299,12 +323,12 @@ namespace ssl_util int rsa_private_decrypt(const std::string& in, std::string& out) { - static RSA * rsa = nullptr; + static EVP_PKEY_CTX* ctx = nullptr; static std::mutex m; std::lock_guard lock(m); - if ( rsa == nullptr) //initialize RSA structure + if ( ctx == nullptr) //initialize RSA structure { FILE * fp = fopen(prik_path.c_str(), "r"); @@ -313,48 +337,66 @@ namespace ssl_util return -1; } - rsa = PEM_read_RSAPrivateKey(fp, &rsa, nullptr, nullptr); + EVP_PKEY* priv_key = PEM_read_PrivateKey(fp, nullptr, nullptr, nullptr); - if ( rsa == nullptr ) + fclose(fp); + + if ( priv_key == nullptr ) { return -1; } - fclose(fp); + if (EVP_PKEY_base_id(priv_key) != EVP_PKEY_RSA) + { + EVP_PKEY_free(priv_key); + return -1; + } + + ctx = EVP_PKEY_CTX_new(priv_key, nullptr); + if (!ctx) + return -1; + + if (EVP_PKEY_decrypt_init(ctx) < 1 || + EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) < 1) + { + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(priv_key); + ctx = nullptr; + return -1; + } } - std::string result; + const auto in_size = in.size(); - int key_size = RSA_size(rsa); - int in_size = in.length(); - char * out_c = (char *) malloc(sizeof(char) * RSA_size(rsa)); + std::size_t key_size = 0; - const char * in_c = in.c_str(); + if (EVP_PKEY_decrypt(ctx, nullptr, &key_size, (unsigned char*)in.data(), in.size()) < 1) + { + return -1; + } + + std::vector out_c(key_size, 0); + std::string result; - for (int index = 0; index < in_size; index += key_size) + std::size_t index = 0; + + while (index < in_size) { - int block_size = key_size; + auto step = (in_size - index) < key_size ? in_size - index : key_size; - if ( index + key_size > in_size ) - { - block_size = in_size - index; - } + const unsigned char* in_p = (unsigned char*)in.data() + index; - int rc = RSA_private_decrypt(block_size, (const unsigned char *) - in_c + index, (unsigned char *) out_c, rsa, RSA_PKCS1_PADDING); + std::size_t out_size = key_size; - if ( rc != -1 ) + if (EVP_PKEY_decrypt(ctx, out_c.data(), &out_size, in_p, step) < 1) { - result.append(out_c, rc); - } - else - { - free(out_c); return -1; } - } - free(out_c); + result.append((char*)out_c.data(), out_size); + + index +=step; + } out = std::move(result); diff --git a/src/datastore/Datastore.cc b/src/datastore/Datastore.cc index 809c3dbd039..a372b42723d 100644 --- a/src/datastore/Datastore.cc +++ b/src/datastore/Datastore.cc @@ -16,7 +16,6 @@ #include "Datastore.h" #include "DatastorePool.h" -#include "GroupPool.h" #include "NebulaLog.h" #include "Nebula.h" #include "VirtualMachineDisk.h" diff --git a/src/datastore/DatastorePool.cc b/src/datastore/DatastorePool.cc index baddd1ad8e4..84fe2d43fa0 100644 --- a/src/datastore/DatastorePool.cc +++ b/src/datastore/DatastorePool.cc @@ -193,33 +193,34 @@ int DatastorePool::allocate( const set &cluster_ids, string& error_str) { - Datastore * ds; - - int db_oid; - string name; - - ostringstream oss; - - ds = new Datastore(uid, gid, uname, gname, umask, - move(ds_template), cluster_ids); + Datastore ds {uid, gid, uname, gname, umask, + move(ds_template), cluster_ids}; // ------------------------------------------------------------------------- // Check name & duplicates // ------------------------------------------------------------------------- - ds->get_template_attribute("NAME", name); + string name; + ds.get_template_attribute("NAME", name); + + *oid = -1; if ( !PoolObjectSQL::name_is_valid(name, error_str) ) { - goto error_name; + return *oid; } // Check for duplicates - db_oid = exist(name); + const auto db_oid = exist(name); if( db_oid != -1 ) { - goto error_duplicated; + ostringstream oss; + + oss << "NAME is already taken by DATASTORE " << db_oid << "."; + error_str = oss.str(); + + return *oid; } *oid = PoolSQL::allocate(ds, error_str); @@ -235,16 +236,6 @@ int DatastorePool::allocate( } } - return *oid; - -error_duplicated: - oss << "NAME is already taken by DATASTORE " << db_oid << "."; - error_str = oss.str(); - -error_name: - delete ds; - *oid = -1; - return *oid; } diff --git a/src/datastore_mad/remotes/ceph/cp b/src/datastore_mad/remotes/ceph/cp index 34f7b23f0b5..8c4e6faae38 100755 --- a/src/datastore_mad/remotes/ceph/cp +++ b/src/datastore_mad/remotes/ceph/cp @@ -61,7 +61,6 @@ done < <($XPATH /DS_DRIVER_ACTION_DATA/DATASTORE/BASE_PATH \ /DS_DRIVER_ACTION_DATA/IMAGE/SIZE \ /DS_DRIVER_ACTION_DATA/IMAGE/TEMPLATE/MD5 \ /DS_DRIVER_ACTION_DATA/IMAGE/TEMPLATE/SHA1 \ - /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/NO_DECOMPRESS \ /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/LIMIT_TRANSFER_BW \ /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/CEPH_USER \ /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/CEPH_KEY \ @@ -81,7 +80,6 @@ SRC="${XPATH_ELEMENTS[i++]}" SIZE="${XPATH_ELEMENTS[i++]}" MD5="${XPATH_ELEMENTS[i++]}" SHA1="${XPATH_ELEMENTS[i++]}" -NO_DECOMPRESS="${XPATH_ELEMENTS[i++]}" LIMIT_TRANSFER_BW="${XPATH_ELEMENTS[i++]}" CEPH_USER="${XPATH_ELEMENTS[i++]}" CEPH_KEY="${XPATH_ELEMENTS[i++]}" @@ -107,8 +105,14 @@ if [ -n "$CEPH_CONF" ]; then RBD="$RBD --conf ${CEPH_CONF}" fi +# EC parameters (--data-pool) are only accepted in some `rbd` commands. It's not officially +# documented but at least the following ones require it: +# - create +# - import +RBDEC="$RBD" + if [ -n "$EC_POOL_NAME" ]; then - RBD="$RBD --data-pool ${EC_POOL_NAME}" + RBDEC="$RBDEC --data-pool ${EC_POOL_NAME}" fi set_up_datastore "$BASE_PATH" "$RESTRICTED_DIRS" "$SAFE_DIRS" @@ -119,7 +123,7 @@ TMP_DST="$STAGING_DIR/$IMAGE_HASH" IMAGE_NAME="one-${ID}" RBD_SOURCE="${POOL_NAME}/${IMAGE_NAME}" -DOWNLOADER_ARGS=`set_downloader_args "$MD5" "$SHA1" "$NO_DECOMPRESS" "$LIMIT_TRANSFER_BW" "$SRC" -` +DOWNLOADER_ARGS=`set_downloader_args "$MD5" "$SHA1" "yes" "$LIMIT_TRANSFER_BW" "$SRC" -` COPY_COMMAND="$UTILS_PATH/downloader.sh $DOWNLOADER_ARGS" @@ -153,21 +157,43 @@ fi REGISTER_CMD=$(cat < ds_xml) + ds_xml = REXML::Document.new(xml).root.elements['DATASTORE'] + ds = TransferManager::Datastore.from_xml(:ds_xml => ds_xml.to_s) - rds = Restic.new ds_xml, :create_repo => true, - :repo_type => :sftp, - :host_type => :hypervisor, - :repo_id => repo_id + rds = Restic.new ds_xml.to_s, :create_repo => true, + :repo_type => :sftp, + :host_type => :hypervisor, + :repo_id => repo_id rds.resticenv_rb rescue StandardError => e STDERR.puts e.full_message @@ -216,5 +217,14 @@ end id = parts[0] short_id = id[0..7] # first 8 chars only -STDOUT.puts "#{short_id} #{parts[1].to_i / (1024 * 1024)}" +vm = REXML::Document.new(xml).root.elements['VM'] +backup_format = + if vm.elements['TEMPLATE/TM_MAD_SYSTEM'].text == 'ceph' && + vm.elements['BACKUPS/BACKUP_CONFIG/MODE']&.text == 'INCREMENT' + 'rbd' + else + 'raw' + end + +STDOUT.puts "#{short_id} #{parts[1].to_i / (1024 * 1024)} #{backup_format}" exit(0) diff --git a/src/datastore_mad/remotes/restic/ls b/src/datastore_mad/remotes/restic/ls index d1cfba3429f..3dfb92813d7 100755 --- a/src/datastore_mad/remotes/restic/ls +++ b/src/datastore_mad/remotes/restic/ls @@ -93,6 +93,9 @@ begin image.chain_up_to(increment_id) end + xml = REXML::Document.new(action).root + format = xml.elements['IMAGE/FORMAT'].text + rds = Restic.new action, :prefix => 'DATASTORE/', :repo_type => :local, :host_type => :frontend @@ -103,7 +106,7 @@ begin ds_id = rds['DATASTORE/ID'] snap = image.selected || image.last - burl = "restic://#{ds_id}/#{image.bj_id}/#{chain}" + burl = "restic#{format == 'rbd' ? '+rbd' : ''}://#{ds_id}/#{image.bj_id}/#{chain}" # -------------------------------------------------------------------------- # Get a list of disk paths stored in the backup diff --git a/src/datastore_mad/remotes/restic/restore b/src/datastore_mad/remotes/restic/restore index 2c755105582..40dd84d8c0f 100755 --- a/src/datastore_mad/remotes/restic/restore +++ b/src/datastore_mad/remotes/restic/restore @@ -125,12 +125,14 @@ one_client = OpenNebula::Client.new token # ------------------------------------------------------------------------------ # Create backup object templates for VM and associated disk images # ------------------------------------------------------------------------------ -restorer = TransferManager::BackupRestore.new :vm_xml64 => vm_xml, - :backup_id => snap, - :bimage => image, - :ds_id => ds_id, - :txml => rds, - :proto => 'restic' +restorer = TransferManager::BackupRestore.new( + :vm_xml64 => vm_xml, + :backup_id => snap, + :bimage => image, + :ds_id => ds_id, + :txml => rds, + :proto => image.proto('restic') +) br_disks = restorer.disk_images disks diff --git a/src/datastore_mad/remotes/restic/stat b/src/datastore_mad/remotes/restic/stat index 4b28b18152a..412ed2f9b3e 100755 --- a/src/datastore_mad/remotes/restic/stat +++ b/src/datastore_mad/remotes/restic/stat @@ -75,7 +75,9 @@ begin rds = Restic.new action, :prefix =>'DATASTORE/' rds.resticenv_rb - file = rds['IMAGE/PATH'].delete_prefix('restic://') + file = rds['IMAGE/PATH'] + file.slice! %r{restic(\+[^:]+)?://} + parts = file.split('/') diskid = parts[-1].match(/disk\.(\d+)/) base_path = "/#{parts[3..-2].join('/')}/" @@ -111,7 +113,7 @@ if !disk exit(-1) end -size = disk.elements['//SIZE'] +size = disk.elements['SIZE'] if !size STDERR.puts "Cannot find size for disk #{diskid[1]} in VM backup info" diff --git a/src/datastore_mad/remotes/restic_downloader.rb b/src/datastore_mad/remotes/restic_downloader.rb index 78c0c18951c..3c4608ed0a3 100755 --- a/src/datastore_mad/remotes/restic_downloader.rb +++ b/src/datastore_mad/remotes/restic_downloader.rb @@ -64,11 +64,13 @@ # restic:////:,.../ restic_url = ARGV[0] -tokens = restic_url.delete_prefix('restic://').split('/') + +proto, url = restic_url.split(%r{://}, 2) +tokens = url.split('/', 4) ds_id = tokens[0].to_i bj_id = tokens[1] snaps = tokens[2].split(',').map {|s| s.split(':')[1] } -disk_path = tokens[3..-1].join('/') +disk_path = "/#{tokens[3]}" disk_index = Pathname.new(disk_path).basename.to_s.split('.')[1] vm_id = disk_path.match('/(\d+)/backup/[^/]+$')[1].to_i @@ -109,52 +111,60 @@ # Prepare image. begin - tmp_dir = "#{rds.tmp_dir}/#{SecureRandom.uuid}" - + tmp_dir = "#{rds.tmp_dir}/#{SecureRandom.uuid}" paths = rds.pull_chain(snaps, disk_index, rds.sftp, tmp_dir) - disk_paths = paths[:disks][:by_index][disk_index] - - tmp_path = "#{tmp_dir}/#{Pathname.new(disk_paths.last).basename}" - - # FULL BACKUP - - if disk_paths.size == 1 - # Return shell code snippets according to the downloader's interface. - STDOUT.puts <<~EOS - command="ssh #{SSH_OPTS} '#{rds.user}@#{rds.sftp}' cat '#{tmp_path}'" - clean_command="ssh #{SSH_OPTS} '#{rds.user}@#{rds.sftp}' rm -rf '#{tmp_dir}/'" + disk_paths = paths[:disks][:by_index][disk_index].map {|d| Pathname.new(d) } + tmp_path = "#{tmp_dir}/#{disk_paths.last.basename}" + + if proto == 'restic+rbd' + # FULL/INCREMENTAL BACKUP (RBD) + + tmp_path = "#{tmp_dir}/disk.#{disk_index}.#{snaps.last[0]}.tar.gz" + script = <<~EOS + set -e -o pipefail; shopt -qs failglob + mkdir -p '#{tmp_dir}/' + tar zcvf '#{tmp_path}' -C #{tmp_dir} #{disk_paths.map {|d| d.basename }.join(' ')} + rm #{disk_paths.map {|d| "#{tmp_dir}/#{d.basename}" }.join(' ')} EOS - exit(0) - end - # INCREMENTAL BACKUP - - script = [<<~EOS] - set -e -o pipefail; shopt -qs failglob - #{rds.resticenv_sh} - EOS - - script << TransferManager::BackupImage.reconstruct_chain(disk_paths, - :workdir => tmp_dir) - - script << TransferManager::BackupImage.merge_chain(disk_paths, - :workdir => tmp_dir) + rc = TransferManager::Action.ssh('prepare_image', + :host => "#{rds.user}@#{rds.sftp}", + :forward => true, + :cmds => script, + :nostdout => false, + :nostderr => false) + + raise StandardError, "Unable to prepare image: #{rc.stderr}" if rc.code != 0 + elsif disk_paths.size == 1 + # FULL BACKUP (QCOW2) + + # No additional preparation needed + true + else + # INCREMENTAL BACKUP (QCOW2) + + script = [<<~EOS] + set -e -o pipefail; shopt -qs failglob + #{rds.resticenv_sh} + #{TransferManager::BackupImage.reconstruct_chain(disk_paths, :workdir => tmp_dir)} + #{TransferManager::BackupImage.merge_chain(disk_paths, :workdir => tmp_dir)} + EOS - rc = TransferManager::Action.ssh 'prepare_image', - :host => "#{rds.user}@#{rds.sftp}", - :forward => true, - :cmds => script.join("\n"), - :nostdout => true, - :nostderr => false + rc = TransferManager::Action.ssh('prepare_image', + :host => "#{rds.user}@#{rds.sftp}", + :forward => true, + :cmds => script.join("\n"), + :nostdout => true, + :nostderr => false) - raise StandardError, "Unable to prepare image: #{rc.stderr}" if rc.code != 0 + raise StandardError, "Unable to prepare image: #{rc.stderr}" if rc.code != 0 + end # Return shell code snippets according to the downloader's interface. STDOUT.puts <<~EOS command="ssh #{SSH_OPTS} '#{rds.user}@#{rds.sftp}' cat '#{tmp_path}'" clean_command="ssh #{SSH_OPTS} '#{rds.user}@#{rds.sftp}' rm -rf '#{tmp_dir}/'" EOS - exit(0) rescue StandardError => e STDERR.puts e.full_message exit(-1) diff --git a/src/datastore_mad/remotes/rsync/backup b/src/datastore_mad/remotes/rsync/backup index 1acccdb3434..1b5cf2e9ecb 100755 --- a/src/datastore_mad/remotes/rsync/backup +++ b/src/datastore_mad/remotes/rsync/backup @@ -62,7 +62,7 @@ require_relative '../../tm/lib/tm_action' TransferManager::Datastore.load_env -ds_xml = STDIN.read +xml = STDIN.read dir = ARGV[0].split(':') _disks = ARGV[1].split(':') @@ -81,20 +81,20 @@ vm_dir = if dsrdir end begin - ds = REXML::Document.new(ds_xml).root + ds_xml = REXML::Document.new(xml).root.elements['DATASTORE'] - rsync_user = ds.elements['TEMPLATE/RSYNC_USER'].text - rsync_host = ds.elements['TEMPLATE/RSYNC_HOST'].text + rsync_user = ds_xml.elements['TEMPLATE/RSYNC_USER'].text + rsync_host = ds_xml.elements['TEMPLATE/RSYNC_HOST'].text - base = ds.elements['BASE_PATH'].text + base = ds_xml.elements['BASE_PATH'].text - if ds.elements['TEMPLATE/RSYNC_ARGS'].nil? + if ds_xml.elements['TEMPLATE/RSYNC_ARGS'].nil? args = '-aS' else - args = ds.elements['TEMPLATE/RSYNC_ARGS'].text + args = ds_xml.elements['TEMPLATE/RSYNC_ARGS'].text end - ds = TransferManager::Datastore.from_xml(:ds_xml => ds_xml) + ds = TransferManager::Datastore.from_xml(:ds_xml => ds_xml.to_s) rescue StandardError => e STDERR.puts e.message exit(-1) @@ -204,5 +204,14 @@ if rc.code != 0 || rc.stdout.empty? exit(-1) end -STDOUT.puts "#{backup_id} #{rc.stdout.lines.last.split[0]}" +vm = REXML::Document.new(xml).root.elements['VM'] +backup_format = + if vm.elements['TEMPLATE/TM_MAD_SYSTEM'].text == 'ceph' && + vm.elements['BACKUPS/BACKUP_CONFIG/MODE']&.text == 'INCREMENT' + 'rbd' + else + 'raw' + end + +STDOUT.puts "#{backup_id} #{rc.stdout.lines.last.split[0]} #{backup_format}" exit(0) diff --git a/src/datastore_mad/remotes/rsync/ls b/src/datastore_mad/remotes/rsync/ls index aa7a43a6f93..70f7facdea6 100755 --- a/src/datastore_mad/remotes/rsync/ls +++ b/src/datastore_mad/remotes/rsync/ls @@ -98,9 +98,10 @@ begin bpath = xml.elements['DATASTORE/BASE_PATH'].text ruser = xml.elements['DATASTORE/TEMPLATE/RSYNC_USER']&.text || 'oneadmin' rhost = xml.elements['DATASTORE/TEMPLATE/RSYNC_HOST'].text + format = xml.elements['IMAGE/FORMAT'].text snap = image.selected || image.last - burl = "rsync://#{ds_id}/#{image.bj_id}/#{chain}" + burl = "rsync#{format == 'rbd' ? '+rbd' : ''}://#{ds_id}/#{image.bj_id}/#{chain}" # -------------------------------------------------------------------------- # Get a list of disk paths stored in the backup diff --git a/src/datastore_mad/remotes/rsync/restore b/src/datastore_mad/remotes/rsync/restore index 4b859d09b8c..2ba662f94f6 100755 --- a/src/datastore_mad/remotes/rsync/restore +++ b/src/datastore_mad/remotes/rsync/restore @@ -165,12 +165,14 @@ one_client = OpenNebula::Client.new token # ------------------------------------------------------------------------------ xml.define_singleton_method('[]') {|xpath| elements[xpath].text } -restorer = TransferManager::BackupRestore.new :vm_xml64 => vm_xml, - :backup_id => snap, - :bimage => image, - :ds_id => ds_id, - :txml => xml, - :proto => 'rsync' +restorer = TransferManager::BackupRestore.new( + :vm_xml64 => vm_xml, + :backup_id => snap, + :bimage => image, + :ds_id => ds_id, + :txml => xml, + :proto => image.proto('rsync') +) br_disks = restorer.disk_images disk_paths diff --git a/src/datastore_mad/remotes/rsync/stat b/src/datastore_mad/remotes/rsync/stat index 62065997e09..673af7c9aa6 100755 --- a/src/datastore_mad/remotes/rsync/stat +++ b/src/datastore_mad/remotes/rsync/stat @@ -74,7 +74,7 @@ begin rsync_host = rds['/DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/RSYNC_HOST'].text rsync_user = rds['/DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/RSYNC_USER'].text - img.slice! 'rsync://' + img.slice! %r{rsync(\+[^:]+)?://} parts = img.split('/') diskid = parts[-1].match(/disk\.([0-9]+)/) @@ -117,7 +117,7 @@ if !disk exit(-1) end -size = disk.elements['//SIZE'] +size = disk.elements['SIZE'] if !size STDERR.puts "Cannot find size for disk #{diskid[1]} in VM backup info" diff --git a/src/datastore_mad/remotes/rsync_downloader.rb b/src/datastore_mad/remotes/rsync_downloader.rb index e9162b7d107..81dae0d840e 100755 --- a/src/datastore_mad/remotes/rsync_downloader.rb +++ b/src/datastore_mad/remotes/rsync_downloader.rb @@ -64,11 +64,13 @@ # rsync://100/3/0:8a3454,1:f6e63e//var/lib/one/datastores/100/6/8a3454/disk.0.0 rsync_url = ARGV[0] -tokens = rsync_url.delete_prefix('rsync://').split('/') + +proto, url = rsync_url.split(%r{://}, 2) +tokens = url.split('/', 4) ds_id = tokens[0].to_i _bj_id = tokens[1] increments = tokens[2].split(',').map {|s| s.split(':') } -disk_path = "/#{tokens[3..-1].join('/')}" +disk_path = "/#{tokens[3]}" disk_index = Pathname.new(disk_path).basename.to_s.split('.')[1] vm_id = disk_path.match("/#{ds_id}/(\\d+)/[^/]+/[^/]+$")[1].to_i @@ -93,54 +95,81 @@ # Prepare image. begin - disk_paths = increments.map do |index, snap| - raw = %(#{base_path}/#{vm_id}/#{snap}/disk.#{disk_index}.#{index}) - cleaned = Pathname.new(raw).cleanpath.to_s - cleaned - end + tmp_dir = "#{rsync_tmp_dir}/#{SecureRandom.uuid}" + if proto == 'rsync+rbd' + # FULL/INCREMENTAL BACKUP (RBD) + + disk_paths = increments.map do |index, snap| + raw = if index == '0' + "#{base_path}/#{vm_id}/#{snap}/disk.#{disk_index}.rbd2" + else + "#{base_path}/#{vm_id}/#{snap}/disk.#{disk_index}.#{index}.rbdiff" + end + Pathname.new(raw).cleanpath + end + + tmp_path = "#{tmp_dir}/disk.#{disk_index}.#{increments.last[0]}.tar.gz" - # FULL BACKUP + script = <<~EOS + set -e -o pipefail; shopt -qs failglob + mkdir -p '#{tmp_dir}/' + tar zcvf '#{tmp_path}'#{disk_paths.map {|d| " -C #{d.dirname} #{d.basename}" }.join('')} + EOS + + rc = TransferManager::Action.ssh('prepare_image', + :host => "#{rsync_user}@#{rsync_host}", + :forward => true, + :cmds => script, + :nostdout => false, + :nostderr => false) + + raise StandardError, "Unable to prepare image: #{rc.stderr}" if rc.code != 0 + + STDOUT.puts <<~EOS + command="ssh #{SSH_OPTS} '#{rsync_user}@#{rsync_host}' cat '#{tmp_path}'" + clean_command="ssh #{SSH_OPTS} '#{rsync_user}@#{rsync_host}' rm -rf '#{tmp_dir}/'" + EOS + elsif increments.size == 1 + # FULL BACKUP (QCOW2) - if disk_paths.size == 1 - # Return shell code snippets according to the downloader's interface. STDOUT.puts <<~EOS command="ssh #{SSH_OPTS} '#{rsync_user}@#{rsync_host}' cat '#{disk_path}'" clean_command="" EOS - exit(0) - end - - # INCREMENTAL BACKUP - - tmp_dir = "#{rsync_tmp_dir}/#{SecureRandom.uuid}" - tmp_path = "#{tmp_dir}/#{Pathname.new(disk_paths.last).basename}" + else + # INCREMENTAL BACKUP (QCOW2) - script = [<<~EOS] - set -e -o pipefail; shopt -qs failglob - EOS + disk_paths = increments.map do |index, snap| + raw = %(#{base_path}/#{vm_id}/#{snap}/disk.#{disk_index}.#{index}) + cleaned = Pathname.new(raw).cleanpath.to_s + cleaned + end - script << TransferManager::BackupImage.reconstruct_chain(disk_paths) + tmp_path = "#{tmp_dir}/#{Pathname.new(disk_paths.last).basename}" - script << "mkdir -p '#{tmp_dir}/'" + script = <<~EOS + set -e -o pipefail; shopt -qs failglob - script << TransferManager::BackupImage.merge_chain(disk_paths, - :destdir => tmp_dir) + #{TransferManager::BackupImage.reconstruct_chain(disk_paths)} + mkdir -p '#{tmp_dir}/' + #{TransferManager::BackupImage.merge_chain(disk_paths, + :destdir => tmp_dir)} + EOS - rc = TransferManager::Action.ssh 'prepare_image', - :host => "#{rsync_user}@#{rsync_host}", - :forward => true, - :cmds => script.join("\n"), - :nostdout => false, - :nostderr => false + rc = TransferManager::Action.ssh('prepare_image', + :host => "#{rsync_user}@#{rsync_host}", + :forward => true, + :cmds => script, + :nostdout => false, + :nostderr => false) - raise StandardError, "Unable to prepare image: #{rc.stderr}" if rc.code != 0 + raise StandardError, "Unable to prepare image: #{rc.stderr}" if rc.code != 0 - # Return shell code snippets according to the downloader's interface. - STDOUT.puts <<~EOS - command="ssh #{SSH_OPTS} '#{rsync_user}@#{rsync_host}' cat '#{tmp_path}'" - clean_command="ssh #{SSH_OPTS} '#{rsync_user}@#{rsync_host}' rm -rf '#{tmp_dir}/'" - EOS - exit(0) + STDOUT.puts <<~EOS + command="ssh #{SSH_OPTS} '#{rsync_user}@#{rsync_host}' cat '#{tmp_path}'" + clean_command="ssh #{SSH_OPTS} '#{rsync_user}@#{rsync_host}' rm -rf '#{tmp_dir}/'" + EOS + end rescue StandardError => e STDERR.puts e.full_message exit(-1) diff --git a/src/dm/DispatchManagerActions.cc b/src/dm/DispatchManagerActions.cc index c1bc82f07a5..18bcabb1359 100644 --- a/src/dm/DispatchManagerActions.cc +++ b/src/dm/DispatchManagerActions.cc @@ -165,6 +165,8 @@ int DispatchManager::import(unique_ptr vm, const RequestAttribut vm->set_running_stime(the_time); + vm->set_vm_info(); + vmpool->update_history(vm.get()); vmpool->update(vm.get()); diff --git a/src/fireedge/src/client/components/Cards/NetworkCard.js b/src/fireedge/src/client/components/Cards/NetworkCard.js index 099f8fcf3e2..3a6f456a711 100644 --- a/src/fireedge/src/client/components/Cards/NetworkCard.js +++ b/src/fireedge/src/client/components/Cards/NetworkCard.js @@ -129,7 +129,7 @@ const NetworkCard = memo( )} {vnMad && } - {LOCK && } + {LOCK && } diff --git a/src/fireedge/src/client/components/Cards/VirtualMachineCard.js b/src/fireedge/src/client/components/Cards/VirtualMachineCard.js index e979e86ea98..60e32f4a050 100644 --- a/src/fireedge/src/client/components/Cards/VirtualMachineCard.js +++ b/src/fireedge/src/client/components/Cards/VirtualMachineCard.js @@ -24,6 +24,7 @@ import { Lock, Network, User, + Internet as HostnameIcon, WarningCircledOutline as WarningIcon, } from 'iconoir-react' @@ -80,7 +81,7 @@ const VirtualMachineCard = memo( USER_TEMPLATE: { LABELS } = {}, GNAME, UNAME, - TEMPLATE: { VCPU = '-', MEMORY } = {}, + TEMPLATE: { VCPU = '-', MEMORY, CONTEXT = {} } = {}, } = vm const { HOSTNAME = '--', VM_MAD: hypervisor } = useMemo( @@ -88,6 +89,8 @@ const VirtualMachineCard = memo( [vm.HISTORY_RECORDS] ) + const { SET_HOSTNAME: VM_HOSTNAME = '' } = CONTEXT + const [time, timeFormat] = useMemo(() => { const fromMill = timeFromMilliseconds(+ETIME || +STIME) @@ -190,10 +193,16 @@ const VirtualMachineCard = memo( {memValue} - + {HOSTNAME} + {!!VM_HOSTNAME && ( + + + {` ${VM_HOSTNAME}`} + + )} {!!UNAME && ( diff --git a/src/fireedge/src/client/components/Cards/VmTemplateCard.js b/src/fireedge/src/client/components/Cards/VmTemplateCard.js index 2238adf3f1f..28e704d0979 100644 --- a/src/fireedge/src/client/components/Cards/VmTemplateCard.js +++ b/src/fireedge/src/client/components/Cards/VmTemplateCard.js @@ -142,7 +142,7 @@ const VmTemplateCard = memo( {HYPERVISOR && } - {LOCK && } + {LOCK && } {isVR && } diff --git a/src/fireedge/src/client/components/Charts/MultiChart/helpers/scripts/dataProcessing.js b/src/fireedge/src/client/components/Charts/MultiChart/helpers/scripts/dataProcessing.js index 4def8952938..b87becbfec3 100644 --- a/src/fireedge/src/client/components/Charts/MultiChart/helpers/scripts/dataProcessing.js +++ b/src/fireedge/src/client/components/Charts/MultiChart/helpers/scripts/dataProcessing.js @@ -114,12 +114,13 @@ export const transformApiResponseToDataset = ( depth = 0, dataArrayPath ) => { - const dataArray = + const dataArray = _.castArray( (_.isEmpty(apiResponse) ? [] : dataArrayPath ? _.get(apiResponse, dataArrayPath) : findFirstArray(apiResponse, depth)) ?? [] + ) const flattenObject = (obj, prefix = '') => Object.keys(obj).reduce((acc, k) => { diff --git a/src/fireedge/src/client/components/Consoles/Guacamole/plugins/display.js b/src/fireedge/src/client/components/Consoles/Guacamole/plugins/display.js index 348808f60ab..f319799e836 100644 --- a/src/fireedge/src/client/components/Consoles/Guacamole/plugins/display.js +++ b/src/fireedge/src/client/components/Consoles/Guacamole/plugins/display.js @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ +import { styled } from '@mui/material' import { + MutableRefObject, // eslint-disable-line no-unused-vars + ReactElement, // eslint-disable-line no-unused-vars useEffect, useMemo, useRef, - MutableRefObject, // eslint-disable-line no-unused-vars - ReactElement, // eslint-disable-line no-unused-vars } from 'react' -import { styled } from '@mui/material' import { GuacamoleSession } from 'client/constants' @@ -43,8 +43,12 @@ const Viewport = styled('div')({ const Display = styled('div')({ zIndex: 1, + margin: '0 auto', overflow: 'hidden', - '& > *': { cursor: 'none' }, + '& > *': { + cursor: 'none', + margin: '0 auto', + }, }) /** diff --git a/src/fireedge/src/client/components/FormControl/ToggleController.js b/src/fireedge/src/client/components/FormControl/ToggleController.js index 57efe68ae7a..f57db0e3981 100644 --- a/src/fireedge/src/client/components/FormControl/ToggleController.js +++ b/src/fireedge/src/client/components/FormControl/ToggleController.js @@ -51,6 +51,7 @@ const ToggleController = memo( notNull = false, readOnly = false, onConditionChange, + defaultValue, }) => { const { field: { ref, value: optionSelected, onChange, onBlur }, @@ -76,6 +77,16 @@ const ToggleController = memo( [onChange, onConditionChange, readOnly, notNull] ) + // Safe loading of default value + useEffect(() => { + if ( + defaultValue && + values?.some(({ text, value }) => [text, value]?.includes(defaultValue)) + ) { + onChange(defaultValue) + } + }, []) + return ( {label && ( @@ -127,6 +138,7 @@ ToggleController.propTypes = { notNull: PropTypes.bool, readOnly: PropTypes.bool, onConditionChange: PropTypes.func, + defaultValue: PropTypes.any, } ToggleController.displayName = 'ToggleController' diff --git a/src/fireedge/src/client/components/Forms/Backup/RestoreForm/Steps/VmDisksTable/index.js b/src/fireedge/src/client/components/Forms/Backup/RestoreForm/Steps/VmDisksTable/index.js index 2911815d3b7..afe4f43bcc8 100644 --- a/src/fireedge/src/client/components/Forms/Backup/RestoreForm/Steps/VmDisksTable/index.js +++ b/src/fireedge/src/client/components/Forms/Backup/RestoreForm/Steps/VmDisksTable/index.js @@ -21,11 +21,20 @@ import { SCHEMA } from 'client/components/Forms/Backup/RestoreForm/Steps/VmDisks import { Step } from 'client/utils' import { T } from 'client/constants' +import { STEP_ID as IMAGE_STEP_ID } from 'client/components/Forms/Backup/RestoreForm/Steps/BackupsTable' export const STEP_ID = 'vmdisk' const Content = ({ data, app: { backupDiskIds = [], vmsId = [] } = {} }) => { - const { setValue } = useFormContext() + const { setValue, getValues } = useFormContext() + const BACKUP_IMAGE = getValues(IMAGE_STEP_ID)?.[0] + const BACKUP_IMAGE_DISK_IDS = BACKUP_IMAGE?.BACKUP_DISK_IDS?.ID + + const getValidArray = (arr) => + Array.isArray(arr) && arr?.length > 0 ? arr : false + + const formatBackupDiskIds = + getValidArray(backupDiskIds) || getValidArray(BACKUP_IMAGE_DISK_IDS) || [] const selectedRow = data?.[0] const handleSelectedRows = (rows) => { @@ -51,7 +60,7 @@ const Content = ({ data, app: { backupDiskIds = [], vmsId = [] } = {} }) => { filter={(disks) => disks && disks?.length > 0 && - disks?.filter((disk) => backupDiskIds?.includes(disk?.DISK_ID)) + disks?.filter((disk) => formatBackupDiskIds?.includes(disk?.DISK_ID)) } /> ) diff --git a/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/AdvancedOptions/schema.js b/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/AdvancedOptions/schema.js index 1d3f0291636..83e76776cdd 100644 --- a/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/AdvancedOptions/schema.js +++ b/src/fireedge/src/client/components/Forms/Image/CreateForm/Steps/AdvancedOptions/schema.js @@ -28,10 +28,10 @@ import { import { useGetSunstoneConfigQuery } from 'client/features/OneApi/system' export const BUS_TYPES = { - VD: T.Vd, - SD: T.Sd, - HD: T.Hd, - CUSTOM: T.Custom, + vd: T.Vd, + sd: T.Sd, + hd: T.Hd, + custom: T.Custom, } const FORMAT_TYPES = { @@ -59,7 +59,7 @@ export const DEV_PREFIX = { .afterSubmit((value, { context }) => { const notEmptyString = value === '' ? undefined : value - return BUS_TYPES.CUSTOM === value + return value === 'custom' ? context?.advanced?.CUSTOM_DEV_PREFIX : notEmptyString }), @@ -70,13 +70,13 @@ export const DEV_PREFIX = { export const CUSTOM_DEV_PREFIX = { name: 'CUSTOM_DEV_PREFIX', dependOf: DEV_PREFIX.name, - htmlType: htmlType(BUS_TYPES.CUSTOM), + htmlType: htmlType('custom'), label: T.CustomBus, type: INPUT_TYPES.TEXT, validation: string() .trim() .when(DEV_PREFIX.name, { - is: (location) => location === BUS_TYPES.CUSTOM, + is: (location) => location === 'custom', then: (schema) => schema.required(), otherwise: (schema) => schema.strip(), }) diff --git a/src/fireedge/src/client/components/Forms/ServiceTemplate/CreateForm/Steps/Extra/customAttributes/schema.js b/src/fireedge/src/client/components/Forms/ServiceTemplate/CreateForm/Steps/Extra/customAttributes/schema.js index ae8c88ed04f..86c28b676f6 100644 --- a/src/fireedge/src/client/components/Forms/ServiceTemplate/CreateForm/Steps/Extra/customAttributes/schema.js +++ b/src/fireedge/src/client/components/Forms/ServiceTemplate/CreateForm/Steps/Extra/customAttributes/schema.js @@ -115,15 +115,25 @@ const DEFAULT_VALUE_TEXT = { name: 'defaultvalue', label: T.DefaultValue, dependOf: CA_TYPE.name, - - htmlType: (type) => type === CA_TYPES.password && INPUT_TYPES.HIDDEN, - + htmlType: (type) => + (type === CA_TYPES.password || type === CA_TYPES.boolean) && + INPUT_TYPES.HIDDEN, type: getTypeProp, - fieldProps: getFieldProps, - validation: string(), + grid: { sm: 2.5, md: 2.5 }, +} +const DEFAULT_VALUE_BOOLEAN = { + name: 'defaultvalue', + label: T.DefaultValue, + dependOf: CA_TYPE.name, + type: INPUT_TYPES.AUTOCOMPLETE, + htmlType: (type) => ![CA_TYPES.boolean].includes(type) && INPUT_TYPES.HIDDEN, + optionsOnly: true, + values: () => arrayToOptions(['NO', 'YES']), + fieldProps: getFieldProps, + validation: string(), grid: { sm: 2.5, md: 2.5 }, } @@ -184,6 +194,7 @@ export const CUSTOM_ATTRIBUTES_FIELDS = [ NAME, DESCRIPTION, DEFAULT_VALUE_TEXT, + DEFAULT_VALUE_BOOLEAN, MANDATORY, DEFAULT_VALUE_RANGE_MIN, DEFAULT_VALUE_RANGE_MAX, diff --git a/src/fireedge/src/client/components/Forms/ServiceTemplate/InstantiateForm/Steps/General/schema.js b/src/fireedge/src/client/components/Forms/ServiceTemplate/InstantiateForm/Steps/General/schema.js index 71474a76d7d..6f65f344447 100644 --- a/src/fireedge/src/client/components/Forms/ServiceTemplate/InstantiateForm/Steps/General/schema.js +++ b/src/fireedge/src/client/components/Forms/ServiceTemplate/InstantiateForm/Steps/General/schema.js @@ -36,7 +36,9 @@ const INSTANCE_FIELD = { name: 'INSTANCES', label: T.NumberOfInstances, type: INPUT_TYPES.TEXT, - validation: number().required(), + validation: number() + .required() + .default(() => 1), fieldProps: { type: 'number', }, diff --git a/src/fireedge/src/client/components/Forms/ServiceTemplate/InstantiateForm/Steps/UserInputs/schema.js b/src/fireedge/src/client/components/Forms/ServiceTemplate/InstantiateForm/Steps/UserInputs/schema.js index b551b417bdc..04a4a1e5cc5 100644 --- a/src/fireedge/src/client/components/Forms/ServiceTemplate/InstantiateForm/Steps/UserInputs/schema.js +++ b/src/fireedge/src/client/components/Forms/ServiceTemplate/InstantiateForm/Steps/UserInputs/schema.js @@ -15,6 +15,7 @@ * ------------------------------------------------------------------------- */ import { string, boolean, number } from 'yup' import { INPUT_TYPES } from 'client/constants' +import { stringToBoolean } from 'client/models/Helper' const getTypeProp = (type) => { switch (type) { @@ -71,8 +72,14 @@ const getValidation = (type, mandatory, defaultValue = undefined) => { .default(() => defaultValue) case 'boolean': return isMandatory - ? boolean().yesOrNo().required() - : boolean().yesOrNo().notRequired() + ? boolean() + .yesOrNo() + .required() + .default(() => stringToBoolean(defaultValue)) + : boolean() + .yesOrNo() + .notRequired() + .default(() => stringToBoolean(defaultValue)) default: return isMandatory ? string() diff --git a/src/fireedge/src/client/components/Forms/ServiceTemplate/InstantiateForm/Steps/index.js b/src/fireedge/src/client/components/Forms/ServiceTemplate/InstantiateForm/Steps/index.js index dfec7d01dd8..ce85cf7867a 100644 --- a/src/fireedge/src/client/components/Forms/ServiceTemplate/InstantiateForm/Steps/index.js +++ b/src/fireedge/src/client/components/Forms/ServiceTemplate/InstantiateForm/Steps/index.js @@ -120,6 +120,7 @@ const Steps = createSteps([General, UserInputs, Network, Charter], { ), })), name: generalData?.NAME, + instances: generalData?.INSTANCES, } return formatTemplate diff --git a/src/fireedge/src/client/components/Forms/Support/CreateForm/Steps/General/schema.js b/src/fireedge/src/client/components/Forms/Support/CreateForm/Steps/General/schema.js index 4a7d0dd3751..316362cb8e3 100644 --- a/src/fireedge/src/client/components/Forms/Support/CreateForm/Steps/General/schema.js +++ b/src/fireedge/src/client/components/Forms/Support/CreateForm/Steps/General/schema.js @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { string, object, ObjectSchema } from 'yup' +import { INPUT_TYPES, SEVERITIES, T } from 'client/constants' import { Field, arrayToOptions, getValidationFromFields } from 'client/utils' -import { T, INPUT_TYPES, SEVERITIES } from 'client/constants' +// eslint-disable-next-line no-unused-vars +import { ObjectSchema, mixed, object, string } from 'yup' /** @type {Field} name field */ export const SUBJECT = { @@ -51,10 +52,23 @@ export const SEVERITY = { grid: { xs: 12, md: 12 }, } +/** @type {Field} Attachment field */ +export const ATTACHMENTS = { + name: 'ATTACHMENTS', + label: T.Upload, + type: INPUT_TYPES.FILE, + validation: mixed() + .notRequired() + .test('fileSize', T.FileTooLarge, (value) => + value?.size ? value.size <= 50 * 1024 ** 2 : true + ), + grid: { xs: 12, md: 12 }, +} + /** * @returns {Field[]} Fields */ -export const FIELDS = [SUBJECT, BODY, SEVERITY] +export const FIELDS = [SUBJECT, BODY, SEVERITY, ATTACHMENTS] /** * @param {object} [stepProps] - Step props diff --git a/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/BasicConfiguration/informationSchema.js b/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/BasicConfiguration/informationSchema.js index 3efc00c56f5..a75df22175d 100644 --- a/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/BasicConfiguration/informationSchema.js +++ b/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/BasicConfiguration/informationSchema.js @@ -34,7 +34,7 @@ const NAME = { const DESCRIPTION = { name: 'description', - label: 'Description', + label: T.Description, type: INPUT_TYPES.TEXT, multiline: true, validation: string().trim().default(''), @@ -64,7 +64,7 @@ const KEEPALIVEPASS = { const VMNAME = { name: 'vmname', label: T.VmName, - tooltip: T.VmTemplateNameHelper, + tooltip: T.VmVrTemplateNameHelper, type: INPUT_TYPES.TEXT, validation: string() .trim() @@ -107,15 +107,6 @@ const HOLD = { grid: { md: 12 }, } -const PERSISTENT = { - name: 'persistent', - label: T.InstantiateAsPersistent, - type: INPUT_TYPES.SWITCH, - tooltip: T.InstantiateAsPersistentConcept, - validation: boolean().default(() => false), - grid: { md: 12 }, -} - export const FIELDS = [ NAME, DESCRIPTION, @@ -124,7 +115,6 @@ export const FIELDS = [ VMNAME, INSTANCES, HOLD, - PERSISTENT, ] export const SCHEMA = getObjectSchemaFromFields(FIELDS) diff --git a/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/Networking/NicMenu/NicCard.js b/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/Networking/NicMenu/NicCard.js index 9a1f8bf29ee..83de65600c2 100644 --- a/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/Networking/NicMenu/NicCard.js +++ b/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/Networking/NicMenu/NicCard.js @@ -47,8 +47,8 @@ const NicCard = ({ info = {}, removeNic, selectNic, active } = {}) => { nicId, VROUTER_MANAGEMENT: management, autonetworkselect, - sshconnection, - rdpconnection, + SSH: sshconnection, + RDP: rdpconnection, } = info return ( diff --git a/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/Networking/NicMenu/informationSchema.js b/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/Networking/NicMenu/informationSchema.js index 6a20b3c1fac..e1e187d7e27 100644 --- a/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/Networking/NicMenu/informationSchema.js +++ b/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/Networking/NicMenu/informationSchema.js @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { mixed, number, boolean } from 'yup' +import { mixed, boolean, array, string } from 'yup' import { VNetworksTable, SecurityGroupsTable } from 'client/components/Tables' -import { getObjectSchemaFromFields } from 'client/utils' - +import { getObjectSchemaFromFields, REG_V4, REG_V6 } from 'client/utils' import { T, INPUT_TYPES } from 'client/constants' const NETWORK = { - name: 'network_id', + name: 'NETWORK_ID', label: T.Network, type: INPUT_TYPES.TABLE, cy: 'network', @@ -36,17 +35,21 @@ const NETWORK = { } const RDP = { - name: 'rdpconnection', + name: 'RDP', label: T.RdpConnection, type: INPUT_TYPES.SWITCH, - validation: boolean().default(() => false), + validation: boolean() + .yesOrNo() + .default(() => false), grid: { md: 6 }, } const SSH = { - name: 'sshconnection', + name: 'SSH', label: T.SshConnection, type: INPUT_TYPES.SWITCH, - validation: boolean().default(() => false), + validation: boolean() + .yesOrNo() + .default(() => false), grid: { md: 6 }, } @@ -54,15 +57,15 @@ const FORCEIPV4 = { name: 'IP', label: T.VirtualRouterNICForceIpv4, type: INPUT_TYPES.TEXT, - validation: number() - .min(7) // Shortest possible IPv4 - .max(16) // Longest possible + validation: string() + .trim() + .matches(REG_V4, { message: T.InvalidIPv4 }) .default(() => undefined), grid: { md: 6 }, } const FLOATINGIP = { - name: 'floating_ip', + name: 'FLOATING_IP', label: T.VirtualRouterNICFloatingIP, type: INPUT_TYPES.CHECKBOX, validation: boolean().yesOrNo(), @@ -73,9 +76,9 @@ const FORCEIPV6 = { name: 'IP6', label: T.VirtualRouterNICForceIpv6, type: INPUT_TYPES.TEXT, - validation: number() - .min(7) - .max(39) + validation: string() + .trim() + .matches(REG_V6, { message: T.InvalidIPv6 }) .default(() => undefined), grid: { md: 6 }, } @@ -89,7 +92,7 @@ const MANAGEMENINTERFACE = { } const SECURITY_GROUPS = { - name: 'secgroup', + name: 'SECURITY_GROUPS', label: T.SecurityGroups, type: INPUT_TYPES.TABLE, cy: 'secgroup', @@ -115,4 +118,4 @@ export const FIELDS = [ SECURITY_GROUPS, ] -export const SCHEMA = getObjectSchemaFromFields(FIELDS) +export const SCHEMA = array().of(getObjectSchemaFromFields(FIELDS)) diff --git a/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/Networking/index.js b/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/Networking/index.js index c10fc06adf8..02ae78b3f46 100644 --- a/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/Networking/index.js +++ b/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/Networking/index.js @@ -42,12 +42,12 @@ const Content = () => { append, remove, } = useFieldArray({ - name: `${STEP_ID}.NIC`, + name: `${STEP_ID}`, }) const watchedNicsArray = useWatch({ control, - name: `${STEP_ID}.NIC`, + name: `${STEP_ID}`, }) const handleAddnewNic = () => { @@ -92,11 +92,11 @@ const Content = () => { ) : ( ), [nics, activeNic] diff --git a/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/index.js b/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/index.js index 0f47a2da2c0..7a5b36d684c 100644 --- a/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/index.js +++ b/src/fireedge/src/client/components/Forms/VRTemplate/InstantiateForm/Steps/index.js @@ -77,23 +77,33 @@ const Steps = createSteps( ) }, transformBeforeSubmit: (formData, vmTemplate) => { - const { [BASIC_ID]: { name, instances, hold, persistent, vmname } = {} } = + const { [BASIC_ID]: { name, instances, hold, vmname } = {} } = formData ?? {} const selectedTemplateID = formData?.template_selection?.vmTemplate delete formData?.template_selection - const templates = [...new Array(instances)].map((__, idx) => ({ - id: vmTemplate?.ID ?? selectedTemplateID, - vrname: name, - name: vmname?.replace(/%idx/gi, idx), // VM NAME - number: instances, - pending: hold, // start on hold - persistent: persistent, - ...(selectedTemplateID && { initiateFromSelection: true }), - ...formData, - })) + // Delete nic id + formData.networking.forEach((nic) => { + delete nic.nicId + }) + + const templates = [ + { + id: vmTemplate?.ID ?? selectedTemplateID, + vrname: name, + name: vmname + ? vmname.includes('%i') + ? vmname + : vmname + '-%i' + : undefined, + number: instances, + pending: hold, // start on hold + ...(selectedTemplateID && { initiateFromSelection: true }), + ...formData, + }, + ] return templates }, diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/NetworksTable/index.js b/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/NetworksTable/index.js index f7185f4e953..f885a143cf3 100644 --- a/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/NetworksTable/index.js +++ b/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/NetworksTable/index.js @@ -14,11 +14,11 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ /* eslint-disable jsdoc/require-jsdoc */ -import PropTypes from 'prop-types' -import { VNetworksTable } from 'client/components/Tables' import { SCHEMA } from 'client/components/Forms/Vm/AttachNicForm/Steps/NetworksTable/schema' +import { VNetworksTable } from 'client/components/Tables' import { T } from 'client/constants' import { useGeneralApi } from 'client/features/General' +import PropTypes from 'prop-types' export const STEP_ID = 'network' @@ -32,6 +32,7 @@ const Content = ({ data, setFormData }) => { setModifiedFields({ network: { NETWORK: true, + NETWORK_ID: true, NETWORK_UID: true, NETWORK_UNAME: true, SECURITY_GROUPS: true, @@ -42,6 +43,7 @@ const Content = ({ data, setFormData }) => { ...prevList, [STEP_ID]: { NETWORK: original?.NAME, + NETWORK_ID: original?.ID, NETWORK_UID: original?.UID, NETWORK_UNAME: original?.UNAME, SECURITY_GROUPS: original?.SECURITY_GROUPS, diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachPciForm/schema.js b/src/fireedge/src/client/components/Forms/Vm/AttachPciForm/schema.js index 4424c35af2c..40190236e96 100644 --- a/src/fireedge/src/client/components/Forms/Vm/AttachPciForm/schema.js +++ b/src/fireedge/src/client/components/Forms/Vm/AttachPciForm/schema.js @@ -14,6 +14,7 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ import { string, ObjectSchema, boolean } from 'yup' +import { useFormContext } from 'react-hook-form' import { useGetHostsAdminQuery } from 'client/features/OneApi/host' import { getPciDevices } from 'client/models/Host' import { @@ -86,7 +87,7 @@ const NAME_FIELD = { .trim() .notRequired() .afterSubmit(() => undefined), - grid: { sm: 12, md: 3 }, + grid: { sm: 12, md: 4 }, } /** @type {Field} PCI device field */ @@ -113,7 +114,7 @@ const DEVICE_FIELD = { return type ? schema.notRequired() : schema.required() }), - grid: { xs: 12, sm: 3, md: 2 }, + grid: { xs: 12, md: 2 }, } /** @type {Field} PCI device field */ @@ -140,7 +141,7 @@ const VENDOR_FIELD = { return type ? schema.notRequired() : schema.required() }), - grid: { xs: 12, sm: 3, md: 2 }, + grid: { xs: 12, md: 2 }, } /** @type {Field} PCI device field */ @@ -167,7 +168,7 @@ const CLASS_FIELD = { return type ? schema.notRequired() : schema.required() }), - grid: { xs: 12, sm: 3, md: 2 }, + grid: { xs: 12, md: 2 }, } /** @type {Field} PCI device field */ @@ -201,6 +202,47 @@ const SHORT_ADDRESS = { }), } +/** @type {Field} Name PCI device field */ +const PROFILE_FIELD = { + name: 'PROFILE', + label: T.Profile, + type: INPUT_TYPES.AUTOCOMPLETE, + values: (dependencies = []) => { + const [selectedPciDevice] = dependencies + const { data = [] } = useGetHostsAdminQuery({ + skip: selectedPciDevice === undefined, + }) + if (selectedPciDevice && data) { + const pciDevices = data.map(getPciDevices).flat() + const [DEVICE, VENDOR, CLASS] = selectedPciDevice?.split(';') + const selectedDevice = pciDevices.find( + (device) => + device?.DEVICE === DEVICE && + device?.VENDOR === VENDOR && + device?.CLASS === CLASS + ) + + const profiles = selectedDevice?.PROFILES?.split(',') || [] + + if (!profiles?.length) { + const { setValue } = useFormContext() + setValue(PROFILE_FIELD.name, '') + } + + return arrayToOptions(profiles) + } + + return arrayToOptions([]) + }, + dependOf: [NAME_FIELD.name, SPECIFIC_DEVICE.name], + htmlType: ([_, specificDevice] = []) => specificDevice && INPUT_TYPES.HIDDEN, + validation: string() + .trim() + .notRequired() + .default(() => ''), + grid: { md: 6 }, +} + /** * @param {object} oneConfig - Config of oned.conf * @param {boolean} adminGroup - User is admin or not @@ -215,6 +257,7 @@ export const PCI_FIELDS = (oneConfig, adminGroup) => VENDOR_FIELD, CLASS_FIELD, SHORT_ADDRESS, + PROFILE_FIELD, ], 'PCI', oneConfig, @@ -229,4 +272,5 @@ export const PCI_SCHEMA = getObjectSchemaFromFields([ VENDOR_FIELD, CLASS_FIELD, SHORT_ADDRESS, + PROFILE_FIELD, ]) diff --git a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/booting/index.js b/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/booting/index.js index 124ff076c79..6a589632124 100644 --- a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/booting/index.js +++ b/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/booting/index.js @@ -15,20 +15,23 @@ * ------------------------------------------------------------------------- */ import { ReactElement, useMemo } from 'react' import PropTypes from 'prop-types' -import { Stack } from '@mui/material' - +import { Stack, FormControl } from '@mui/material' +import Legend from 'client/components/Forms/Legend' +import { BootOrder } from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration' import FormWithSchema from 'client/components/Forms/FormWithSchema' import { SECTIONS } from 'client/components/Forms/Vm/UpdateConfigurationForm/booting/schema' -import { HYPERVISORS } from 'client/constants' +import { HYPERVISORS, T } from 'client/constants' /** * @param {object} props - Component props * @param {HYPERVISORS} props.hypervisor - VM hypervisor * @param {object} props.oneConfig - Config of oned.conf * @param {boolean} props.adminGroup - User is admin or not + * @param {object} props.vm - VM template * @returns {ReactElement} OS section component */ -const OsSection = ({ hypervisor, oneConfig, adminGroup }) => { +const OsSection = ({ hypervisor, oneConfig, adminGroup, vm }) => { + const enableBootOrder = !!vm?.TEMPLATE?.DISK || !!vm?.TEMPLATE?.NIC const sections = useMemo( () => SECTIONS({ hypervisor, oneConfig, adminGroup }), [hypervisor] @@ -40,6 +43,15 @@ const OsSection = ({ hypervisor, oneConfig, adminGroup }) => { gap="1em" sx={{ gridTemplateColumns: { sm: '1fr', md: '1fr 1fr' } }} > + {enableBootOrder && ( + + + + + )} {sections.map(({ id, ...section }) => ( ))} @@ -51,6 +63,7 @@ OsSection.propTypes = { hypervisor: PropTypes.string, oneConfig: PropTypes.object, adminGroup: PropTypes.bool, + vm: PropTypes.object, } export default OsSection diff --git a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/content.js b/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/content.js index 0d3d5eada27..9b3926c6e13 100644 --- a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/content.js +++ b/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/content.js @@ -37,9 +37,10 @@ import { T, HYPERVISORS } from 'client/constants' * @param {HYPERVISORS} props.hypervisor - VM hypervisor * @param {object} props.oneConfig - OpenNEbula configuration * @param {boolean} props.adminGroup - If the user is admin + * @param {object} props.vm - VM template * @returns {ReactElement} Form content component */ -const Content = ({ hypervisor, oneConfig, adminGroup }) => { +const Content = ({ hypervisor, oneConfig, adminGroup, vm }) => { const { formState: { errors }, } = useFormContext() @@ -55,6 +56,7 @@ const Content = ({ hypervisor, oneConfig, adminGroup }) => { hypervisor={hypervisor} oneConfig={oneConfig} adminGroup={adminGroup} + vm={vm} /> ), error: !!errors?.OS, @@ -107,6 +109,7 @@ Content.propTypes = { hypervisor: PropTypes.string, oneConfig: PropTypes.object, adminGroup: PropTypes.bool, + vm: PropTypes.object, } export default Content diff --git a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/index.js b/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/index.js index 7084e56c0af..73d11afe709 100644 --- a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/index.js +++ b/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/index.js @@ -15,6 +15,7 @@ * ------------------------------------------------------------------------- */ import { reach } from 'yup' +import { set } from 'lodash' import { SCHEMA } from 'client/components/Forms/Vm/UpdateConfigurationForm/schema' import ContentForm from 'client/components/Forms/Vm/UpdateConfigurationForm/content' import { @@ -30,7 +31,8 @@ const UpdateConfigurationForm = createForm(SCHEMA, undefined, { const template = vmTemplate?.TEMPLATE ?? {} const context = template?.CONTEXT ?? {} const backupConfig = vmTemplate?.BACKUPS?.BACKUP_CONFIG ?? {} - + const bootOrder = template?.OS?.BOOT + const nics = [].concat(template?.NIC ?? []).flat() const knownTemplate = schema.cast( { ...vmTemplate, ...template }, { stripUnknown: true, context: { ...template } } @@ -72,16 +74,40 @@ const UpdateConfigurationForm = createForm(SCHEMA, undefined, { ...getUnknownAttributes(backupConfig, knownBackupConfig), } + // Easy compatibility with the bootOrder component by specifying the same form paths as in the VM Template + !!bootOrder && set(knownTemplate, 'extra.OS.BOOT', bootOrder) + !!template?.DISK && + set( + knownTemplate, + 'extra.DISK', + template?.DISK?.map((disk) => ({ + ...disk, + NAME: `DISK${disk?.DISK_ID}`, + })) + ) + !!nics?.length && set(knownTemplate, 'extra.NIC', nics) + return knownTemplate }, transformBeforeSubmit: (formData) => { + const { extra, ...restFormData } = formData // Encode script on base 64, if needed, on context section - if (isBase64(formData?.CONTEXT?.START_SCRIPT)) { - formData.CONTEXT.START_SCRIPT_BASE64 = formData?.CONTEXT?.START_SCRIPT - delete formData?.CONTEXT?.START_SCRIPT + const updatedFormData = { + ...restFormData, + OS: { + ...restFormData.OS, + BOOT: extra?.OS?.BOOT || restFormData.OS?.BOOT, + }, + } + if (isBase64(updatedFormData?.CONTEXT?.START_SCRIPT)) { + updatedFormData.CONTEXT.START_SCRIPT_BASE64 = + updatedFormData?.CONTEXT?.START_SCRIPT + delete updatedFormData?.CONTEXT?.START_SCRIPT } else { - delete formData?.CONTEXT?.START_SCRIPT_BASE64 + delete updatedFormData?.CONTEXT?.START_SCRIPT_BASE64 } + + return updatedFormData }, }) diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/bootOrder.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/bootOrder.js index 13a2b77ed61..ed57f19f38a 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/bootOrder.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/bootOrder.js @@ -131,7 +131,9 @@ const BootOrder = () => { const nics = useMemo(() => { const nicId = `${EXTRA_ID}.${NIC_ID[0]}` - const nicValues = getValues([nicId]).flat() + const nicValues = getValues([nicId]) + .flat() + .filter((nic) => !!nic) // Strips internal undefined return ( nicValues?.map((nic, idx) => ({ @@ -139,7 +141,7 @@ const BootOrder = () => { NAME: ( <> - {[nic?.NAME, nic.NETWORK].filter(Boolean).join(': ')} + {[nic?.NAME, nic?.NETWORK].filter(Boolean).join(': ')} ), })) ?? [] diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/bootSchema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/bootSchema.js index 2bc15216dc0..40d4bd33a17 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/bootSchema.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/bootSchema.js @@ -100,6 +100,7 @@ export const KERNEL_CMD = { .notRequired() .default(() => undefined), fieldProps: { placeholder: 'ro console=tty1' }, + grid: { md: 12 }, } /** @type {Field} Path bootloader field */ @@ -157,7 +158,7 @@ export const FIRMWARE = { fieldProps: { freeSolo: true, }, - grid: { md: 12 }, + grid: { md: 8 }, } /** @type {Field} Firmware secure field */ @@ -167,7 +168,7 @@ export const FIRMWARE_SECURE = { notOnHypervisors: [lxc], type: INPUT_TYPES.CHECKBOX, validation: boolean().yesOrNo(), - grid: { md: 12 }, + grid: { md: 4 }, } /** @type {Field[]} List of Boot fields */ @@ -176,9 +177,9 @@ export const BOOT_FIELDS = [ SD_DISK_BUS, MACHINE_TYPES, ROOT_DEVICE, + FIRMWARE, + FIRMWARE_SECURE, KERNEL_CMD, BOOTLOADER, UUID, - FIRMWARE, - FIRMWARE_SECURE, ] diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/index.js index f7386050cfc..cbcb135c434 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/index.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/index.js @@ -37,11 +37,13 @@ import { T } from 'client/constants' import { useGeneralApi } from 'client/features/General' import { useWatch, useFormContext } from 'react-hook-form' +import useStyles from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/styles' export const TAB_ID = 'OS' const Booting = ({ hypervisor, oneConfig, adminGroup, ...props }) => { const { setFieldPath } = useGeneralApi() + const classes = useStyles() useEffect(() => { setFieldPath(`extra.OsCpu`) }, []) @@ -61,15 +63,11 @@ const Booting = ({ hypervisor, oneConfig, adminGroup, ...props }) => { const nicsAlias = getValues(`${EXTRA_ID}.${NIC_ID[1]}`) return ( - + {(!!disks?.length || !!nics?.length || !!nicsAlias?.length) && ( @@ -81,7 +79,7 @@ const Booting = ({ hypervisor, oneConfig, adminGroup, ...props }) => { id={EXTRA_ID} saveState={true} cy={`${EXTRA_ID}-${id}`} - hiddenLegend={id === 'os-ramdisk' && !kernelWatch && !kernelDsWatch} + rootProps={{ className: classes[id] }} {...section} /> ))} @@ -109,4 +107,4 @@ const TAB = { export default TAB -export { reorderBootAfterRemove, BOOT_ORDER_NAME } +export { reorderBootAfterRemove, BOOT_ORDER_NAME, BootOrder } diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/kernelSchema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/kernelSchema.js index c5ab87cf73f..ffb11d13275 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/kernelSchema.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/kernelSchema.js @@ -46,6 +46,8 @@ export const KERNEL_PATH_ENABLED = { validation: boolean() .strip() .default(() => false), + + grid: { md: 4 }, } /** @type {Field} Kernel DS field */ @@ -84,6 +86,8 @@ export const KERNEL_DS = { form?.setValue(`extra.${KERNEL_DS_NAME}`, undefined) } }, + + grid: { md: 8 }, } /** @type {Field} Kernel path field */ @@ -104,6 +108,7 @@ export const KERNEL = { form?.setValue(`extra.${KERNEL_NAME}`, '') } }, + grid: { md: 8 }, } /** @type {Field[]} List of Kernel fields */ diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/ramdiskSchema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/ramdiskSchema.js index 59317c14bc9..c7c04c24ab5 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/ramdiskSchema.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/ramdiskSchema.js @@ -43,7 +43,6 @@ export const RAMDISK_PATH_ENABLED = { notOnHypervisors: [lxc], type: INPUT_TYPES.SWITCH, dependOf: [`$extra.${KERNEL_DS_NAME}`, `$extra.${KERNEL_NAME}`], - htmlType: ([ds, path] = []) => !(ds || path) && INPUT_TYPES.HIDDEN, fieldProps: (_, form) => { const ds = form?.getValues(`extra.${KERNEL_DS_NAME}`) const path = form?.getValues(`extra.${KERNEL_NAME}`) @@ -58,6 +57,8 @@ export const RAMDISK_PATH_ENABLED = { validation: boolean() .strip() .default(() => false), + + grid: { md: 4 }, } /** @type {Field} Ramdisk DS field */ @@ -72,8 +73,7 @@ export const RAMDISK_DS = { `$extra.${KERNEL_NAME}`, `$extra.${KERNEL_PATH_ENABLED_NAME}`, ], - htmlType: ([enabled = false, ds, path] = []) => - (enabled || !(ds || path)) && INPUT_TYPES.HIDDEN, + htmlType: ([enabled = false] = []) => enabled && INPUT_TYPES.HIDDEN, fieldProps: (_, form) => { const ds = form?.getValues(`extra.${KERNEL_DS_NAME}`) const path = form?.getValues(`extra.${KERNEL_NAME}`) @@ -107,12 +107,17 @@ export const RAMDISK_DS = { ), value: (_, form) => { if ( - form?.getValues(`extra.${RAMDISK_PATH_ENABLED_NAME}`) && + (form?.getValues(`extra.${RAMDISK_PATH_ENABLED_NAME}`) || + !form?.getValues(`extra.${KERNEL_PATH_ENABLED_NAME}`) || + !form?.getValues(`extra.${KERNEL_NAME}`) || + !form?.getValues(`extra.${KERNEL_DS_NAME}`)) && form?.setValue ) { form?.setValue(`extra.${RAMDISK_DS_NAME}`, undefined) } }, + + grid: { md: 8 }, } /** @type {Field} Ramdisk path field */ @@ -143,9 +148,9 @@ export const RAMDISK = { return ds || path ? {} : { disabled: true } }, - htmlType: ([enabled = false, ds, path] = []) => - (!enabled || !(ds || path)) && INPUT_TYPES.HIDDEN, + htmlType: ([enabled = false] = []) => !enabled && INPUT_TYPES.HIDDEN, validation: ramdiskValidation, + grid: { md: 8 }, } /** @type {Field[]} List of Ramdisk fields */ diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/schema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/schema.js index 50cd393223c..1d05aeab6f2 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/schema.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/schema.js @@ -50,30 +50,30 @@ const SECTIONS = (hypervisor, oneConfig, adminGroup) => [ ), }, { - id: 'os-features', - legend: T.Features, + id: 'os-kernel', + legend: `${T.Kernel}`, fields: disableFields( - filterFieldsByHypervisor(FEATURES_FIELDS, hypervisor), + filterFieldsByHypervisor(KERNEL_FIELDS, hypervisor), 'OS', oneConfig, adminGroup ), }, { - id: 'os-kernel', - legend: T.Kernel, + id: 'os-ramdisk', + legend: `${T.Ramdisk}`, fields: disableFields( - filterFieldsByHypervisor(KERNEL_FIELDS, hypervisor), + filterFieldsByHypervisor(RAMDISK_FIELDS, hypervisor), 'OS', oneConfig, adminGroup ), }, { - id: 'os-ramdisk', - legend: T.Ramdisk, + id: 'os-features', + legend: T.Features, fields: disableFields( - filterFieldsByHypervisor(RAMDISK_FIELDS, hypervisor), + filterFieldsByHypervisor(FEATURES_FIELDS, hypervisor), 'OS', oneConfig, adminGroup diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/styles.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/styles.js new file mode 100644 index 00000000000..a076ca537ba --- /dev/null +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/styles.js @@ -0,0 +1,47 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2024, OpenNebula Project, OpenNebula Systems * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); you may * + * not use this file except in compliance with the License. You may obtain * + * a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * ------------------------------------------------------------------------- */ +import makeStyles from '@mui/styles/makeStyles' + +const useStyles = makeStyles((theme) => ({ + root: { + display: 'grid', + gridTemplateColumns: '1fr 1fr', + gridTemplateRows: 'auto', + gap: theme.spacing(1), + overflow: 'auto', + [theme.breakpoints.down('sm')]: { + gridTemplateColumns: '1fr', + }, + }, + 'os-cpu-model': { + gridColumn: '1 / span 2', + // gridRow: '1', + padding: theme.spacing(1), + [theme.breakpoints.down('sm')]: { + gridColumn: '1 / -1', + }, + }, + 'os-raw': { + gridColumn: '1 / span 2', + // gridRow: '1', + padding: theme.spacing(1), + [theme.breakpoints.down('sm')]: { + gridColumn: '1 / -1', + }, + }, +})) + +export default useStyles diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/context/configurationSchema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/context/configurationSchema.js index e6f9c16aa82..388ed1bbc90 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/context/configurationSchema.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/context/configurationSchema.js @@ -72,9 +72,10 @@ export const ENCODE_START_SCRIPT = { name: 'CONTEXT.ENCODE_START_SCRIPT', label: T.EncodeScriptInBase64, ...switchField, + htmlType: INPUT_TYPES.HIDDEN, validation: lazy((_, { context }) => boolean() - .default(() => !!context?.CONTEXT?.START_SCRIPT_BASE64) + .default(() => true) .afterSubmit(() => undefined) ), } @@ -91,12 +92,7 @@ export const START_SCRIPT = { .trim() .ensure() .notRequired() - .afterSubmit((value, { context }) => - context?.extra?.CONTEXT?.ENCODE_START_SCRIPT || - context?.CONTEXT?.ENCODE_START_SCRIPT - ? encodeBase64(value) - : value - ), + .afterSubmit((value, { context }) => encodeBase64(value)), grid: { md: 12 }, fieldProps: { rows: 4 }, } diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/index.js index 5c282bf3961..29fd202d068 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/index.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/index.js @@ -27,7 +27,9 @@ import Storage from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraCo import Networking from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/networking' import Placement from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/placement' import ScheduleAction from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/scheduleAction' -import Booting from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting' +import Booting, { + BootOrder, +} from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting' import Context from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/context' import Pci from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/pci' import InputOutput from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput' @@ -166,4 +168,5 @@ Content.propTypes = { isVrouter: PropTypes.bool, } +export { BootOrder } export default ExtraConfiguration diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/pci/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/pci/index.js index c0c5c39e787..8422b3b0017 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/pci/index.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/pci/index.js @@ -100,6 +100,9 @@ const PciDevices = ({ oneConfig, adminGroup }) => { CLASS: { __delete__: true, }, + PROFILE: { + __delete__: true, + }, SHORT_ADDRESS: true, }) } else { @@ -107,6 +110,7 @@ const PciDevices = ({ oneConfig, adminGroup }) => { DEVICE: true, VENDOR: true, CLASS: true, + PROFILE: true, SHORT_ADDRESS: { __delete__: true, }, diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/placement/schema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/placement/schema.js index 6866e71bc55..1d5ba1a09db 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/placement/schema.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/placement/schema.js @@ -34,15 +34,12 @@ import { transformXmlString } from 'client/models/Helper' const addHypervisorRequirement = (schedRequirements, hypervisor) => { // Regular expression pattern to match (HYPERVISOR=VALUE) - const regexPattern = /\(HYPERVISOR=(kvm|dummy|lxc|qemu)\)/ + const regexPattern = /HYPERVISOR=(kvm|dummy|lxc|qemu)/ // If exists a condition with hypervisor, replace the type. If not, add the hypervisor type. if (regexPattern.test(schedRequirements)) { // Replace the matched pattern with the new hypervisor - return schedRequirements.replace( - regexPattern, - '(HYPERVISOR=' + hypervisor + ')' - ) + return schedRequirements.replace(regexPattern, 'HYPERVISOR=' + hypervisor) } else { // Add the condition only if (!hypervisor) { @@ -50,8 +47,8 @@ const addHypervisorRequirement = (schedRequirements, hypervisor) => { } return schedRequirements - ? `(${schedRequirements}) & (HYPERVISOR=${hypervisor})` - : `(HYPERVISOR=${hypervisor})` + ? `${schedRequirements} | HYPERVISOR=${hypervisor}` + : `HYPERVISOR=${hypervisor}` } } @@ -63,18 +60,26 @@ const HOST_REQ_FIELD = (isUpdate, modifiedFields, instantiate) => ({ type: INPUT_TYPES.TEXT, dependOf: [ '$general.HYPERVISOR', - '$extra.CLUSTER_HOST_TABLE', + '$extra.PLACEMENT_CLUSTER_TABLE', + '$extra.PLACEMENT_HOST_TABLE', '$extra.CLUSTER_HOST_TYPE', ], watcher: (dependencies, { formContext }) => { - const [hypervisor, clusterHostTable, clusterHostType] = dependencies - const tableType = clusterHostType?.includes(T.Cluster) ? 'CLUSTER' : 'HOST' - const regexPattern = new RegExp(`\\b${tableType}_ID\\s*=\\s*(\\d+)`) + const [hypervisor, clusterTable, hostTable, clusterHostType] = dependencies + + const clusterHostTable = clusterHostType?.includes(T.Cluster) + ? clusterTable + : hostTable + if (!hypervisor) { + return + } + const tableType = clusterHostType?.includes(T.Cluster) ? 'CLUSTER_' : '' + const regexPattern = new RegExp(`\\b${tableType}ID\\s*=\\s*\\d+`) const actualValue = formContext.getValues('extra.SCHED_REQUIREMENTS') - const parts = actualValue?.split('&')?.map((part) => part?.trim()) + const parts = actualValue?.split('|')?.map((part) => part?.trim()) const matchedParts = parts?.filter((part) => regexPattern.test(part)) const nonMatchedParts = parts?.filter((part) => !regexPattern.test(part)) @@ -95,7 +100,7 @@ const HOST_REQ_FIELD = (isUpdate, modifiedFields, instantiate) => ({ const newExpressions = clusterHostTable ?.filter((id) => !remainingIDs.includes(id)) - .map((id) => `(${tableType}_ID = ${id})`) + .map((id) => `${tableType}ID = ${id}`) const updatedParts = [ ...(nonMatchedParts ?? []), @@ -103,11 +108,11 @@ const HOST_REQ_FIELD = (isUpdate, modifiedFields, instantiate) => ({ ...(newExpressions ?? []), ] - const updatedValue = updatedParts?.join(' & ') ?? '' + const updatedValue = updatedParts?.join(' | ') ?? '' // Check if the hypervisor condition already exists in the actualValue const hasHypervisorCondition = actualValue?.includes( - `(HYPERVISOR=${hypervisor})` + `HYPERVISOR=${hypervisor}` ) // Add the hypervisor condition only if it doesn't exist @@ -220,26 +225,47 @@ const DS_RANK_FIELD = { const TABLE_TYPE = { name: 'CLUSTER_HOST_TYPE', type: INPUT_TYPES.TOGGLE, - values: () => - arrayToOptions([T.SelectCluster, T.SelectHost], { - addEmpty: false, - }), + values: arrayToOptions([T.SelectCluster, T.SelectHost], { + addEmpty: false, + }), validation: string() .trim() .required() - .default(() => T.Host), + .default(() => T.SelectCluster), notNull: true, + defaultValue: T.SelectCluster, grid: { xs: 12, md: 12 }, } /** @type {Field} Cluster selection field */ -const CLUSTER_HOST_TABLE = { - name: 'CLUSTER_HOST_TABLE', +const CLUSTER_TABLE = { + name: 'PLACEMENT_CLUSTER_TABLE', dependOf: 'CLUSTER_HOST_TYPE', type: INPUT_TYPES.TABLE, - Table: (tableType) => - tableType?.includes(T.Cluster) ? ClustersTable : HostsTable, + Table: ClustersTable, + htmlType: (tableType) => + !tableType?.includes(T.Cluster) && INPUT_TYPES.HIDDEN, singleSelect: false, + fieldProps: { + preserveState: true, + }, + validation: array(string().trim()) + .required() + .default(() => undefined), + grid: { xs: 12, md: 12 }, +} + +/** @type {Field} Cluster selection field */ +const HOST_TABLE = { + name: 'PLACEMENT_HOST_TABLE', + dependOf: 'CLUSTER_HOST_TYPE', + type: INPUT_TYPES.TABLE, + Table: HostsTable, + htmlType: (tableType) => !tableType?.includes(T.Host) && INPUT_TYPES.HIDDEN, + singleSelect: false, + fieldProps: { + preserveState: true, + }, validation: array(string().trim()) .required() .default(() => undefined), @@ -266,7 +292,8 @@ const SECTIONS = (oneConfig, adminGroup, isUpdate, modifiedFields) => [ fields: disableFields( [ TABLE_TYPE, - CLUSTER_HOST_TABLE, + CLUSTER_TABLE, + HOST_TABLE, HOST_REQ_FIELD(isUpdate, modifiedFields), HOST_POLICY_TYPE_FIELD, HOST_RANK_FIELD, diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/index.js index 52e30957659..ce8d508ce88 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/index.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/index.js @@ -25,6 +25,7 @@ import General, { STEP_ID as GENERAL_ID, } from 'client/components/Forms/VmTemplate/CreateForm/Steps/General' +import { T } from 'client/constants' import { userInputsToArray } from 'client/models/Helper' import { createSteps, getUnknownAttributes, decodeBase64 } from 'client/utils' @@ -95,8 +96,37 @@ const Steps = createSteps([General, ExtraConfiguration, CustomVariables], { } } + // Init placement + const schedRequirements = vmTemplate?.TEMPLATE?.SCHED_REQUIREMENTS + if (schedRequirements) { + objectSchema[EXTRA_ID].SCHED_REQUIREMENTS = schedRequirements + const parts = schedRequirements?.split(' | ') + const tableIds = parts?.reduce((ids, part) => { + if (part?.includes('ID')) { + const isCluster = part.toUpperCase().includes(T.Cluster.toUpperCase()) + const tableId = isCluster ? T.Cluster : T.Host + const partId = part?.split(' = ')?.at(-1)?.trim() + if (!partId) return ids + ;(ids[tableId] ??= []).push(partId) + } + + return ids + }, {}) + + if (tableIds?.[T.Cluster]) { + objectSchema[EXTRA_ID].PLACEMENT_CLUSTER_TABLE = tableIds[T.Cluster] + } + + if (tableIds?.[T.Host]) { + objectSchema[EXTRA_ID].PLACEMENT_HOST_TABLE = tableIds[T.Host] + } + } + + const defaultType = T.SelectCluster + objectSchema[EXTRA_ID].CLUSTER_HOST_TYPE = defaultType + const knownTemplate = schema.cast(objectSchema, { - stripUnknown: true, + stripUnknown: false, context: { ...vmTemplate, [EXTRA_ID]: vmTemplate.TEMPLATE }, }) diff --git a/src/fireedge/src/client/components/MultipleTags/index.js b/src/fireedge/src/client/components/MultipleTags/index.js index 5c2731a0469..f0d2bfe355d 100644 --- a/src/fireedge/src/client/components/MultipleTags/index.js +++ b/src/fireedge/src/client/components/MultipleTags/index.js @@ -14,12 +14,26 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ import { Stack, Tooltip, Typography, styled } from '@mui/material' -import PropTypes from 'prop-types' -import { ReactElement, isValidElement, useMemo } from 'react' - import { Translate } from 'client/components/HOC' import { StatusChip } from 'client/components/Status' import { T } from 'client/constants' +import PropTypes from 'prop-types' +import { ReactElement, isValidElement, useMemo } from 'react' + +/** + * Truncate string. + * + * @param {string} label - string. + * @param {number} [maxLength] - max lenght. + * @returns {string} - string truncated + */ +const truncateLabel = (label, maxLength) => { + if (label.length > maxLength) { + return `${label.substring(0, maxLength)}...` + } + + return label +} const StyledText = styled(Typography)(({ theme }) => ({ '&': { @@ -40,9 +54,15 @@ const StyledText = styled(Typography)(({ theme }) => ({ * @param {string[]|TagType} props.tags - Tags to display * @param {number} [props.limitTags] - Limit the number of tags to display * @param {boolean} [props.clipboard] - If true, the chip will be clickable + * @param {false|number} [props.truncateText] - number by truncate the string * @returns {ReactElement} - Tag list */ -const MultipleTags = ({ tags, limitTags = 1, clipboard = false }) => { +const MultipleTags = ({ + tags, + limitTags = 1, + clipboard = false, + truncateText = false, +}) => { if (tags?.length === 0) { return null } @@ -53,6 +73,7 @@ const MultipleTags = ({ tags, limitTags = 1, clipboard = false }) => { if (isValidElement(tag)) return tag const text = tag.text ?? tag + truncateText && (tag.text = truncateLabel(text, truncateText)) return ( { } MultipleTags.propTypes = { - tags: PropTypes.array, - clipboard: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]), + tags: PropTypes.any, limitTags: PropTypes.number, + clipboard: PropTypes.bool, + truncateText: PropTypes.number, } +MultipleTags.displayName = 'MultipleTags' export default MultipleTags diff --git a/src/fireedge/src/client/components/Tables/ACLs/index.js b/src/fireedge/src/client/components/Tables/ACLs/index.js index a764b650966..7ec620f1c91 100644 --- a/src/fireedge/src/client/components/Tables/ACLs/index.js +++ b/src/fireedge/src/client/components/Tables/ACLs/index.js @@ -13,14 +13,15 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useMemo, Component, useState } from 'react' -import { useViews } from 'client/features/Auth' -import { useGetAclsExtendedQuery } from 'client/features/OneApi/acl' - -import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' import ACLColumns from 'client/components/Tables/ACLs/columns' import ACLRow from 'client/components/Tables/ACLs/row' -import { RESOURCE_NAMES, ACL_TABLE_VIEWS } from 'client/constants' +import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' +import { ACL_TABLE_VIEWS, RESOURCE_NAMES, T } from 'client/constants' +import { useViews } from 'client/features/Auth' +import { useGetAclsExtendedQuery } from 'client/features/OneApi/acl' +import { sentenceCase } from 'client/utils' +import { Component, useMemo, useState } from 'react' const DEFAULT_DATA_CY = 'acls' @@ -72,23 +73,55 @@ const ACLsTable = (props) => { }, } - return ( - data && ( - String(row.ID)} - RowComponent={useMemo(() => ACLRow(viewType), [viewType])} - singleSelect={singleSelect} - tableViews={tableViews} - {...rest} - /> - ) - ) + const listHeader = [ + { header: T.ID, id: 'id', accessor: 'ID' }, + { + header: T.AppliesTo, + id: 'applies', + accessor: ({ USER }) => + USER?.type ? sentenceCase(`${USER?.type} ${USER?.name ?? ''}`) : '', + }, + { + header: T.AffectedResources, + id: 'affected-resources', + accessor: ({ RESOURCE }) => + Array.isArray(RESOURCE?.resources) + ? sentenceCase(RESOURCE.resources.join(', ')) + : '', + }, + { + header: T.AllowedOperations, + id: 'allowed-operations', + accessor: ({ RIGHTS }) => sentenceCase(RIGHTS?.string || ''), + }, + { + header: T.Zone, + id: 'zone', + accessor: ({ ZONE }) => ZONE?.name || T.All, + }, + ] + const CardStyle = useMemo(() => ACLRow(viewType), [viewType]) + const { component, header } = WrapperRow(CardStyle) + + const EnhancedTableProps = { + columns, + data, + rootProps, + searchProps, + refetch, + isLoading: isFetching, + getRowId: (row) => String(row.ID), + singleSelect, + RowComponent: component, + headerList: header && listHeader, + ...rest, + } + !header && (EnhancedTableProps.tableViews = tableViews) + + return data && } +ACLsTable.propTypes = { ...EnhancedTable.propTypes } +ACLsTable.displayName = 'ACLsTable' + export default ACLsTable diff --git a/src/fireedge/src/client/components/Tables/ACLs/row.js b/src/fireedge/src/client/components/Tables/ACLs/row.js index b524ea825c1..91e26ebd23f 100644 --- a/src/fireedge/src/client/components/Tables/ACLs/row.js +++ b/src/fireedge/src/client/components/Tables/ACLs/row.js @@ -13,20 +13,20 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -/* eslint-disable jsdoc/require-jsdoc */ -import PropTypes from 'prop-types' import { + ACLCardCLI, ACLCardIcons, ACLCardNames, - ACLCardCLI, + ACLCardReadableRule, ACLCardResources, ACLCardRule, - ACLCardReadableRule, } from 'client/components/Cards' import { ACL_TABLE_VIEWS } from 'client/constants' +/* eslint-disable jsdoc/require-jsdoc */ +import PropTypes from 'prop-types' const Row = (viewType) => { - const aclRow = ({ original, value, ...props }) => { + const aclRow = ({ original, value, headerList, rowDataCy, ...props }) => { // Check what view show in the table cards if (viewType === ACL_TABLE_VIEWS.NAMES.type) { return diff --git a/src/fireedge/src/client/components/Tables/AllImages/row.js b/src/fireedge/src/client/components/Tables/AllImages/row.js index 6e32ccc9d35..9198ecf6a1a 100644 --- a/src/fireedge/src/client/components/Tables/AllImages/row.js +++ b/src/fireedge/src/client/components/Tables/AllImages/row.js @@ -16,26 +16,26 @@ /* eslint-disable jsdoc/require-jsdoc */ import PropTypes from 'prop-types' +import { Typography } from '@mui/material' import { - Lock, - User, - Group, Db as DatastoreIcon, + Archive as DiskTypeIcon, + Group, + Lock, ModernTv, Pin as PersistentIcon, - Archive as DiskTypeIcon, + User, } from 'iconoir-react' -import { Typography } from '@mui/material' -import Timer from 'client/components/Timer' -import { StatusCircle, StatusChip } from 'client/components/Status' +import { StatusChip, StatusCircle } from 'client/components/Status' import { rowStyles } from 'client/components/Tables/styles' +import Timer from 'client/components/Timer' import { T } from 'client/constants' -import * as ImageModel from 'client/models/Image' import * as Helper from 'client/models/Helper' +import * as ImageModel from 'client/models/Image' -const Row = ({ original, value, ...props }) => { +const Row = ({ original, value, headerList, rowDataCy, ...props }) => { const classes = rowStyles() const { ID, @@ -126,6 +126,8 @@ Row.propTypes = { value: PropTypes.object, isSelected: PropTypes.bool, handleClick: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } export default Row diff --git a/src/fireedge/src/client/components/Tables/BackupJobs/index.js b/src/fireedge/src/client/components/Tables/BackupJobs/index.js index 13c1d8acbca..eeb72a54502 100644 --- a/src/fireedge/src/client/components/Tables/BackupJobs/index.js +++ b/src/fireedge/src/client/components/Tables/BackupJobs/index.js @@ -13,18 +13,30 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { ReactElement, useMemo } from 'react' - -import { useViews } from 'client/features/Auth' -import { useGetBackupJobsQuery } from 'client/features/OneApi/backupjobs' - +import MultipleTags from 'client/components/MultipleTags' +import { StatusCircle } from 'client/components/Status' import BackupJobsColumns from 'client/components/Tables/BackupJobs/columns' import BackupJobsRow from 'client/components/Tables/BackupJobs/row' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' -import { RESOURCE_NAMES } from 'client/constants' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' +import Timer from 'client/components/Timer' +import { RESOURCE_NAMES, T } from 'client/constants' +import COLOR from 'client/constants/color' +import { useAuth, useViews } from 'client/features/Auth' +import { useGetBackupJobsQuery } from 'client/features/OneApi/backupjobs' +import { + getColorFromString, + getUniqueLabels, + timeFromMilliseconds, +} from 'client/models/Helper' +import { ReactElement, useMemo } from 'react' const DEFAULT_DATA_CY = 'backupjobs' +const haveValues = function (object) { + return Object.values(object).length > 0 +} + /** * @param {object} props - Props * @returns {ReactElement} Backup Jobs table @@ -46,6 +58,107 @@ const BackupJobsTable = (props) => { [view] ) + const listHeader = [ + { + header: '', + id: 'status-icon', + accessor: ({ + OUTDATED_VMS, + BACKING_UP_VMS, + ERROR_VMS, + LAST_BACKUP_TIME, + }) => { + const status = useMemo(() => { + const completed = { + color: COLOR.success.main, + tooltip: T.Completed, + } + const noStarted = { + color: COLOR.warning.main, + tooltip: T.NotStartedYet, + } + + const error = { + color: COLOR.error.main, + tooltip: T.Error, + } + + const onGoing = { + color: COLOR.info.main, + tooltip: T.OnGoing, + } + + if (haveValues(ERROR_VMS)) { + return error + } + + if (!haveValues(OUTDATED_VMS) && !haveValues(BACKING_UP_VMS)) { + return LAST_BACKUP_TIME === '0' ? noStarted : completed + } + + if (haveValues(OUTDATED_VMS)) { + return completed + } + + if (haveValues(BACKING_UP_VMS)) { + return onGoing + } + }, [OUTDATED_VMS, BACKING_UP_VMS, ERROR_VMS, LAST_BACKUP_TIME]) + + return + }, + }, + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { header: T.Owner, id: 'owner', accessor: 'UNAME' }, + { header: T.Group, id: 'group', accessor: 'GNAME' }, + { header: T.Priority, id: 'priority', accessor: 'PRIORITY' }, + { + header: T.LastBackupTimeInfo, + id: 'last-time', + accessor: ({ LAST_BACKUP_TIME }) => { + const LastBackupTime = +LAST_BACKUP_TIME + if (LastBackupTime > 0) { + const timer = timeFromMilliseconds(LastBackupTime) + + return ( + + + + ) + } else { + return '' + } + }, + }, + { + header: T.Labels, + id: 'labels', + accessor: ({ TEMPLATE: { LABELS } = {} }) => { + const { labels: userLabels } = useAuth() + const labels = useMemo( + () => + getUniqueLabels(LABELS).reduce((acc, label) => { + if (userLabels?.includes(label)) { + acc.push({ + text: label, + dataCy: `label-${label}`, + stateColor: getColorFromString(label), + }) + } + + return acc + }, []), + [LABELS] + ) + + return + }, + }, + ] + + const { component, header } = WrapperRow(BackupJobsRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={BackupJobsRow} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/BackupJobs/row.js b/src/fireedge/src/client/components/Tables/BackupJobs/row.js index b890a84fc47..1ea2f96047a 100644 --- a/src/fireedge/src/client/components/Tables/BackupJobs/row.js +++ b/src/fireedge/src/client/components/Tables/BackupJobs/row.js @@ -23,7 +23,7 @@ import backupjobApi, { import { jsonToXml } from 'client/models/Helper' const Row = memo( - ({ original, value, onClickLabel, ...props }) => { + ({ original, value, onClickLabel, headerList, rowDataCy, ...props }) => { const [update] = useUpdateBackupJobMutation() const state = backupjobApi.endpoints.getBackupJobs.useQueryState( @@ -67,6 +67,8 @@ Row.propTypes = { className: PropTypes.string, onClick: PropTypes.func, onClickLabel: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } Row.displayName = 'VirtualDataCenterRow' diff --git a/src/fireedge/src/client/components/Tables/Backups/index.js b/src/fireedge/src/client/components/Tables/Backups/index.js index d909ff29c7b..373ff6b609a 100644 --- a/src/fireedge/src/client/components/Tables/Backups/index.js +++ b/src/fireedge/src/client/components/Tables/Backups/index.js @@ -13,15 +13,18 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useMemo, ReactElement } from 'react' - -import { useViews } from 'client/features/Auth' -import { useGetBackupsQuery } from 'client/features/OneApi/image' - -import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import MultipleTags from 'client/components/MultipleTags' +import { StatusCircle } from 'client/components/Status' import backupColumns from 'client/components/Tables/Backups/columns' import BackupRow from 'client/components/Tables/Backups/row' -import { RESOURCE_NAMES } from 'client/constants' +import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' +import { RESOURCE_NAMES, T } from 'client/constants' +import { useAuth, useViews } from 'client/features/Auth' +import { useGetBackupsQuery } from 'client/features/OneApi/image' +import { getColorFromString, getUniqueLabels } from 'client/models/Helper' +import { getState, getType } from 'client/models/Image' +import { ReactElement, useMemo } from 'react' const DEFAULT_DATA_CY = 'backups' @@ -87,6 +90,50 @@ const BackupsTable = (props) => { const isFetchingAll = () => isFetchingVm ? !!(isFetchingVm && isFetching) : isFetching + const listHeader = [ + { + header: '', + id: 'status-icon', + accessor: (template) => { + const { color: stateColor, name: stateName } = getState(template) + + return + }, + }, + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { header: T.Owner, id: 'owner', accessor: 'UNAME' }, + { header: T.Group, id: 'group', accessor: 'GNAME' }, + { header: T.Datastore, id: 'datastore', accessor: 'DATASTORE' }, + { header: T.Type, id: 'type', accessor: (template) => getType(template) }, + { + header: T.Labels, + id: 'labels', + accessor: (_, { label: LABELS = [] }) => { + const { labels: userLabels } = useAuth() + const labels = useMemo( + () => + getUniqueLabels(LABELS).reduce((acc, label) => { + if (userLabels?.includes(label)) { + acc.push({ + text: label, + dataCy: `label-${label}`, + stateColor: getColorFromString(label), + }) + } + + return acc + }, []), + [LABELS] + ) + + return + }, + }, + ] + + const { component, header } = WrapperRow(BackupRow) + return ( { refetch={refetchAll} isLoading={isFetchingAll()} getRowId={(row) => String(row.ID)} - RowComponent={BackupRow} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/Backups/row.js b/src/fireedge/src/client/components/Tables/Backups/row.js index 5adbd5e877a..c0e82ee6696 100644 --- a/src/fireedge/src/client/components/Tables/Backups/row.js +++ b/src/fireedge/src/client/components/Tables/Backups/row.js @@ -13,39 +13,46 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -/* eslint-disable jsdoc/require-jsdoc */ -import PropTypes from 'prop-types' import { useAuth } from 'client/features/Auth' -import { useMemo, useCallback } from 'react' import imageApi, { useUpdateImageMutation } from 'client/features/OneApi/image' +/* eslint-disable jsdoc/require-jsdoc */ +import PropTypes from 'prop-types' +import { useCallback, useMemo } from 'react' -import { - Lock, - User, - Group, - Db as DatastoreIcon, - ModernTv, - Pin as PersistentIcon, - Archive as DiskTypeIcon, -} from 'iconoir-react' import { Typography } from '@mui/material' import MultipleTags from 'client/components/MultipleTags' import { - jsonToXml, - getUniqueLabels, getColorFromString, + getUniqueLabels, + jsonToXml, } from 'client/models/Helper' +import { + Db as DatastoreIcon, + Archive as DiskTypeIcon, + Group, + Lock, + ModernTv, + Pin as PersistentIcon, + User, +} from 'iconoir-react' -import Timer from 'client/components/Timer' -import { StatusCircle, StatusChip } from 'client/components/Status' +import { Tr } from 'client/components/HOC' +import { StatusChip, StatusCircle } from 'client/components/Status' import { rowStyles } from 'client/components/Tables/styles' +import Timer from 'client/components/Timer' import { T } from 'client/constants' -import { Tr } from 'client/components/HOC' -import * as ImageModel from 'client/models/Image' import * as Helper from 'client/models/Helper' +import * as ImageModel from 'client/models/Image' -const Row = ({ original, value, onClickLabel, ...props }) => { +const Row = ({ + original, + value, + onClickLabel, + headerList, + rowDataCy, + ...props +}) => { const [update] = useUpdateImageMutation() const { labels: userLabels } = useAuth() @@ -187,6 +194,8 @@ Row.propTypes = { isSelected: PropTypes.bool, handleClick: PropTypes.func, onClickLabel: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } export default Row diff --git a/src/fireedge/src/client/components/Tables/Clusters/index.js b/src/fireedge/src/client/components/Tables/Clusters/index.js index 229de30211e..7638e6bdc60 100644 --- a/src/fireedge/src/client/components/Tables/Clusters/index.js +++ b/src/fireedge/src/client/components/Tables/Clusters/index.js @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useMemo, ReactElement } from 'react' +import { ReactElement, useMemo } from 'react' -import { useViews } from 'client/features/Auth' -import { useGetClustersQuery } from 'client/features/OneApi/cluster' - -import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' import ClusterColumns from 'client/components/Tables/Clusters/columns' import ClusterRow from 'client/components/Tables/Clusters/row' -import { RESOURCE_NAMES } from 'client/constants' +import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' +import { RESOURCE_NAMES, T } from 'client/constants' +import { useViews } from 'client/features/Auth' +import { useGetClustersQuery } from 'client/features/OneApi/cluster' const DEFAULT_DATA_CY = 'clusters' @@ -74,6 +74,29 @@ const ClustersTable = (props) => { [view] ) + const listHeader = [ + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { + header: T.Hosts, + id: 'hosts', + accessor: ({ HOSTS }) => (Array.isArray(HOSTS.ID) ? HOSTS.ID.length : 1), + }, + { + header: T.Vnets, + id: 'vnets', + accessor: ({ VNETS }) => (Array.isArray(VNETS.ID) ? VNETS.ID.length : 1), + }, + { + header: T.Datastore, + id: 'datastores', + accessor: ({ DATASTORES }) => + Array.isArray(DATASTORES.ID) ? DATASTORES.ID.length : 1, + }, + ] + + const { component, header } = WrapperRow(ClusterRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={ClusterRow} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/Clusters/row.js b/src/fireedge/src/client/components/Tables/Clusters/row.js index f255f79b62c..1183521075f 100644 --- a/src/fireedge/src/client/components/Tables/Clusters/row.js +++ b/src/fireedge/src/client/components/Tables/Clusters/row.js @@ -16,15 +16,15 @@ /* eslint-disable jsdoc/require-jsdoc */ import PropTypes from 'prop-types' -import { HardDrive, NetworkAlt, Folder, Cloud } from 'iconoir-react' import { Typography } from '@mui/material' +import { Cloud, Folder, HardDrive, NetworkAlt } from 'iconoir-react' import { rowStyles } from 'client/components/Tables/styles' import { Tr } from 'client/components/HOC' import { T } from 'client/constants' -const Row = ({ original, value, ...props }) => { +const Row = ({ original, value, headerList, rowDataCy, ...props }) => { const classes = rowStyles() const { ID, NAME, HOSTS, DATASTORES, VNETS, PROVIDER_NAME } = value @@ -76,6 +76,8 @@ Row.propTypes = { value: PropTypes.object, isSelected: PropTypes.bool, handleClick: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } export default Row diff --git a/src/fireedge/src/client/components/Tables/Datastores/index.js b/src/fireedge/src/client/components/Tables/Datastores/index.js index 95a7bad684e..8ce4bde8c00 100644 --- a/src/fireedge/src/client/components/Tables/Datastores/index.js +++ b/src/fireedge/src/client/components/Tables/Datastores/index.js @@ -13,11 +13,8 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { ReactElement, useCallback, useEffect, useMemo, useState } from 'react' - -import { useViews } from 'client/features/Auth' -import { useGetDatastoresQuery } from 'client/features/OneApi/datastore' - +import MultipleTags from 'client/components/MultipleTags' +import { LinearProgressWithLabel, StatusCircle } from 'client/components/Status' import DatastoreColumns from 'client/components/Tables/Datastores/columns' import DatastoreRow from 'client/components/Tables/Datastores/row' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' @@ -25,7 +22,13 @@ import { areArraysEqual, sortStateTables, } from 'client/components/Tables/Enhanced/Utils/DataTableUtils' -import { RESOURCE_NAMES } from 'client/constants' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' +import { DS_THRESHOLD, RESOURCE_NAMES, T } from 'client/constants' +import { useAuth, useViews } from 'client/features/Auth' +import { useGetDatastoresQuery } from 'client/features/OneApi/datastore' +import { getCapacityInfo, getState, getType } from 'client/models/Datastore' +import { getColorFromString, getUniqueLabels } from 'client/models/Helper' +import { ReactElement, useCallback, useEffect, useMemo, useState } from 'react' import { useFormContext } from 'react-hook-form' const DEFAULT_DATA_CY = 'datastores' @@ -135,6 +138,82 @@ const DatastoresTable = (props) => { } }) + const listHeader = [ + { + header: T.Status, + id: 'status', + accessor: (template) => { + const { color: stateColor, name: stateName } = getState(template) + + return + }, + }, + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { header: T.Owner, id: 'owner', accessor: 'UNAME' }, + { header: T.Group, id: 'group', accessor: 'GNAME' }, + { + header: T.Capacity, + id: 'capacity', + accessor: (template) => { + const capacity = useMemo(() => getCapacityInfo(template), [template]) + const { percentOfUsed, percentLabel } = capacity + + return ( + + ) + }, + }, + { + header: T.Cluster, + id: 'cluster', + accessor: ({ CLUSTERS }) => { + const clusters = useMemo( + () => [CLUSTERS?.ID ?? []].flat(), + [CLUSTERS?.ID] + ) + + return clusters.length && clusters[0] + }, + }, + { + header: T.Type, + id: 'type', + accessor: (template) => getType(template), + }, + { + header: T.Labels, + id: 'labels', + accessor: ({ TEMPLATE: { LABELS } = {} }) => { + const { labels: userLabels } = useAuth() + const labels = useMemo( + () => + getUniqueLabels(LABELS).reduce((acc, label) => { + if (userLabels?.includes(label)) { + acc.push({ + text: label, + dataCy: `label-${label}`, + stateColor: getColorFromString(label), + }) + } + + return acc + }, []), + [LABELS] + ) + + return + }, + }, + ] + const { component, header } = WrapperRow(DatastoreRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={DatastoreRow} dataDepend={values} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/Datastores/row.js b/src/fireedge/src/client/components/Tables/Datastores/row.js index 753ab49c9fb..f282db2ea5c 100644 --- a/src/fireedge/src/client/components/Tables/Datastores/row.js +++ b/src/fireedge/src/client/components/Tables/Datastores/row.js @@ -13,27 +13,42 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { memo, useCallback, useMemo } from 'react' +import { jsonToXml } from 'client/models/Helper' import PropTypes from 'prop-types' +import { memo, useCallback, useMemo } from 'react' -import { - useGetDatastoresQuery, +import { DatastoreCard } from 'client/components/Cards' +import api, { useUpdateDatastoreMutation, } from 'client/features/OneApi/datastore' -import { DatastoreCard } from 'client/components/Cards' -import { jsonToXml } from 'client/models/Helper' const Row = memo( - ({ original, onClickLabel, ...props }) => { - const { data: datastores } = useGetDatastoresQuery(undefined) - const selectedDatastore = datastores?.find( - (datastore) => +datastore.ID === +original.ID - ) + ({ + original, + value, + onClickLabel, + zone, + headerList, + rowDataCy, + ...props + }) => { const [update] = useUpdateDatastoreMutation() + const { + data: datastores, + error, + isLoading, + } = api.endpoints.getDatastores.useQueryState({ zone }) + + const selectedDatastore = useMemo( + () => + datastores?.find((datastore) => +datastore.ID === +original.ID) ?? + original, + [datastores, original] + ) const memoDs = useMemo( () => selectedDatastore ?? original, - [datastores, original] + [selectedDatastore, original, update, isLoading, error, datastores] ) const handleDeleteLabel = useCallback( @@ -62,7 +77,11 @@ const Row = memo( Row.propTypes = { original: PropTypes.object, + value: PropTypes.object, onClickLabel: PropTypes.func, + zone: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } Row.displayName = 'DatastoreRow' diff --git a/src/fireedge/src/client/components/Tables/Enhanced/Utils/CategoryFilter.js b/src/fireedge/src/client/components/Tables/Enhanced/Utils/CategoryFilter.js index cc00865d214..12b18d37dc4 100644 --- a/src/fireedge/src/client/components/Tables/Enhanced/Utils/CategoryFilter.js +++ b/src/fireedge/src/client/components/Tables/Enhanced/Utils/CategoryFilter.js @@ -55,6 +55,10 @@ const StyledAutocompletePopper = styled(Popper)( }) ) +const CustomPopper = (props) => ( + +) + /** * Render category filter to table. * @@ -63,23 +67,43 @@ const StyledAutocompletePopper = styled(Popper)( * @returns {ReactElement} Component JSX */ const CategoryFilter = ({ - column: { Header, filterValue = [], setFilter, preFilteredRows, id }, + column: { + Header, + filterValue = [], + setFilter, + preFilteredRows, + id, + translation, + }, }) => { // Calculate the options for filtering using the preFilteredRows const options = useMemo(() => { - const uniqueOptions = new Set() + const uniqueOptions = [] preFilteredRows?.forEach((row) => { const rowValue = row.values[id] // If the row value is an array, we get all the values of the array - rowValue !== undefined && - (Array.isArray(rowValue) - ? rowValue.forEach((value) => uniqueOptions.add(value)) - : uniqueOptions.add(rowValue)) + if (rowValue !== undefined) { + if (Array.isArray(rowValue)) { + rowValue.forEach((value) => { + const newId = translation ? translation[value] : value + + if (!uniqueOptions.some((option) => option.id === newId)) { + uniqueOptions.push({ id: newId, label: `${value}` }) + } + }) + } else { + const newId = translation ? translation[rowValue] : rowValue + + if (!uniqueOptions.some((option) => option.id === newId)) { + uniqueOptions.push({ id: newId, label: `${rowValue}` }) + } + } + } }) - return [...uniqueOptions.values()] + return uniqueOptions // [] }, [id, preFilteredRows]) if (options.length === 0) { @@ -93,11 +117,13 @@ const CategoryFilter = ({ disableCloseOnSelect limitTags={2} color="secondary" - value={filterValue} sx={{ minWidth: 300, position: 'relative' }} options={options} - onChange={(_, newValue) => setFilter(newValue)} - PopperComponent={StyledAutocompletePopper} + getOptionLabel={(option) => option.id} + onChange={(_, newValue) => { + setFilter(newValue[newValue?.length - 1]?.label || '') + }} + PopperComponent={CustomPopper} renderInput={({ inputProps, ...inputParams }) => ( diff --git a/src/fireedge/src/client/components/Tables/Enhanced/WrapperRow.js b/src/fireedge/src/client/components/Tables/Enhanced/WrapperRow.js new file mode 100644 index 00000000000..13365b5b47c --- /dev/null +++ b/src/fireedge/src/client/components/Tables/Enhanced/WrapperRow.js @@ -0,0 +1,104 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2024, OpenNebula Project, OpenNebula Systems * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); you may * + * not use this file except in compliance with the License. You may obtain * + * a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * ------------------------------------------------------------------------- */ +import { TableCell, TableRow } from '@mui/material' +import makeStyles from '@mui/styles/makeStyles' +import { SERVER_CONFIG } from 'client/constants' +import { useAuth } from 'client/features/Auth' +import get from 'lodash.get' +import PropTypes from 'prop-types' +import { ReactElement, memo } from 'react' + +const listStyles = makeStyles(({ palette }) => ({ + row: { + '&.selected': { + boxShadow: `inset 0px -0.5px 0px 2px ${palette.secondary.main}`, + }, + }, +})) + +/** + * @param {object} props - Props + * @returns {ReactElement} Generic Row + */ +const RowStyle = memo( + ({ + original = {}, + value = {}, + onClickLabel, + onDeleteLabel, + globalErrors, + headerList = [], + className, + rowDataCy = '', + ...props + }) => { + const { ID = '' } = original + const styles = listStyles() + + return ( + + {headerList.map(({ id, accessor }) => { + switch (typeof accessor) { + case 'string': + return {get(original, accessor)} + case 'function': + return {accessor(original, value)} + default: + return '' + } + })} + + ) + }, + (prev, next) => prev.className === next.className +) + +RowStyle.propTypes = { + original: PropTypes.object, + value: PropTypes.object, + onClickLabel: PropTypes.func, + onDeleteLabel: PropTypes.func, + globalErrors: PropTypes.array, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, + className: PropTypes.string, +} + +RowStyle.displayName = 'RowStyle' + +/** + * @param {ReactElement} RowCardComponent - Standard row component (Card). + * @returns {ReactElement} Generic Row + */ +const WrapperRow = (RowCardComponent) => { + const { settings: { FIREEDGE: fireedge = {} } = {} } = useAuth() + const { ROW_STYLE } = fireedge + const { rowStyle } = SERVER_CONFIG + + const data = ROW_STYLE || rowStyle + const header = data === 'list' + + return { + component: header ? RowStyle : RowCardComponent, + header, + } +} + +export default WrapperRow diff --git a/src/fireedge/src/client/components/Tables/Enhanced/index.js b/src/fireedge/src/client/components/Tables/Enhanced/index.js index 7cab185b823..451be4d5792 100644 --- a/src/fireedge/src/client/components/Tables/Enhanced/index.js +++ b/src/fireedge/src/client/components/Tables/Enhanced/index.js @@ -15,9 +15,19 @@ * ------------------------------------------------------------------------- */ /* eslint-disable jsdoc/require-jsdoc */ import PropTypes from 'prop-types' -import { useEffect, useMemo, useState } from 'react' +import { memo, useEffect, useMemo, useState } from 'react' -import { Alert, Box, Chip, Grid } from '@mui/material' +import { + Alert, + Box, + Chip, + Grid, + Table, + TableBody, + TableCell, + TableHead, + TableRow, +} from '@mui/material' import clsx from 'clsx' import InfoEmpty from 'iconoir-react/dist/InfoEmpty' import RemoveIcon from 'iconoir-react/dist/RemoveSquare' @@ -52,6 +62,118 @@ import _ from 'lodash' const RELOAD_STATE = 'RELOAD_STATE' +const DataListPerPage = memo( + ({ + page = [], + prepareRow, + RowComponent, + headerList, + messageValues, + setFilter, + state, + disableRowSelect, + onRowClick, + readOnly, + singleSelect, + toggleAllRowsSelected, + zoneId, + cannotFilterByLabel, + styles, + rootProps: rootPropsTable, + }) => { + if (!page.length) { + return '' + } + + const valuesPerPages = page.map((row) => { + prepareRow(row) + /** @type {UseRowSelectRowProps} */ + const { getRowProps, original, values, toggleRowSelected, isSelected } = + row + const { key, ...rowProps } = getRowProps() + + return ( + { + const currentFilter = + state.filters + ?.filter(({ id }) => id === LABEL_COLUMN_ID) + ?.map(({ value }) => value) + ?.flat() || [] + const nextFilter = [...new Set([...currentFilter, label])] + setFilter(LABEL_COLUMN_ID, nextFilter) + }, + })} + onClick={(e) => { + typeof onRowClick === 'function' && onRowClick(original) + if (!disableRowSelect && !readOnly) { + if ( + singleSelect || + (!singleSelect && !(e.ctrlKey || e.metaKey)) + ) { + toggleAllRowsSelected?.(false) + } + toggleRowSelected?.(!isSelected) + } + }} + /> + ) + }) + + return headerList ? ( + + + + {headerList.map(({ header = '', id = '' }) => ( + + {header} + + ))} + + + {valuesPerPages} +
+ ) : ( + <>{valuesPerPages} + ) + } +) + +DataListPerPage.propTypes = { + page: PropTypes.any, + prepareRow: PropTypes.func, + RowComponent: PropTypes.any, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + messageValues: PropTypes.array, + setFilter: PropTypes.func, + state: PropTypes.any, + disableRowSelect: PropTypes.bool, + onRowClick: PropTypes.func, + readOnly: PropTypes.bool, + singleSelect: PropTypes.bool, + toggleAllRowsSelected: PropTypes.func, + zoneId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + cannotFilterByLabel: PropTypes.any, + styles: PropTypes.any, + rootProps: PropTypes.shape({ + 'data-cy': PropTypes.string, + }), +} + +DataListPerPage.displayName = 'DataListPerPage' + const EnhancedTable = ({ columns, globalActions, @@ -83,6 +205,7 @@ const EnhancedTable = ({ readOnly = false, tableViews, zoneId, + headerList, }) => { const styles = EnhancedTableStyles({ readOnly: readOnly, @@ -376,7 +499,7 @@ const EnhancedTable = ({ }} /> -
+
{!!messages.length && } {/* NO DATA MESSAGE */} {!isLoading && @@ -390,58 +513,24 @@ const EnhancedTable = ({ ))} {/* DATALIST PER PAGE */} - {page.map((row) => { - prepareRow(row) - - /** @type {UseRowSelectRowProps} */ - const { - getRowProps, - original, - values, - toggleRowSelected, - isSelected, - } = row - const { key, ...rowProps } = getRowProps() - - return ( - { - const currentFilter = - state.filters - ?.filter(({ id }) => id === LABEL_COLUMN_ID) - ?.map(({ value }) => value) - ?.flat() || [] - - const nextFilter = [...new Set([...currentFilter, label])] - setFilter(LABEL_COLUMN_ID, nextFilter) - }, - })} - onClick={(e) => { - typeof onRowClick === 'function' && onRowClick(original) - - if (!disableRowSelect && !readOnly) { - if ( - singleSelect || - (!singleSelect && !(e.ctrlKey || e.metaKey)) - ) { - toggleAllRowsSelected?.(false) - } - toggleRowSelected?.(!isSelected) - } - }} - /> - ) - })} +
) @@ -490,6 +579,7 @@ EnhancedTable.propTypes = { readOnly: PropTypes.bool, tableViews: PropTypes.object, zoneId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), } export * from 'client/components/Tables/Enhanced/Utils' diff --git a/src/fireedge/src/client/components/Tables/Enhanced/styles.js b/src/fireedge/src/client/components/Tables/Enhanced/styles.js index 949e7f3f635..d44ed2e4c94 100644 --- a/src/fireedge/src/client/components/Tables/Enhanced/styles.js +++ b/src/fireedge/src/client/components/Tables/Enhanced/styles.js @@ -132,4 +132,7 @@ export default makeStyles(({ palette, typography, breakpoints }) => ({ gap: '0.8em', padding: '1em', }, + cellHeaders: { + fontWeight: 'bold', + }, })) diff --git a/src/fireedge/src/client/components/Tables/Files/index.js b/src/fireedge/src/client/components/Tables/Files/index.js index 301f265f5c2..36b89b83b65 100644 --- a/src/fireedge/src/client/components/Tables/Files/index.js +++ b/src/fireedge/src/client/components/Tables/Files/index.js @@ -13,15 +13,17 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useMemo, ReactElement } from 'react' - -import { useViews } from 'client/features/Auth' -import { useGetFilesQuery } from 'client/features/OneApi/image' +import { ReactElement, useMemo } from 'react' +import { StatusCircle } from 'client/components/Status' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import ImageColumns from 'client/components/Tables/Images/columns' import ImageRow from 'client/components/Tables/Images/row' -import { RESOURCE_NAMES } from 'client/constants' +import { RESOURCE_NAMES, T } from 'client/constants' +import { useViews } from 'client/features/Auth' +import { useGetFilesQuery } from 'client/features/OneApi/image' +import { getState, getType } from 'client/models/Image' const DEFAULT_DATA_CY = 'images' @@ -61,6 +63,26 @@ const FilesTable = (props) => { [view] ) + const listHeader = [ + { + header: '', + id: 'status-icon', + accessor: (vm) => { + const { color: stateColor, name: stateName } = getState(vm) + + return + }, + }, + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { header: T.Owner, id: 'owner', accessor: 'UNAME' }, + { header: T.Group, id: 'group', accessor: 'GNAME' }, + { header: T.Datastore, id: 'datastore', accessor: 'DATASTORE' }, + { header: T.Type, id: 'type', accessor: (template) => getType(template) }, + ] + + const { component, header } = WrapperRow(ImageRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={ImageRow} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/Groups/index.js b/src/fireedge/src/client/components/Tables/Groups/index.js index 8b567855943..8f8c1b2dc8a 100644 --- a/src/fireedge/src/client/components/Tables/Groups/index.js +++ b/src/fireedge/src/client/components/Tables/Groups/index.js @@ -13,13 +13,16 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useMemo, Component } from 'react' -import { useViews } from 'client/features/Auth' -import { useGetGroupsQuery } from 'client/features/OneApi/group' +import { LinearProgressWithTooltip } from 'client/components/Status' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import GroupColumns from 'client/components/Tables/Groups/columns' import GroupRow from 'client/components/Tables/Groups/row' -import { RESOURCE_NAMES } from 'client/constants' +import { RESOURCE_NAMES, T } from 'client/constants' +import { useViews } from 'client/features/Auth' +import { useGetGroupsQuery } from 'client/features/OneApi/group' +import { getQuotaUsage } from 'client/models/Group' +import { Component, useMemo } from 'react' const DEFAULT_DATA_CY = 'groups' @@ -58,6 +61,74 @@ const GroupsTable = (props) => { [view] ) + const listHeader = [ + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { + header: T.Users, + id: 'users', + accessor: ({ USERS }) => (Array.isArray(USERS?.ID) ? USERS.ID.length : 0), + }, + { + header: T.VMs, + id: 'vms', + accessor: ({ VM_QUOTA }) => { + const vmQuotaUsage = useMemo( + () => getQuotaUsage('VM', VM_QUOTA), + [VM_QUOTA] + ) + + return ( + + ) + }, + }, + { + header: T.Datastores, + id: 'datastores', + accessor: ({ DATASTORE_QUOTA }) => { + const datastoreQuotaUsage = useMemo( + () => getQuotaUsage('DATASTORE', DATASTORE_QUOTA), + [DATASTORE_QUOTA] + ) + + return ( + + ) + }, + }, + { + header: T.Networks, + id: 'networks', + accessor: ({ NETWORK_QUOTA }) => { + const networkQuotaUsage = useMemo( + () => getQuotaUsage('NETWORK', NETWORK_QUOTA), + [NETWORK_QUOTA] + ) + + return ( + + ) + }, + }, + ] + const { component, header } = WrapperRow(GroupRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={GroupRow} singleSelect={singleSelect} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) } +GroupsTable.propTypes = { ...EnhancedTable.propTypes } +GroupsTable.displayName = 'GroupsTable' + export default GroupsTable diff --git a/src/fireedge/src/client/components/Tables/Hosts/index.js b/src/fireedge/src/client/components/Tables/Hosts/index.js index 82af6df6a5e..8e7bcaac6f7 100644 --- a/src/fireedge/src/client/components/Tables/Hosts/index.js +++ b/src/fireedge/src/client/components/Tables/Hosts/index.js @@ -15,17 +15,22 @@ * ------------------------------------------------------------------------- */ import { ReactElement, useCallback, useEffect, useMemo, useState } from 'react' -import { useViews } from 'client/features/Auth' -import { useGetHostsQuery } from 'client/features/OneApi/host' - +import { Tr } from 'client/components/HOC' +import MultipleTags from 'client/components/MultipleTags' +import { LinearProgressWithLabel, StatusCircle } from 'client/components/Status' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' import { areArraysEqual, sortStateTables, } from 'client/components/Tables/Enhanced/Utils/DataTableUtils' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import HostColumns from 'client/components/Tables/Hosts/columns' import HostRow from 'client/components/Tables/Hosts/row' -import { RESOURCE_NAMES } from 'client/constants' +import { HOST_THRESHOLD, RESOURCE_NAMES, T } from 'client/constants' +import { useAuth, useViews } from 'client/features/Auth' +import { useGetHostsQuery } from 'client/features/OneApi/host' +import { getColorFromString, getUniqueLabels } from 'client/models/Helper' +import { getAllocatedInfo, getState } from 'client/models/Host' import { useFormContext } from 'react-hook-form' const DEFAULT_DATA_CY = 'hosts' @@ -136,6 +141,90 @@ const HostsTable = (props) => { }) useEffect(() => refetch(), []) + const listHeader = [ + { + header: '', + id: 'status-icon', + accessor: (host) => { + const { color: stateColor, name: stateName } = getState(host) + + return + }, + }, + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { header: T.Cluster, id: 'cluster', accessor: 'CLUSTER' }, + { + header: T.Rvms, + id: 'rvms', + accessor: ({ HOST_SHARE }) => HOST_SHARE?.RUNNING_VMS || 0, + }, + { + header: T.AllocatedCpu, + id: 'cpu', + accessor: (host) => { + const { percentCpuUsed, percentCpuLabel, colorCpu } = + getAllocatedInfo(host) + + return ( + + ) + }, + }, + { + header: T.AllocatedMemory, + id: 'memory', + accessor: (host) => { + const { percentMemUsed, percentMemLabel, colorMem } = + getAllocatedInfo(host) + + return ( + + ) + }, + }, + { + header: T.Labels, + id: 'labels', + accessor: ({ TEMPLATE: { LABELS } = {} }) => { + const { labels: userLabels } = useAuth() + + const labels = useMemo( + () => + getUniqueLabels(LABELS).reduce((acc, label) => { + if (userLabels?.includes(label)) { + acc.push({ + text: label, + dataCy: `label-${label}`, + stateColor: getColorFromString(label), + }) + } + + return acc + }, []), + [LABELS] + ) + + return + }, + }, + ] + const { component, header } = WrapperRow(HostRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={HostRow} dataDepend={values} zoneId={zoneId} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/Hosts/row.js b/src/fireedge/src/client/components/Tables/Hosts/row.js index 09ebe25aca5..724ef596fbe 100644 --- a/src/fireedge/src/client/components/Tables/Hosts/row.js +++ b/src/fireedge/src/client/components/Tables/Hosts/row.js @@ -20,14 +20,22 @@ import PropTypes from 'prop-types' import { memo, useCallback, useMemo } from 'react' const Row = memo( - ({ original, value, onClickLabel, zone, ...props }) => { + ({ + original, + value, + onClickLabel, + zone, + headerList, + rowDataCy, + ...props + }) => { const [update] = useUpdateHostMutation() const { data: hosts, error, isLoading, - } = hostApi.endpoints.getHosts.useQuery({ zone }) + } = hostApi.endpoints.getHosts.useQueryState({ zone }) const host = useMemo( () => hosts?.find((h) => +h.ID === +original.ID) ?? original, @@ -71,6 +79,8 @@ Row.propTypes = { handleClick: PropTypes.func, onClickLabel: PropTypes.func, zone: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } Row.displayName = 'HostRow' diff --git a/src/fireedge/src/client/components/Tables/Images/columns.js b/src/fireedge/src/client/components/Tables/Images/columns.js index f27f7f60047..16865280732 100644 --- a/src/fireedge/src/client/components/Tables/Images/columns.js +++ b/src/fireedge/src/client/components/Tables/Images/columns.js @@ -23,7 +23,12 @@ const COLUMNS = [ { Header: 'Name', id: 'name', accessor: 'NAME' }, { Header: 'Owner', accessor: 'UNAME' }, { Header: 'Group', accessor: 'GNAME' }, - { Header: 'Locked', id: 'locked', accessor: 'LOCK' }, + { + Header: 'Locked', + id: 'locked', + accessor: ImageModel.getImageLocked, + translation: { true: T.Locked, false: T.Unlocked }, + }, { Header: 'State', id: 'STATE', diff --git a/src/fireedge/src/client/components/Tables/Images/index.js b/src/fireedge/src/client/components/Tables/Images/index.js index 4529b73d70d..3d03fa585ec 100644 --- a/src/fireedge/src/client/components/Tables/Images/index.js +++ b/src/fireedge/src/client/components/Tables/Images/index.js @@ -13,15 +13,18 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useMemo, ReactElement } from 'react' - -import { useViews } from 'client/features/Auth' -import { useGetImagesQuery } from 'client/features/OneApi/image' - +import MultipleTags from 'client/components/MultipleTags' +import { StatusCircle } from 'client/components/Status' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import ImageColumns from 'client/components/Tables/Images/columns' import ImageRow from 'client/components/Tables/Images/row' -import { RESOURCE_NAMES } from 'client/constants' +import { RESOURCE_NAMES, T } from 'client/constants' +import { useAuth, useViews } from 'client/features/Auth' +import { useGetImagesQuery } from 'client/features/OneApi/image' +import { getColorFromString, getUniqueLabels } from 'client/models/Helper' +import { getState, getType } from 'client/models/Image' +import { ReactElement, useMemo } from 'react' const DEFAULT_DATA_CY = 'images' @@ -46,6 +49,51 @@ const ImagesTable = (props) => { [view] ) + const listHeader = [ + { + header: '', + id: 'status-icon', + accessor: (vm) => { + const { color: stateColor, name: stateName } = getState(vm) + + return + }, + }, + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { header: T.Owner, id: 'owner', accessor: 'UNAME' }, + { header: T.Group, id: 'group', accessor: 'GNAME' }, + { header: T.Datastore, id: 'datastore', accessor: 'DATASTORE' }, + { header: T.Type, id: 'type', accessor: (template) => getType(template) }, + { header: T.VMs, id: 'vms', accessor: 'RUNNING_VMS' }, + { + header: T.Labels, + id: 'labels', + accessor: (_, onClickLabel, onDeleteLabel, { label: LABELS = [] }) => { + const { labels: userLabels } = useAuth() + const labels = useMemo( + () => + getUniqueLabels(LABELS).reduce((acc, label) => { + if (userLabels?.includes(label)) { + acc.push({ + text: label, + dataCy: `label-${label}`, + stateColor: getColorFromString(label), + }) + } + + return acc + }, []), + [LABELS, onDeleteLabel, onClickLabel] + ) + + return + }, + }, + ] + + const { component, header } = WrapperRow(ImageRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={ImageRow} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/Images/row.js b/src/fireedge/src/client/components/Tables/Images/row.js index 6c30f31a768..da27a538211 100644 --- a/src/fireedge/src/client/components/Tables/Images/row.js +++ b/src/fireedge/src/client/components/Tables/Images/row.js @@ -45,7 +45,14 @@ import { prettyBytes } from 'client/utils' import * as Helper from 'client/models/Helper' import * as ImageModel from 'client/models/Image' -const Row = ({ original, value, onClickLabel, ...props }) => { +const Row = ({ + original, + value, + onClickLabel, + headerList, + rowDataCy, + ...props +}) => { const [update] = useUpdateImageMutation() const { labels: userLabels } = useAuth() @@ -116,7 +123,7 @@ const Row = ({ original, value, onClickLabel, ...props }) => { {NAME} - {locked && } + {locked && } {labels.map((label) => ( @@ -167,6 +174,8 @@ Row.propTypes = { isSelected: PropTypes.bool, handleClick: PropTypes.func, onClickLabel: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } export default Row diff --git a/src/fireedge/src/client/components/Tables/Increments/row.js b/src/fireedge/src/client/components/Tables/Increments/row.js index abbb9674080..f5e27fdb56e 100644 --- a/src/fireedge/src/client/components/Tables/Increments/row.js +++ b/src/fireedge/src/client/components/Tables/Increments/row.js @@ -16,22 +16,22 @@ /* eslint-disable jsdoc/require-jsdoc */ import PropTypes from 'prop-types' +import { Typography } from '@mui/material' import { - HardDrive as SizeIcon, RefreshCircular as FullIcon, Refresh as IncrementIcon, + HardDrive as SizeIcon, } from 'iconoir-react' -import { Typography } from '@mui/material' -import Timer from 'client/components/Timer' import { StatusChip } from 'client/components/Status' import { rowStyles } from 'client/components/Tables/styles' +import Timer from 'client/components/Timer' import { T } from 'client/constants' import { prettyBytes } from 'client/utils' import * as Helper from 'client/models/Helper' -const Row = ({ original, value, ...props }) => { +const Row = ({ original, value, headerList, rowDataCy, ...props }) => { const classes = rowStyles() const { ID, TYPE, DATE, SIZE, SOURCE } = value @@ -78,6 +78,8 @@ Row.propTypes = { value: PropTypes.object, isSelected: PropTypes.bool, handleClick: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } export default Row diff --git a/src/fireedge/src/client/components/Tables/MarketplaceApps/index.js b/src/fireedge/src/client/components/Tables/MarketplaceApps/index.js index e11e6d87154..2bce8ac2b55 100644 --- a/src/fireedge/src/client/components/Tables/MarketplaceApps/index.js +++ b/src/fireedge/src/client/components/Tables/MarketplaceApps/index.js @@ -13,15 +13,19 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useMemo, ReactElement } from 'react' - -import { useViews } from 'client/features/Auth' -import { useGetMarketplaceAppsQuery } from 'client/features/OneApi/marketplaceApp' - +import MultipleTags from 'client/components/MultipleTags' +import { StatusCircle } from 'client/components/Status' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import MarketplaceAppColumns from 'client/components/Tables/MarketplaceApps/columns' import MarketplaceAppRow from 'client/components/Tables/MarketplaceApps/row' -import { RESOURCE_NAMES } from 'client/constants' +import { RESOURCE_NAMES, T } from 'client/constants' +import { useAuth, useViews } from 'client/features/Auth' +import { useGetMarketplaceAppsQuery } from 'client/features/OneApi/marketplaceApp' +import { getColorFromString, getUniqueLabels } from 'client/models/Helper' +import { getState, getType } from 'client/models/MarketplaceApp' +import { prettyBytes } from 'client/utils' +import { ReactElement, useMemo } from 'react' const DEFAULT_DATA_CY = 'apps' @@ -56,6 +60,65 @@ const MarketplaceAppsTable = (props) => { [view] ) + const listHeader = [ + { + header: '', + id: 'status-icon', + accessor: (vm) => { + const { color: stateColor, name: stateName } = getState(vm) + + return + }, + }, + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { header: T.Owner, id: 'owner', accessor: 'UNAME' }, + { header: T.Group, id: 'group', accessor: 'GNAME' }, + { + header: T.Size, + id: 'Size', + accessor: ({ SIZE }) => prettyBytes(+SIZE, 'MB'), + }, + { + header: T.Type, + id: 'type', + accessor: (template) => + useMemo(() => getType(template), [template?.TYPE]), + }, + { + header: T.Marketplace, + id: 'marketplace', + accessor: 'MARKETPLACE', + }, + { header: T.Zone, id: 'zone', accessor: 'ZONE_ID' }, + { + header: T.Labels, + id: 'labels', + accessor: ({ TEMPLATE: { LABELS } = {} }) => { + const { labels: userLabels } = useAuth() + const labels = useMemo( + () => + getUniqueLabels(LABELS).reduce((acc, label) => { + if (userLabels?.includes(label)) { + acc.push({ + text: label, + dataCy: `label-${label}`, + stateColor: getColorFromString(label), + }) + } + + return acc + }, []), + [LABELS] + ) + + return + }, + }, + ] + + const { component, header } = WrapperRow(MarketplaceAppRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={MarketplaceAppRow} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/MarketplaceApps/row.js b/src/fireedge/src/client/components/Tables/MarketplaceApps/row.js index a78b0cee08a..56173d7e647 100644 --- a/src/fireedge/src/client/components/Tables/MarketplaceApps/row.js +++ b/src/fireedge/src/client/components/Tables/MarketplaceApps/row.js @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { memo, useMemo, useCallback } from 'react' import PropTypes from 'prop-types' +import { memo, useCallback, useMemo } from 'react' +import { MarketplaceAppCard } from 'client/components/Cards' import api, { useUpdateAppMutation, } from 'client/features/OneApi/marketplaceApp' -import { MarketplaceAppCard } from 'client/components/Cards' import { jsonToXml } from 'client/models/Helper' const Row = memo( - ({ original, value, onClickLabel, ...props }) => { + ({ original, value, onClickLabel, headerList, rowDataCy, ...props }) => { const [update] = useUpdateAppMutation() const state = api.endpoints.getMarketplaceApps.useQueryState(undefined, { @@ -64,6 +64,8 @@ Row.propTypes = { className: PropTypes.string, onClick: PropTypes.func, onClickLabel: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } Row.displayName = 'MarketplaceAppRow' diff --git a/src/fireedge/src/client/components/Tables/Marketplaces/index.js b/src/fireedge/src/client/components/Tables/Marketplaces/index.js index 76836ab886b..ff7fd11132a 100644 --- a/src/fireedge/src/client/components/Tables/Marketplaces/index.js +++ b/src/fireedge/src/client/components/Tables/Marketplaces/index.js @@ -13,16 +13,19 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useMemo, ReactElement } from 'react' import PropTypes from 'prop-types' +import { ReactElement, useMemo } from 'react' -import { useViews } from 'client/features/Auth' -import { useGetMarketplacesQuery } from 'client/features/OneApi/marketplace' - +import { Tr } from 'client/components/HOC' +import { LinearProgressWithLabel, StatusCircle } from 'client/components/Status' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import MarketplaceColumns from 'client/components/Tables/Marketplaces/columns' import MarketplaceRow from 'client/components/Tables/Marketplaces/row' -import { RESOURCE_NAMES } from 'client/constants' +import { MARKET_THRESHOLD, RESOURCE_NAMES, T } from 'client/constants' +import { useViews } from 'client/features/Auth' +import { useGetMarketplacesQuery } from 'client/features/OneApi/marketplace' +import { getCapacityInfo, getState } from 'client/models/Datastore' const DEFAULT_DATA_CY = 'marketplaces' @@ -53,6 +56,52 @@ const MarketplacesTable = ({ filter, ...props }) => { [view] ) + const listHeader = [ + { + header: '', + id: 'status-icon', + accessor: (vm) => { + const { color: stateColor, name: stateName } = getState(vm) + + return + }, + }, + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { header: T.Owner, id: 'owner', accessor: 'UNAME' }, + { header: T.Group, id: 'group', accessor: 'GNAME' }, + { + header: T.Capacity, + id: 'capacity', + accessor: (template) => { + const capacity = useMemo(() => getCapacityInfo(template), [template]) + const { percentOfUsed, percentLabel } = capacity + + return ( + + ) + }, + }, + { + header: T.Apps, + id: 'apps', + accessor: ({ MARKETPLACEAPPS }) => + useMemo( + () => [MARKETPLACEAPPS?.ID ?? []].flat().length || 0, + [MARKETPLACEAPPS?.ID] + ), + }, + { header: T.Zone, id: 'zone', accessor: 'ZONE_ID' }, + ] + + const { component, header } = WrapperRow(MarketplaceRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={MarketplaceRow} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/Marketplaces/row.js b/src/fireedge/src/client/components/Tables/Marketplaces/row.js index 521c03ac8bd..0e3219b1db5 100644 --- a/src/fireedge/src/client/components/Tables/Marketplaces/row.js +++ b/src/fireedge/src/client/components/Tables/Marketplaces/row.js @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { memo, useMemo } from 'react' import PropTypes from 'prop-types' +import { memo, useMemo } from 'react' -import marketplaceApi from 'client/features/OneApi/marketplace' import { MarketplaceCard } from 'client/components/Cards' +import marketplaceApi from 'client/features/OneApi/marketplace' const Row = memo( - ({ original, value, ...props }) => { + ({ original, value, headerList, rowDataCy, ...props }) => { const state = marketplaceApi.endpoints.getMarketplaces.useQueryState( undefined, { @@ -42,6 +42,8 @@ Row.propTypes = { isSelected: PropTypes.bool, className: PropTypes.string, handleClick: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } Row.displayName = 'MarketplaceRow' diff --git a/src/fireedge/src/client/components/Tables/SecurityGroups/index.js b/src/fireedge/src/client/components/Tables/SecurityGroups/index.js index cacb52da777..9baf61ab242 100644 --- a/src/fireedge/src/client/components/Tables/SecurityGroups/index.js +++ b/src/fireedge/src/client/components/Tables/SecurityGroups/index.js @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useMemo, ReactElement } from 'react' - -import { useViews } from 'client/features/Auth' -import { useGetSecGroupsQuery } from 'client/features/OneApi/securityGroup' - +import MultipleTags from 'client/components/MultipleTags' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import SecurityGroupColumns from 'client/components/Tables/SecurityGroups/columns' import SecurityGroupsRow from 'client/components/Tables/SecurityGroups/row' -import { RESOURCE_NAMES } from 'client/constants' +import { RESOURCE_NAMES, T } from 'client/constants' +import { useAuth, useViews } from 'client/features/Auth' +import { useGetSecGroupsQuery } from 'client/features/OneApi/securityGroup' +import { getColorFromString, getUniqueLabels } from 'client/models/Helper' +import { ReactElement, useMemo } from 'react' const DEFAULT_DATA_CY = 'secgroup' @@ -51,6 +52,40 @@ const SecurityGroupsTable = (props) => { [view] ) + const listHeader = [ + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { header: T.Owner, id: 'owner', accessor: 'UNAME' }, + { header: T.Group, id: 'group', accessor: 'GNAME' }, + { + header: T.Labels, + id: 'labels', + accessor: ({ TEMPLATE: { LABELS } = {} }) => { + const { labels: userLabels } = useAuth() + const labels = useMemo( + () => + getUniqueLabels(LABELS).reduce((acc, label) => { + if (userLabels?.includes(label)) { + acc.push({ + text: label, + dataCy: `label-${label}`, + stateColor: getColorFromString(label), + }) + } + + return acc + }, []), + + [LABELS] + ) + + return + }, + }, + ] + + const { component, header } = WrapperRow(SecurityGroupsRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={SecurityGroupsRow} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/SecurityGroups/row.js b/src/fireedge/src/client/components/Tables/SecurityGroups/row.js index 84c45084561..fbdd1671f38 100644 --- a/src/fireedge/src/client/components/Tables/SecurityGroups/row.js +++ b/src/fireedge/src/client/components/Tables/SecurityGroups/row.js @@ -13,16 +13,16 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { memo, useMemo, useCallback } from 'react' -import PropTypes from 'prop-types' +import { SecurityGroupCard } from 'client/components/Cards' import secGroupApi, { useUpdateSecGroupMutation, } from 'client/features/OneApi/securityGroup' -import { SecurityGroupCard } from 'client/components/Cards' import { jsonToXml } from 'client/models/Helper' +import PropTypes from 'prop-types' +import { memo, useCallback, useMemo } from 'react' const Row = memo( - ({ original, value, onClickLabel, ...props }) => { + ({ original, value, onClickLabel, headerList, rowDataCy, ...props }) => { const [update] = useUpdateSecGroupMutation() const { @@ -74,6 +74,8 @@ Row.propTypes = { isSelected: PropTypes.bool, handleClick: PropTypes.func, onClickLabel: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } Row.displayName = 'SecurityGroupRow' diff --git a/src/fireedge/src/client/components/Tables/ServiceTemplates/index.js b/src/fireedge/src/client/components/Tables/ServiceTemplates/index.js index 2bd71526a34..534827535d2 100644 --- a/src/fireedge/src/client/components/Tables/ServiceTemplates/index.js +++ b/src/fireedge/src/client/components/Tables/ServiceTemplates/index.js @@ -13,17 +13,23 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useMemo, ReactElement } from 'react' import { Alert } from '@mui/material' - -import { useViews } from 'client/features/Auth' -import { useGetServiceTemplatesQuery } from 'client/features/OneApi/serviceTemplate' - +import { Translate } from 'client/components/HOC' +import MultipleTags from 'client/components/MultipleTags' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import ServiceTemplateColumns from 'client/components/Tables/ServiceTemplates/columns' import ServiceTemplateRow from 'client/components/Tables/ServiceTemplates/row' -import { Translate } from 'client/components/HOC' -import { T, RESOURCE_NAMES } from 'client/constants' +import Timer from 'client/components/Timer' +import { RESOURCE_NAMES, T } from 'client/constants' +import { useAuth, useViews } from 'client/features/Auth' +import { useGetServiceTemplatesQuery } from 'client/features/OneApi/serviceTemplate' +import { + getColorFromString, + getUniqueLabels, + timeFromMilliseconds, +} from 'client/models/Helper' +import { ReactElement, useMemo } from 'react' const DEFAULT_DATA_CY = 'service-templates' @@ -53,6 +59,49 @@ const ServiceTemplatesTable = (props) => { [view] ) + const listHeader = [ + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Owner, id: 'owner', accessor: 'UNAME' }, + { header: T.Group, id: 'group', accessor: 'GNAME' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { + header: T.StartTime, + id: 'start-time', + accessor: ({ + TEMPLATE: { BODY: { registration_time: regTime } = {} }, + }) => { + const time = useMemo(() => timeFromMilliseconds(+regTime), [regTime]) + + return + }, + }, + { + header: T.Labels, + id: 'labels', + accessor: ({ TEMPLATE: { BODY: { labels: LABELS = {} } = {} } }) => { + const { labels: userLabels } = useAuth() + const labels = useMemo( + () => + getUniqueLabels(LABELS).reduce((acc, label) => { + if (userLabels?.includes(label)) { + acc.push({ + text: label, + dataCy: `label-${label}`, + stateColor: getColorFromString(label), + }) + } + + return acc + }, []), + [LABELS] + ) + + return + }, + }, + ] + const { component, header } = WrapperRow(ServiceTemplateRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={ServiceTemplateRow} noDataMessage={ error?.status === 500 && ( @@ -70,6 +118,8 @@ const ServiceTemplatesTable = (props) => { ) } + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/ServiceTemplates/row.js b/src/fireedge/src/client/components/Tables/ServiceTemplates/row.js index ee6a9a0e66f..90cc4646a6d 100644 --- a/src/fireedge/src/client/components/Tables/ServiceTemplates/row.js +++ b/src/fireedge/src/client/components/Tables/ServiceTemplates/row.js @@ -13,16 +13,16 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { memo, useMemo, useCallback } from 'react' import PropTypes from 'prop-types' +import { memo, useCallback, useMemo } from 'react' +import { ServiceTemplateCard } from 'client/components/Cards' import serviceTemplateApi, { useUpdateServiceTemplateMutation, } from 'client/features/OneApi/serviceTemplate' -import { ServiceTemplateCard } from 'client/components/Cards' const Row = memo( - ({ original, value, ...props }) => { + ({ original, value, headerList, rowDataCy, ...props }) => { const [update] = useUpdateServiceTemplateMutation() const state = @@ -63,6 +63,8 @@ Row.propTypes = { isSelected: PropTypes.bool, className: PropTypes.string, handleClick: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } Row.displayName = 'ServiceTemplateRow' diff --git a/src/fireedge/src/client/components/Tables/Services/index.js b/src/fireedge/src/client/components/Tables/Services/index.js index 2cbe8e49e95..aba408ce484 100644 --- a/src/fireedge/src/client/components/Tables/Services/index.js +++ b/src/fireedge/src/client/components/Tables/Services/index.js @@ -13,17 +13,20 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useMemo, ReactElement } from 'react' import { Alert } from '@mui/material' - -import { useViews } from 'client/features/Auth' -import { useGetServicesQuery } from 'client/features/OneApi/service' - +import { Translate } from 'client/components/HOC' +import { StatusCircle } from 'client/components/Status' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import ServiceColumns from 'client/components/Tables/Services/columns' import ServiceRow from 'client/components/Tables/Services/row' -import { Translate } from 'client/components/HOC' -import { T, RESOURCE_NAMES } from 'client/constants' +import Timer from 'client/components/Timer' +import { RESOURCE_NAMES, T } from 'client/constants' +import { useViews } from 'client/features/Auth' +import { useGetServicesQuery } from 'client/features/OneApi/service' +import { timeFromMilliseconds } from 'client/models/Helper' +import { getState } from 'client/models/Service' +import { ReactElement, useMemo } from 'react' const DEFAULT_DATA_CY = 'services' @@ -48,6 +51,35 @@ const ServicesTable = (props) => { [view] ) + const listHeader = [ + { + header: '', + id: 'status-icon', + accessor: (service) => { + const { color: stateColor, name: stateName } = getState(service) + + return + }, + }, + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Owner, id: 'owner', accessor: 'UNAME' }, + { header: T.Group, id: 'group', accessor: 'GNAME' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { + header: T.StartTime, + id: 'start-time', + accessor: ({ TEMPLATE: { BODY: { start_time: startTime } = {} } }) => { + const time = useMemo( + () => timeFromMilliseconds(+startTime), + [startTime] + ) + + return + }, + }, + ] + const { component, header } = WrapperRow(ServiceRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={ServiceRow} noDataMessage={ error?.status === 500 && ( @@ -65,6 +96,8 @@ const ServicesTable = (props) => { ) } + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/Services/row.js b/src/fireedge/src/client/components/Tables/Services/row.js index f9fcd2e81d3..b865132287d 100644 --- a/src/fireedge/src/client/components/Tables/Services/row.js +++ b/src/fireedge/src/client/components/Tables/Services/row.js @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { memo, useMemo } from 'react' import PropTypes from 'prop-types' +import { memo, useMemo } from 'react' -import serviceApi from 'client/features/OneApi/service' import { ServiceCard } from 'client/components/Cards' +import serviceApi from 'client/features/OneApi/service' const Row = memo( - ({ original, value, ...props }) => { + ({ original, value, headerList, rowDataCy, ...props }) => { const state = serviceApi.endpoints.getServices.useQueryState(undefined, { selectFromResult: ({ data = [] }) => data.find((service) => +service.ID === +original.ID), @@ -39,6 +39,8 @@ Row.propTypes = { isSelected: PropTypes.bool, className: PropTypes.string, handleClick: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } Row.displayName = 'ServiceRow' diff --git a/src/fireedge/src/client/components/Tables/Support/actions.js b/src/fireedge/src/client/components/Tables/Support/actions.js index bd16b118d29..01f02c8a255 100644 --- a/src/fireedge/src/client/components/Tables/Support/actions.js +++ b/src/fireedge/src/client/components/Tables/Support/actions.js @@ -63,12 +63,14 @@ const Actions = () => { SUBJECT: subject, BODY: body, SEVERITY: severity, + ATTACHMENTS: attachments, } = formData.template await createTicket({ subject, body, version, severity, + attachments, }) }, }, diff --git a/src/fireedge/src/client/components/Tables/Support/index.js b/src/fireedge/src/client/components/Tables/Support/index.js index 7e43257a19e..3c9b683e140 100644 --- a/src/fireedge/src/client/components/Tables/Support/index.js +++ b/src/fireedge/src/client/components/Tables/Support/index.js @@ -13,15 +13,14 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useMemo, ReactElement } from 'react' - -import { useViews } from 'client/features/Auth' -import { useGetTicketsQuery } from 'client/features/OneApi/support' - import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import SupportColumns from 'client/components/Tables/Support/columns' import SupportRow from 'client/components/Tables/Support/row' -import { RESOURCE_NAMES } from 'client/constants' +import { RESOURCE_NAMES, T } from 'client/constants' +import { useViews } from 'client/features/Auth' +import { useGetTicketsQuery } from 'client/features/OneApi/support' +import { ReactElement, useMemo } from 'react' const DEFAULT_DATA_CY = 'support' @@ -62,6 +61,13 @@ const SupportTable = (props) => { [view] ) + const listHeader = [ + { header: T.ID, id: 'id', accessor: 'id' }, + { header: T.Subject, id: 'subject', accessor: 'subject' }, + { header: T.Status, id: 'status', accessor: 'status' }, + ] + const { component, header } = WrapperRow(SupportRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.id)} - RowComponent={SupportRow} initialState={initialState} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/Support/row.js b/src/fireedge/src/client/components/Tables/Support/row.js index 0bd6e93cb87..3aea1f1bd47 100644 --- a/src/fireedge/src/client/components/Tables/Support/row.js +++ b/src/fireedge/src/client/components/Tables/Support/row.js @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { memo } from 'react' import PropTypes from 'prop-types' +import { memo } from 'react' import { SupportCard } from 'client/components/Cards' const Row = memo( - ({ original, value, ...props }) => ( + ({ original, value, headerList, rowDataCy, ...props }) => ( ), (prev, next) => prev.className === next.className @@ -31,6 +31,8 @@ Row.propTypes = { isSelected: PropTypes.bool, className: PropTypes.string, onClick: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } Row.displayName = 'SupportRow' diff --git a/src/fireedge/src/client/components/Tables/Users/index.js b/src/fireedge/src/client/components/Tables/Users/index.js index dd1c8948c8d..6d1e9242e6e 100644 --- a/src/fireedge/src/client/components/Tables/Users/index.js +++ b/src/fireedge/src/client/components/Tables/Users/index.js @@ -13,15 +13,17 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useMemo, ReactElement } from 'react' - -import { useViews } from 'client/features/Auth' -import { useGetUsersQuery } from 'client/features/OneApi/user' +import { ReactElement, useMemo } from 'react' +import { LinearProgressWithTooltip } from 'client/components/Status' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import UserColumns from 'client/components/Tables/Users/columns' import UserRow from 'client/components/Tables/Users/row' -import { RESOURCE_NAMES } from 'client/constants' +import { RESOURCE_NAMES, T } from 'client/constants' +import { useViews } from 'client/features/Auth' +import { useGetUsersQuery } from 'client/features/OneApi/user' +import { getQuotaUsage } from 'client/models/User' const DEFAULT_DATA_CY = 'users' @@ -52,6 +54,76 @@ const UsersTable = (props) => { [view] ) + const listHeader = [ + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { header: T.Group, id: 'group', accessor: 'GNAME' }, + { + header: T.Enabled, + id: 'enabled', + accessor: ({ ENABLED }) => (+ENABLED ? T.Yes : T.No), + }, + { header: T.AuthDriver, id: 'auth-driver', accessor: 'AUTH_DRIVER' }, + { + header: T.VMs, + id: 'vms', + accessor: ({ VM_QUOTA }) => { + const vmQuotaUsage = useMemo( + () => getQuotaUsage('VM', VM_QUOTA), + [VM_QUOTA] + ) + + return ( + + ) + }, + }, + { + header: T.Datastores, + id: 'datastores', + accessor: ({ DATASTORE_QUOTA }) => { + const datastoreQuotaUsage = useMemo( + () => getQuotaUsage('DATASTORE', DATASTORE_QUOTA), + [DATASTORE_QUOTA] + ) + + return ( + + ) + }, + }, + { + header: T.Networks, + id: 'networks', + accessor: ({ NETWORK_QUOTA }) => { + const networkQuotaUsage = useMemo( + () => getQuotaUsage('NETWORK', NETWORK_QUOTA), + [NETWORK_QUOTA] + ) + + return ( + + ) + }, + }, + ] + const { component, header } = WrapperRow(UserRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={UserRow} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/Users/row.js b/src/fireedge/src/client/components/Tables/Users/row.js index b743efd185f..1d7f7bd6899 100644 --- a/src/fireedge/src/client/components/Tables/Users/row.js +++ b/src/fireedge/src/client/components/Tables/Users/row.js @@ -18,7 +18,7 @@ import PropTypes from 'prop-types' import { UserCard } from 'client/components/Cards' -const Row = ({ original, value, ...props }) => ( +const Row = ({ original, value, headerList, rowDataCy, ...props }) => ( ) @@ -27,6 +27,8 @@ Row.propTypes = { value: PropTypes.object, isSelected: PropTypes.bool, handleClick: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } export default Row diff --git a/src/fireedge/src/client/components/Tables/VNetworkTemplates/index.js b/src/fireedge/src/client/components/Tables/VNetworkTemplates/index.js index 10962361e8b..85c09697664 100644 --- a/src/fireedge/src/client/components/Tables/VNetworkTemplates/index.js +++ b/src/fireedge/src/client/components/Tables/VNetworkTemplates/index.js @@ -14,15 +14,15 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useMemo, ReactElement } from 'react' - -import { useViews } from 'client/features/Auth' -import { useGetVNTemplatesQuery } from 'client/features/OneApi/networkTemplate' +import { ReactElement, useMemo } from 'react' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import VNetworkTemplateColumns from 'client/components/Tables/VNetworkTemplates/columns' import VNetworkTemplateRow from 'client/components/Tables/VNetworkTemplates/row' -import { RESOURCE_NAMES } from 'client/constants' +import { RESOURCE_NAMES, T } from 'client/constants' +import { useViews } from 'client/features/Auth' +import { useGetVNTemplatesQuery } from 'client/features/OneApi/networkTemplate' const DEFAULT_DATA_CY = 'vnet-templates' @@ -47,6 +47,15 @@ const VNetworkTemplatesTable = (props) => { [view] ) + const listHeader = [ + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { header: T.Owner, id: 'owner', accessor: 'UNAME' }, + { header: T.Group, id: 'group', accessor: 'GNAME' }, + ] + + const { component, header } = WrapperRow(VNetworkTemplateRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={VNetworkTemplateRow} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/VNetworkTemplates/row.js b/src/fireedge/src/client/components/Tables/VNetworkTemplates/row.js index 874fe3ee5be..e30b7698ce7 100644 --- a/src/fireedge/src/client/components/Tables/VNetworkTemplates/row.js +++ b/src/fireedge/src/client/components/Tables/VNetworkTemplates/row.js @@ -19,13 +19,13 @@ import PropTypes from 'prop-types' import { Typography } from '@mui/material' import { Cloud, Group, Lock, User } from 'iconoir-react' -import { rowStyles } from 'client/components/Tables/styles' import { Tr } from 'client/components/HOC' +import { rowStyles } from 'client/components/Tables/styles' import { T } from 'client/constants' import * as Helper from 'client/models/Helper' -const Row = ({ original, value, ...props }) => { +const Row = ({ original, value, headerList, rowDataCy, ...props }) => { const classes = rowStyles() const { ID, NAME, UNAME, GNAME, LOCK, REGTIME, PROVISION_ID } = value @@ -68,6 +68,8 @@ Row.propTypes = { value: PropTypes.object, isSelected: PropTypes.bool, handleClick: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } export default Row diff --git a/src/fireedge/src/client/components/Tables/VNetworks/columns.js b/src/fireedge/src/client/components/Tables/VNetworks/columns.js index 6526f29a7db..7620727b549 100644 --- a/src/fireedge/src/client/components/Tables/VNetworks/columns.js +++ b/src/fireedge/src/client/components/Tables/VNetworks/columns.js @@ -15,7 +15,11 @@ * ------------------------------------------------------------------------- */ import { Column } from 'react-table' -import { getState, getVNManager } from 'client/models/VirtualNetwork' +import { + getState, + getVNManager, + getVirtualNetLocked, +} from 'client/models/VirtualNetwork' import { T } from 'client/constants' /** @type {Column[]} Virtual Network columns */ @@ -25,7 +29,12 @@ const COLUMNS = [ { Header: T.State, id: 'state', accessor: (row) => getState(row)?.name }, { Header: T.Owner, id: 'owner', accessor: 'UNAME' }, { Header: T.Group, id: 'group', accessor: 'GNAME' }, - { Header: T.Locked, id: 'locked', accessor: 'LOCK' }, + { + Header: T.Locked, + id: 'locked', + accessor: getVirtualNetLocked, + translation: { true: T.Locked, false: T.Unlocked }, + }, { Header: T.Driver, id: 'vn_mad', accessor: getVNManager }, { Header: T.Label, diff --git a/src/fireedge/src/client/components/Tables/VNetworks/index.js b/src/fireedge/src/client/components/Tables/VNetworks/index.js index cb41d7e43cb..b881d906676 100644 --- a/src/fireedge/src/client/components/Tables/VNetworks/index.js +++ b/src/fireedge/src/client/components/Tables/VNetworks/index.js @@ -13,19 +13,23 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { ReactElement, useCallback, useEffect, useMemo, useState } from 'react' - -import { useViews } from 'client/features/Auth' -import { useGetVNetworksQuery } from 'client/features/OneApi/network' - +import { Tr } from 'client/components/HOC' +import MultipleTags from 'client/components/MultipleTags' +import { LinearProgressWithLabel, StatusCircle } from 'client/components/Status' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' import { areArraysEqual, sortStateTables, } from 'client/components/Tables/Enhanced/Utils/DataTableUtils' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import VNetworkColumns from 'client/components/Tables/VNetworks/columns' import VNetworkRow from 'client/components/Tables/VNetworks/row' -import { RESOURCE_NAMES } from 'client/constants' +import { RESOURCE_NAMES, T, VNET_THRESHOLD } from 'client/constants' +import { useAuth, useViews } from 'client/features/Auth' +import { useGetVNetworksQuery } from 'client/features/OneApi/network' +import { getColorFromString, getUniqueLabels } from 'client/models/Helper' +import { getLeasesInfo, getState } from 'client/models/VirtualNetwork' +import { ReactElement, useCallback, useEffect, useMemo, useState } from 'react' import { useFormContext } from 'react-hook-form' const DEFAULT_DATA_CY = 'vnets' @@ -137,6 +141,79 @@ const VNetworksTable = (props) => { }) useEffect(() => refetch(), []) + const listHeader = [ + { + header: '', + id: 'status-icon', + accessor: (template) => { + const { color: stateColor, name: stateName } = getState(template) + + return + }, + }, + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { header: T.Owner, id: 'owner', accessor: 'UNAME' }, + { header: T.Group, id: 'group', accessor: 'GNAME' }, + { + header: T.Clusters, + id: 'clusters', + accessor: ({ CLUSTERS }) => { + const clusters = useMemo( + () => [CLUSTERS?.ID ?? []].flat(), + [CLUSTERS?.ID] + ) + + return clusters.length && clusters[0] + }, + }, + { + header: T.Leases, + id: 'leases', + accessor: (template) => { + const leasesInfo = useMemo(() => getLeasesInfo(template), [template]) + const { percentOfUsed, percentLabel } = leasesInfo + + return ( + + ) + }, + }, + { + header: T.Labels, + id: 'labels', + accessor: ({ TEMPLATE: { LABELS } = {} }) => { + const { labels: userLabels } = useAuth() + const labels = useMemo( + () => + getUniqueLabels(LABELS).reduce((acc, label) => { + if (userLabels?.includes(label)) { + acc.push({ + text: label, + dataCy: `label-${label}`, + stateColor: getColorFromString(label), + }) + } + + return acc + }, []), + + [LABELS] + ) + + return + }, + }, + ] + + const { component, header } = WrapperRow(VNetworkRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={VNetworkRow} dataDepend={values} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/VNetworks/row.js b/src/fireedge/src/client/components/Tables/VNetworks/row.js index 06a1d5b2ab7..e0207c19c7a 100644 --- a/src/fireedge/src/client/components/Tables/VNetworks/row.js +++ b/src/fireedge/src/client/components/Tables/VNetworks/row.js @@ -13,21 +13,21 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { memo, useMemo, useCallback } from 'react' -import PropTypes from 'prop-types' import { jsonToXml } from 'client/models/Helper' +import PropTypes from 'prop-types' +import { memo, useCallback, useMemo } from 'react' -import api, { useUpdateVNetMutation } from 'client/features/OneApi/network' import { NetworkCard } from 'client/components/Cards' +import api, { useUpdateVNetMutation } from 'client/features/OneApi/network' const Row = memo( - ({ original, value, onClickLabel, ...props }) => { + ({ original, value, onClickLabel, headerList, rowDataCy, ...props }) => { const [update] = useUpdateVNetMutation() const { data: vnetworks, error, isLoading, - } = api.endpoints.getVNetworks.useQuery(undefined) + } = api.endpoints.getVNetworks.useQueryState(undefined) const vnetwork = useMemo( () => vnetworks?.find((vnet) => +vnet.ID === +original.ID) ?? original, @@ -69,6 +69,8 @@ Row.propTypes = { className: PropTypes.string, onClick: PropTypes.func, onClickLabel: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } Row.displayName = 'VirtualNetworkRow' diff --git a/src/fireedge/src/client/components/Tables/VRouterTemplates/index.js b/src/fireedge/src/client/components/Tables/VRouterTemplates/index.js index 043c6d27fa0..b15ae45bd96 100644 --- a/src/fireedge/src/client/components/Tables/VRouterTemplates/index.js +++ b/src/fireedge/src/client/components/Tables/VRouterTemplates/index.js @@ -29,6 +29,7 @@ import { BoxIso as DownloadIcon } from 'iconoir-react' import { Tr, Translate } from 'client/components/HOC' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import VRouterTemplateColumns from 'client/components/Tables/VRouterTemplates/columns' import VRouterTemplateRow from 'client/components/Tables/VRouterTemplates/row' import { useStyles } from 'client/components/Tabs/EmptyTab/styles' @@ -37,6 +38,7 @@ import { useExportAppMutation, useLazyGetMarketplaceAppsQuery, } from 'client/features/OneApi/marketplaceApp' +import { timeToString } from 'client/models/Helper' import InfoEmpty from 'iconoir-react/dist/InfoEmpty' import { debounce } from 'lodash' @@ -262,6 +264,20 @@ const VRouterTemplatesTable = (props) => { ) + const listHeader = [ + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { header: T.Owner, id: 'owner', accessor: 'UNAME' }, + { header: T.Group, id: 'group', accessor: 'GNAME' }, + { + header: T.RegistrationTime, + id: 'registration-time', + accessor: (template) => timeToString(template.REGTIME), + }, + ] + + const { component, header } = WrapperRow(VRouterTemplateRow) + return ( { refetch={refetch} isLoading={loading} getRowId={(row) => String(row.ID)} - RowComponent={VRouterTemplateRow} noDataCustomRenderer={} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/VRouterTemplates/row.js b/src/fireedge/src/client/components/Tables/VRouterTemplates/row.js index 0eb6b727573..15fdcfbac23 100644 --- a/src/fireedge/src/client/components/Tables/VRouterTemplates/row.js +++ b/src/fireedge/src/client/components/Tables/VRouterTemplates/row.js @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { memo, useMemo, useCallback } from 'react' import PropTypes from 'prop-types' +import { memo, useCallback, useMemo } from 'react' +import { VmTemplateCard as VRouterTemplateCard } from 'client/components/Cards' import vrouterTemplatesApi, { useUpdateTemplateMutation, } from 'client/features/OneApi/vmTemplate' -import { VmTemplateCard as VRouterTemplateCard } from 'client/components/Cards' import { jsonToXml } from 'client/models/Helper' const Row = memo( - ({ original, value, onClickLabel, ...props }) => { + ({ original, value, onClickLabel, headerList, rowDataCy, ...props }) => { const [update] = useUpdateTemplateMutation() const state = @@ -68,6 +68,8 @@ Row.propTypes = { className: PropTypes.string, onClick: PropTypes.func, onClickLabel: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } Row.displayName = 'VRouterTemplateRow' diff --git a/src/fireedge/src/client/components/Tables/VRouters/index.js b/src/fireedge/src/client/components/Tables/VRouters/index.js index d012c0c3956..e0e60a5851c 100644 --- a/src/fireedge/src/client/components/Tables/VRouters/index.js +++ b/src/fireedge/src/client/components/Tables/VRouters/index.js @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useMemo, ReactElement } from 'react' - -import { useViews } from 'client/features/Auth' -import { useGetVRoutersQuery } from 'client/features/OneApi/vrouter' +import { ReactElement, useMemo } from 'react' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import VRouterColumns from 'client/components/Tables/VRouters/columns' import VRouterRow from 'client/components/Tables/VRouters/row' -import { RESOURCE_NAMES } from 'client/constants' +import { RESOURCE_NAMES, T } from 'client/constants' +import { useViews } from 'client/features/Auth' +import { useGetVRoutersQuery } from 'client/features/OneApi/vrouter' const DEFAULT_DATA_CY = 'vrouters' @@ -51,6 +51,15 @@ const VRoutersTable = (props) => { [view] ) + const listHeader = [ + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { header: T.Owner, id: 'owner', accessor: 'UNAME' }, + { header: T.Group, id: 'group', accessor: 'GNAME' }, + ] + + const { component, header } = WrapperRow(VRouterRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={VRouterRow} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/VRouters/row.js b/src/fireedge/src/client/components/Tables/VRouters/row.js index 910dbd94af0..2da2a61d7a4 100644 --- a/src/fireedge/src/client/components/Tables/VRouters/row.js +++ b/src/fireedge/src/client/components/Tables/VRouters/row.js @@ -16,15 +16,15 @@ /* eslint-disable jsdoc/require-jsdoc */ import PropTypes from 'prop-types' -import { User, Group, EmptyPage, ModernTv } from 'iconoir-react' import { Typography } from '@mui/material' +import { EmptyPage, Group, ModernTv, User } from 'iconoir-react' import { rowStyles } from 'client/components/Tables/styles' import { Tr } from 'client/components/HOC' import { T } from 'client/constants' -const Row = ({ original, value, ...props }) => { +const Row = ({ original, value, headerList, rowDataCy, ...props }) => { const classes = rowStyles() const { ID, NAME, UNAME, GNAME, VMS, TEMPLATE_ID } = value @@ -65,6 +65,8 @@ Row.propTypes = { value: PropTypes.object, isSelected: PropTypes.bool, handleClick: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } export default Row diff --git a/src/fireedge/src/client/components/Tables/VirtualDataCenters/index.js b/src/fireedge/src/client/components/Tables/VirtualDataCenters/index.js index 9ef73cfc70f..a22d760c4d0 100644 --- a/src/fireedge/src/client/components/Tables/VirtualDataCenters/index.js +++ b/src/fireedge/src/client/components/Tables/VirtualDataCenters/index.js @@ -13,18 +13,22 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useMemo, ReactElement } from 'react' - -import { useViews } from 'client/features/Auth' -import { useGetVDCsQuery } from 'client/features/OneApi/vdc' - +import MultipleTags from 'client/components/MultipleTags' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import VDCColumns from 'client/components/Tables/VirtualDataCenters/columns' import VDCRow from 'client/components/Tables/VirtualDataCenters/row' -import { RESOURCE_NAMES } from 'client/constants' +import { ALL_SELECTED, RESOURCE_NAMES, T } from 'client/constants' +import { useAuth, useViews } from 'client/features/Auth' +import { useGetVDCsQuery } from 'client/features/OneApi/vdc' +import { getColorFromString, getUniqueLabels } from 'client/models/Helper' +import { ReactElement, useMemo } from 'react' const DEFAULT_DATA_CY = 'vdcs' +const isAllSelected = (resourceArray) => + resourceArray.length === 1 && resourceArray[0] === ALL_SELECTED + /** * @param {object} props - Props * @returns {ReactElement} VM Templates table @@ -46,6 +50,99 @@ const VDCsTable = (props) => { [view] ) + const listHeader = [ + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { + header: T.Groups, + id: 'group', + accessor: ({ GROUPS }) => + useMemo(() => { + const { ID: groupsIds = [] } = GROUPS + const groupsArray = Array.isArray(groupsIds) ? groupsIds : [groupsIds] + + return groupsArray.length + }, [GROUPS.ID]), + }, + { + header: T.Clusters, + id: 'clusters', + accessor: ({ CLUSTERS }) => + useMemo(() => { + const { CLUSTER: clustersInfo = [] } = CLUSTERS + const clustersArray = ( + Array.isArray(clustersInfo) ? clustersInfo : [clustersInfo] + ).map((cluster) => cluster.CLUSTER_ID) + + return isAllSelected(clustersArray) ? T.All : clustersArray.length + }, [CLUSTERS.CLUSTER]), + }, + { + header: T.Hosts, + id: 'hosts', + accessor: ({ HOSTS }) => + useMemo(() => { + const { HOST: hostsInfo = [] } = HOSTS + const hostsArray = ( + Array.isArray(hostsInfo) ? hostsInfo : [hostsInfo] + ).map((host) => host.HOST_ID) + + return isAllSelected(hostsArray) ? T.All : hostsArray.length + }, [HOSTS.HOST]), + }, + { + header: T.Vnets, + id: 'vnets', + accessor: ({ VNETS }) => + useMemo(() => { + const { VNET: vnetsInfo = [] } = VNETS + const vnetsArray = ( + Array.isArray(vnetsInfo) ? vnetsInfo : [vnetsInfo] + ).map((vnet) => vnet.VNET_ID) + + return isAllSelected(vnetsArray) ? T.All : vnetsArray.length + }, [VNETS.VNET]), + }, + { + header: T.Datastores, + id: 'datastores', + accessor: ({ DATASTORES }) => + useMemo(() => { + const { DATASTORE: datastoresInfo = [] } = DATASTORES + const datastoresArray = ( + Array.isArray(datastoresInfo) ? datastoresInfo : [datastoresInfo] + ).map((ds) => ds.DATASTORE_ID) + + return isAllSelected(datastoresArray) ? T.All : datastoresArray.length + }, [DATASTORES.DATASTORE]), + }, + { + header: T.Labels, + id: 'labels', + accessor: ({ TEMPLATE: { LABELS } }) => { + const { labels: userLabels } = useAuth() + const labels = useMemo( + () => + getUniqueLabels(LABELS).reduce((acc, label) => { + if (userLabels?.includes(label)) { + acc.push({ + text: label, + dataCy: `label-${label}`, + stateColor: getColorFromString(label), + }) + } + + return acc + }, []), + [LABELS] + ) + + return + }, + }, + ] + const { component, header } = WrapperRow(VDCRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={VDCRow} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/VirtualDataCenters/row.js b/src/fireedge/src/client/components/Tables/VirtualDataCenters/row.js index 27b2082cac0..842fd1d1cbd 100644 --- a/src/fireedge/src/client/components/Tables/VirtualDataCenters/row.js +++ b/src/fireedge/src/client/components/Tables/VirtualDataCenters/row.js @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { memo, useMemo, useCallback } from 'react' import PropTypes from 'prop-types' +import { memo, useCallback, useMemo } from 'react' -import vdcApi, { useUpdateVDCMutation } from 'client/features/OneApi/vdc' import { VirtualDataCenterCard } from 'client/components/Cards' +import vdcApi, { useUpdateVDCMutation } from 'client/features/OneApi/vdc' import { jsonToXml } from 'client/models/Helper' const Row = memo( - ({ original, value, onClickLabel, ...props }) => { + ({ original, value, onClickLabel, headerList, rowDataCy, ...props }) => { const [update] = useUpdateVDCMutation() const state = vdcApi.endpoints.getVDCs.useQueryState(undefined, { @@ -62,6 +62,8 @@ Row.propTypes = { className: PropTypes.string, onClick: PropTypes.func, onClickLabel: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } Row.displayName = 'VirtualDataCenterRow' diff --git a/src/fireedge/src/client/components/Tables/VmDisks/row.js b/src/fireedge/src/client/components/Tables/VmDisks/row.js index 437116b2e04..dcc77fedbfa 100644 --- a/src/fireedge/src/client/components/Tables/VmDisks/row.js +++ b/src/fireedge/src/client/components/Tables/VmDisks/row.js @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { memo, useMemo } from 'react' import PropTypes from 'prop-types' +import { memo, useMemo } from 'react' import { DiskCard } from 'client/components/Cards' const Row = memo( - ({ original, value, ...props }) => { + ({ original, value, headerList, rowDataCy, ...props }) => { const memoDisk = useMemo(() => original, [original]) return @@ -33,6 +33,8 @@ Row.propTypes = { isSelected: PropTypes.bool, className: PropTypes.string, handleClick: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } Row.displayName = 'VmDiskRow' diff --git a/src/fireedge/src/client/components/Tables/VmGroups/index.js b/src/fireedge/src/client/components/Tables/VmGroups/index.js index 5cf07b72d88..3f4b78777d7 100644 --- a/src/fireedge/src/client/components/Tables/VmGroups/index.js +++ b/src/fireedge/src/client/components/Tables/VmGroups/index.js @@ -15,13 +15,13 @@ * ------------------------------------------------------------------------- */ import { ReactElement, useEffect, useMemo } from 'react' -import { useViews } from 'client/features/Auth' -import { useGetVMGroupsQuery } from 'client/features/OneApi/vmGroup' - import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import VmGroupColumns from 'client/components/Tables/VmGroups/columns' import VmGroupRow from 'client/components/Tables/VmGroups/row' -import { RESOURCE_NAMES } from 'client/constants' +import { RESOURCE_NAMES, T } from 'client/constants' +import { useViews } from 'client/features/Auth' +import { useGetVMGroupsQuery } from 'client/features/OneApi/vmGroup' const DEFAULT_DATA_CY = 'vmgroups' @@ -48,6 +48,15 @@ const VmGroupsTable = (props) => { useEffect(() => refetch(), []) + const listHeader = [ + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { header: T.Owner, id: 'owner', accessor: 'UNAME' }, + { header: T.Group, id: 'group', accessor: 'GNAME' }, + ] + + const { component, header } = WrapperRow(VmGroupRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={VmGroupRow} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/VmGroups/row.js b/src/fireedge/src/client/components/Tables/VmGroups/row.js index 1c7fc479ac0..871758353e8 100644 --- a/src/fireedge/src/client/components/Tables/VmGroups/row.js +++ b/src/fireedge/src/client/components/Tables/VmGroups/row.js @@ -18,7 +18,7 @@ import PropTypes from 'prop-types' import { VmGroupCard } from 'client/components/Cards' -const Row = ({ original, value, ...props }) => ( +const Row = ({ original, value, headerList, rowDataCy, ...props }) => ( ) @@ -27,6 +27,8 @@ Row.propTypes = { value: PropTypes.object, isSelected: PropTypes.bool, handleClick: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } export default Row diff --git a/src/fireedge/src/client/components/Tables/VmTemplates/columns.js b/src/fireedge/src/client/components/Tables/VmTemplates/columns.js index 3ef234420ac..97f043a3e77 100644 --- a/src/fireedge/src/client/components/Tables/VmTemplates/columns.js +++ b/src/fireedge/src/client/components/Tables/VmTemplates/columns.js @@ -16,6 +16,7 @@ import { Column } from 'react-table' import { T } from 'client/constants' +import { getVMTemplateLocked } from 'client/models/VirtualMachineTemplate' /** @type {Column[]} VM Template columns */ const COLUMNS = [ @@ -30,7 +31,12 @@ const COLUMNS = [ accessor: 'TEMPLATE.LABELS', filter: 'includesSome', }, - { Header: T.Locked, id: 'locked', accessor: 'LOCK' }, + { + Header: T.Locked, + id: 'locked', + accessor: getVMTemplateLocked, + translation: { true: T.Locked, false: T.Unlocked }, + }, { Header: T.Logo, id: 'logo', accessor: 'TEMPLATE.LOGO' }, { Header: T.VirtualRouter, id: 'vrouter', accessor: 'TEMPLATE.VROUTER' }, ] diff --git a/src/fireedge/src/client/components/Tables/VmTemplates/index.js b/src/fireedge/src/client/components/Tables/VmTemplates/index.js index fc62d9ce24a..d8683842fd2 100644 --- a/src/fireedge/src/client/components/Tables/VmTemplates/index.js +++ b/src/fireedge/src/client/components/Tables/VmTemplates/index.js @@ -13,15 +13,20 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { ReactElement, useEffect, useMemo } from 'react' - -import { useViews } from 'client/features/Auth' -import { useGetTemplatesQuery } from 'client/features/OneApi/vmTemplate' - +import MultipleTags from 'client/components/MultipleTags' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import VmTemplateColumns from 'client/components/Tables/VmTemplates/columns' import VmTemplateRow from 'client/components/Tables/VmTemplates/row' -import { RESOURCE_NAMES } from 'client/constants' +import { RESOURCE_NAMES, T } from 'client/constants' +import { useAuth, useViews } from 'client/features/Auth' +import { useGetTemplatesQuery } from 'client/features/OneApi/vmTemplate' +import { + getColorFromString, + getUniqueLabels, + timeToString, +} from 'client/models/Helper' +import { ReactElement, useEffect, useMemo } from 'react' const DEFAULT_DATA_CY = 'vm-templates' @@ -47,6 +52,45 @@ const VmTemplatesTable = (props) => { ) useEffect(() => refetch(), []) + const listHeader = [ + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { header: T.Owner, id: 'owner', accessor: 'UNAME' }, + { header: T.Group, id: 'group', accessor: 'GNAME' }, + { + header: T.RegistrationTime, + id: 'registration-time', + accessor: (vm) => timeToString(vm.REGTIME), + }, + { + header: T.Labels, + id: 'labels', + accessor: ({ TEMPLATE: { LABELS } = {} }) => { + const { labels: userLabels } = useAuth() + const labels = useMemo( + () => + getUniqueLabels(LABELS).reduce((acc, label) => { + if (userLabels?.includes(label)) { + acc.push({ + text: label, + dataCy: `label-${label}`, + stateColor: getColorFromString(label), + }) + } + + return acc + }, []), + + [LABELS] + ) + + return + }, + }, + ] + + const { component, header } = WrapperRow(VmTemplateRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={VmTemplateRow} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/VmTemplates/row.js b/src/fireedge/src/client/components/Tables/VmTemplates/row.js index 4bf9cf52dd4..faff40a1e82 100644 --- a/src/fireedge/src/client/components/Tables/VmTemplates/row.js +++ b/src/fireedge/src/client/components/Tables/VmTemplates/row.js @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { memo, useMemo, useCallback } from 'react' import PropTypes from 'prop-types' +import { memo, useCallback, useMemo } from 'react' +import { VmTemplateCard } from 'client/components/Cards' import vmTemplateApi, { useUpdateTemplateMutation, } from 'client/features/OneApi/vmTemplate' -import { VmTemplateCard } from 'client/components/Cards' import { jsonToXml } from 'client/models/Helper' const Row = memo( - ({ original, value, onClickLabel, ...props }) => { + ({ original, value, onClickLabel, headerList, rowDataCy, ...props }) => { const [update] = useUpdateTemplateMutation() const state = vmTemplateApi.endpoints.getTemplates.useQueryState( @@ -67,6 +67,8 @@ Row.propTypes = { className: PropTypes.string, onClick: PropTypes.func, onClickLabel: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } Row.displayName = 'VmTemplateRow' diff --git a/src/fireedge/src/client/components/Tables/Vms/columns.js b/src/fireedge/src/client/components/Tables/Vms/columns.js index 79952e2cfb8..ea5a0ab9c47 100644 --- a/src/fireedge/src/client/components/Tables/Vms/columns.js +++ b/src/fireedge/src/client/components/Tables/Vms/columns.js @@ -19,8 +19,10 @@ import { T } from 'client/constants' import { getIps, getLastHistory, + getVMLocked, getState, getType, + getVmHostname, } from 'client/models/VirtualMachine' /** @type {Column[]} VM columns */ @@ -35,7 +37,12 @@ const COLUMNS = [ { Header: T.Owner, id: 'owner', accessor: 'UNAME' }, { Header: T.Group, id: 'group', accessor: 'GNAME' }, { Header: T.StartTime, id: 'time', accessor: 'STIME' }, - { Header: T.Locked, id: 'locked', accessor: 'LOCK' }, + { + Header: T.Locked, + id: 'locked', + accessor: getVMLocked, + translation: { true: T.Locked, false: T.Unlocked }, + }, { Header: T.Label, id: 'label', @@ -55,6 +62,11 @@ const COLUMNS = [ }, { Header: T.Hostname, + id: 'vmhostname', + accessor: (row) => getVmHostname(row)?.pop() ?? '', + }, + { + Header: T.Host, id: 'hostname', accessor: (row) => getLastHistory(row)?.HOSTNAME, }, diff --git a/src/fireedge/src/client/components/Tables/Vms/index.js b/src/fireedge/src/client/components/Tables/Vms/index.js index 77e24c0182a..ce9a14e837b 100644 --- a/src/fireedge/src/client/components/Tables/Vms/index.js +++ b/src/fireedge/src/client/components/Tables/Vms/index.js @@ -15,21 +15,38 @@ * ------------------------------------------------------------------------- */ import { ReactElement, useMemo } from 'react' -import { useViews } from 'client/features/Auth' -import { useGetVmsQuery } from 'client/features/OneApi/vm' - +import { ConsoleButton } from 'client/components/Buttons' +import MultipleTags from 'client/components/MultipleTags' +import { StatusCircle } from 'client/components/Status' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import VmColumns from 'client/components/Tables/Vms/columns' import VmRow from 'client/components/Tables/Vms/row' import { RESOURCE_NAMES, STATES, + T, + VM_ACTIONS, VM_EXTENDED_POOL, VM_STATES, } from 'client/constants' +import { useAuth, useViews } from 'client/features/Auth' +import { useGeneral } from 'client/features/General' +import { useGetVmsQuery } from 'client/features/OneApi/vm' + +import { getColorFromString, getUniqueLabels } from 'client/models/Helper' +import { + getIps, + getLastHistory, + getState, + getVmHostname, +} from 'client/models/VirtualMachine' const DEFAULT_DATA_CY = 'vms' +const { VNC, RDP, SSH, VMRC } = VM_ACTIONS +const CONNECTION_TYPES = [VNC, RDP, SSH, VMRC] + /** * @param {object} props - Props * @returns {ReactElement} Virtual Machines table @@ -121,6 +138,96 @@ const VmsTable = (props) => { [view] ) + const { zone, defaultZone } = useGeneral() + const listHeader = [ + { + header: '', + id: 'status-icon', + accessor: (vm) => { + const { + color: stateColor, + name: stateName, + displayName: stateDisplayName, + } = getState(vm) + + return ( + + ) + }, + }, + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { header: T.Owner, id: 'owner', accessor: 'UNAME' }, + { header: T.Group, id: 'group', accessor: 'GNAME' }, + { + header: T.State, + id: 'state', + accessor: (vm) => getState(vm)?.name, + }, + { + header: T.Hostname, + id: 'vmhostname', + accessor: (vm) => getVmHostname(vm), + }, + { + header: T.Host, + id: 'hostname', + accessor: (vm) => getLastHistory(vm)?.HOSTNAME, + }, + { + header: T.IP, + id: 'ips', + accessor: (vm) => getIps(vm).join(), + }, + { + header: T.Labels, + id: 'labels', + accessor: ({ USER_TEMPLATE: { LABELS } = {} }) => { + const { labels: userLabels } = useAuth() + const labels = useMemo( + () => + getUniqueLabels(LABELS).reduce((acc, label) => { + if (userLabels?.includes(label)) { + acc.push({ + text: label, + dataCy: `label-${label}`, + stateColor: getColorFromString(label), + }) + } + + return acc + }, []), + + [LABELS] + ) + + return + }, + }, + ] + + zone === defaultZone && + listHeader.push({ + header: '', + id: 'consoles', + accessor: (vm) => ( + <> + {CONNECTION_TYPES.map((connectionType) => ( + + ))} + + ), + }) + + const { component, header } = WrapperRow(VmRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={VmRow} initialState={initialState} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/Vms/row.js b/src/fireedge/src/client/components/Tables/Vms/row.js index 81d644b885d..a7012211ff4 100644 --- a/src/fireedge/src/client/components/Tables/Vms/row.js +++ b/src/fireedge/src/client/components/Tables/Vms/row.js @@ -27,7 +27,15 @@ const { VNC, RDP, SSH, VMRC } = VM_ACTIONS const CONNECTION_TYPES = [VNC, RDP, SSH, VMRC] const Row = memo( - ({ original, value, onClickLabel, globalErrors, ...props }) => { + ({ + original, + value, + onClickLabel, + globalErrors, + headerList, + rowDataCy, + ...props + }) => { // This is for not showing VNC coneections when the user use other zone. const { zone, defaultZone } = useGeneral() @@ -88,6 +96,8 @@ Row.propTypes = { onClick: PropTypes.func, onClickLabel: PropTypes.func, globalErrors: PropTypes.array, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } Row.displayName = 'VirtualMachineRow' diff --git a/src/fireedge/src/client/components/Tables/Wilds/row.js b/src/fireedge/src/client/components/Tables/Wilds/row.js index f36bacc66e9..23183836838 100644 --- a/src/fireedge/src/client/components/Tables/Wilds/row.js +++ b/src/fireedge/src/client/components/Tables/Wilds/row.js @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { memo, ReactElement } from 'react' import PropTypes from 'prop-types' +import { memo, ReactElement } from 'react' import { Typography } from '@mui/material' @@ -28,7 +28,7 @@ import { Row as RowType } from 'react-table' * @param {Function} props.handleClick - Action by click * @returns {ReactElement} - Table row */ -const Row = memo(({ original, ...props }) => { +const Row = memo(({ original, headerList, rowDataCy, ...props }) => { const classes = rowStyles() const { DEPLOY_ID, VM_NAME } = original @@ -52,6 +52,8 @@ Row.propTypes = { original: PropTypes.object, isSelected: PropTypes.bool, handleClick: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } Row.displayName = 'WildsRow' diff --git a/src/fireedge/src/client/components/Tables/Zombies/row.js b/src/fireedge/src/client/components/Tables/Zombies/row.js index fad71672eef..52c09bd9e6f 100644 --- a/src/fireedge/src/client/components/Tables/Zombies/row.js +++ b/src/fireedge/src/client/components/Tables/Zombies/row.js @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { memo, ReactElement } from 'react' import PropTypes from 'prop-types' +import { memo, ReactElement } from 'react' import { Typography } from '@mui/material' @@ -28,7 +28,7 @@ import { Row as RowType } from 'react-table' * @param {Function} props.handleClick - Action by click * @returns {ReactElement} - Table row */ -const Row = memo(({ original, ...props }) => { +const Row = memo(({ original, headerList, rowDataCy, ...props }) => { const classes = rowStyles() const { DEPLOY_ID, VM_NAME } = original @@ -49,6 +49,8 @@ Row.propTypes = { original: PropTypes.object, isSelected: PropTypes.bool, handleClick: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } Row.displayName = 'ZombiesRow' diff --git a/src/fireedge/src/client/components/Tables/Zones/index.js b/src/fireedge/src/client/components/Tables/Zones/index.js index e0363e19994..eb812a07f00 100644 --- a/src/fireedge/src/client/components/Tables/Zones/index.js +++ b/src/fireedge/src/client/components/Tables/Zones/index.js @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useMemo, ReactElement } from 'react' - -import { useViews } from 'client/features/Auth' -import { useGetZonesQuery } from 'client/features/OneApi/zone' +import { ReactElement, useMemo } from 'react' import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced' +import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow' import ZoneColumns from 'client/components/Tables/Zones/columns' import ZoneRow from 'client/components/Tables/Zones/row' -import { RESOURCE_NAMES } from 'client/constants' +import { RESOURCE_NAMES, T } from 'client/constants' +import { useViews } from 'client/features/Auth' +import { useGetZonesQuery } from 'client/features/OneApi/zone' const DEFAULT_DATA_CY = 'zones' @@ -46,6 +46,13 @@ const ZonesTable = (props) => { [view] ) + const listHeader = [ + { header: T.ID, id: 'id', accessor: 'ID' }, + { header: T.Name, id: 'name', accessor: 'NAME' }, + { header: T.Endpoint, id: 'endpoint', accessor: 'TEMPLATE.ENDPOINT' }, + ] + const { component, header } = WrapperRow(ZoneRow) + return ( { refetch={refetch} isLoading={isFetching} getRowId={(row) => String(row.ID)} - RowComponent={ZoneRow} + RowComponent={component} + headerList={header && listHeader} {...rest} /> ) diff --git a/src/fireedge/src/client/components/Tables/Zones/row.js b/src/fireedge/src/client/components/Tables/Zones/row.js index b9bab070163..7a0563b0ae3 100644 --- a/src/fireedge/src/client/components/Tables/Zones/row.js +++ b/src/fireedge/src/client/components/Tables/Zones/row.js @@ -17,15 +17,15 @@ import PropTypes from 'prop-types' import { Typography } from '@mui/material' -import { HomeShield } from 'iconoir-react' import { Tr } from 'client/components/HOC' -import { T } from 'client/constants' import { StatusCircle } from 'client/components/Status' import { rowStyles } from 'client/components/Tables/styles' +import { T } from 'client/constants' +import { HomeShield } from 'iconoir-react' import * as ZoneModel from 'client/models/Zone' -const Row = ({ original, value, ...props }) => { +const Row = ({ original, value, headerList, rowDataCy, ...props }) => { const classes = rowStyles() const { ID, NAME, ENDPOINT } = value @@ -57,6 +57,8 @@ Row.propTypes = { value: PropTypes.object, isSelected: PropTypes.bool, handleClick: PropTypes.func, + headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), + rowDataCy: PropTypes.string, } export default Row diff --git a/src/fireedge/src/client/components/Tabs/Datastore/Info/information.js b/src/fireedge/src/client/components/Tabs/Datastore/Info/information.js index cacc3f58380..45c1fb7019b 100644 --- a/src/fireedge/src/client/components/Tabs/Datastore/Info/information.js +++ b/src/fireedge/src/client/components/Tabs/Datastore/Info/information.js @@ -21,7 +21,6 @@ import { StatusChip, LinearProgressWithLabel } from 'client/components/Status' import { List } from 'client/components/Tabs/Common' import { getState, getType, getCapacityInfo } from 'client/models/Datastore' -import { stringToBoolean } from 'client/models/Helper' import { prettyBytes } from 'client/utils' import { T, Datastore, DATASTORE_ACTIONS, DS_THRESHOLD } from 'client/constants' @@ -38,9 +37,9 @@ const InformationPanel = ({ datastore = {}, actions }) => { const { ID, NAME, BASE_PATH, TEMPLATE } = datastore - const isShared = stringToBoolean(TEMPLATE.SHARED) - const limit = - !isShared && TEMPLATE.LIMIT_MB ? prettyBytes(TEMPLATE.LIMIT_MB, 'MB') : '-' + const limit = TEMPLATE.LIMIT_MB + ? prettyBytes(TEMPLATE.LIMIT_MB, 'MB', 1) + : '-' const { percentOfUsed, percentLabel } = getCapacityInfo(datastore) const { color: stateColor, name: stateName } = getState(datastore) diff --git a/src/fireedge/src/client/components/Tabs/Host/Graphs/index.js b/src/fireedge/src/client/components/Tabs/Host/Graphs/index.js index 4695b267a57..d2cb7837fb5 100644 --- a/src/fireedge/src/client/components/Tabs/Host/Graphs/index.js +++ b/src/fireedge/src/client/components/Tabs/Host/Graphs/index.js @@ -32,7 +32,7 @@ import { Tr } from 'client/components/HOC' const HostGraphTab = ({ id }) => { const { data: { MONITORING_DATA: { MONITORING: monitoring = [] } = {} } = {}, - } = useGetHostMonitoringQuery(id) || {} + } = useGetHostMonitoringQuery({ id: id }) || {} const cpuMemoryData = ( Array.isArray(monitoring) ? monitoring : [monitoring] diff --git a/src/fireedge/src/client/components/Tabs/Support/Comments/CommentBar/index.js b/src/fireedge/src/client/components/Tabs/Support/Comments/CommentBar/index.js index a7a532a5311..b61c3849317 100644 --- a/src/fireedge/src/client/components/Tabs/Support/Comments/CommentBar/index.js +++ b/src/fireedge/src/client/components/Tabs/Support/Comments/CommentBar/index.js @@ -60,14 +60,14 @@ const CommentBar = ({ resolver: yupResolver(FORM.SCHEMA), }) - const onSubmit = (fields) => { + const onSubmit = async (fields) => { const commentBody = { id: ticket.id, body: marked.parse(sanitize`${fields.BODY}`), - // attachments: fields.ATTACHMENTS, + attachments: fields.ATTACHMENTS, } - fields.solved && (commentBody.solved = true) - update(commentBody) + fields.SOLVED && (commentBody.solved = true) + await update(commentBody) setComments([ ...comments, { diff --git a/src/fireedge/src/client/components/Tabs/Support/Comments/CommentBar/schema.js b/src/fireedge/src/client/components/Tabs/Support/Comments/CommentBar/schema.js index 22c4ebc42a2..83d87f39194 100644 --- a/src/fireedge/src/client/components/Tabs/Support/Comments/CommentBar/schema.js +++ b/src/fireedge/src/client/components/Tabs/Support/Comments/CommentBar/schema.js @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { boolean, object, string } from 'yup' -// eslint-disable-next-line no-unused-vars import { INPUT_TYPES, T } from 'client/constants' +import { boolean, mixed, object, string } from 'yup' +// eslint-disable-next-line no-unused-vars import { Field, ObjectSchema, getValidationFromFields } from 'client/utils' /** @type {Field} Body message field */ @@ -31,16 +31,29 @@ export const BODY = { /** @type {Field} Solved field */ export const SOLVED = { name: 'SOLVED', - label: `${T.Description} (${T.WeSupportMarkdown})`, + label: T.MarkAsclosed, type: INPUT_TYPES.CHECKBOX, validation: boolean().default(() => false), grid: { xs: 12, md: 12 }, } +/** @type {Field} Attachment field */ +export const ATTACHMENTS = { + name: 'ATTACHMENTS', + label: T.Upload, + type: INPUT_TYPES.FILE, + validation: mixed() + .notRequired() + .test('fileSize', T.FileTooLarge, (value) => + value?.size ? value.size <= 50 * 1024 ** 2 : true + ), + grid: { xs: 12, md: 12 }, +} + /** * @returns {Field[]} Fields */ -export const FIELDS = [BODY, SOLVED] +export const FIELDS = [BODY, SOLVED, ATTACHMENTS] /** * @param {object} [stepProps] - Step props diff --git a/src/fireedge/src/client/components/Tabs/Vm/Configuration.js b/src/fireedge/src/client/components/Tabs/Vm/Configuration.js index 13fdb36013f..9554e1cfed2 100644 --- a/src/fireedge/src/client/components/Tabs/Vm/Configuration.js +++ b/src/fireedge/src/client/components/Tabs/Vm/Configuration.js @@ -135,7 +135,7 @@ const VmConfigurationTab = ({ }, form: () => UpdateConfigurationForm({ - stepProps: { hypervisor, oneConfig, adminGroup }, + stepProps: { hypervisor, oneConfig, adminGroup, vm }, initialValues: vm, }), onSubmit: handleUpdateConf, diff --git a/src/fireedge/src/client/constants/translates.js b/src/fireedge/src/client/constants/translates.js index 985dae36726..84407872a6a 100644 --- a/src/fireedge/src/client/constants/translates.js +++ b/src/fireedge/src/client/constants/translates.js @@ -197,8 +197,8 @@ module.exports = { SelectDisk: 'Select disk', SelectDockerHubTag: 'Select DockerHub image tag (default latest)', SelectGroup: 'Select a group', - SelectHost: 'Select host', - SelectHosts: 'Select hosts', + SelectHost: 'Select Host', + SelectHosts: 'Select Hosts', SelectMarketplace: 'Select Marketplace', SelectNetwork: 'Select a network', SelectVirtualNetworks: 'Select virtual networks', @@ -427,6 +427,9 @@ module.exports = { /* sections - settings */ Settings: 'Settings', + AppliesTo: 'Applies To', + AllowedOperations: 'Allowed operations', + AffectedResources: 'Affected resources', Schema: 'Schema', Dark: 'Dark', Light: 'Light', @@ -516,6 +519,7 @@ module.exports = { ResolutionTicket: 'You should just use this. If you want to place the ticket as solved', AddComment: 'Add comment to close the ticket', + MarkAsclosed: 'Please consider to this request resolved', /* sections - system */ User: 'User', @@ -590,6 +594,7 @@ module.exports = { Zone: 'Zone', Zones: 'Zones', Vnet: 'Vnet', + Vnets: 'Vnets', 'cluster.form.create.general.help.title': 'Cluster', 'cluster.form.create.general.help.paragraph.1.1': 'Clusters group together hosts, datastores and virtual networks that are configured to work together. A cluster is used to:', @@ -655,6 +660,7 @@ module.exports = { Images: 'Images', File: 'File', Files: 'Files', + FileTooLarge: 'File too large', Marketplace: 'Marketplace', Marketplaces: 'Marketplaces', App: 'App', @@ -700,7 +706,7 @@ module.exports = { Ceph: 'Ceph', LVM: 'LVM', RawDeviceMapping: 'Raw device mapping', - StorageRestic: 'Backup - Restic (EE only)', + StorageRestic: 'Backup - Restic', StorageRsync: 'Backup - RSync', /* datastore */ @@ -885,6 +891,7 @@ module.exports = { Prolog: 'Prolog', EndTime: 'End time', Locked: 'Locked', + Unlocked: 'Unlocked', Attributes: 'Attributes', Type: 'Type', Data: 'Data', @@ -927,6 +934,7 @@ module.exports = { Reconnect: 'Reconnect', FullScreen: 'Full screen', FullScreenInfo: 'Full screen information in datatables', + RowStyle: 'DataTables Row Style', Screenshot: 'Screenshot', LastConnection: 'Last connection', PartOf: 'Part of', @@ -1105,6 +1113,10 @@ module.exports = { Defaults to 'template name-' when empty. When creating several VMs, the wildcard %%idx will be replaced with a number starting from 0`, + VmVrTemplateNameHelper: ` + Defaults to 'template name-' when empty. + When creating several VMs, the wildcard %%i will be + replaced with a number starting from 0`, NumberOfInstances: 'Number of instances', NumberOfVms: 'Number of VMs', MakeTemplateAvailableForVROnly: @@ -1245,7 +1257,7 @@ module.exports = { FirmwareSecure: 'Firmware secure', CpuModel: 'CPU Model', CpuFeature: 'CPU Features', - CustomPath: 'Customize with path', + CustomPath: 'Custom path', /* VM Template schema - OS & CPU - kernel */ Kernel: 'Kernel', KernelExpression: 'Kernel expression', @@ -1479,6 +1491,7 @@ module.exports = { AutomaticDeletion: 'Automatic deletion', Role: 'Role', Roles: 'Roles', + Card: 'Card', Cardinality: 'Cardinality', Parents: 'Parents', ParentRoles: 'Parent roles', diff --git a/src/fireedge/src/client/containers/ServiceTemplates/Instantiate.js b/src/fireedge/src/client/containers/ServiceTemplates/Instantiate.js index d8a67a38bf8..bd2ff231fda 100644 --- a/src/fireedge/src/client/containers/ServiceTemplates/Instantiate.js +++ b/src/fireedge/src/client/containers/ServiceTemplates/Instantiate.js @@ -62,11 +62,18 @@ function CreateServiceTemplate() { useGetDatastoresQuery(undefined, { refetchOnMountOrArgChange: false }) const onSubmit = async (jsonTemplate) => { + const { instances = 1 } = jsonTemplate + try { - await instantiate({ - id: templateId, - template: jsonTemplate, - }).unwrap() + await Promise.all( + Array.from({ length: instances }, async () => + instantiate({ + id: templateId, + template: jsonTemplate, + }).unwrap() + ) + ) + history.push(PATH.INSTANCE.SERVICES.LIST) enqueueSuccess(T.SuccessServiceTemplateInitiated, [templateId, NAME]) } catch {} diff --git a/src/fireedge/src/client/containers/Settings/ConfigurationUI/schema.js b/src/fireedge/src/client/containers/Settings/ConfigurationUI/schema.js index e3292158aa7..e48aca8ab04 100644 --- a/src/fireedge/src/client/containers/Settings/ConfigurationUI/schema.js +++ b/src/fireedge/src/client/containers/Settings/ConfigurationUI/schema.js @@ -119,6 +119,22 @@ const FULL_SCREEN_INFO_FIELD = { grid: { md: 12 }, } +const ROW_STYLE_FIELD = { + name: 'ROW_STYLE', + label: T.RowStyle, + type: INPUT_TYPES.AUTOCOMPLETE, + optionsOnly: true, + values: [ + { text: T.Card, value: 'card' }, + { text: T.List, value: 'list' }, + ], + validation: string() + .trim() + .required() + .default(() => 'card'), + grid: { md: 12 }, +} + /** * @param {object} props - Props * @param {object} props.views - views. @@ -131,6 +147,7 @@ export const FIELDS = (props) => [ LANG_FIELD, VIEW_FIELD(props), ZONE_ENDPOINT_FIELD(props), + ROW_STYLE_FIELD, DISABLE_ANIMATIONS_FIELD, FULL_SCREEN_INFO_FIELD, ] diff --git a/src/fireedge/src/client/containers/Settings/index.js b/src/fireedge/src/client/containers/Settings/index.js index aff3bd81e40..d5d78590959 100644 --- a/src/fireedge/src/client/containers/Settings/index.js +++ b/src/fireedge/src/client/containers/Settings/index.js @@ -43,7 +43,7 @@ const Settings = () => { diff --git a/src/fireedge/src/client/containers/VirtualRouterTemplates/Instantiate.js b/src/fireedge/src/client/containers/VirtualRouterTemplates/Instantiate.js index 2cef5661e03..efb006727dc 100644 --- a/src/fireedge/src/client/containers/VirtualRouterTemplates/Instantiate.js +++ b/src/fireedge/src/client/containers/VirtualRouterTemplates/Instantiate.js @@ -20,7 +20,6 @@ import { useGeneralApi } from 'client/features/General' import { useGetGroupsQuery } from 'client/features/OneApi/group' import { useGetUsersQuery } from 'client/features/OneApi/user' import { useGetTemplateQuery } from 'client/features/OneApi/vmTemplate' -import { convertKeysToCase } from 'client/utils' import { useInstantiateVRouterTemplateMutation, @@ -76,20 +75,40 @@ function InstantiateVrTemplate() { const promises = templates.map(async (t) => { t.template = jsonToXml(t) + /** + * In allocate request template send only the following info: + * - Name - Name of the vrouter + * - Description - Description of the vrouter + * - Keep alive ID + * - Keep alive password + * - NICS + */ const allocationResult = await allocate({ template: jsonToXml({ NAME: t.vrname, - ...(t?.networking?.NIC - ? { NIC: convertKeysToCase(t.networking.NIC, false) } - : {}), + DESCRIPTION: t.general?.description, + KEEPALIVED_ID: t.general?.keepaliveid, + KEEPALIVED_PASSWORD: t.general?.keepalivepass, + NIC: t.networking, }), }).unwrap() + /** + * In instantiate request send only the following info: + * - id - If of the vrouter (created in the previous allocate request) + * - templateId - Id of the vrouter template + * - number - Number of virtual machines that are gonna be instantiated + * - pending - Start virtual machines on hold state + * - template - XML template only with the user inputs + */ return instantiate({ - ...t, + fromPostbody: t?.initiateFromSelection, id: allocationResult, templateId: templateId ?? parseInt(t?.id, 10), - ...(t?.initiateFromSelection && { fromPostbody: true }), + number: t?.number, + name: t?.name, + pending: t?.pending, + template: jsonToXml(t?.user_inputs), }).unwrap() }) diff --git a/src/fireedge/src/client/containers/VmTemplates/Create.js b/src/fireedge/src/client/containers/VmTemplates/Create.js index 95d0eb6bc62..2727a2fcb41 100644 --- a/src/fireedge/src/client/containers/VmTemplates/Create.js +++ b/src/fireedge/src/client/containers/VmTemplates/Create.js @@ -38,6 +38,7 @@ import { PATH } from 'client/apps/sunstone/routesOne' import { jsonToXml } from 'client/models/Helper' import { filterTemplateData, transformActionsCreate } from 'client/utils/parser' +import { isDevelopment } from 'client/utils' import { TAB_FORM_MAP, T } from 'client/constants' import { useSystemData } from 'client/features/Auth' @@ -131,7 +132,9 @@ function CreateVmTemplate() { history.push(PATH.TEMPLATE.VMS.LIST) enqueueSuccess(T.SuccessVMTemplateUpdated, [templateId, NAME]) } - } catch {} + } catch (error) { + isDevelopment() && console.log('VM Template error: ', error) + } } return templateId && diff --git a/src/fireedge/src/client/features/OneApi/host.js b/src/fireedge/src/client/features/OneApi/host.js index 00a8dd80a75..7b2f9f7cef6 100644 --- a/src/fireedge/src/client/features/OneApi/host.js +++ b/src/fireedge/src/client/features/OneApi/host.js @@ -405,6 +405,7 @@ export const { useLazyGetHostQuery, useGetHostsQuery, useGetHostsAdminQuery, + useLazyGetHostsAdminQuery, useLazyGetHostsQuery, useGetHostMonitoringQuery, useLazyGetHostMonitoringQuery, diff --git a/src/fireedge/src/client/features/OneApi/support.js b/src/fireedge/src/client/features/OneApi/support.js index 88263cb58b1..9b7e2a18a2f 100644 --- a/src/fireedge/src/client/features/OneApi/support.js +++ b/src/fireedge/src/client/features/OneApi/support.js @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ +import { TicketComment } from 'client/constants' +import { ONE_RESOURCES_POOL, oneApi } from 'client/features/OneApi' +import http from 'client/utils/rest' import { Actions as ActionsSupport, Commands as CommandsSupport, } from 'server/routes/api/support/routes' import { Actions, Commands } from 'server/routes/api/zendesk/routes' -import { TicketComment } from 'client/constants' -import { ONE_RESOURCES_POOL, oneApi } from 'client/features/OneApi' - const { SUPPORT_POOL } = ONE_RESOURCES_POOL const authSupportApi = oneApi.injectEndpoints({ @@ -162,11 +162,29 @@ const authSupportApi = oneApi.injectEndpoints({ * @returns {object} Response data from request * @throws Fails when response isn't code 200 */ - query: (params) => { - const name = Actions.ZENDESK_UPDATE - const command = { name, ...Commands[name] } + queryFn: async (params) => { + const { attachments, id, body, solved } = params + try { + const data = new FormData() + attachments && data.append('attachments', attachments) + solved && data.append('solved', solved) + data.append('body', body) - return { params, command } + const response = await http.request({ + url: `/api/zendesk/${id}`, + method: 'PUT', + data, + headers: { + 'Content-Type': 'multipart/form-data', + }, + }) + + return { data: response.data } + } catch (axiosError) { + const { response } = axiosError + + return { error: { status: response?.status, data: response?.data } } + } }, invalidatesTags: (_, __, { id }) => [{ type: SUPPORT_POOL, id }], }), @@ -182,11 +200,31 @@ const authSupportApi = oneApi.injectEndpoints({ * @returns {object} Response data from request * @throws Fails when response isn't code 200 */ - query: (params) => { - const name = Actions.ZENDESK_CREATE - const command = { name, ...Commands[name] } + queryFn: async (params) => { + const { attachments, subject, body, version, severity } = params + try { + const data = new FormData() + data.append('subject', subject) + data.append('body', body) + data.append('version', version) + data.append('severity', severity) + attachments && data.append('attachments', attachments) - return { params, command } + const response = await http.request({ + url: `/api/zendesk`, + method: 'POST', + data, + headers: { + 'Content-Type': 'multipart/form-data', + }, + }) + + return { data: response.data } + } catch (axiosError) { + const { response } = axiosError + + return { error: { status: response?.status, data: response?.data } } + } }, invalidatesTags: [SUPPORT_POOL], }), diff --git a/src/fireedge/src/client/models/Host.js b/src/fireedge/src/client/models/Host.js index 57a1b7e6142..88ea737bf2c 100644 --- a/src/fireedge/src/client/models/Host.js +++ b/src/fireedge/src/client/models/Host.js @@ -104,7 +104,13 @@ export const getHugepageSizes = (host) => { return numaNodes .filter((node) => node?.NODE_ID && node?.HUGEPAGE) - .map((node) => node.HUGEPAGE.map(({ SIZE }) => +SIZE)) + .map((node) => { + const hugepages = Array.isArray(node.HUGEPAGE) + ? node.HUGEPAGE + : [node.HUGEPAGE] + + return hugepages.map(({ SIZE }) => +SIZE) + }) .flat() } diff --git a/src/fireedge/src/client/models/Image.js b/src/fireedge/src/client/models/Image.js index 560b3df6e3e..a54aaa3e33a 100644 --- a/src/fireedge/src/client/models/Image.js +++ b/src/fireedge/src/client/models/Image.js @@ -21,7 +21,7 @@ import { Image, DiskSnapshot, } from 'client/constants' -import { prettyBytes } from 'client/utils' +import { prettyBytes, getLocked } from 'client/utils' /** * Returns the image type. @@ -49,6 +49,13 @@ export const getState = ({ STATE } = {}) => IMAGE_STATES[+STATE] export const getDiskType = ({ DISK_TYPE } = {}) => isNaN(+DISK_TYPE) ? DISK_TYPE : DISK_TYPES[+DISK_TYPE] +/** + * + * @param {Image} image - Image + * @returns {string} - If image is locked/unlocked + */ +export const getImageLocked = getLocked + /** * Returns the disk name. * diff --git a/src/fireedge/src/client/models/VirtualMachine.js b/src/fireedge/src/client/models/VirtualMachine.js index 4c9cec2a4d8..12914068428 100644 --- a/src/fireedge/src/client/models/VirtualMachine.js +++ b/src/fireedge/src/client/models/VirtualMachine.js @@ -37,6 +37,8 @@ import { VM_STATES, } from 'client/constants' +import { getLocked } from 'client/utils' + /** * This function removes, from the given list, * the Virtual machines in state DONE. @@ -60,6 +62,19 @@ export const getHistoryAction = (action) => HISTORY_ACTIONS[+action] export const getHistoryRecords = (vm) => [vm?.HISTORY_RECORDS?.HISTORY ?? []].flat() +/** + * @param {VM} vm - Virtual machine + * @returns {object} Context vars from resource + */ +export const getContext = (vm) => [vm?.TEMPLATE?.CONTEXT ?? []].flat() + +/** + * @param {VM} vm - Virtual machine + * @returns {string} VM hostname from resource + */ +export const getVmHostname = (vm) => + [getContext(vm)?.pop()?.SET_HOSTNAME ?? []].flat() + /** * @param {VM} vm - Virtual machine * @returns {HistoryRecord} Last history record from resource @@ -70,6 +85,12 @@ export const getLastHistory = (vm) => { return records.at(-1) ?? {} } +/** + * + * @param {VM} vm - Virtual Machine + * @returns {string} - If VM is locked/unlocked + */ +export const getVMLocked = getLocked /** * @param {VM} vm - Virtual machine * @returns {string} Resource type: VR, FLOW or VM diff --git a/src/fireedge/src/client/models/VirtualMachineTemplate.js b/src/fireedge/src/client/models/VirtualMachineTemplate.js new file mode 100644 index 00000000000..134769f1c75 --- /dev/null +++ b/src/fireedge/src/client/models/VirtualMachineTemplate.js @@ -0,0 +1,24 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2024, OpenNebula Project, OpenNebula Systems * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); you may * + * not use this file except in compliance with the License. You may obtain * + * a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * ------------------------------------------------------------------------- */ +import { VmTemplate } from 'client/constants' + +import { getLocked } from 'client/utils' + +/** + * @param {VmTemplate} vmTemplate - Virtual machine template + * @returns {string} - Return if virtual machine template is locked/unlocked + */ +export const getVMTemplateLocked = getLocked diff --git a/src/fireedge/src/client/models/VirtualNetwork.js b/src/fireedge/src/client/models/VirtualNetwork.js index cc5c8221eeb..f958db82f6a 100644 --- a/src/fireedge/src/client/models/VirtualNetwork.js +++ b/src/fireedge/src/client/models/VirtualNetwork.js @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { isIPv6, isIPv4, isMAC } from 'client/utils' +import { isIPv6, isIPv4, isMAC, getLocked } from 'client/utils' import { VirtualNetwork, AddressRange, @@ -119,3 +119,10 @@ export const isAvailableAction = (action, vnets = []) => { return VN_ACTIONS_BY_STATE[action]?.includes(state) }) } + +/** + * + * @param {VirtualNetwork} virtualNetwork - Virtual Machine + * @returns {string} - If virtual network is locked/unlocked + */ +export const getVirtualNetLocked = getLocked diff --git a/src/fireedge/src/client/utils/helpers.js b/src/fireedge/src/client/utils/helpers.js index dbfc3cc87fd..b2e18bcac8e 100644 --- a/src/fireedge/src/client/utils/helpers.js +++ b/src/fireedge/src/client/utils/helpers.js @@ -676,3 +676,19 @@ export const findKeyWithPath = (() => { return search })() + +/** + * Check if data is an array and if it's only one element, return an array with one element. + * + * @param {object} data - Object or list + * @returns {Array} An array with one element or the same array + */ +export const responseDataToArray = (data) => + data ? (Array.isArray(data) ? data : [data]) : undefined + +/** + * + * @param {object} OpennebulaObject - OpennebulaObject + * @returns {string} - If OpennebulaObject is locked/unlocked + */ +export const getLocked = (OpennebulaObject) => !!+OpennebulaObject.LOCK?.LOCKED diff --git a/src/fireedge/src/client/utils/parser/vmTemplateFilter.js b/src/fireedge/src/client/utils/parser/vmTemplateFilter.js index d3f0ee8d4d5..3c09b041a26 100644 --- a/src/fireedge/src/client/utils/parser/vmTemplateFilter.js +++ b/src/fireedge/src/client/utils/parser/vmTemplateFilter.js @@ -77,7 +77,11 @@ const defaultValuesCreate = { }, } -const defaultValuesUpdate = {} +const defaultValuesUpdate = { + extra: { + Context: { CONTEXT: { START_SCRIPT: true, START_SCRIPT_BASE64: true } }, + }, +} /** * Filter the data of the form data with the values that were modified by the user and not adding the ones that could be added by default. The goal is to create the most simplify template that we can. @@ -295,7 +299,10 @@ const reduceExtra = ( } }) } else if (section === 'Storage') { - handleStorage(formData, correctionMap, newExtra, section, 'DISK') + handleStorage(formData, correctionMap, newExtra, section, [ + 'DISK', + 'TM_MAD_SYSTEM', + ]) } else { handleOtherSections( formData, @@ -472,65 +479,76 @@ const handleNetwork = ( * @param {object} correctionMap - Map with the fields that will be touched and modified by the user * @param {object} newExtra - The extra section of the form. * @param {string} section - Section of the form (this function will have always Storage) - * @param {string} type - Section type inside network section + * @param {Array} types - Array of section types inside storage section */ -const handleStorage = (formData, correctionMap, newExtra, section, type) => { - if (!formData.extra[type]) return +const handleStorage = (formData, correctionMap, newExtra, section, types) => { + for (const type of types) { + if ( + typeof formData?.extra?.[type] === 'string' && + type === 'TM_MAD_SYSTEM' + ) { + newExtra[type] = formData?.extra[type] + } - // const sectionModifications = correctionMap.extra[section] || [] - const existingData = _.cloneDeep(newExtra[type]) + if (!formData.extra[type]) return - // Delete the items that were deleted by the user to get the correct indexes. - const wrappedExistingData = deleteItemsOnExistingData( - Array.isArray(existingData) ? existingData : [existingData], - correctionMap.extra[section] - ) + // const sectionModifications = correctionMap.extra[section] || [] + const existingData = _.cloneDeep(newExtra[type]) - // Delete the items that were deleted by the user to get the correct indexes. - const sectionModifications = deleteItemsOnExistingData( - correctionMap.extra[section], - correctionMap.extra[section] - ) + // Delete the items that were deleted by the user to get the correct indexes. + const wrappedExistingData = deleteItemsOnExistingData( + Array.isArray(existingData) ? existingData : [existingData], + correctionMap.extra[section] + ) - // Iterate over the final data - const modifiedData = formData.extra[type].map((disk, index) => { - // Check if the index of the item it's on the modifications map and has value - if ( - index < sectionModifications.length && - sectionModifications[index] !== null - ) { - // Get the fields where the modifications were done - const diskModifications = Object.keys( - sectionModifications[index] - )?.reduce( - (acc, key) => ({ ...acc, ...sectionModifications[index][key] }), - {} - ) + // Delete the items that were deleted by the user to get the correct indexes. + const sectionModifications = deleteItemsOnExistingData( + correctionMap.extra[section], + correctionMap.extra[section] + ) - // Iterate over each field of the item and, if it is one of the field that was modified, add the modification to the new data - return Object.keys(disk).reduce((acc, key) => { + // Iterate over the final data + if (Array.isArray(formData?.extra[type])) { + const modifiedData = formData.extra[type].map((disk, index) => { + // Check if the index of the item it's on the modifications map and has value if ( - typeof diskModifications[key] === 'boolean' && - diskModifications[key] - ) { - acc[key] = disk[key] - } else if ( - typeof diskModifications[key] === 'object' && - diskModifications[key].__delete__ + index < sectionModifications.length && + sectionModifications[index] !== null ) { - delete acc[key] - } else if (key === 'SIZE' && diskModifications.SIZEUNIT) { - acc[key] = disk[key] - } + // Get the fields where the modifications were done + const diskModifications = Object.keys( + sectionModifications[index] + )?.reduce( + (acc, key) => ({ ...acc, ...sectionModifications[index][key] }), + {} + ) - return acc - }, wrappedExistingData?.[index] || {}) - } + // Iterate over each field of the item and, if it is one of the field that was modified, add the modification to the new data + return Object.keys(disk).reduce((acc, key) => { + if ( + typeof diskModifications[key] === 'boolean' && + diskModifications[key] + ) { + acc[key] = disk[key] + } else if ( + typeof diskModifications[key] === 'object' && + diskModifications[key].__delete__ + ) { + delete acc[key] + } else if (key === 'SIZE' && diskModifications.SIZEUNIT) { + acc[key] = disk[key] + } + + return acc + }, wrappedExistingData?.[index] || {}) + } - return disk - }) + return disk + }) - newExtra[type] = modifiedData + newExtra[type] = modifiedData + } + } } /** @@ -767,15 +785,18 @@ const transformActionsCommon = (template) => { }) } - // If template has RAW attribute if (template.RAW) { - // // Add type (hypervisor) on RAW data if exists data, if not, delete RAW section. - if (template.RAW?.DATA) template.RAW.TYPE = template.HYPERVISOR - else delete template.RAW + // Clone template.RAW to ensure its mutable + template.RAW = { ...template.RAW } - // ISSUE #6418: Raw data is in XML format, so it needs to be transform before sennding it to the API (otherwise the value of RAW.DATA will be treat as part of the XML template) - template?.RAW?.DATA && - (template.RAW.DATA = transformXmlString(template.RAW.DATA)) + if (template.RAW.DATA) { + // DATA exists, so we add TYPE and transform DATA + template.RAW.TYPE = template.HYPERVISOR + template.RAW.DATA = transformXmlString(template.RAW.DATA) + } else { + // DATA doesn't exist, remove RAW from template + delete template.RAW + } } } diff --git a/src/fireedge/src/client/utils/schema.js b/src/fireedge/src/client/utils/schema.js index 7ebaa4ac004..34b69baa310 100644 --- a/src/fireedge/src/client/utils/schema.js +++ b/src/fireedge/src/client/utils/schema.js @@ -326,7 +326,7 @@ export const schemaUserInput = ({ } case USER_INPUT_TYPES.boolean: return { - type: INPUT_TYPES.CHECKBOX, + type: INPUT_TYPES.SWITCH, validation: boolean() .concat(requiredSchema(mandatory, boolean())) .default(() => stringToBoolean(defaultValue)) diff --git a/src/fireedge/src/server/routes/api/index.js b/src/fireedge/src/server/routes/api/index.js index c6a01822edc..ad43e2e9438 100644 --- a/src/fireedge/src/server/routes/api/index.js +++ b/src/fireedge/src/server/routes/api/index.js @@ -26,11 +26,13 @@ const { defaultWebpackMode, defaultConfigErrorMessage, defaultTmpPath, + from: fromData, } = require('server/utils/constants/defaults') const { writeInLogger } = require('server/utils/logger') const { getSunstoneConfig } = require('server/utils/yml') genPathResources() +const { postBody } = fromData const appConfig = getSunstoneConfig() const optsMulter = { dest: appConfig.tmpdir || defaultTmpPath } @@ -121,6 +123,7 @@ routes.forEach((file) => { }) } serverDataSource.files = parseFiles(req && req.files) + serverDataSource[postBody] = req.body return action( res, diff --git a/src/fireedge/src/server/routes/api/sunstone/functions.js b/src/fireedge/src/server/routes/api/sunstone/functions.js index 0bbed38e9d1..50796787541 100644 --- a/src/fireedge/src/server/routes/api/sunstone/functions.js +++ b/src/fireedge/src/server/routes/api/sunstone/functions.js @@ -139,11 +139,11 @@ const getViews = ( // Check that the group has info if (vmgroupData && vmgroupData.GROUP && vmgroupData.GROUP.NAME) { // Check if the user is admin of the group - const admins = Array.isArray(vmgroupData.GROUP.ADMINS) - ? vmgroupData.GROUP.ADMINS - : [vmgroupData.GROUP.ADMINS] + const admins = Array.isArray(vmgroupData.GROUP?.ADMINS?.ID) + ? vmgroupData.GROUP.ADMINS?.ID + : [vmgroupData.GROUP.ADMINS?.ID] const isAdminGroup = admins.some( - (admin) => admin.ID === dataUser.USER.ID + (admin) => admin === dataUser.USER.ID ) // Get the views on the group template diff --git a/src/fireedge/src/server/routes/api/zendesk/functions.js b/src/fireedge/src/server/routes/api/zendesk/functions.js index 8d678415216..8bc333cd3f6 100644 --- a/src/fireedge/src/server/routes/api/zendesk/functions.js +++ b/src/fireedge/src/server/routes/api/zendesk/functions.js @@ -34,6 +34,7 @@ const httpBadRequest = httpResponse(badRequest, '', '') * @param {string} configFormatCreate.body - body * @param {string} configFormatCreate.version - one version * @param {string} configFormatCreate.severity - ticket severity + * @param {object} configFormatCreate.attachments - attachment file * @returns {object|undefined} format message create ticket */ const formatCreate = ({ @@ -41,10 +42,11 @@ const formatCreate = ({ body = '', version = '', severity = '', + attachments = [], }) => { if (!(subject && body && version && severity)) return - return { + const rtn = { request: { subject, comment: { @@ -58,6 +60,11 @@ const formatCreate = ({ tags: [severity], }, } + + attachments?.length > 0 && + (rtn.request.comment.uploads = attachments.filter((att) => att)) + + return rtn } /** @@ -313,7 +320,7 @@ const create = ( params = {}, userData = {} ) => { - const { subject, body, version, severity } = params + const { subject, body, version, severity, attachments } = params const { user, password } = userData if ( subject && @@ -326,21 +333,61 @@ const create = ( ) { const session = getSession(user, password) if (session.zendesk && session.zendesk.id) { - /** CREATE TICKET ZENDESK */ const zendeskClient = zendesk.createClient(session.zendesk) - const ticket = formatCreate(params) - zendeskClient.requests.create(ticket, (err, req, result) => { - let method = ok - let data = '' - if (err) { - method = internalServerError - data = parseBufferError(err) - } else if (result) { - data = result - } - response.locals.httpCode = httpResponse(method, data) - next() - }) + + const sendRequest = (requestParams = {}) => { + /** CREATE TICKET ZENDESK */ + const ticket = formatCreate(requestParams) + zendeskClient.requests.create(ticket, (err, _, result) => { + let method = ok + let data = '' + + if (err) { + method = internalServerError + data = parseBufferError(err) + } else if (result) { + data = result + } + response.locals.httpCode = httpResponse(method, data) + next() + }) + } + + /** UPLOAD FILES */ + let uploadedAttachments + if ( + attachments && + typeof zendeskClient?.attachments?.upload === 'function' + ) { + attachments.forEach((att = {}) => { + if (att && att.originalname && att.path) { + zendeskClient.attachments.upload( + att.path, + { + filename: att.originalname, + }, + (err, _, result) => { + const token = + (result && result.upload && result.upload.token) || '' + if (uploadedAttachments) { + uploadedAttachments.push(token) + } else { + uploadedAttachments = [token] + } + if ( + !err && + token && + uploadedAttachments.length === attachments.length + ) { + sendRequest({ ...params, attachments: uploadedAttachments }) + } + } + ) + } + }) + } else { + sendRequest({ ...params, attachments }) + } } else { response.locals.httpCode = httpResponse(unauthorized) next() @@ -374,6 +421,7 @@ const update = ( ) => { const { id, body, attachments } = params const { user, password } = userData + if (Number.isInteger(parseInt(id, 10)) && body && user && password) { const session = getSession(userData.user, userData.password) if (session.zendesk && session.zendesk.id) { @@ -401,8 +449,7 @@ const update = ( let uploadedAttachments if ( attachments && - zendeskClient.attachments && - typeof zendeskClient.attachments.upload === 'function' + typeof zendeskClient?.attachments?.upload === 'function' ) { attachments.forEach((att = {}) => { if (att && att.originalname && att.path) { @@ -411,7 +458,7 @@ const update = ( { filename: att.originalname, }, - (err, req, result) => { + (err, _, result) => { const token = (result && result.upload && result.upload.token) || '' if (uploadedAttachments) { diff --git a/src/fireedge/src/server/routes/api/zendesk/routes.js b/src/fireedge/src/server/routes/api/zendesk/routes.js index 3e248fcb850..252fcf7afce 100644 --- a/src/fireedge/src/server/routes/api/zendesk/routes.js +++ b/src/fireedge/src/server/routes/api/zendesk/routes.js @@ -70,6 +70,9 @@ module.exports = { severity: { from: postBody, }, + attachments: { + from: 'files', + }, }, }, [ZENDESK_UPDATE]: { diff --git a/src/fireedge/src/server/routes/entrypoints/App.js b/src/fireedge/src/server/routes/entrypoints/App.js index 2d06521be29..0831319ce36 100644 --- a/src/fireedge/src/server/routes/entrypoints/App.js +++ b/src/fireedge/src/server/routes/entrypoints/App.js @@ -138,7 +138,7 @@ router.get('*', async (req, res) => { const PRELOAD_STATE = { ...(store.getState() || {}) } - if (appConfig?.default_zone?.id !== 'undefined' && PRELOAD_STATE?.general) { + if (appConfig?.default_zone?.id !== undefined && PRELOAD_STATE?.general) { PRELOAD_STATE.general = { ...PRELOAD_STATE.general, ...{ diff --git a/src/group/GroupPool.cc b/src/group/GroupPool.cc index e04461d757f..e2e6625ebb4 100644 --- a/src/group/GroupPool.cc +++ b/src/group/GroupPool.cc @@ -32,10 +32,7 @@ using namespace std; /* -------------------------------------------------------------------------- */ const string GroupPool::ONEADMIN_NAME = "oneadmin"; -const int GroupPool::ONEADMIN_ID = 0; - const string GroupPool::USERS_NAME = "users"; -const int GroupPool::USERS_ID = 1; /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -44,9 +41,6 @@ GroupPool::GroupPool(SqlDB * db, bool is_slave, const vector& restricted_attrs) : PoolSQL(db, one_db::group_table) { - ostringstream oss; - string error_str; - //Federation slaves do not need to init the pool if (is_slave) { @@ -56,28 +50,36 @@ GroupPool::GroupPool(SqlDB * db, bool is_slave, //lastOID is set in PoolSQL::init_cb if (get_lastOID() == -1) { - int rc; - Group * group; - // Build the default oneadmins & users group - group = new Group(ONEADMIN_ID, ONEADMIN_NAME); + Group group_oneadmin {ONEADMIN_ID, ONEADMIN_NAME}; - rc = PoolSQL::allocate(group, error_str); + string error_str; + auto rc = PoolSQL::allocate(group_oneadmin, error_str); if (rc < 0) { - goto error_groups; + ostringstream oss; + + oss << "Error trying to create default group: " << error_str; + NebulaLog::log("GROUP", Log::ERROR, oss); + + throw runtime_error(oss.str()); } - group = new Group(USERS_ID, USERS_NAME); + Group group {USERS_ID, USERS_NAME}; - group->sunstone_views("cloud", "cloud", "groupadmin", "groupadmin,cloud"); + group.sunstone_views("cloud", "cloud", "groupadmin", "groupadmin,cloud"); rc = PoolSQL::allocate(group, error_str); if (rc < 0) { - goto error_groups; + ostringstream oss; + + oss << "Error trying to create default group: " << error_str; + NebulaLog::log("GROUP", Log::ERROR, oss); + + throw runtime_error(oss.str()); } set_lastOID(99); @@ -87,12 +89,6 @@ GroupPool::GroupPool(SqlDB * db, bool is_slave, GroupTemplate::parse_restricted(restricted_attrs); return; - -error_groups: - oss << "Error trying to create default group: " << error_str; - NebulaLog::log("GROUP", Log::ERROR, oss); - - throw runtime_error(oss.str()); } /* -------------------------------------------------------------------------- */ @@ -100,11 +96,7 @@ GroupPool::GroupPool(SqlDB * db, bool is_slave, int GroupPool::allocate(string name, int * oid, string& error_str) { - int db_oid; - - Group * group; - - ostringstream oss; + *oid = -1; if (Nebula::instance().is_federation_slave()) { @@ -112,38 +104,34 @@ int GroupPool::allocate(string name, int * oid, string& error_str) "GroupPool::allocate called, but this " "OpenNebula is a federation slave"); - return -1; + return *oid; } // Check name if ( !PoolObjectSQL::name_is_valid(name, error_str) ) { - goto error_name; + return *oid; } // Check for duplicates - db_oid = exist(name); + const auto db_oid = exist(name); if( db_oid != -1 ) { - goto error_duplicated; + ostringstream oss; + + oss << "NAME is already taken by GROUP " << db_oid << "."; + error_str = oss.str(); + + return *oid; } // Build a new Group object - group = new Group(-1, name); + Group group{-1, name}; // Insert the Object in the pool *oid = PoolSQL::allocate(group, error_str); - return *oid; - -error_duplicated: - oss << "NAME is already taken by GROUP " << db_oid << "."; - error_str = oss.str(); - -error_name: - *oid = -1; - return *oid; } diff --git a/src/hm/HookPool.cc b/src/hm/HookPool.cc index ecfb5d454bc..3e31623d4f6 100644 --- a/src/hm/HookPool.cc +++ b/src/hm/HookPool.cc @@ -23,45 +23,28 @@ using namespace std; int HookPool::allocate(unique_ptr