Make vasum-cli arguments shorter/nicer with autocomplete, rename tool vasum-cli to vsm 12/44612/23
authorKrzysztof Dynowski <k.dynowski@samsung.com>
Thu, 23 Jul 2015 14:27:57 +0000 (16:27 +0200)
committerDariusz Michaluk <d.michaluk@samsung.com>
Wed, 29 Jul 2015 11:26:25 +0000 (04:26 -0700)
[Feature]       Shorter and nicer arguments for command line tool
[Cause]         Messy underscore, too many commands
[Solution]      More generic commands, shorter, easier to enter names
[Verification]  Build, install, use vsm with various arguments
                Try autocompletion for arguments

Change-Id: I2e8d1bff3a5dcf96df1250b29b8df55c8f23171d

cli/CMakeLists.txt
cli/command-line-interface.cpp
cli/command-line-interface.hpp
cli/main.cpp
cli/support/vasum-cli-completion.sh.in [deleted file]
cli/support/vsm-completion.sh.in [new file with mode: 0755]
common/netlink/netlink.cpp
packaging/vasum.spec
server/netdev.cpp

index fe4b067..0f4fbfe 100644 (file)
@@ -24,7 +24,7 @@ FILE(GLOB cli_SRCS *.cpp *.hpp
                     ${COMMON_FOLDER}/utils/c-array.hpp)
 
 ## Setup target ################################################################
-SET(CLI_CODENAME "${PROJECT_NAME}-cli")
+SET(CLI_CODENAME "vsm")
 ADD_EXECUTABLE(${CLI_CODENAME} ${cli_SRCS})
 
 ## Readline detection ##########################################################
@@ -52,11 +52,11 @@ INCLUDE_DIRECTORIES(${CLIENT_FOLDER})
 INCLUDE_DIRECTORIES(${COMMON_FOLDER})
 TARGET_LINK_LIBRARIES(${CLI_CODENAME} ${PROJECT_NAME}-client ${LIB_DEPS_LIBRARIES} Ipc)
 
