Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions control-center/actions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -1455,6 +1455,33 @@ function Field({f, val, setVal}) {
</label>
);
}
if (f.type === "rows") {
const rows = val || [];
const set = (i, k, x) => setVal(rows.map((r, j) => j === i ? Object.assign({}, r, {[k]: x}) : r));
const cell = (r, i, c) => {
const w = {flex: c.flex || 1, minWidth: 0};
if (c.type === "select") return <select key={c.k} className="finput sm" style={w} value={r[c.k] || ""} onChange={e => set(i, c.k, e.target.value)}>
{(c.options || []).map(o => <option key={o.v} value={o.v}>{o.l}</option>)}</select>;
return <input key={c.k} className="finput sm" style={w} type={c.type === "number" ? "number" : "text"} placeholder={c.placeholder || ""} value={r[c.k] == null ? "" : r[c.k]} onChange={e => set(i, c.k, e.target.value)} />;
};
return (
<label className="field">
<span className="flabel">{f.label} <em>({rows.length}{f.max ? ` of ${f.max}` : ""})</em></span>
<div className="schedbox">
<div className="schedrow head">{f.cols.map(c => <span key={c.k} className="sl" style={{flex: c.flex || 1}}>{c.label}</span>)}<span style={{width: 24}}></span></div>
{rows.map((r, i) => (
<div className="schedrow" key={i}>
{f.cols.map(c => cell(r, i, c))}
<button type="button" className="kebab" title="Remove" onClick={() => setVal(rows.filter((_, j) => j !== i))}><Icon n="x" s={11} /></button>
</div>
))}
<button type="button" className="schedadd" disabled={f.max && rows.length >= f.max} onClick={() => setVal(rows.concat(f.add ? f.add(rows) : {}))}>
<Icon n="plus" s={11} />{f.addLabel || "Add"}</button>
</div>
{f.hint && <span className="fhint">{f.hint}</span>}
</label>
);
}
if (f.type === "bschedule") {
const rows = val || [];
const set = (i, k, x) => setVal(rows.map((r, j) => j === i ? Object.assign({}, r, {[k]: x}) : r));
Expand Down
16 changes: 11 additions & 5 deletions control-center/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const LAYER_META = {
restores: {label: "Restores", icon: "cloud"}, restore: {icon: "cloud"},
siteprofiles: {label: "Site profiles", icon: "k8s"}, siteprofile: {icon: "k8s"},
dhcpservers: {label: "DHCP servers", icon: "link"}, dhcpserver: {icon: "link"},
sitedeploys: {label: "Site storage", icon: "cluster"}, sitedeploy: {icon: "cluster"},
drconfig: {label: "DR configuration", icon: "gauge"},
slots: {label: "Replication slots", icon: "volume"}, slot: {icon: "volume"},
replops: {label: "Operations", icon: "clock"}, replop: {icon: "clock"},
Expand Down Expand Up @@ -67,6 +68,7 @@ const pTSched = id => [{t: "dr"}, {t: "tschedules"}, {t: "tsched", id}];
const pRestore = id => [{t: "dr"}, {t: "restores"}, {t: "restore", id}];
const pSProf = id => [{t: "dr"}, {t: "siteprofiles"}, {t: "siteprofile", id}];
const pDhcp = id => [{t: "dr"}, {t: "dhcpservers"}, {t: "dhcpserver", id}];
const pSiteDeploy = id => [{t: "dr"}, {t: "sitedeploys"}, {t: "sitedeploy", id}];
const pPair = id => [{t: "dr"}, {t: "pairs"}, {t: "pair", id}];
const pSlot = id => [{t: "dr"}, {t: "slots"}, {t: "slot", id}];
const pReplOp = id => [{t: "dr"}, {t: "replops"}, {t: "replop", id}];
Expand Down Expand Up @@ -98,6 +100,7 @@ const detailPath = o => o.kind === "cluster" ? pC(o.id)
: o.kind === "restore" ? pRestore(o.id)
: o.kind === "siteprofile" ? pSProf(o.id)
: o.kind === "dhcpserver" ? pDhcp(o.id)
: o.kind === "sitedeploy" ? pSiteDeploy(o.id)
: o.kind === "pair" ? pPair(o.id)
: o.kind === "slot" ? pSlot(o.id)
: o.kind === "replops" ? pReplOp(o.id)
Expand Down Expand Up @@ -160,7 +163,7 @@ const SORT_KEYS = {
policy: ["name", "members"],
pplan: ["health", "name", "newest"], drpath: ["health", "name", "newest"], papp: ["health", "name", "newest"],
rplan: ["health", "name", "newest"], raction: ["newest", "health", "name", "oldest"], tbubble: ["newest", "health", "name", "oldest"],
tsched: ["health", "name", "newest"], restore: ["newest", "health", "name"], siteprofile: ["health", "name"], dhcpserver: ["health", "name"],
tsched: ["health", "name", "newest"], restore: ["newest", "health", "name"], siteprofile: ["health", "name"], dhcpserver: ["health", "name"], sitedeploy: ["health", "name", "newest"],
pair: ["health", "name", "slots", "newest"],
slot: ["health", "name", "newest"],
replops: ["newest", "health", "name"],
Expand Down Expand Up @@ -227,6 +230,7 @@ const VIEWS = {
restores: {kind: "restore", load: p => p.t === "papp" ? drhub.appRestores(p.id) : drhub.restores(), api: () => drcrd("restoreactions", true)},
siteprofiles: {kind: "siteprofile", load: () => drhub.siteProfiles(), api: () => "GET /apis/sitemap.simplyblock.io/v1alpha1/siteprofiles"},
dhcpservers: {kind: "dhcpserver", load: p => p.t === "siteprofile" ? drhub.siteDHCPServers(p.id) : drhub.dhcpServers(), api: () => "GET /apis/sitemap.simplyblock.io/v1alpha1/dhcpservers"},
sitedeploys: {kind: "sitedeploy", load: () => drhub.siteDeploys(), api: () => "GET /apis/storage.simplyblock.io/v1alpha2/storagesitedeployments"},
storageclasses: {kind: "storageclass",
load: p => p.t === "pool" ? api.poolStorageClasses(p.id) : api.k8sStorageClasses(p.id),
api: () => "GET /apis/storage.k8s.io/v1/storageclasses"},
Expand Down Expand Up @@ -276,6 +280,7 @@ const DETAIL_API = {
rplan: drcrd("recoveryplans/{name}", true), raction: drcrd("recoveryactions/{name}", true), tbubble: drcrd("testbubbles/{name}", true),
tsched: drcrd("testschedules/{name}", true), restore: drcrd("restoreactions/{name}", true), siteprofile: "GET /apis/sitemap.simplyblock.io/v1alpha1/siteprofiles/{name}",
dhcpserver: "GET /apis/sitemap.simplyblock.io/v1alpha1/dhcpservers/{name}",
sitedeploy: "GET /apis/storage.simplyblock.io/v1alpha2/namespaces/{ns}/storagesitedeployments/{name}",
pair: crd1("replicationpairs"), rpolicy: crd1("replicationpolicies"),
slot: crd1("replicationslots"), replops: crd1("replicationops"),
zone: prop("zones/{uuid}"), cgroup: prop("consistency-groups/{uuid}"),
Expand All @@ -287,7 +292,7 @@ const DETAIL_API = {
const KIND_LABEL = {cluster: "cluster", host: "host", node: "storage node", device: "device", pool: "storage pool",
volume: "logical volume", snapshot: "snapshot", backup: "backup", policy: "backup policy",
pplan: "protection plan", drpath: "DR path", papp: "protected application", rplan: "recovery plan", raction: "recovery action",
tbubble: "test", tsched: "test schedule", restore: "restore", siteprofile: "site profile", dhcpserver: "DHCP server",
tbubble: "test", tsched: "test schedule", restore: "restore", siteprofile: "site profile", dhcpserver: "DHCP server", sitedeploy: "site storage deployment",
pair: "replication pair", rpolicy: "replication policy",
slot: "replication slot", replops: "replication operation", zone: "zone",
cgroup: "consistency group", cgsnapshot: "group snapshot", migration: "migration",
Expand Down Expand Up @@ -394,12 +399,12 @@ function DiscoveryView({kid, nav}) {
const TILE = {cluster: ClusterTile, host: HostTile, node: NodeTile, device: DeviceTile, pool: PoolTile,
volume: VolumeTile, snapshot: SnapshotTile, backup: BackupTile, policy: PolicyTile,
pplan: PPlanTile, drpath: DRPathTile, papp: PAppTile, rplan: RPlanTile, raction: RActionTile, tbubble: TBubbleTile,
tsched: TSchedTile, restore: RestoreTile, siteprofile: SiteProfileTile, dhcpserver: DHCPServerTile, pair: PairTile, rpolicy: RPolicyTile,
tsched: TSchedTile, restore: RestoreTile, siteprofile: SiteProfileTile, dhcpserver: DHCPServerTile, sitedeploy: SiteDeployTile, pair: PairTile, rpolicy: RPolicyTile,
slot: SlotTile, replops: ReplOpsTile, zone: ZoneTile, cgroup: CgroupTile, cgsnapshot: CgSnapshotTile,
migration: MigrationTile, k8sc: K8sTile, storageclass: StorageClassTile, pvc: PvcTile, bucket: BucketTile,
deployconfig: DeployConfigTile, mpath: MPathTile, appgroup: AppGroupTile};
const TKEY = {cluster: "c", host: "h", node: "n", device: "d", pool: "p", volume: "v", snapshot: "s",
backup: "b", pplan: "o", drpath: "o", papp: "o", rplan: "o", raction: "o", tbubble: "o", tsched: "o", restore: "o", siteprofile: "o", dhcpserver: "o", policy: "p", pair: "p", rpolicy: "p",
backup: "b", pplan: "o", drpath: "o", papp: "o", rplan: "o", raction: "o", tbubble: "o", tsched: "o", restore: "o", siteprofile: "o", dhcpserver: "o", sitedeploy: "o", policy: "p", pair: "p", rpolicy: "p",
slot: "s", replops: "o", zone: "s", cgroup: "g", cgsnapshot: "s", migration: "m", k8sc: "k", storageclass: "s", pvc: "p", bucket: "b",
deployconfig: "d", mpath: "m", appgroup: "g"};

Expand Down Expand Up @@ -490,7 +495,8 @@ function OverviewView({seg, parent, nav, prefs, rev, up, upLabel}) {
: seg.t === "paths" ? <button className="btn primary" onClick={() => drhub.plans().then(ps => window.__ui.dialog(newPathDialog(ps), {kind: "drpath", id: "new"}))}><Icon n="plus" s={12} />Declare path</button>
: seg.t === "protectedapps" ? <button className="btn primary" onClick={() => Promise.all([drhub.plans(), drhub.config().catch(() => null)]).then(([ps, c]) => window.__ui.dialog(protectAppDialogDR(ps, c), {kind: "papp", id: "new"}))}><Icon n="shield" s={12} />Protect application</button>
: seg.t === "rplans" ? <button className="btn primary" onClick={() => Promise.all([drhub.paths(), drhub.apps()]).then(([ps, as]) => window.__ui.dialog(newRPlanDialog(ps, as), {kind: "rplan", id: "new"}))}><Icon n="plus" s={12} />New recovery plan</button>
: seg.t === "dhcpservers" ? <button className="btn primary" onClick={() => drhub.siteProfiles().then(ss => window.__ui.dialog(newDHCPServerDialog(parent && parent.t === "siteprofile" && REG[parent.id] ? [REG[parent.id].name] : ss.map(s => s.name)), {kind: "dhcpserver", id: "new"}))}><Icon n="plus" s={12} />Register DHCP server</button>
: seg.t === "sitedeploys" ? <button className="btn primary" onClick={() => Promise.all([drhub.managedClusters(), drhub.siteProfiles().catch(() => []), drhub.siteDeploys()]).then(([mcs, sps, sds]) => window.__ui.dialog(deploySiteDialog(mcs.length ? mcs.map(m => m.metadata.name) : sps.map(s => s.name), sds.map(d => d.site)), {kind: "sitedeploy", id: "new"}))}><Icon n="plus" s={12} />Deploy storage</button>
: seg.t === "dhcpservers" ? <button className="btn primary" onClick={() => drhub.siteProfiles().then(ss => window.__ui.dialog(newDHCPServerDialog(parent && parent.t === "siteprofile" && REG[parent.id] ? [REG[parent.id].name] : ss.map(s => s.name), ss), {kind: "dhcpserver", id: "new"}))}><Icon n="plus" s={12} />Register DHCP server</button>
: seg.t === "pairs" ? <button className="btn primary" onClick={() => window.__ui.dialog(newPairDialog(), {kind: "pair", id: "new"})}><Icon n="plus" s={12} />New pair</button>
: seg.t === "rpolicies" ? <button className="btn primary" onClick={() => window.__ui.dialog(newReplPolicyDialog(parent && parent.t === "pair" ? REG[parent.id] : null), {kind: "rpolicy", id: "new"})}><Icon n="plus" s={12} />New policy</button>
: seg.t === "__pairs_old" ? <button className="btn primary" onClick={() => window.__ui.dialog(newPairDialog(), {kind: "cluster pair", id: "new"})}><Icon n="plus" s={12} />Pair clusters</button>
Expand Down
8 changes: 8 additions & 0 deletions control-center/deploy/k8s/rbac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,14 @@ rules:
- apiGroups: ["sitemap.simplyblock.io"]
resources: ["dhcpservers"]
verbs: ["create", "update", "patch", "delete"]
# a managed site's storage deployment is requested, sized and approved
# from the hub console (StorageSiteDeployment, carried to the site by OCM)
- apiGroups: ["storage.simplyblock.io"]
resources: ["storagesitedeployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["cluster.open-cluster-management.io"]
resources: ["managedclusters"]
verbs: ["get", "list", "watch"]
# the console asks the API server what its identity may do, to disable controls
- apiGroups: ["authorization.k8s.io"]
resources: ["selfsubjectaccessreviews", "selfsubjectrulesreviews"]
Expand Down
2 changes: 1 addition & 1 deletion control-center/details-data.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ const DETAIL_KIND = {pair: "PairDetail", rpolicy: "RPolicyDetail", zone: "ZoneDe
k8sc: "K8sDetail", storageclass: "StorageClassDetail", pvc: "PvcDetail", bucket: "BucketDetail",
// DR hub kinds live in drhub.jsx
pplan: "PPlanDetail", drpath: "DRPathDetail", papp: "PAppDetail", rplan: "RPlanDetail", raction: "RActionDetail",
tbubble: "TBubbleDetail", tsched: "TSchedDetail", restore: "RestoreDetail", siteprofile: "SiteProfileDetail", dhcpserver: "DHCPServerDetail",
tbubble: "TBubbleDetail", tsched: "TSchedDetail", restore: "RestoreDetail", siteprofile: "SiteProfileDetail", dhcpserver: "DHCPServerDetail", sitedeploy: "SiteDeployDetail",
deployconfig: "DeployConfigDetail", mpath: "MPathDetail", appgroup: "AppGroupDetail"};
const Detail = ({obj, nav}) => {
const C = DETAILS[obj.kind] || (DETAIL_KIND[obj.kind] ? window[DETAIL_KIND[obj.kind]] : null);
Expand Down
Loading
Loading