author | wenzelm |
Fri, 21 Jul 2023 13:02:07 +0200 | |
changeset 78426 | 0be7e94fd243 |
parent 78425 | 62d7ef1da441 |
child 78427 | 5b7d1cb073db |
permissions | -rw-r--r-- |
78398 | 1 |
/* Title: Pure/Tools/build_cluster.scala |
2 |
Author: Makarius |
|
3 |
||
4 |
Management of build cluster: independent ssh hosts with access to shared |
|
5 |
PostgreSQL server. |
|
6 |
*/ |
|
7 |
||
8 |
package isabelle |
|
9 |
||
10 |
||
11 |
object Build_Cluster { |
|
78413 | 12 |
/* host specifications */ |
13 |
||
78399 | 14 |
object Host { |
78408
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
15 |
private val rfc822_specials = """()<>@,;:\"[]""" |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
16 |
|
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
17 |
private val USER = "user" |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
18 |
private val PORT = "port" |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
19 |
private val JOBS = "jobs" |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
20 |
private val NUMA = "numa" |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
21 |
|
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
22 |
val parameters: Options = |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
23 |
Options.inline(""" |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
24 |
option user : string = "" -- "explicit SSH user" |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
25 |
option port : int = 0 -- "explicit SSH port" |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
26 |
option jobs : int = 1 -- "maximum number of parallel jobs" |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
27 |
option numa : bool = false -- "cyclic shuffling of NUMA CPU nodes" |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
28 |
""") |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
29 |
|
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
30 |
def is_parameter(spec: Options.Spec): Boolean = parameters.defined(spec.name) |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
31 |
|
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
32 |
lazy val test_options: Options = Options.init0() |
78399 | 33 |
|
34 |
def apply( |
|
78416 | 35 |
name: String = "", |
78408
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
36 |
user: String = parameters.string(USER), |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
37 |
port: Int = parameters.int(PORT), |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
38 |
jobs: Int = parameters.int(JOBS), |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
39 |
numa: Boolean = parameters.bool(NUMA), |
78399 | 40 |
options: List[Options.Spec] = Nil |
78416 | 41 |
): Host = new Host(name, user, port, jobs, numa, options) |
78401 | 42 |
|
78408
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
43 |
def parse(str: String): Host = { |
78416 | 44 |
val name = str.takeWhile(c => !rfc822_specials.contains(c)) |
78408
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
45 |
val (params, options) = |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
46 |
try { |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
47 |
val rest = { |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
48 |
val n = str.length |
78416 | 49 |
val m = name.length |
78408
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
50 |
val l = |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
51 |
if (m == n) n |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
52 |
else if (str(m) == ':') m + 1 |
78423 | 53 |
else error("Missing \":\" after host name") |
78408
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
54 |
str.substring(l) |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
55 |
} |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
56 |
val (specs1, specs2) = Options.parse_specs(rest).partition(is_parameter) |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
57 |
(parameters ++ specs1, { test_options ++ specs2; specs2 }) |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
58 |
} |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
59 |
catch { |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
60 |
case ERROR(msg) => |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
61 |
cat_error(msg, "The error(s) above occurred in host specification " + quote(str)) |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
62 |
} |
78416 | 63 |
apply(name = name, |
78408
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
64 |
user = params.string(USER), |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
65 |
port = params.int(PORT), |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
66 |
jobs = params.int(JOBS), |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
67 |
numa = params.bool(NUMA), |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
68 |
options = options) |
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
69 |
} |
78426 | 70 |
|
71 |
def print_name(s: String): String = |
|
72 |
Token.quote_name(Options.specs_syntax.keywords, s) |
|
73 |
||
74 |
def print_option(spec: Options.Spec): String = { |
|
75 |
def print_value = |
|
76 |
spec.value.get match { |
|
77 |
case s@Value.Boolean(_) => s |
|
78 |
case s@Value.Long(_) => s |
|
79 |
case s@Value.Double(_) => s |
|
80 |
case s => print_name(s) |
|
81 |
} |
|
82 |
spec.name + if_proper(spec.value, "=" + print_value) |
|
83 |
} |
|
78399 | 84 |
} |
85 |
||
86 |
final class Host private( |
|
78416 | 87 |
name: String, |
78399 | 88 |
user: String, |
89 |
port: Int, |
|
90 |
jobs: Int, |
|
91 |
numa: Boolean, |
|
92 |
options: List[Options.Spec] |
|
93 |
) { |
|
78425 | 94 |
def is_local: Boolean = SSH.is_local(name) |
78413 | 95 |
|
78408
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
96 |
override def toString: String = print |
78399 | 97 |
|
98 |
def print: String = { |
|
78408
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
99 |
val params = |
78399 | 100 |
List( |
78426 | 101 |
if (user.isEmpty) "" else Properties.Eq(Host.USER, Host.print_name(user)), |
78399 | 102 |
if (port == 0) "" else Properties.Eq(Host.PORT, port.toString), |
103 |
if (jobs == 1) "" else Properties.Eq(Host.JOBS, jobs.toString), |
|
104 |
if_proper(numa, Host.NUMA) |
|
105 |
).filter(_.nonEmpty) |
|
78426 | 106 |
val rest = (params ::: options.map(Host.print_option)).mkString(",") |
78408
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
wenzelm
parents:
78401
diff
changeset
|
107 |
|
78425 | 108 |
SSH.print_local(name) + if_proper(rest, ":" + rest) |
78399 | 109 |
} |
110 |
||
111 |
def open_ssh_session(options: Options): SSH.Session = |
|
78416 | 112 |
SSH.open_session(options, name, port = port, user = user) |
78399 | 113 |
} |
78413 | 114 |
|
115 |
||
116 |
/* remote sessions */ |
|
117 |
||
118 |
class Session(host: Host) extends AutoCloseable { |
|
119 |
override def close(): Unit = () |
|
120 |
} |
|
78398 | 121 |
} |
78413 | 122 |
|
123 |
// class extensible via Build.Engine.build_process() and Build_Process.init_cluster() |
|
124 |
class Build_Cluster( |
|
78421 | 125 |
build_context: Build.Context, |
78413 | 126 |
remote_hosts: List[Build_Cluster.Host], |
127 |
progress: Progress = new Progress |
|
128 |
) extends AutoCloseable { |
|
78424 | 129 |
require(remote_hosts.nonEmpty && !remote_hosts.exists(_.is_local), "remote hosts required") |
78413 | 130 |
|
131 |
override def toString: String = remote_hosts.mkString("Build_Cluster(", ", ", ")") |
|
132 |
||
133 |
progress.echo("Remote hosts:\n" + cat_lines(remote_hosts.map(" " + _))) |
|
134 |
||
135 |
def start(): Unit = () |
|
136 |
def stop(): Unit = () |
|
137 |
||
138 |
override def close(): Unit = () |
|
139 |
} |