-CONFIGURE_FILE(support/vasum-cli-completion.sh.in
-               ${CMAKE_BINARY_DIR}/vasum-cli-completion.sh
+CONFIGURE_FILE(support/vsm-completion.sh.in
+               ${CMAKE_BINARY_DIR}/vsm-completion.sh
                @ONLY)
 
 ## Install #####################################################################
 INSTALL(TARGETS ${CLI_CODENAME} DESTINATION bin)
-INSTALL(FILES   ${CMAKE_BINARY_DIR}/vasum-cli-completion.sh
+INSTALL(FILES   ${CMAKE_BINARY_DIR}/vsm-completion.sh
         DESTINATION ${SYSCONF_INSTALL_DIR}/bash_completion.d)
index 1338afe..e8922ee 100644 (file)
 #include <iomanip>
 #include <algorithm>
 #include <vector>
-#include <fcntl.h>
 #include <cassert>
+#include <fcntl.h>
 #include <linux/if_link.h>
 #include <arpa/inet.h>
 #include <unistd.h>
+#include <string.h>
 
 using namespace std;
 
@@ -72,15 +73,6 @@ std::string zoneStateToString(const VsmZoneState& state)
     return name;
 }
 
-std::string zoneToString(const VsmZone& zone)
-{
-    std::string out = std::string("Name: ") + vsm_zone_get_id(zone)
-        + std::string("\nTerminal: ") + std::to_string(vsm_zone_get_terminal(zone))
-        + std::string("\nState: ") + zoneStateToString(vsm_zone_get_state(zone))
-        + std::string("\nRoot: ") + vsm_zone_get_rootfs(zone);
-    return out;
-}
-
 std::string netdevTypeToString(const VsmNetdevType& netdevType)
 {
     std::string type;
@@ -139,14 +131,61 @@ enum macvlan_mode macvlanFromString(const std::string& mode) {
     throw runtime_error("Unsupported macvlan mode");
 }
 
+void buildZoneList(std::vector<std::string>& list)
+{
+    using namespace std::placeholders;
+    VsmArrayString ids;
+
+    CommandLineInterface::executeCallback(bind(vsm_get_zone_ids, _1, &ids));
+    for (VsmString* id = ids; *id; ++id) {
+        list.push_back(*id);
+    }
+    vsm_array_string_free(ids);
+}
+
 } // namespace
 
+const std::vector<std::string> CommandLineInterface::buildCompletionList(const Args& a) const
+{
+    std::vector<std::string> v;
+
+    if (a.size() > mArgsSpec.size() + 1) {
+        return v;
+    }
+
+    ArgSpec as = mArgsSpec[a.size() - 2];
+    string::size_type s = 0U;
+    string::size_type e = s;
+    while (e != string::npos) {
+        e = as.format.find('|', s);
+        std::string ss = as.format.substr(s, e - s);
+        s = e + 1;
+
+        if (ss == "{ZONE}") {
+            buildZoneList(v);
+        }
+        else if (ss == "{NETDEV}") {
+            //TODO: get list of available interfaces
+            v.push_back("lo");
+            v.push_back("eth0");
+        }
+        else if (ss.length() > 0) {
+            v.push_back(ss);
+        }
+    }
+
+    return v;
+}
+
 void CommandLineInterface::connect()
 {
     VsmStatus status;
+    if (CommandLineInterface::client != nullptr) {
+        return;
+    }
 
     CommandLineInterface::client = vsm_client_create();
-    if (NULL == CommandLineInterface::client) {
+    if (CommandLineInterface::client == nullptr) {
         throw runtime_error("Can't create client");
     }
 
@@ -154,7 +193,7 @@ void CommandLineInterface::connect()
     if (VSMCLIENT_SUCCESS != status) {
         string msg = vsm_get_status_message(CommandLineInterface::client);
         vsm_client_free(CommandLineInterface::client);
-        CommandLineInterface::client = NULL;
+        CommandLineInterface::client = nullptr;
         throw runtime_error(msg);
     }
 }
@@ -164,13 +203,17 @@ void CommandLineInterface::disconnect()
     string msg;
     VsmStatus status;
 
+    if (CommandLineInterface::client == nullptr) {
+        return ;
+    }
+
     status = vsm_disconnect(CommandLineInterface::client);
     if (VSMCLIENT_SUCCESS != status) {
         msg = vsm_get_status_message(CommandLineInterface::client);
     }
 
     vsm_client_free(CommandLineInterface::client);
-    CommandLineInterface::client = NULL;
+    CommandLineInterface::client = nullptr;
 
     if (VSMCLIENT_SUCCESS != status) {
         throw runtime_error(msg);
@@ -179,9 +222,9 @@ void CommandLineInterface::disconnect()
 
 void CommandLineInterface::executeCallback(const function<VsmStatus(VsmClient)>& fun)
 {
-    VsmStatus status;
+    CommandLineInterface::connect();
 
-    status = fun(CommandLineInterface::client);
+    VsmStatus status = fun(CommandLineInterface::client);
     if (VSMCLIENT_SUCCESS != status) {
         throw runtime_error(vsm_get_status_message(CommandLineInterface::client));
     }
@@ -201,7 +244,7 @@ void CommandLineInterface::printUsage(std::ostream& out) const
 {
     out << mName;
     for (const auto& args : mArgsSpec) {
-        out << " " << args.first;
+        out << " " << args.name;
     }
 
     out << "\n\n"
@@ -211,7 +254,7 @@ void CommandLineInterface::printUsage(std::ostream& out) const
     if (!mArgsSpec.empty()) {
         out << "\n\tOptions\n";
         for (const auto& args : mArgsSpec) {
-            out << "\t\t" << args.first << " -- " << args.second << "\n";
+            out << "\t\t" << args.name << " -- " << args.description << "\n";
         }
     }
     out << "\n";
@@ -222,7 +265,7 @@ bool CommandLineInterface::isAvailable(unsigned int mode) const
     return (mAvailability & mode) == mode;
 }
 
-void CommandLineInterface::execute(const Args& argv)
+void CommandLineInterface::execute(const Args& argv) const
 {
     mExecutorCallback(argv);
 }
@@ -242,7 +285,7 @@ void set_active_zone(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argv.size() <= 1) {
+    if (argv.size() < 2) {
         throw runtime_error("Not enough parameters");
     }
 
@@ -253,7 +296,7 @@ void create_zone(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argv.size() <= 1) {
+    if (argv.size() < 2) {
         throw runtime_error("Not enough parameters");
     }
 
@@ -268,7 +311,7 @@ void destroy_zone(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argv.size() <= 1) {
+    if (argv.size() < 2) {
         throw runtime_error("Not enough parameters");
     }
 
@@ -279,7 +322,7 @@ void shutdown_zone(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argv.size() <= 1) {
+    if (argv.size() < 2) {
         throw runtime_error("Not enough parameters");
     }
 
@@ -290,7 +333,7 @@ void start_zone(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argv.size() <= 1) {
+    if (argv.size() < 2) {
         throw runtime_error("Not enough parameters");
     }
 
@@ -301,7 +344,7 @@ void console_zone(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argv.size() <= 1) {
+    if (argv.size() < 2) {
         throw runtime_error("Not enough parameters");
     }
 
@@ -310,7 +353,7 @@ void console_zone(const Args& argv)
 
     if (zoneStateToString(vsm_zone_get_state(zone)) != "RUNNING") {
         vsm_zone_free(zone);
-        throw runtime_error("Zone is not running");
+        throw runtime_error("Zone '" + argv[1] + "' is not running");
     }
 
     std::string zonesPath = vsm_zone_get_rootfs(zone);
@@ -334,7 +377,7 @@ void lock_zone(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argv.size() <= 1) {
+    if (argv.size() < 2) {
         throw runtime_error("Not enough parameters");
     }
 
@@ -345,14 +388,14 @@ void unlock_zone(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argv.size() <= 1) {
+    if (argv.size() < 2) {
         throw runtime_error("Not enough parameters");
     }
 
     CommandLineInterface::executeCallback(bind(vsm_unlock_zone, _1, argv[1].c_str()));
 }
 
-void get_zones_status(const Args& /* argv */)
+void get_zones_status(const Args& argv)
 {
     using namespace std::placeholders;
 
@@ -360,14 +403,24 @@ void get_zones_status(const Args& /* argv */)
     VsmString activeId;
     Table table;
 
-    CommandLineInterface::executeCallback(bind(vsm_get_zone_ids, _1, &ids));
+    if (argv.size() < 2) {
+        CommandLineInterface::executeCallback(bind(vsm_get_zone_ids, _1, &ids));
+    }
+    else {
+        ids = reinterpret_cast<char**>(calloc(argv.size(), sizeof(char*)));
+        for (unsigned i = 1; i<argv.size(); ++i) {
+            ids[i - 1] = ::strdup(argv[i].c_str());
+        }
+    }
+
+
     CommandLineInterface::executeCallback(bind(vsm_get_active_zone_id, _1, &activeId));
     table.push_back({"Active", "Id", "State", "Terminal", "Root"});
     for (VsmString* id = ids; *id; ++id) {
         VsmZone zone;
         CommandLineInterface::executeCallback(bind(vsm_lookup_zone_by_id, _1, *id, &zone));
         assert(string(vsm_zone_get_id(zone)) == string(*id));
-        table.push_back({string(vsm_zone_get_id(zone)) == string(activeId) ? "*" : "",
+        table.push_back({string(vsm_zone_get_id(zone)) == string(activeId) ? "YES" : "NO",
                          vsm_zone_get_id(zone),
                          zoneStateToString(vsm_zone_get_state(zone)),
                          to_string(vsm_zone_get_terminal(zone)),
@@ -394,7 +447,7 @@ void get_zone_ids(const Args& /*argv*/)
     vsm_array_string_free(ids);
 }
 
-void get_active_zone_id(const Args& /*argv*/)
+void get_active_zone(const Args& /*argv*/)
 {
     using namespace std::placeholders;
 
@@ -404,24 +457,11 @@ void get_active_zone_id(const Args& /*argv*/)
     vsm_string_free(id);
 }
 
-void lookup_zone_by_id(const Args& argv)
-{
-    using namespace std::placeholders;
-    if (argv.size() <= 1) {
-        throw runtime_error("Not enough parameters");
-    }
-
-    VsmZone zone;
-    CommandLineInterface::executeCallback(bind(vsm_lookup_zone_by_id, _1, argv[1].c_str(), &zone));
-    cout << zoneToString(zone) << endl;
-    vsm_zone_free(zone);
-}
-
 void grant_device(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argv.size() <= 2) {
+    if (argv.size() < 3) {
         throw runtime_error("Not enough parameters");
     }
 
@@ -433,78 +473,61 @@ void revoke_device(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argv.size() <= 2) {
+    if (argv.size() < 3) {
         throw runtime_error("Not enough parameters");
     }
 
     CommandLineInterface::executeCallback(bind(vsm_revoke_device, _1, argv[1].c_str(), argv[2].c_str()));
 }
 
-void create_netdev_veth(const Args& argv)
+void create_netdev(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argv.size() <= 3) {
+    if (argv.size() < 2) {
         throw runtime_error("Not enough parameters");
     }
-    CommandLineInterface::executeCallback(bind(vsm_create_netdev_veth,
+
+    std::string nettype = argv[2];
+    if (nettype == "phys") {
+        if (argv.size() < 4) {
+            throw runtime_error("Not enough parameters");
+        }
+        CommandLineInterface::executeCallback(bind(vsm_create_netdev_phys,
                   _1,
                   argv[1].c_str(),
-                  argv[2].c_str(),
                   argv[3].c_str()));
-}
-
-void create_netdev_macvlan(const Args& argv)
-{
-    using namespace std::placeholders;
-
-    if (argv.size() <= 4) {
-        throw runtime_error("Not enough parameters");
     }
-    CommandLineInterface::executeCallback(bind(vsm_create_netdev_macvlan,
+    else if (nettype == "veth") {
+        if (argv.size() < 5) {
+            throw runtime_error("Not enough parameters");
+        }
+        CommandLineInterface::executeCallback(bind(vsm_create_netdev_veth,
                   _1,
                   argv[1].c_str(),
-                  argv[2].c_str(),
                   argv[3].c_str(),
-                  macvlanFromString(argv[4].c_str())));
-}
-
-void create_netdev_phys(const Args& argv)
-{
-    using namespace std::placeholders;
-
-    if (argv.size() <= 2) {
-        throw runtime_error("Not enough parameters");
+                  argv[4].c_str()));
     }
-    CommandLineInterface::executeCallback(bind(vsm_create_netdev_phys,
+    else if (nettype == "macvlan") {
+        if (argv.size() < 6) {
+            throw runtime_error("Not enough parameters");
+        }
+        CommandLineInterface::executeCallback(bind(vsm_create_netdev_macvlan,
                   _1,
                   argv[1].c_str(),
-                  argv[2].c_str()));
-}
-
-void lookup_netdev_by_name(const Args& argv)
-{
-    using namespace std::placeholders;
-
-    if (argv.size() <= 2) {
-        throw runtime_error("Not enough parameters");
+                  argv[3].c_str(),
+                  argv[4].c_str(),
+                  macvlanFromString(argv[5].c_str())));
     }
-    VsmNetdev netdev = NULL;
-    CommandLineInterface::executeCallback(bind(vsm_lookup_netdev_by_name,
-                  _1,
-                  argv[1].c_str(),
-                  argv[2].c_str(),
-                  &netdev));
-    cout << netdevToString(netdev) << endl;
-    vsm_netdev_free(netdev);
-
+    else
+        throw runtime_error("Wrong nettype option " + nettype);
 }
 
 void destroy_netdev(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argv.size() <= 2) {
+    if (argv.size() < 3) {
         throw runtime_error("Not enough parameters");
     }
     CommandLineInterface::executeCallback(bind(vsm_destroy_netdev,
@@ -513,113 +536,134 @@ void destroy_netdev(const Args& argv)
                   argv[2].c_str()));
 }
 
-void zone_get_netdevs(const Args& argv)
+void netdev_list(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argv.size() <= 1) {
+    if (argv.size() < 2) {
         throw runtime_error("Not enough parameters");
     }
-    VsmArrayString ids;
-    CommandLineInterface::executeCallback(bind(vsm_zone_get_netdevs,
+    if (argv.size() < 3) {
+        VsmArrayString ids;
+        CommandLineInterface::executeCallback(bind(vsm_zone_get_netdevs,
                   _1,
                   argv[1].c_str(),
                   &ids));
-    string delim;
-    for (VsmString* id = ids; *id; ++id) {
-        cout << delim << *id;
-        delim = ", ";
-    }
-    if (delim.empty()) {
-        cout << "There is no network device in zone";
-    }
-    cout << endl;
-    vsm_array_string_free(ids);
-}
+        string delim;
+        for (VsmString* id = ids; *id; ++id) {
+            cout << delim << *id;
+            delim = ", ";
+        }
+        if (delim.empty()) {
+            cout << "There is no network device in zone";
+        }
+        cout << endl;
+        vsm_array_string_free(ids);
+    }
+    else {
+        VsmNetdev netdev = NULL;
+        in_addr ipv4;
+        in6_addr ipv6;
+        char buf[INET_ADDRSTRLEN|INET6_ADDRSTRLEN];
+        CommandLineInterface::executeCallback(bind(vsm_lookup_netdev_by_name,
+                  _1,
+                  argv[1].c_str(),
+                  argv[2].c_str(),
+                  &netdev));
+        cout << netdevToString(netdev) << endl;
+        vsm_netdev_free(netdev);
 
-void netdev_get_ipv4_addr(const Args& argv)
-{
-    using namespace std::placeholders;
+        CommandLineInterface::executeCallback(bind(vsm_netdev_get_ipv4_addr,
+                  _1,
+                  argv[1].c_str(),
+                  argv[2].c_str(),
+                  &ipv4));
+        if (inet_ntop(AF_INET, &ipv4, buf, INET_ADDRSTRLEN) == NULL) {
+            throw runtime_error("Wrong address received");
+        }
+        cout << buf << endl;
 
-    if (argv.size() <= 2) {
-        throw runtime_error("Not enough parameters");
-    }
-    in_addr addr;
-    CommandLineInterface::executeCallback(bind(vsm_netdev_get_ipv4_addr,
+        CommandLineInterface::executeCallback(bind(vsm_netdev_get_ipv6_addr,
                   _1,
                   argv[1].c_str(),
                   argv[2].c_str(),
-                  &addr));
-    char buf[INET_ADDRSTRLEN];
-    if (inet_ntop(AF_INET, &addr, buf, INET_ADDRSTRLEN) == NULL) {
-        throw runtime_error("Server gave the wrong address format");
+                  &ipv6));
+        if (inet_ntop(AF_INET6, &ipv6, buf, INET6_ADDRSTRLEN) == NULL) {
+            throw runtime_error("Wrong address received");
+        }
+        cout << buf << endl;
     }
-    cout << buf << endl;
 }
 
-void netdev_get_ipv6_addr(const Args& argv)
+void netdev_add_ip_addr(const Args& argv)
 {
     using namespace std::placeholders;
-
-    if (argv.size() <= 2) {
+    if (argv.size() < 5) {
         throw runtime_error("Not enough parameters");
     }
-    in6_addr addr;
-    CommandLineInterface::executeCallback(bind(vsm_netdev_get_ipv6_addr,
+    if (argv[3].find(':') == std::string::npos) {
+        in_addr addr;
+        if (inet_pton(AF_INET, argv[3].c_str(), &addr) != 1) {
+            throw runtime_error("Wrong address format");
+        };
+        CommandLineInterface::executeCallback(bind(vsm_netdev_set_ipv4_addr,
+                  _1,
+                  argv[1].c_str(),
+                  argv[2].c_str(),
+                  &addr,
+                  stoi(argv[4].c_str())));
+    }
+    else {
+        in6_addr addr;
+        if (inet_pton(AF_INET6, argv[3].c_str(), &addr) != 1) {
+            throw runtime_error("Wrong address format");
+        };
+        CommandLineInterface::executeCallback(bind(vsm_netdev_set_ipv6_addr,
                   _1,
                   argv[1].c_str(),
                   argv[2].c_str(),
-                  &addr));
-    char buf[INET6_ADDRSTRLEN];
-    if (inet_ntop(AF_INET6, &addr, buf, INET6_ADDRSTRLEN) == NULL) {
-        throw runtime_error("Server gave the wrong address format");
+                  &addr,
+                  stoi(argv[4].c_str())));
     }
-    cout << buf << endl;
 }
 
-void netdev_set_ipv4_addr(const Args& argv)
+void netdev_del_ip_addr(const Args& argv)
 {
     using namespace std::placeholders;
-
-    if (argv.size() <= 4) {
+    if (argv.size() < 5) {
         throw runtime_error("Not enough parameters");
     }
-    in_addr addr;
-    if (inet_pton(AF_INET, argv[3].c_str(), &addr) != 1) {
-        throw runtime_error("Wrong address format");
-    };
-    CommandLineInterface::executeCallback(bind(vsm_netdev_set_ipv4_addr,
+    if (argv[3].find(':') == std::string::npos) {
+        in_addr addr;
+        if (inet_pton(AF_INET, argv[3].c_str(), &addr) != 1) {
+            throw runtime_error("Wrong address format");
+        };
+        CommandLineInterface::executeCallback(bind(vsm_netdev_del_ipv4_addr,
                   _1,
                   argv[1].c_str(),
                   argv[2].c_str(),
                   &addr,
                   stoi(argv[4].c_str())));
-}
-
-void netdev_set_ipv6_addr(const Args& argv)
-{
-    using namespace std::placeholders;
-
-    if (argv.size() <= 4) {
-        throw runtime_error("Not enough parameters");
     }
-    in6_addr addr;
-    if (inet_pton(AF_INET6, argv[3].c_str(), &addr) != 1) {
-        throw runtime_error("Wrong address format");
-    };
-    CommandLineInterface::executeCallback(bind(vsm_netdev_set_ipv6_addr,
+    else {
+        in6_addr addr;
+        if (inet_pton(AF_INET6, argv[3].c_str(), &addr) != 1) {
+            throw runtime_error("Wrong address format");
+        };
+        CommandLineInterface::executeCallback(bind(vsm_netdev_del_ipv6_addr,
                   _1,
                   argv[1].c_str(),
                   argv[2].c_str(),
                   &addr,
                   stoi(argv[4].c_str())));
+    }
 }
 
 void netdev_up(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argv.size() <= 2) {
+    if (argv.size() < 3) {
         throw runtime_error("Not enough parameters");
     }
     CommandLineInterface::executeCallback(bind(vsm_netdev_up,
@@ -632,7 +676,7 @@ void netdev_down(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argv.size() <= 2) {
+    if (argv.size() < 3) {
         throw runtime_error("Not enough parameters");
     }
     CommandLineInterface::executeCallback(bind(vsm_netdev_down,
index f13fab5..e63549b 100644 (file)
@@ -40,6 +40,12 @@ namespace cli {
 
 typedef std::vector<std::string> Args;
 
+struct ArgSpec {
+    std::string name;
+    std::string description;
+    std::string format;
+};
+
 /**
  * Class that implements command pattern.
  */
@@ -54,7 +60,7 @@ public:
     /**
      * @see CommandLineInterface::CommandLineInterface
      */
-    typedef std::vector<std::pair<std::string, std::string>> ArgsSpec;
+    typedef std::vector<ArgSpec> ArgsSpec;
 
     /**
      * Dummy constructor (for stl usage)
@@ -133,8 +139,9 @@ public:
      *
      * @param argv Command line arguments
      */
-    void execute(const Args& argv);
+    void execute(const Args& argv) const;
 
+    const std::vector<std::string> buildCompletionList(const Args& argv) const;
 
 private:
     static VsmClient client;
@@ -233,14 +240,7 @@ void get_zone_ids(const Args& argv);
  *
  * @see vsm_get_active_zone_id
  */
-void get_active_zone_id(const Args& argv);
-
-/**
- * Parses command line arguments and call vsm_lookup_zone_by_id
- *
- * @see vsm_lookup_zone_by_id
- */
-void lookup_zone_by_id(const Args& argv);
+void get_active_zone(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_grant_device
@@ -257,32 +257,11 @@ void grant_device(const Args& argv);
 void revoke_device(const Args& argv);
 
 /**
- * Parses command line arguments and call vsm_create_netdev_veth
- *
- * @see vsm_create_netdev_veth
- */
-void create_netdev_veth(const Args& argv);
-
-/**
- * Parses command line arguments and call vsm_create_netdev_macvlan
- *
- * @see vsm_create_netdev_macvlan
- */
-void create_netdev_macvlan(const Args& argv);
-
-/**
- * Parses command line arguments and call vsm_create_netdev_phys
+ * Parses command line arguments and call vsm_create_netdev_*
  *
- * @see vsm_create_netdev_phys
+ * @see vsm_create_netdev_veth,vsm_create_netdev_macvlan,vsm_create_netdev_phys
  */
-void create_netdev_phys(const Args& argv);
-
-/**
- * Parses command line arguments and call vsm_lookup_netdev_by_name
- *
- * @see vsm_lookup_netdev_by_name
- */
-void lookup_netdev_by_name(const Args& argv);
+void create_netdev(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_destroy_netdev
@@ -292,39 +271,27 @@ void lookup_netdev_by_name(const Args& argv);
 void destroy_netdev(const Args& argv);
 
 /**
- * Parses command line arguments and prints result of vsm_zone_get_netdevs
- *
- * @see vsm_zone_get_netdevs
- */
-void zone_get_netdevs(const Args& argv);
-
-/**
- * Parses command line arguments and prints result of vsm_netdev_get_ipv4_addr
- *
- * @see vsm_netdev_get_ipv4_addr
- */
-void netdev_get_ipv4_addr(const Args& argv);
-
-/**
- * Parses command line arguments and and prints result of vsm_netdev_get_ipv6_addr
+ * Parses command line arguments and prints result of vsm_zone_get_netdevs,
+ *  vsm_lookup_netdev_by_name, vsm_netdev_get_ipv4_addr, vsm_netdev_get_ipv6_addr
  *
- * @see vsm_netdev_get_ipv6_addr
+ * @see vsm_zone_get_netdevs, vsm_lookup_netdev_by_name,
+ * @see vsm_netdev_get_ipv4_addr, vsm_netdev_get_ipv6_addr
  */
-void netdev_get_ipv6_addr(const Args& argv);
+void netdev_list(const Args& argv);
 
 /**
- * Parses command line arguments and call vsm_netdev_set_ipv4_addr
+ * Parses command line arguments and call vsm_netdev_set_ipv4_addr, vsm_netdev_set_ipv6_addr
  *
- * @see vsm_netdev_set_ipv4_addr
+ * @see vsm_netdev_set_ipv4_addr, vsm_netdev_set_ipv6_addr
  */
-void netdev_set_ipv4_addr(const Args& argv);
+void netdev_add_ip_addr(const Args& argv);
 
 /**
- * Parses command line arguments and call vsm_netdev_set_ipv6_addr
+ * Parses command line arguments and call vsm_netdev_del_ipv4_addr, vsm_netdev_del_ipv6_addr
  *
- * @see vsm_netdev_set_ipv6_addr
+ * @see vsm_netdev_del_ipv4_addr, vsm_netdev_del_ipv6_addr
  */
-void netdev_set_ipv6_addr(const Args& argv);
+void netdev_del_ip_addr(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_netdev_up
index efff7c6..9bec3e7 100644 (file)
@@ -45,305 +45,195 @@ using namespace vasum::cli;
 namespace {
 
 static int interactiveMode = 0;
-std::map<std::string, CommandLineInterface> commands = {
+std::vector<CommandLineInterface> commands = {
     {
-        "lock_queue", {
-            lock_queue,
-            "lock_queue",
-            "Exclusively lock the command queue",
-            MODE_INTERACTIVE,
-            {}
-        }
-    },
-    {
-        "unlock_queue", {
-            unlock_queue,
-            "unlock_queue",
-            "Unlock the queue",
-            MODE_INTERACTIVE,
-            {}
-        }
-    },
-    {
-        "set_active_zone", {
-            set_active_zone,
-            "set_active_zone",
-            "Set active (foreground) zone",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"}}
-        }
-    },
-    {
-        "create_zone", {
-            create_zone,
-            "create_zone",
-            "Create and add zone",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"},
-             {"[zone_tname]", "optional zone template name"}}
-        }
-    },
-    {
-        "destroy_zone", {
-            destroy_zone,
-            "destroy_zone",
-            "Destroy zone",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"}}
-        }
-    },
-    {
-        "shutdown_zone", {
-            shutdown_zone,
-            "shutdown_zone",
-            "Shutdown zone",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"}}
-        }
+        create_zone,
+        "create",
+        "Create and add zone",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {{"zone_id", "zone name", ""},
+         {"[zone_tname]", "optional zone template name", ""}}
     },
     {
-        "start_zone", {
-            start_zone,
-            "start_zone",
-            "Start zone",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"}}
-        }
+        destroy_zone,
+        "destroy",
+        "Destroy zone",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {{"zone_id", "zone name", "{ZONE}"}}
     },
     {
-        "console_zone", {
-            console_zone,
-            "console_zone",
-            "Log into zone",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"}}
-        }
-    },
-    {
-        "lock_zone", {
-            lock_zone,
-            "lock_zone",
-            "Lock zone",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"}}
-        }
+        start_zone,
+        "start",
+        "Start zone",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {{"zone_id", "zone name", "{ZONE}"}}
     },
     {
-        "unlock_zone", {
-            unlock_zone,
-            "unlock_zone",
-            "Unlock zone",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"}}
-        }
+        console_zone,
+        "console",
+        "Attach to zone text console",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {{"zone_id", "zone name", "{ZONE}"}}
     },
     {
-        "get_zones_status", {
-            get_zones_status,
-            "get_zones_status",
-            "Get list of zone with some useful informations (id, state, terminal, root path)",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {}
-        }
+        shutdown_zone,
+        "shutdown",
+        "Shutdown zone",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {{"zone_id", "zone name", "{ZONE}"}}
     },
     {
-        "get_zone_ids", {
-            get_zone_ids,
-            "get_zone_ids",
-            "Get all zone ids",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {}
-        }
+        lock_zone,
+        "suspend",
+        "Suspend (lock) zone",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {{"zone_id", "zone name", "{ZONE}"}}
     },
     {
-        "get_active_zone_id", {
-            get_active_zone_id,
-            "get_active_zone_id",
-            "Get active (foreground) zone ids",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {}
-        }
+        unlock_zone,
+        "resume",
+        "Resume (unlock) zone",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {{"zone_id", "zone name", "{ZONE}"}}
     },
     {
-        "lookup_zone_by_id", {
-            lookup_zone_by_id,
-            "lookup_zone_by_id",
-            "Prints informations about zone",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"}}
-        }
+        set_active_zone,
+        "set-active",
+        "Set active (foreground) zone",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {{"zone_id", "zone name", "{ZONE}"}}
     },
     {
-        "grant_device", {
-            grant_device,
-            "grant_device",
-            "Grants access to the given device",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"},
-             {"device_name", " device name"}}
-        }
+        get_active_zone,
+        "get-active",
+        "Get active (foreground) zone",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {}
     },
     {
-        "revoke_device", {
-            revoke_device,
-            "revoke_device",
-            "Revokes access to the given device",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"},
-             {"device_name", " device name"}}
-        }
+        get_zone_ids,
+        "list",
+        "Get available zone ids",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {}
     },
     {
-        "create_netdev_veth", {
-            create_netdev_veth,
-            "create_netdev_veth",
-            "Create netdev in zone",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"},
-             {"zone_netdev_id", "network device id"},
-             {"host_netdev_id", "host bridge id"}}
-        }
+        get_zones_status,
+        "status",
+        "List status for one or all zones (id, state, terminal, root path)",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {{"[zone_id]", "zone name", "{ZONE}"}}
     },
     {
-        "create_netdev_macvlan", {
-            create_netdev_macvlan,
-            "create_netdev_macvlan",
-            "Create netdev in zone",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"},
-             {"zone_netdev_id", "network device id"},
-             {"host_netdev_id", "host bridge id"},
-             {"mode", "macvlan mode (private, vepa, bridge, passthru)"}}
-        }
+        clean_up_zones_root,
+        "clean",
+        "Clean up zones root directory",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {}
     },
     {
-        "create_netdev_phys", {
-            create_netdev_phys,
-            "create_netdev_phys",
-            "Create/move netdev to zone",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"},
-             {"netdev_id", "network device name"}}
-        }
+        grant_device,
+        "device-grant",
+        "Grants access to the given device",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {{"zone_id", "zone name", "{ZONE}"},
+         {"device", "device name", ""}}
     },
     {
-        "lookup_netdev_by_name", {
-            lookup_netdev_by_name,
-            "lookup_netdev_by_name",
-            "Get netdev flags",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"},
-             {"netdev_id", "network device name"}}
-        }
+        revoke_device,
+        "device-revoke",
+        "Revokes access to the given device",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {{"zone_id", "zone name", "{ZONE}"},
+         {"device", "device name", ""}}
     },
     {
-        "destroy_netdev", {
-            destroy_netdev,
-            "destroy_netdev",
-            "Destroy netdev in zone",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"},
-             {"netdev_id", "network device name"}}
-        }
+        create_netdev,
+        "net-create",
+        "Create network interface in zone",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {
+         {"zone_id", "zone name", "{ZONE}"},
+         {"netdevtype", "interface  type", "macvlan|phys|veth"},
+         {"zone_netdev", "interface name (eth0)", "eth0|eth1"},
+         {"host_netdev", "bridge name (virbr0)", "virbr0|virbr1"},
+         {"mode", "macvlan mode (private, vepa, bridge, passthru)", "private|vepa|bridge|passthru"}}
     },
     {
-        "zone_get_netdevs", {
-            zone_get_netdevs,
-            "zone_get_netdevs",
-            "List network devices in the zone",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"}}
-        }
+        destroy_netdev,
+        "net-destroy",
+        "Destroy netdev in zone",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {{"zone_id", "zone name", "{ZONE}"},
+         {"netdev", "interface name (eth0)", "{NETDEV}"}}
     },
     {
-        "netdev_get_ipv4_addr", {
-            netdev_get_ipv4_addr,
-            "netdev_get_ipv4_addr",
-            "Get ipv4 address",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"},
-             {"netdev_id", "network device name"}}
-        }
+        netdev_list,
+        "net-list",
+        "List network devices in the zone",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {{"zone_id", "zone name", "{ZONE}"},
+         {"[netdev]", "interface name (eth0)", "{NETDEV}"}}
     },
     {
-        "netdev_get_ipv6_addr", {
-            netdev_get_ipv6_addr,
-            "netdev_get_ipv6_addr",
-            "Get ipv6 address",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"},
-             {"netdev_id", "network device name"}}
-        }
+        netdev_up,
+        "net-up",
+        "Setup a network device in the zone up",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {{"zone_id", "zone name", "{ZONE}"},
+         {"netdev", "interface name (eth0)", "{NETDEV}"}}
     },
     {
-        "netdev_set_ipv4_addr", {
-            netdev_set_ipv4_addr,
-            "netdev_set_ipv4_addr",
-            "Set ipv4 address",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"},
-             {"netdev_id", "network device name"},
-             {"address", "ipv4 address"},
-             {"prefix_len", "bit length of prefix"}}
-        }
+        netdev_down,
+        "net-down",
+        "Setup a network device in the zone down",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {{"zone_id", "zone name", "{ZONE}"},
+         {"netdev", "interface name (eth0)", "{NETDEV}"}}
     },
     {
-        "netdev_set_ipv6_addr", {
-            netdev_set_ipv6_addr,
-            "netdev_set_ipv6_addr",
-            "Set ipv6 address",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"},
-             {"netdev_id", "network device name"},
-             {"address", "ipv6 address"},
-             {"prefix_len", "bit length of prefix"}}
-        }
+        netdev_add_ip_addr,
+        "net-ip-add",
+        "Add ip/mask address to network interface",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {{"zone_id", "zone name", "{ZONE}"},
+         {"netdev", "interface name (eth0)", "{NETDEV}"},
+         {"ip", "address IPv4 or IPv6", ""},
+         {"prefix", "mask length in bits", "24"}}
     },
     {
-        "netdev_up", {
-            netdev_up,
-            "netdev_up",
-            "Turn up a network device in the zone",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"},
-             {"netdev_id", "network device id"}}
-        }
+        netdev_del_ip_addr,
+        "net-ip-del",
+        "Del ip/mask address from network interface",
+        MODE_COMMAND_LINE | MODE_INTERACTIVE,
+        {{"zone_id", "zone name", "{ZONE}"},
+         {"netdev", "interface name (eth0)", "{NETDEV}"},
+         {"ip", "address IPv4 or IPv6", ""},
+         {"prefix", "mask length in bits", "24"}}
     },
     {
-        "netdev_down", {
-            netdev_down,
-            "netdev_down",
-            "Turn down a network device in the zone",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"},
-             {"netdev_id", "network device id"}}
-        }
+        lock_queue,
+        "qlock",
+        "Exclusively lock the command queue",
+        MODE_INTERACTIVE,
+        {}
     },
     {
-        "zone_get_netdevs", {
-            zone_get_netdevs,
-            "zone_get_netdevs",
-            "Turn down a network device in the zone",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {{"zone_id", "id zone name"},
-             {"netdev_id", "network device id"}}
-        }
+        unlock_queue,
+        "qunlock",
+        "Unlock the queue",
+        MODE_INTERACTIVE,
+        {}
     },
-    {
-        "clean_up_zones_root", {
-            clean_up_zones_root,
-            "clean_up_zones_root",
-            "Clean up zones root directory",
-            MODE_COMMAND_LINE | MODE_INTERACTIVE,
-            {}
-        }
-    }
 };
 
+std::map<std::string,const CommandLineInterface> commandMap;
+
 // wrappers for CommandLineInterface
 
 void printUsage(std::ostream& out, const std::string& name, unsigned int mode)
 {
+    const std::vector<std::string> addLineBefore = {"device-grant", "net-create", "qlock"};
     std::string n;
     if (!name.empty()) {
         n = name + " ";
@@ -361,9 +251,12 @@ void printUsage(std::ostream& out, const std::string& name, unsigned int mode)
     out << "command can be one of the following:\n";
 
     for (const auto& command : commands) {
-        if (command.second.isAvailable(mode)) {
-            out << "   " << std::setw(30) << std::left << command.second.getName()
-                << command.second.getDescription() << "\n";
+        if (command.isAvailable(mode)) {
+            if (std::find(addLineBefore.begin(), addLineBefore.end(), command.getName()) != addLineBefore.end()) {
+                out << std::endl;
+            }
+            out << "   " << std::setw(25) << std::left << command.getName()
+                << command.getDescription() << std::endl;
         }
     }
 
@@ -396,13 +289,14 @@ int disconnect()
 
 int executeCommand(const Args& argv, int mode)
 {
-    auto pair = commands.find(argv[0]);
-    if (pair == commands.end()) {
+    auto pair = commandMap.find(argv[0]);
+    if (pair == commandMap.end()) {
         return EXIT_FAILURE;
     }
 
-    CommandLineInterface& command = pair->second;
+    const CommandLineInterface& command = pair->second;
     if (!command.isAvailable(mode)) {
+        std::cerr << "Command not available in this mode" << std::endl;
         return EXIT_FAILURE;
     }
 
@@ -423,81 +317,43 @@ int executeCommand(const Args& argv, int mode)
 }
 
 // readline completion support
-
-char* object_generator(const char* text, int state)
+const std::vector<std::string> buildComplList(const Args& argv);
+char *completion_generator(const char* text, int state)
 {
-    static std::vector<std::string> objs;
-    static size_t len = 0, index = 0;
-
+    static std::vector<std::string> list;
+    static unsigned index = 0;
     if (state == 0) {
-        objs.clear();
-        len = ::strlen(text);
+        list.clear();
         index = 0;
 
-        using namespace std::placeholders;
-        VsmArrayString ids = NULL;
-        try {
-            CommandLineInterface::executeCallback(bind(vsm_get_zone_ids, _1, &ids));
-        } catch (const std::runtime_error& ex) {
-            // Quietly ignore, nothing we can do anyway
-        }
-
-        if (ids != NULL) {
-            for (VsmString* id = ids; *id; ++id) {
-                if (::strncmp(text, *id, len) == 0) {
-                    objs.push_back(*id);
-                }
-            }
+        char *ln = rl_line_buffer;
+        std::istringstream iss(ln);
+        Args argv{std::istream_iterator<std::string>{iss},
+                  std::istream_iterator<std::string>{}};
 
-            vsm_array_string_free(ids);
+        size_t len = strlen(text);
+        if (len == 0 && argv.size() > 0) {
+            argv.push_back("");
         }
-    }
 
-    if (index < objs.size()) {
-        return ::strdup(objs[index++].c_str());
-    }
-
-    return NULL;
-}
-
-char* cmd_generator(const char* text, int state)
-{
-    static std::vector<std::string> cmds;
-    static size_t len = 0, index = 0;
-
-    if (state == 0) {
-        cmds.clear();
-        len = ::strlen(text);
-        index = 0;
-
-        for (const auto& command : commands) {
-            if (command.second.isAvailable(MODE_INTERACTIVE)) {
-                const std::string& cmd = command.second.getName();
-                if (::strncmp(text, cmd.c_str(), len) == 0) {
-                    cmds.push_back(cmd);
-                }
+        const std::vector<std::string>& l = buildComplList(argv);
+        for (const auto &i : l) {
+            if (strncmp(text, i.c_str(), len) == 0) {
+                list.push_back(i);
             }
         }
     }
-
-    if (index < cmds.size()) {
-        return ::strdup(cmds[index++].c_str());
+    if (index < list.size()) {
+        return ::strdup(list[index++].c_str());
     }
 
     return NULL;
 }
 
-char** completion(const char* text, int start, int /*end*/)
+char** completion(const char* text, int /*start*/, int /*end*/)
 {
-    char **matches = NULL;
-
-    if (start == 0) {
-        matches = ::rl_completion_matches(text, &cmd_generator);
-    } else {
-        matches = ::rl_completion_matches(text, &object_generator);
-    }
-
-    return matches;
+    ::rl_attempted_completion_over = 1; //disable default completion
+    return ::rl_completion_matches(text, &completion_generator);
 }
 
 static bool readline_from(const std::string& prompt, std::istream& stream, std::string& ln)
@@ -529,6 +385,7 @@ static int processStream(std::istream& stream)
         return EXIT_FAILURE;
     }
 
+    int rc = EXIT_FAILURE;
     std::string ln;
     while (readline_from(">>> ", stream, ln)) {
         if (ln.empty() || ln[0] == '#') { //skip empty line or comment
@@ -539,16 +396,18 @@ static int processStream(std::istream& stream)
         Args argv{std::istream_iterator<std::string>{iss},
                   std::istream_iterator<std::string>{}};
 
-        if (commands.count(argv[0]) == 0) {
+        if (commandMap.count(argv[0]) == 0) {
             printUsage(std::cout, "", MODE_INTERACTIVE);
             continue;
         }
 
-        executeCommand(argv, MODE_INTERACTIVE);
+        rc = executeCommand(argv, MODE_INTERACTIVE);
+        if (rc == EXIT_FAILURE && !interactiveMode) {
+            break;
+        }
     }
 
-    disconnect();
-    return EXIT_SUCCESS;
+    return rc;
 }
 
 static int processFile(const std::string& fn)
@@ -563,15 +422,43 @@ static int processFile(const std::string& fn)
     return processStream(stream);
 }
 
-int bashComplMode()
+void printList(const std::vector<std::string>& list)
 {
-    for (const auto& command : commands) {
-        if (command.second.isAvailable(MODE_COMMAND_LINE)) {
-            std::cout << command.second.getName() << "\n";
+    for (const auto& i : list) {
+        std::cout << i << std::endl;
+    }
+}
+
+const std::vector<std::string> buildComplList(const Args& argv)
+{
+    if (argv.size() < 2) {
+        std::vector<std::string> list;
+        for (const auto& command : commands) {
+            if (command.isAvailable(MODE_COMMAND_LINE)) {
+                list.push_back(command.getName());
+            }
+        }
+        return list;
+    } else {
+        std::string cmd = argv[0];
+        if (commandMap.find(cmd) != commandMap.end()) {
+            return commandMap[cmd].buildCompletionList(argv);
         }
+        return std::vector<std::string>();
     }
+}
 
-    return EXIT_SUCCESS;
+int bashComplMode(int argc, const char *argv[])
+{
+    int rc = EXIT_FAILURE;
+    try {
+        Args args(argv, argv + argc);
+        printList(buildComplList(args));
+        rc = EXIT_SUCCESS;
+    } catch (const std::runtime_error& ex) {
+    }
+
+    return rc;
 }
 
 int cliMode(const int argc, const char** argv)
@@ -581,22 +468,15 @@ int cliMode(const int argc, const char** argv)
         return EXIT_SUCCESS;
     }
 
-    if (commands.find(argv[1]) == commands.end()) {
+    if (commandMap.find(argv[1]) == commandMap.end()) {
         printUsage(std::cout, argv[0], MODE_COMMAND_LINE);
         return EXIT_FAILURE;
     }
 
-    //TODO: Connect when it is necessary
-    //f.e. vasum-cli <command> -h doesn't need connection
-    if (connect() != EXIT_SUCCESS) {
-        return EXIT_FAILURE;
-    }
-
     // pass all the arguments excluding argv[0] - the executable name
-    Args commandArgs(argv+1, argv+argc);
+    Args commandArgs(argv + 1, argv + argc);
     int rc = executeCommand(commandArgs, MODE_COMMAND_LINE);
 
-    disconnect();
     return rc;
 }
 
@@ -605,26 +485,38 @@ int cliMode(const int argc, const char** argv)
 
 int main(const int argc, const char *argv[])
 {
+    for (const auto& command : commands) {
+        commandMap.insert(std::pair<std::string,const CommandLineInterface>(command.getName(),command));
+    }
+
+    int rc = EXIT_FAILURE;
     if (argc > 1) {
+
         //process arguments
         if (std::string(argv[1]) == "--bash-completion") {
-            return bashComplMode();
-        }
-
-        if (std::string(argv[1]) == "-f") {
+            rc = bashComplMode(argc - 2, argv + 2);
+        } else if (std::string(argv[1]) == "-f") {
             if (argc < 3) {
                 std::cerr << "Filename expected" << std::endl;
-                return EXIT_FAILURE;
+                rc = EXIT_FAILURE;
+            }
+            else {
+                rc = processFile(std::string(argv[2]));
             }
-            return processFile(std::string(argv[2]));
+        } else {
+            rc = cliMode(argc, argv);
         }
 
-        return cliMode(argc, argv);
-    }
+    } else {
+
+        if (isatty(0) == 1) {
+            interactiveMode = 1;
+            ::rl_attempted_completion_function = completion;
+        }
 
-    if (isatty(0) == 1) {
-        interactiveMode = 1;
-        ::rl_attempted_completion_function = completion;
+        rc = processStream(std::cin);
     }
-    return processStream(std::cin);
+
+    disconnect();
+    return rc;
 }
diff --git a/cli/support/vasum-cli-completion.sh.in b/cli/support/vasum-cli-completion.sh.in
deleted file mode 100755 (executable)
index e04b56f..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-# Check for bash
-[ -z "$BASH_VERSION" ] && return
-
-__@PROJECT_NAME@_cli() {
-    local cur="${COMP_WORDS[COMP_CWORD]}"
-
-    COMPREPLY=()
-    if [ "$COMP_CWORD" == "1" ]; then
-        COMPREPLY=($(compgen -W "$(@CLI_CODENAME@ --bash-completion)" -- $cur))
-    elif [ "$COMP_CWORD" == "2" ]; then
-        COMPREPLY=($(compgen -W "-h" -- $cur))
-    fi
-}
-
-complete -F __@PROJECT_NAME@_cli @CLI_CODENAME@
diff --git a/cli/support/vsm-completion.sh.in b/cli/support/vsm-completion.sh.in
new file mode 100755 (executable)
index 0000000..1177348
--- /dev/null
@@ -0,0 +1,10 @@
+# Check for bash
+[ -z "$BASH_VERSION" ] && return
+
+__@PROJECT_NAME@_cli() {
+    local exe=$1 cur=$2 prev=$3
+    words=`@CLI_CODENAME@ --bash-completion "${COMP_WORDS[@]:1}"`
+    COMPREPLY=($(compgen -W "$words" -- $cur))
+}
+
+complete -F __@PROJECT_NAME@_cli vsm
index b048031..dd11ef3 100644 (file)
@@ -109,7 +109,7 @@ void Netlink::open(int netNsPid)
         mFd = utils::passNamespacedFd(netNsPid, CLONE_NEWNET, fdFactory);
     }
     if (mFd == -1) {
-        throw VasumException("Can't open netlink connection");
+        throw VasumException("Can't open netlink connection (zone not running)");
     }
 
     sockaddr_nl local = utils::make_clean<sockaddr_nl>();
@@ -178,13 +178,13 @@ std::unique_ptr<std::vector<char>> Netlink::rcv(unsigned int nlmsgSeq)
                 // It is NACK/ACK message
                 nlmsgerr *err = reinterpret_cast<nlmsgerr*>(NLMSG_DATA(answer));
                 if (answer->nlmsg_seq != nlmsgSeq) {
-                    throw VasumException("Sending failed: answer message was mismatched");
+                    throw VasumException("Receive failed: answer message was mismatched");
                 }
                 if (err->error) {
-                    throw VasumException("Sending failed: " + getSystemErrorMessage(-err->error));
+                    throw VasumException("Receive failed: " + getSystemErrorMessage(-err->error));
                 }
             } else if (answer->nlmsg_type == NLMSG_OVERRUN) {
-                throw VasumException("Sending failed: data lost");
+                throw VasumException("Receive failed: data lost");
             }
         }
         if (lastOk == NULL) {
index 0ec93da..22de326 100644 (file)
@@ -243,7 +243,7 @@ Command Line Interface for vasum.
 
 %files cli
 %defattr(644,root,root,755)
-%attr(755,root,root) %{_bindir}/vasum-cli
+%attr(755,root,root) %{_bindir}/vsm
 
 %package cli-completion
 Summary:          Vasum Command Line Interface bash completion
@@ -255,7 +255,7 @@ Requires:         vasum-cli = %{epoch}:%{version}-%{release}
 Command Line Interface bash completion for vasum.
 
 %files cli-completion
-%attr(755,root,root) %{_sysconfdir}/bash_completion.d/vasum-cli-completion.sh
+%attr(755,root,root) %{_sysconfdir}/bash_completion.d/vsm-completion.sh
 
 ## Test Package ################################################################
 %package tests
index 8c53d38..bf2576c 100644 (file)
@@ -488,35 +488,42 @@ Attrs getAttrs(const pid_t nsPid, const std::string& netdev)
     infoPeer.ifi_change = 0xFFFFFFFF;
     nlm.put(infoPeer)
         .put(IFLA_IFNAME, netdev);
-    NetlinkResponse response = send(nlm, nsPid);
-    if (!response.hasMessage()) {
-        throw VasumException("Can't get interface information");
-    }
-    response.fetch(infoPeer);
-
     Attrs attrs;
-    while (response.hasAttribute()) {
-        uint32_t mtu, link;
-        int attrType = response.getAttributeType();
-        switch (attrType) {
-            case IFLA_MTU:
-                response.fetch(IFLA_MTU, mtu);
-                attrs.push_back(make_tuple("mtu", std::to_string(mtu)));
-                break;
-            case IFLA_LINK:
-                response.fetch(IFLA_LINK, link);
-                attrs.push_back(make_tuple("link", std::to_string(link)));
-                break;
-            default:
-                response.skipAttribute();
-                break;
+    try {
+        NetlinkResponse response = send(nlm, nsPid);
+        if (!response.hasMessage()) {
+            throw VasumException("Can't get interface information");
         }
+        response.fetch(infoPeer);
+
+        while (response.hasAttribute()) {
+            uint32_t mtu, link;
+            int attrType = response.getAttributeType();
+            switch (attrType) {
+                case IFLA_MTU:
+                    response.fetch(IFLA_MTU, mtu);
+                    attrs.push_back(make_tuple("mtu", std::to_string(mtu)));
+                    break;
+                case IFLA_LINK:
+                    response.fetch(IFLA_LINK, link);
+                    attrs.push_back(make_tuple("link", std::to_string(link)));
+                    break;
+                default:
+                    response.skipAttribute();
+                    break;
+            }
+        }
+    } catch (const std::exception& ex) {
+        LOGE(ex.what());
+        throw VasumException(netdev + ": " + ex.what());
     }
+
     attrs.push_back(make_tuple("flags", std::to_string(infoPeer.ifi_flags)));
     attrs.push_back(make_tuple("type", std::to_string(infoPeer.ifi_type)));
     for (const auto& address : getIpAddresses(nsPid, AF_INET, infoPeer.ifi_index)) {
         attrs.push_back(make_tuple("ipv4", joinAddresses(address)));
     }
+
     for (const auto& address : getIpAddresses(nsPid, AF_INET6, infoPeer.ifi_index)) {
         attrs.push_back(make_tuple("ipv6", joinAddresses(address)));
     }