Loading CMakeLists.txt +2 −1 Changes for CMakeLists.txt: 2 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -100,7 +100,8 @@ webctl_module(lvm modules/lvm/lvm.cpp modules/common/storaged.cpp) target_link_libraries(webctl_mod_lvm PRIVATE sysbus::sysbus) webctl_module(disks modules/disks/disks.cpp modules/common/storaged.cpp) target_link_libraries(webctl_mod_disks PRIVATE sysbus::sysbus) # iSCSI initiator: a client of iscsid; needs iscsid/api.h (iscsid-dev) # iSCSI: initiator (a client of iscsid) and exported targets (sharedd); # needs iscsid/api.h (iscsid-dev) and sharedd/api.h (sharedd-dev) webctl_module(iscsi modules/iscsi/iscsi.cpp) target_link_libraries(webctl_mod_iscsi PRIVATE sysbus::sysbus) # mounts are syslite .mount units (sysbus CreateMount); mke2fs/mkfs.fat Loading modules/iscsi/iscsi.cpp +185 −15 Changes for modules/iscsi/iscsi.cpp: 185 added lines, 15 removed lines. Original line number Diff line number Diff line /* webctl module "iscsi": the iSCSI initiator iscsid, entirely over its * sysbus API (sysbus::Client::callService, include/iscsid/api.h). /* webctl module "iscsi": both sides of iSCSI, over sysbus * (sysbus::Client::callService) -- the initiator iscsid (disks of other * machines used here, include/iscsid/api.h) and the targets sharedd * exports with the kernel's LIO (volumes of this machine for others, * include/sharedd/api.h). Either works without the other daemon. * * Initiator (iscsid): * info -> {status{initiatorName, kernel, kernelError}, settings, * targets[{name, address, port, autoLogin, auth, username, * passwordSet, usernameIn, passwordInSet, state, error, sid, Loading @@ -17,6 +21,17 @@ * settings {initiatorName, initiatorAlias, loginTimeout, * replacementTimeout, noopInterval, noopTimeout} * reload * info also has initiatorUnavailable (why iscsid can't be asked) instead * of initiator/settings/targets * * Exported targets (sharedd): * info adds export{state, error, settings{enabled, baseName, portals[]}, * targets[{name, iqn, comment, luns[], readOnly, open, initiators[], * chapUser, chapPasswordSet, chapUserIn, chapPasswordInSet, sessions[], * error}], devices[{device, kind, size, inUse}]}, or exportUnavailable * exportCreate {target}, exportUpdate {name, target} (chapPassword / * chapPasswordIn missing or null: kept), exportDelete {name} * exportSettings {enabled, baseName, portals[]} */ #include <cstdlib> Loading @@ -24,6 +39,7 @@ #include <vector> #include <iscsid/api.h> #include <sharedd/api.h> #include <sysbus/client.h> #include <webctl/module.h> Loading @@ -39,29 +55,175 @@ const char *yesNo(bool b) { return b ? "yes" : "no"; } long long num(const std::string &v) { return std::strtoll(v.c_str(), nullptr, 10); } // an iscsid call; on failure the error object to return json_object *callIscsid(const char *method, const Fields &args, sysbus::Message &reply) { // a call to iscsid or sharedd; on failure the error object to return json_object *callDaemon(const char *service, const char *method, const Fields &args, sysbus::Message &reply) { sysbus::Client bus; if (!bus.connect()) return error(tr("sysbus isn't reachable: %1", "sysbus ist nicht erreichbar: %1", {bus.lastError()}), 503); Fields all = args; all.emplace_back("lang", webctl::language()); // discovery may wait for the login timeout of an unreachable portal reply = bus.callService(iscsid::Service, method, all, 120000); reply = bus.callService(service, method, all, 120000); if (reply.type != sysbus::MessageType::Error) return nullptr; const std::string &name = reply.member; if (name == sysbus::error::ServiceUnknown) return error(tr("iscsid isn't running (Services: iscsid)", "iscsid läuft nicht (Dienste: iscsid)"), 503); return error(tr("%1 isn't running (Services: %1)", "%1 läuft nicht (Dienste: %1)", {service}), 503); int status = name == sysbus::error::AccessDenied ? 403 : name == sysbus::error::InvalidArgs ? 400 : 500; return error(reply.get("message", name), status); } json_object *callIscsid(const char *method, const Fields &args, sysbus::Message &reply) { return callDaemon(iscsid::Service, method, args, reply); } json_object *callSharedd(const char *method, const Fields &args, sysbus::Message &reply) { return callDaemon(sharedd::Service, method, args, reply); } json_object *simple(const char *method, const Fields &f) { sysbus::Message r; if (json_object *e = callIscsid(method, f, r)) return e; return webctl::ok(); } json_object *simpleSharedd(const char *method, const Fields &f) { sysbus::Message r; if (json_object *e = callSharedd(method, f, r)) return e; return webctl::ok(); } json_object *strings(const std::vector<std::string> &v) { json_object *a = json_object_new_array(); for (auto &x : v) json_object_array_add(a, jstr(x)); return a; } // the error's text into out[key], the error object freed void unavailable(json_object *out, const char *key, json_object *e) { json_object_object_add(out, key, json_object_get(json_object_object_get(e, "error"))); json_object_put(e); } // ---- exported targets (sharedd) ---- json_object *exportTargetJson(const std::vector<std::pair<std::string, std::string>> &f) { json_object *o = json_object_new_object(); std::vector<std::string> luns, initiators, sessions; for (auto &p : f) { const std::string &k = p.first, &v = p.second; if (k == "name" || k == "iqn" || k == "comment" || k == "error") json_object_object_add(o, k.c_str(), jstr(v)); else if (k == "lun") luns.push_back(v); else if (k == "initiator") initiators.push_back(v); else if (k == "session") sessions.push_back(v); else if (k == "read_only") json_object_object_add(o, "readOnly", json_object_new_boolean(v == "yes")); else if (k == "open") json_object_object_add(o, "open", json_object_new_boolean(v == "yes")); else if (k == "chap_user") json_object_object_add(o, "chapUser", jstr(v)); else if (k == "chap_user_in") json_object_object_add(o, "chapUserIn", jstr(v)); else if (k == "chap_password_set") json_object_object_add(o, "chapPasswordSet", json_object_new_boolean(v == "yes")); else if (k == "chap_password_in_set") json_object_object_add(o, "chapPasswordInSet", json_object_new_boolean(v == "yes")); } json_object_object_add(o, "luns", strings(luns)); json_object_object_add(o, "initiators", strings(initiators)); json_object_object_add(o, "sessions", strings(sessions)); return o; } // sharedd's iSCSI side into out["export"]; nullptr or the error json_object *exportInfo(json_object *out) { sysbus::Message r; if (json_object *e = callSharedd(sharedd::method::Status, {}, r)) return e; json_object *ex = json_object_new_object(); json_object_object_add(ex, "state", jstr(r.get("iscsi_state"))); json_object_object_add(ex, "error", jstr(r.get("iscsi_error"))); if (json_object *e = callSharedd(sharedd::method::GetSettings, {}, r)) return json_object_put(ex), e; json_object *set = json_object_new_object(); json_object_object_add(set, "enabled", json_object_new_boolean(r.get("iscsi") == "yes")); json_object_object_add(set, "baseName", jstr(r.get("iscsi_base_name"))); json_object_object_add(set, "portals", strings(r.getAll("iscsi_portal"))); json_object_object_add(ex, "settings", set); if (json_object *e = callSharedd(sharedd::method::ListIscsiTargets, {}, r)) return json_object_put(ex), e; json_object *targets = json_object_new_array(); std::vector<std::pair<std::string, std::string>> cur; auto flush = [&] { if (!cur.empty()) json_object_array_add(targets, exportTargetJson(cur)); cur.clear(); }; for (auto &f : r.fields) { if (f.first == "name") flush(); cur.push_back(f); } flush(); json_object_object_add(ex, "targets", targets); if (json_object *e = callSharedd(sharedd::method::ListIscsiDevices, {}, r)) return json_object_put(ex), e; json_object *devices = json_object_new_array(), *dev = nullptr; for (auto &f : r.fields) { if (f.first == "device") { dev = json_object_new_object(); json_object_array_add(devices, dev); json_object_object_add(dev, "device", jstr(f.second)); } else if (dev && f.first == "size") { json_object_object_add(dev, "size", json_object_new_int64(num(f.second))); } else if (dev && (f.first == "kind")) { json_object_object_add(dev, "kind", jstr(f.second)); } else if (dev && f.first == "in_use") { json_object_object_add(dev, "inUse", jstr(f.second)); } } json_object_object_add(ex, "devices", devices); json_object_object_add(out, "export", ex); return nullptr; } // a target from the UI as sharedd's fields; keepSecrets: missing/null // passwords stay as they are std::string exportFields(json_object *t, Fields &out, bool keepSecrets) { if (!t || !json_object_is_type(t, json_type_object)) return tr("no target given", "kein Target angegeben"); out.emplace_back("comment", webctl::str(t, "comment")); out.emplace_back("read_only", yesNo(webctl::boolean(t, "readOnly"))); out.emplace_back("open", yesNo(webctl::boolean(t, "open"))); for (auto &k : {std::make_pair("luns", "lun"), std::make_pair("initiators", "initiator")}) { json_object *list = nullptr; if (!json_object_object_get_ex(t, k.first, &list) || !list) continue; if (!json_object_is_type(list, json_type_array)) return tr("%1 must be a list", "%1 muss eine Liste sein", {k.first}); for (size_t i = 0; i < json_object_array_length(list); ++i) { const char *v = json_object_get_string(json_object_array_get_idx(list, i)); if (v && *v) out.emplace_back(k.second, v); } } out.emplace_back("chap_user", webctl::str(t, "chapUser")); out.emplace_back("chap_user_in", webctl::str(t, "chapUserIn")); for (auto &k : {std::make_pair("chapPassword", "chap_password"), std::make_pair("chapPasswordIn", "chap_password_in")}) { json_object *v = nullptr; bool given = json_object_object_get_ex(t, k.first, &v) && v; if (given || !keepSecrets) out.emplace_back(k.second, webctl::str(t, k.first)); } return ""; } json_object *exportAction(const std::string &action, json_object *a) { if (action == "exportDelete") return simpleSharedd(sharedd::method::DeleteIscsiTarget, {{"name", webctl::str(a, "name")}}); if (action == "exportSettings") { Fields f = {{"iscsi", yesNo(webctl::boolean(a, "enabled", true))}, {"iscsi_base_name", webctl::str(a, "baseName")}}; json_object *portals = nullptr; if (json_object_object_get_ex(a, "portals", &portals) && portals && json_object_is_type(portals, json_type_array)) for (size_t i = 0; i < json_object_array_length(portals); ++i) { const char *v = json_object_get_string(json_object_array_get_idx(portals, i)); f.emplace_back("iscsi_portal", v ? v : ""); } return simpleSharedd(sharedd::method::SetSettings, f); } json_object *t = nullptr; json_object_object_get_ex(a, "target", &t); bool update = action == "exportUpdate"; Fields f = {{"name", update ? webctl::str(a, "name") : webctl::str(t, "name")}}; std::string why = exportFields(t, f, update); if (!why.empty()) return error(why); if (update && webctl::str(t, "name") != f[0].second) f.emplace_back("new_name", webctl::str(t, "name")); return simpleSharedd(update ? sharedd::method::EditIscsiTarget : sharedd::method::CreateIscsiTarget, f); } // the target named by name/address/port in args Fields key(json_object *a) { return {{"target", webctl::str(a, "name")}, Loading Loading @@ -111,21 +273,17 @@ json_object *targetsJson(const sysbus::Message &r) { return targets; } json_object *info() { json_object *out = json_object_new_object(); // iscsid's side into out; nullptr or the error json_object *initiatorInfo(json_object *out) { sysbus::Message r; if (json_object *e = callIscsid(iscsid::method::Status, {}, r)) { json_object_object_add(out, "unavailable", json_object_get(json_object_object_get(e, "error"))); json_object_put(e); return out; } if (json_object *e = callIscsid(iscsid::method::Status, {}, r)) return e; json_object *st = json_object_new_object(); json_object_object_add(st, "initiatorName", jstr(r.get("initiator_name"))); json_object_object_add(st, "kernel", json_object_new_boolean(r.get("kernel") == "yes")); json_object_object_add(st, "kernelError", jstr(r.get("kernel_error"))); json_object_object_add(out, "initiator", st); // not "status": the frontend takes that for the HTTP status if (json_object *e = callIscsid(iscsid::method::GetSettings, {}, r)) return json_object_put(out), e; if (json_object *e = callIscsid(iscsid::method::GetSettings, {}, r)) return e; json_object *set = json_object_new_object(); json_object_object_add(set, "initiatorName", jstr(r.get("initiator_name"))); json_object_object_add(set, "initiatorAlias", jstr(r.get("initiator_alias"))); Loading @@ -134,8 +292,19 @@ json_object *info() { json_object_object_add(set, p.first, json_object_new_int64(num(r.get(p.second)))); json_object_object_add(out, "settings", set); if (json_object *e = callIscsid(iscsid::method::ListTargets, {}, r)) return json_object_put(out), e; if (json_object *e = callIscsid(iscsid::method::ListTargets, {}, r)) return e; json_object_object_add(out, "targets", targetsJson(r)); return nullptr; } json_object *info() { json_object *out = json_object_new_object(); if (json_object *e = initiatorInfo(out)) { // the half that answers is still shown for (const char *k : {"initiator", "settings", "targets"}) json_object_object_del(out, k); unavailable(out, "initiatorUnavailable", e); } if (json_object *e = exportInfo(out)) unavailable(out, "exportUnavailable", e); return out; } Loading Loading @@ -196,6 +365,7 @@ json_object *call(const webctl_request *req) { json_object *a = req->args; if (action == "info") return info(); if (action.compare(0, 6, "export") == 0) return exportAction(action, a); if (action == "discover") return discover(a); if (action == "reload") return simple(iscsid::method::Reload, {}); if (action == "login") return simple(iscsid::method::Login, key(a)); Loading www/modules/iscsi.js +183 −8 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
CMakeLists.txt +2 −1 Changes for CMakeLists.txt: 2 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -100,7 +100,8 @@ webctl_module(lvm modules/lvm/lvm.cpp modules/common/storaged.cpp) target_link_libraries(webctl_mod_lvm PRIVATE sysbus::sysbus) webctl_module(disks modules/disks/disks.cpp modules/common/storaged.cpp) target_link_libraries(webctl_mod_disks PRIVATE sysbus::sysbus) # iSCSI initiator: a client of iscsid; needs iscsid/api.h (iscsid-dev) # iSCSI: initiator (a client of iscsid) and exported targets (sharedd); # needs iscsid/api.h (iscsid-dev) and sharedd/api.h (sharedd-dev) webctl_module(iscsi modules/iscsi/iscsi.cpp) target_link_libraries(webctl_mod_iscsi PRIVATE sysbus::sysbus) # mounts are syslite .mount units (sysbus CreateMount); mke2fs/mkfs.fat Loading
modules/iscsi/iscsi.cpp +185 −15 Changes for modules/iscsi/iscsi.cpp: 185 added lines, 15 removed lines. Original line number Diff line number Diff line /* webctl module "iscsi": the iSCSI initiator iscsid, entirely over its * sysbus API (sysbus::Client::callService, include/iscsid/api.h). /* webctl module "iscsi": both sides of iSCSI, over sysbus * (sysbus::Client::callService) -- the initiator iscsid (disks of other * machines used here, include/iscsid/api.h) and the targets sharedd * exports with the kernel's LIO (volumes of this machine for others, * include/sharedd/api.h). Either works without the other daemon. * * Initiator (iscsid): * info -> {status{initiatorName, kernel, kernelError}, settings, * targets[{name, address, port, autoLogin, auth, username, * passwordSet, usernameIn, passwordInSet, state, error, sid, Loading @@ -17,6 +21,17 @@ * settings {initiatorName, initiatorAlias, loginTimeout, * replacementTimeout, noopInterval, noopTimeout} * reload * info also has initiatorUnavailable (why iscsid can't be asked) instead * of initiator/settings/targets * * Exported targets (sharedd): * info adds export{state, error, settings{enabled, baseName, portals[]}, * targets[{name, iqn, comment, luns[], readOnly, open, initiators[], * chapUser, chapPasswordSet, chapUserIn, chapPasswordInSet, sessions[], * error}], devices[{device, kind, size, inUse}]}, or exportUnavailable * exportCreate {target}, exportUpdate {name, target} (chapPassword / * chapPasswordIn missing or null: kept), exportDelete {name} * exportSettings {enabled, baseName, portals[]} */ #include <cstdlib> Loading @@ -24,6 +39,7 @@ #include <vector> #include <iscsid/api.h> #include <sharedd/api.h> #include <sysbus/client.h> #include <webctl/module.h> Loading @@ -39,29 +55,175 @@ const char *yesNo(bool b) { return b ? "yes" : "no"; } long long num(const std::string &v) { return std::strtoll(v.c_str(), nullptr, 10); } // an iscsid call; on failure the error object to return json_object *callIscsid(const char *method, const Fields &args, sysbus::Message &reply) { // a call to iscsid or sharedd; on failure the error object to return json_object *callDaemon(const char *service, const char *method, const Fields &args, sysbus::Message &reply) { sysbus::Client bus; if (!bus.connect()) return error(tr("sysbus isn't reachable: %1", "sysbus ist nicht erreichbar: %1", {bus.lastError()}), 503); Fields all = args; all.emplace_back("lang", webctl::language()); // discovery may wait for the login timeout of an unreachable portal reply = bus.callService(iscsid::Service, method, all, 120000); reply = bus.callService(service, method, all, 120000); if (reply.type != sysbus::MessageType::Error) return nullptr; const std::string &name = reply.member; if (name == sysbus::error::ServiceUnknown) return error(tr("iscsid isn't running (Services: iscsid)", "iscsid läuft nicht (Dienste: iscsid)"), 503); return error(tr("%1 isn't running (Services: %1)", "%1 läuft nicht (Dienste: %1)", {service}), 503); int status = name == sysbus::error::AccessDenied ? 403 : name == sysbus::error::InvalidArgs ? 400 : 500; return error(reply.get("message", name), status); } json_object *callIscsid(const char *method, const Fields &args, sysbus::Message &reply) { return callDaemon(iscsid::Service, method, args, reply); } json_object *callSharedd(const char *method, const Fields &args, sysbus::Message &reply) { return callDaemon(sharedd::Service, method, args, reply); } json_object *simple(const char *method, const Fields &f) { sysbus::Message r; if (json_object *e = callIscsid(method, f, r)) return e; return webctl::ok(); } json_object *simpleSharedd(const char *method, const Fields &f) { sysbus::Message r; if (json_object *e = callSharedd(method, f, r)) return e; return webctl::ok(); } json_object *strings(const std::vector<std::string> &v) { json_object *a = json_object_new_array(); for (auto &x : v) json_object_array_add(a, jstr(x)); return a; } // the error's text into out[key], the error object freed void unavailable(json_object *out, const char *key, json_object *e) { json_object_object_add(out, key, json_object_get(json_object_object_get(e, "error"))); json_object_put(e); } // ---- exported targets (sharedd) ---- json_object *exportTargetJson(const std::vector<std::pair<std::string, std::string>> &f) { json_object *o = json_object_new_object(); std::vector<std::string> luns, initiators, sessions; for (auto &p : f) { const std::string &k = p.first, &v = p.second; if (k == "name" || k == "iqn" || k == "comment" || k == "error") json_object_object_add(o, k.c_str(), jstr(v)); else if (k == "lun") luns.push_back(v); else if (k == "initiator") initiators.push_back(v); else if (k == "session") sessions.push_back(v); else if (k == "read_only") json_object_object_add(o, "readOnly", json_object_new_boolean(v == "yes")); else if (k == "open") json_object_object_add(o, "open", json_object_new_boolean(v == "yes")); else if (k == "chap_user") json_object_object_add(o, "chapUser", jstr(v)); else if (k == "chap_user_in") json_object_object_add(o, "chapUserIn", jstr(v)); else if (k == "chap_password_set") json_object_object_add(o, "chapPasswordSet", json_object_new_boolean(v == "yes")); else if (k == "chap_password_in_set") json_object_object_add(o, "chapPasswordInSet", json_object_new_boolean(v == "yes")); } json_object_object_add(o, "luns", strings(luns)); json_object_object_add(o, "initiators", strings(initiators)); json_object_object_add(o, "sessions", strings(sessions)); return o; } // sharedd's iSCSI side into out["export"]; nullptr or the error json_object *exportInfo(json_object *out) { sysbus::Message r; if (json_object *e = callSharedd(sharedd::method::Status, {}, r)) return e; json_object *ex = json_object_new_object(); json_object_object_add(ex, "state", jstr(r.get("iscsi_state"))); json_object_object_add(ex, "error", jstr(r.get("iscsi_error"))); if (json_object *e = callSharedd(sharedd::method::GetSettings, {}, r)) return json_object_put(ex), e; json_object *set = json_object_new_object(); json_object_object_add(set, "enabled", json_object_new_boolean(r.get("iscsi") == "yes")); json_object_object_add(set, "baseName", jstr(r.get("iscsi_base_name"))); json_object_object_add(set, "portals", strings(r.getAll("iscsi_portal"))); json_object_object_add(ex, "settings", set); if (json_object *e = callSharedd(sharedd::method::ListIscsiTargets, {}, r)) return json_object_put(ex), e; json_object *targets = json_object_new_array(); std::vector<std::pair<std::string, std::string>> cur; auto flush = [&] { if (!cur.empty()) json_object_array_add(targets, exportTargetJson(cur)); cur.clear(); }; for (auto &f : r.fields) { if (f.first == "name") flush(); cur.push_back(f); } flush(); json_object_object_add(ex, "targets", targets); if (json_object *e = callSharedd(sharedd::method::ListIscsiDevices, {}, r)) return json_object_put(ex), e; json_object *devices = json_object_new_array(), *dev = nullptr; for (auto &f : r.fields) { if (f.first == "device") { dev = json_object_new_object(); json_object_array_add(devices, dev); json_object_object_add(dev, "device", jstr(f.second)); } else if (dev && f.first == "size") { json_object_object_add(dev, "size", json_object_new_int64(num(f.second))); } else if (dev && (f.first == "kind")) { json_object_object_add(dev, "kind", jstr(f.second)); } else if (dev && f.first == "in_use") { json_object_object_add(dev, "inUse", jstr(f.second)); } } json_object_object_add(ex, "devices", devices); json_object_object_add(out, "export", ex); return nullptr; } // a target from the UI as sharedd's fields; keepSecrets: missing/null // passwords stay as they are std::string exportFields(json_object *t, Fields &out, bool keepSecrets) { if (!t || !json_object_is_type(t, json_type_object)) return tr("no target given", "kein Target angegeben"); out.emplace_back("comment", webctl::str(t, "comment")); out.emplace_back("read_only", yesNo(webctl::boolean(t, "readOnly"))); out.emplace_back("open", yesNo(webctl::boolean(t, "open"))); for (auto &k : {std::make_pair("luns", "lun"), std::make_pair("initiators", "initiator")}) { json_object *list = nullptr; if (!json_object_object_get_ex(t, k.first, &list) || !list) continue; if (!json_object_is_type(list, json_type_array)) return tr("%1 must be a list", "%1 muss eine Liste sein", {k.first}); for (size_t i = 0; i < json_object_array_length(list); ++i) { const char *v = json_object_get_string(json_object_array_get_idx(list, i)); if (v && *v) out.emplace_back(k.second, v); } } out.emplace_back("chap_user", webctl::str(t, "chapUser")); out.emplace_back("chap_user_in", webctl::str(t, "chapUserIn")); for (auto &k : {std::make_pair("chapPassword", "chap_password"), std::make_pair("chapPasswordIn", "chap_password_in")}) { json_object *v = nullptr; bool given = json_object_object_get_ex(t, k.first, &v) && v; if (given || !keepSecrets) out.emplace_back(k.second, webctl::str(t, k.first)); } return ""; } json_object *exportAction(const std::string &action, json_object *a) { if (action == "exportDelete") return simpleSharedd(sharedd::method::DeleteIscsiTarget, {{"name", webctl::str(a, "name")}}); if (action == "exportSettings") { Fields f = {{"iscsi", yesNo(webctl::boolean(a, "enabled", true))}, {"iscsi_base_name", webctl::str(a, "baseName")}}; json_object *portals = nullptr; if (json_object_object_get_ex(a, "portals", &portals) && portals && json_object_is_type(portals, json_type_array)) for (size_t i = 0; i < json_object_array_length(portals); ++i) { const char *v = json_object_get_string(json_object_array_get_idx(portals, i)); f.emplace_back("iscsi_portal", v ? v : ""); } return simpleSharedd(sharedd::method::SetSettings, f); } json_object *t = nullptr; json_object_object_get_ex(a, "target", &t); bool update = action == "exportUpdate"; Fields f = {{"name", update ? webctl::str(a, "name") : webctl::str(t, "name")}}; std::string why = exportFields(t, f, update); if (!why.empty()) return error(why); if (update && webctl::str(t, "name") != f[0].second) f.emplace_back("new_name", webctl::str(t, "name")); return simpleSharedd(update ? sharedd::method::EditIscsiTarget : sharedd::method::CreateIscsiTarget, f); } // the target named by name/address/port in args Fields key(json_object *a) { return {{"target", webctl::str(a, "name")}, Loading Loading @@ -111,21 +273,17 @@ json_object *targetsJson(const sysbus::Message &r) { return targets; } json_object *info() { json_object *out = json_object_new_object(); // iscsid's side into out; nullptr or the error json_object *initiatorInfo(json_object *out) { sysbus::Message r; if (json_object *e = callIscsid(iscsid::method::Status, {}, r)) { json_object_object_add(out, "unavailable", json_object_get(json_object_object_get(e, "error"))); json_object_put(e); return out; } if (json_object *e = callIscsid(iscsid::method::Status, {}, r)) return e; json_object *st = json_object_new_object(); json_object_object_add(st, "initiatorName", jstr(r.get("initiator_name"))); json_object_object_add(st, "kernel", json_object_new_boolean(r.get("kernel") == "yes")); json_object_object_add(st, "kernelError", jstr(r.get("kernel_error"))); json_object_object_add(out, "initiator", st); // not "status": the frontend takes that for the HTTP status if (json_object *e = callIscsid(iscsid::method::GetSettings, {}, r)) return json_object_put(out), e; if (json_object *e = callIscsid(iscsid::method::GetSettings, {}, r)) return e; json_object *set = json_object_new_object(); json_object_object_add(set, "initiatorName", jstr(r.get("initiator_name"))); json_object_object_add(set, "initiatorAlias", jstr(r.get("initiator_alias"))); Loading @@ -134,8 +292,19 @@ json_object *info() { json_object_object_add(set, p.first, json_object_new_int64(num(r.get(p.second)))); json_object_object_add(out, "settings", set); if (json_object *e = callIscsid(iscsid::method::ListTargets, {}, r)) return json_object_put(out), e; if (json_object *e = callIscsid(iscsid::method::ListTargets, {}, r)) return e; json_object_object_add(out, "targets", targetsJson(r)); return nullptr; } json_object *info() { json_object *out = json_object_new_object(); if (json_object *e = initiatorInfo(out)) { // the half that answers is still shown for (const char *k : {"initiator", "settings", "targets"}) json_object_object_del(out, k); unavailable(out, "initiatorUnavailable", e); } if (json_object *e = exportInfo(out)) unavailable(out, "exportUnavailable", e); return out; } Loading Loading @@ -196,6 +365,7 @@ json_object *call(const webctl_request *req) { json_object *a = req->args; if (action == "info") return info(); if (action.compare(0, 6, "export") == 0) return exportAction(action, a); if (action == "discover") return discover(a); if (action == "reload") return simple(iscsid::method::Reload, {}); if (action == "login") return simple(iscsid::method::Login, key(a)); Loading
www/modules/iscsi.js +183 −8 File changed.Preview size limit exceeded, changes collapsed. Show changes