pmcf 1.8.1 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,10 +28,10 @@ async function generateMachineInfo(host, dir) {
28
28
  etcDir,
29
29
  "machine-info",
30
30
  Object.entries({
31
- CHASSIS: host.model.chassis,
31
+ CHASSIS: host.chassis,
32
32
  DEPLOYMENT: host.deployment,
33
33
  LOCATION: host.location.name,
34
- HARDWARE_VENDOR: host.model.vendor,
34
+ HARDWARE_VENDOR: host.vendor,
35
35
  HARDWARE_MODEL: host.modelName
36
36
  }).map(([k, v]) => `${k}=${v}`)
37
37
  );
@@ -62,7 +62,7 @@ async function generateLocationDefs(location, dir) {
62
62
  await mkdir(locationDir, { recursive: true });
63
63
 
64
64
  copyFile(
65
- join(world.directory, location.directory, "location.json"),
65
+ join(location.directory, "location.json"),
66
66
  join(locationDir, "location.json")
67
67
  );
68
68
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmcf",
3
- "version": "1.8.1",
3
+ "version": "1.9.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
package/src/model.mjs CHANGED
@@ -27,20 +27,14 @@ export class Base {
27
27
  return undefined;
28
28
  }
29
29
 
30
- if (name.endsWith("/" + this.typeFileName)) {
31
- return name.substring(0, name.length - this.typeFileName.length - 1);
32
- }
33
-
34
- return name;
30
+ return name.replace(/\/\w+\.json$/, "");
35
31
  }
36
32
 
