8 package isabelle |
8 package isabelle |
9 |
9 |
10 |
10 |
11 object Build_Cluster { |
11 object Build_Cluster { |
12 object Host { |
12 object Host { |
13 val PORT = "port" |
13 private val rfc822_specials = """()<>@,;:\"[]""" |
14 val JOBS = "jobs" |
14 |
15 val NUMA = "numa" |
15 private val USER = "user" |
|
16 private val PORT = "port" |
|
17 private val JOBS = "jobs" |
|
18 private val NUMA = "numa" |
|
19 |
|
20 val parameters: Options = |
|
21 Options.inline(""" |
|
22 option user : string = "" -- "explicit SSH user" |
|
23 option port : int = 0 -- "explicit SSH port" |
|
24 option jobs : int = 1 -- "maximum number of parallel jobs" |
|
25 option numa : bool = false -- "cyclic shuffling of NUMA CPU nodes" |
|
26 """) |
|
27 |
|
28 def is_parameter(spec: Options.Spec): Boolean = parameters.defined(spec.name) |
|
29 |
|
30 lazy val test_options: Options = Options.init0() |
16 |
31 |
17 def apply( |
32 def apply( |
18 host: String, |
33 host: String = "", |
19 user: String = "", |
34 user: String = parameters.string(USER), |
20 port: Int = 0, |
35 port: Int = parameters.int(PORT), |
21 jobs: Int = 1, |
36 jobs: Int = parameters.int(JOBS), |
22 numa: Boolean = false, |
37 numa: Boolean = parameters.bool(NUMA), |
23 options: List[Options.Spec] = Nil |
38 options: List[Options.Spec] = Nil |
24 ): Host = new Host(host, user, port, jobs, numa, options) |
39 ): Host = new Host(host, user, port, jobs, numa, options) |
25 |
40 |
26 def parse(str: String): Host = Host(host = str) // FIXME proper parse |
41 def parse(str: String): Host = { |
|
42 val host = str.takeWhile(c => !rfc822_specials.contains(c)) |
|
43 val (params, options) = |
|
44 try { |
|
45 val rest = { |
|
46 val n = str.length |
|
47 val m = host.length |
|
48 val l = |
|
49 if (m == n) n |
|
50 else if (str(m) == ':') m + 1 |
|
51 else error("Colon (:) expected after host name") |
|
52 str.substring(l) |
|
53 } |
|
54 val (specs1, specs2) = Options.parse_specs(rest).partition(is_parameter) |
|
55 (parameters ++ specs1, { test_options ++ specs2; specs2 }) |
|
56 } |
|
57 catch { |
|
58 case ERROR(msg) => |
|
59 cat_error(msg, "The error(s) above occurred in host specification " + quote(str)) |
|
60 } |
|
61 apply(host = host, |
|
62 user = params.string(USER), |
|
63 port = params.int(PORT), |
|
64 jobs = params.int(JOBS), |
|
65 numa = params.bool(NUMA), |
|
66 options = options) |
|
67 } |
27 } |
68 } |
28 |
69 |
29 final class Host private( |
70 final class Host private( |
30 host: String, |
71 host: String, |
31 user: String, |
72 user: String, |
32 port: Int, |
73 port: Int, |
33 jobs: Int, |
74 jobs: Int, |
34 numa: Boolean, |
75 numa: Boolean, |
35 options: List[Options.Spec] |
76 options: List[Options.Spec] |
36 ) { |
77 ) { |
37 require(host.nonEmpty, "Undefined host name") |
78 override def toString: String = print |
38 |
79 |
39 override def toString: String = print |
|
40 def print: String = { |
80 def print: String = { |
41 val props = |
81 val params = |
42 List( |
82 List( |
|
83 if (user.isEmpty) "" else Properties.Eq(Host.USER, user), |
43 if (port == 0) "" else Properties.Eq(Host.PORT, port.toString), |
84 if (port == 0) "" else Properties.Eq(Host.PORT, port.toString), |
44 if (jobs == 1) "" else Properties.Eq(Host.JOBS, jobs.toString), |
85 if (jobs == 1) "" else Properties.Eq(Host.JOBS, jobs.toString), |
45 if_proper(numa, Host.NUMA) |
86 if_proper(numa, Host.NUMA) |
46 ).filter(_.nonEmpty) |
87 ).filter(_.nonEmpty) |
47 val rest = (props ::: options).mkString(",") |
88 val rest = (params ::: options).mkString(",") |
48 if_proper(user, user + "@") + host + if_proper(rest, ":" + rest) |
89 |
|
90 if (host.isEmpty) ":" + rest |
|
91 else if (rest.isEmpty) host |
|
92 else host + ":" + rest |
49 } |
93 } |
50 |
94 |
51 def open_ssh_session(options: Options): SSH.Session = |
95 def open_ssh_session(options: Options): SSH.Session = |
52 SSH.open_session(options, host, port = port, user = user) |
96 SSH.open_session(options, host, port = port, user = user) |
53 } |
97 } |