37
33
  constructor(owner, data) {
38
34
  this.owner = owner;
39
35
 
40
36
  if (data) {
41
- if (data.name) {
42
- this.name = data.name;
43
- }
37
+ this.name = data.name;
44
38
  if (data.description) {
45
39
  this.description = data.description;
46
40
  }
@@ -82,6 +76,10 @@ export class Base {
82
76
  return this.#directory || join(this.owner.directory, this.name);
83
77
  }
84
78
 
79
+ get fullName() {
80
+ return join(this.owner.fullName, this.name);
81
+ }
82
+
85
83
  expand(object) {
86
84
  if (typeof object === "string") {
87
85
  return object.replaceAll(/\$\{([^\}]*)\}/g, (match, m1) => {
@@ -109,7 +107,7 @@ export class Base {
109
107
  }
110
108
 
111
109
  toString() {
112
- return this.typeName + ":" + (this.owner?.name || "") + "/" + this.name;
110
+ return `${this.fullName}(${this.typeName})`;
113
111
  }
114
112
 
115
113
  get propertyNames() {
@@ -133,8 +131,13 @@ export class Owner extends Base {
133
131
  }
134
132
  }
135
133
 
134
+ addObject(object) {
135
+ this.world.addObject(object);
136
+ }
137
+
136
138
  addHost(host) {
137
139
  this.#hosts.set(host.name, host);
140
+ this.addObject(host);
138
141
  }
139
142
 
140
143
  network(name) {
@@ -244,7 +247,11 @@ export class Owner extends Base {
244
247
 
245
248
  export class World extends Owner {
246
249
  static get types() {
247
- return _types;
250
+ return _typesByName;
251
+ }
252
+
253
+ static get typeName() {
254
+ return "world";
248
255
  }
249
256
 
250
257
  #byName = new Map();
@@ -252,39 +259,64 @@ export class World extends Owner {
252
259
  constructor(directory) {
253
260
  super(undefined, { name: "" });
254
261
  this.directory = directory;
262
+ this.addObject(this);
263
+ }
264
+
265
+ get fullName() {
266
+ return "";
255
267
  }
256
268
 
257
269
  get world() {
258
270
  return this;
259
271
  }
260
272
 
261
- async _loadType(name, type) {
262
- const baseName = type.baseName(name);
273
+ async load(name, options) {
274
+ if (name === "") {
275
+ return this;
276
+ }
277
+ const baseName = Base.baseName(name);
263
278
 
264
279
  let object = this.#byName.get(baseName);
265
280
 
266
281
  if (!object) {
267
- const data = JSON.parse(
268
- await readFile(
269
- join(this.directory, baseName, type.typeFileName),
270
- "utf8"
271
- )
272
- );
273
-
274
- let owner;
275
282
  let path = baseName.split("/");
276
-
277
- if (path.length > 1 && path[0] !== "model" && path[0] != "services") {
278
- // TODO
279
- path.length -= 1;
280
- owner = await this._loadType(path.join("/"), Location);
283
+ path.pop();
284
+
285
+ let data;
286
+ let type = options?.type;
287
+ if (type) {
288
+ data = JSON.parse(
289
+ await readFile(
290
+ join(this.directory, baseName, type.typeFileName),
291
+ "utf8"
292
+ )
293
+ );
281
294
  } else {
282
- owner = this;
295
+ for (type of _types) {
296
+ try {
297
+ data = JSON.parse(
298
+ await readFile(
299
+ join(this.directory, baseName, type.typeFileName),
300
+ "utf8"
301
+ )
302
+ );
303
+ break;
304
+ } catch {}
305
+ }
306
+
307
+ if (!data) {
308
+ return this.load(path.join("/"), options);
309
+ }
283
310
  }
284
311
 
285
- data.name = baseName;
312
+ const owner = await this.load(path.join("/"));
313
+
314
+ const length = owner.fullName.length;
315
+ const n = baseName[length] === "/" ? length + 1 : length;
316
+ data.name = baseName.substring(n);
286
317
 
287
318
  type = await type.prepareData(this, data);
319
+
288
320
  object = new type(owner, data);
289
321
  this.addObject(object);
290
322
  }
@@ -292,27 +324,27 @@ export class World extends Owner {
292
324
  return object;
293
325
  }
294
326
 
295
- async load() {
327
+ async loadAll() {
296
328
  for (let type of Object.values(World.types)) {
297
329
  for await (const name of glob(type.fileNameGlob, {
298
330
  cwd: this.directory
299
331
  })) {
300
- await this._loadType(name, type);
332
+ await this.load(name, { type });
301
333
  }
302
334
  }
303
335
  }
304
336
 
305
337
  addObject(object) {
306
- this.#byName.set(object.name, object);
338
+ this.#byName.set(object.fullName, object);
307
339
  }
308
340
 
309
341
  async named(name) {
310
- await this.load();
342
+ await this.loadAll();
311
343
  return this.#byName.get(name);
312
344
  }
313
345
 
314
346
  async *locations() {
315
- await this.load();
347
+ await this.loadAll();
316
348
 
317
349
  for (const object of this.#byName.values()) {
318
350
  if (object instanceof Location) {
@@ -322,7 +354,7 @@ export class World extends Owner {
322
354
  }
323
355
 
324
356
  async *hosts() {
325
- await this.load();
357
+ await this.loadAll();
326
358
 
327
359
  for (const object of this.#byName.values()) {
328
360
  if (object instanceof Host) {
@@ -338,11 +370,11 @@ export class World extends Owner {
338
370
  }
339
371
 
340
372
  async location(name) {
341
- return this._loadType(name, Location);
373
+ return this.load(name, { type: Location });
342
374
  }
343
375
 
344
376
  async host(name) {
345
- return this._loadType(name, Host);
377
+ return this.load(name, { type: Host });
346
378
  }
347
379
 
348
380
  async *networkAddresses() {
@@ -508,6 +540,7 @@ export class Host extends Base {
508
540
  networkInterfaces = {};
509
541
  services = {};
510
542
  postinstall = [];
543
+ #isModel = false;
511
544
  #extends = [];
512
545
  #provides = new Set();
513
546
  #replaces = new Set();
@@ -516,6 +549,8 @@ export class Host extends Base {
516
549
  #os;
517
550
  #distribution;
518
551
  #deployment;
552
+ #chassis;
553
+ #vendor;
519
554
  #location;
520
555
 
521
556
  static get typeName() {
@@ -531,16 +566,17 @@ export class Host extends Base {
531
566
  data.extends = await Promise.all(data.extends.map(e => world.host(e)));
532
567
  }
533
568
 
534
- if (data.name?.indexOf("model/") >= 0) {
535
- return Model;
536
- }
537
-
538
569
  return this;
539
570
  }
540
571
 
541
572
  constructor(owner, data) {
542
573
  super(owner, data);
543
574
 
575
+ if (data.model) {
576
+ this.#isModel = true;
577
+ delete data.model;
578
+ }
579
+
544
580
  if (data.location !== undefined) {
545
581
  this.#location = data.location;
546
582
  delete data.location;
@@ -550,6 +586,14 @@ export class Host extends Base {
550
586
  this.#deployment = data.deployment;
551
587
  delete data.deployment;
552
588
  }
589
+ if (data.chassis !== undefined) {
590
+ this.#chassis = data.chassis;
591
+ delete data.chassis;
592
+ }
593
+ if (data.vendor !== undefined) {
594
+ this.#vendor = data.vendor;
595
+ delete data.vendor;
596
+ }
553
597
  if (data.extends !== undefined) {
554
598
  this.#extends = data.extends;
555
599
  delete data.extends;
@@ -584,18 +628,8 @@ export class Host extends Base {
584
628
  owner.addHost(this);
585
629
 
586
630
  for (const [name, iface] of Object.entries(this.networkInterfaces)) {
587
- iface.host = this;
588
631
  iface.name = name;
589
- if (iface.network) {
590
- const network = owner.network(iface.network);
591
-
592
- if (network) {
593
- iface.network = network;
594
- network.addHost(this);
595
- } else {
596
- this.error("Missing network", iface.network);
597
- }
598
- }
632
+ this.networkInterfaces[name] = new NetworkInterface(this, iface);
599
633
  }
600
634
 
601
635
  for (const [name, data] of Object.entries(
@@ -607,7 +641,15 @@ export class Host extends Base {
607
641
  }
608
642
 
609
643
  get deployment() {
610
- return this.#deployment || this.extends.find(e => e.deployment);
644
+ return this.#deployment || this.extends.find(e => e.deployment)?.deployment;
645
+ }
646
+
647
+ get chassis() {
648
+ return this.#chassis || this.extends.find(e => e.chassis)?.chassis;
649
+ }
650
+
651
+ get vendor() {
652
+ return this.#vendor || this.extends.find(e => e.vendor)?.vendor;
611
653
  }
612
654
 
613
655
  get extends() {
@@ -652,8 +694,12 @@ export class Host extends Base {
652
694
  return this.#distribution || this.extends.find(e => e.distribution);
653
695
  }
654
696
 
697
+ get isModel() {
698
+ return this.#isModel;
699
+ }
700
+
655
701
  get model() {
656
- return this.extends.find(h => h instanceof Model);
702
+ return this.extends.find(h => h.isModel);
657
703
  }
658
704
 
659
705
  get domain() {
@@ -692,8 +738,6 @@ export class Host extends Base {
692
738
  }
693
739
 
694
740
  async publicKey(type = "ed25519") {
695
- console.log("DIR", join(this.directory, `ssh_host_${type}_key.pub`));
696
-
697
741
  return readFile(join(this.directory, `ssh_host_${type}_key.pub`), "utf8");
698
742
  }
699
743
 
@@ -723,7 +767,32 @@ export class Host extends Base {
723
767
  }
724
768
  }
725
769
 
726
- export class Model extends Host {}
770
+ export class NetworkInterface extends Base {
771
+ static get typeName() {
772
+ return "network_interface";
773
+ }
774
+
775
+ constructor(owner, data) {
776
+ super(owner, data);
777
+
778
+ if (data.network) {
779
+ const network = owner.owner.network(data.network);
780
+
781
+ if (network) {
782
+ data.network = network;
783
+ network.addHost(owner);
784
+ } else {
785
+ this.error("Missing network", data.network);
786
+ }
787
+ }
788
+
789
+ Object.assign(this, data);
790
+ }
791
+
792
+ get host() {
793
+ return this.owner;
794
+ }
795
+ }
727
796
 
728
797
  export class Subnet extends Base {
729
798
  networks = new Set();
@@ -838,12 +907,8 @@ export class Service extends Base {
838
907
  }
839
908
  }
840
909
 
841
- const _types = Object.fromEntries(
842
- [Location, Network, Subnet, Host, /*Model,*/ Service].map(t => [
843
- t.typeName,
844
- t
845
- ])
846
- );
910
+ const _types = [Location, Network, Subnet, Host, Service];
911
+ const _typesByName = Object.fromEntries(_types.map(t => [t.typeName, t]));
847
912
 
848
913
  export async function writeLines(dir, name, lines) {
849
914
  await mkdir(dir, { recursive: true });
package/types/model.d.mts CHANGED
@@ -17,6 +17,7 @@ export class Base {
17
17
  network(name: any): Promise<any>;
18
18
  set directory(directory: any);
19
19
  get directory(): any;
20
+ get fullName(): string;
20
21
  expand(object: any): any;
21
22
  error(...args: any[]): void;
22
23
  info(...args: any[]): void;
@@ -27,6 +28,7 @@ export class Base {
27
28
  }
28
29
  export class Owner extends Base {
29
30
  hosts(): AsyncGenerator<any, void, unknown>;
31
+ addObject(object: any): void;
30
32
  addHost(host: any): void;
31
33
  network(name: any): any;
32
34
  networks(): AsyncGenerator<any, void, unknown>;
@@ -52,9 +54,8 @@ export class World extends Owner {
52
54
  };
53
55
  constructor(directory: any);
54
56
  get world(): this;
55
- _loadType(name: any, type: any): Promise<any>;
56
- load(): Promise<void>;
57
- addObject(object: any): void;
57
+ load(name: any, options: any): any;
58
+ loadAll(): Promise<void>;
58
59
  named(name: any): Promise<any>;
59
60
  locations(): AsyncGenerator<Location, void, unknown>;
60
61
  hosts(): AsyncGenerator<Host, void, unknown>;
@@ -94,6 +95,8 @@ export class Host extends Base {
94
95
  services: {};
95
96
  postinstall: any[];
96
97
  get deployment(): any;
98
+ get chassis(): any;
99
+ get vendor(): any;
97
100
  get extends(): any[];
98
101
  get provides(): Set<any>;
99
102
  get replaces(): Set<any>;
@@ -102,7 +105,8 @@ export class Host extends Base {
102
105
  get master(): boolean;
103
106
  get os(): any;
104
107
  get distribution(): any;
105
- get model(): Model;
108
+ get isModel(): boolean;
109
+ get model(): any;
106
110
  get domain(): any;
107
111
  get modelName(): any;
108
112
  get hostName(): any;
@@ -119,7 +123,7 @@ export class Host extends Base {
119
123
  };
120
124
  #private;
121
125
  }
122
- export class Model extends Host {
126
+ export class NetworkInterface extends Base {
123
127
  }
124
128
  export class Subnet extends Base {
125
129
  networks: Set<any>;