forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
boot_data.py
172 lines (135 loc) · 5.52 KB
/
boot_data.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Functions used to provision Fuchsia boot images."""
import common
import logging
import os
import subprocess
import tempfile
import time
import uuid
_SSH_CONFIG_TEMPLATE = """
Host *
CheckHostIP no
StrictHostKeyChecking no
ForwardAgent no
ForwardX11 no
UserKnownHostsFile {known_hosts}
User fuchsia
IdentitiesOnly yes
IdentityFile {identity}
ServerAliveInterval 2
ServerAliveCountMax 5
ControlMaster auto
ControlPersist 1m
ControlPath /tmp/ssh-%r@%h:%p"""
FVM_TYPE_QCOW = 'qcow'
FVM_TYPE_SPARSE = 'sparse'
def _TargetCpuToSdkBinPath(target_arch):
"""Returns the path to the SDK 'target' file directory for |target_cpu|."""
return os.path.join(common.SDK_ROOT, 'target', target_arch)
def _ProvisionSSH(output_dir):
"""Provisions the key files used by the SSH daemon, and generates a
configuration file used by clients for connecting to SSH.
Returns a tuple with:
#0: the client configuration file
#1: a list of file path pairs: (<path in image>, <path on build filesystem>).
"""
host_key_path = output_dir + '/ssh_key'
host_pubkey_path = host_key_path + '.pub'
id_key_path = output_dir + '/id_ed25519'
id_pubkey_path = id_key_path + '.pub'
known_hosts_path = output_dir + '/known_hosts'
ssh_config_path = GetSSHConfigPath(output_dir)
logging.debug('Generating SSH credentials.')
if not os.path.isfile(host_key_path):
subprocess.check_call(['ssh-keygen', '-t', 'ed25519', '-h', '-f',
host_key_path, '-P', '', '-N', ''],
stdout=open(os.devnull))
if not os.path.isfile(id_key_path):
subprocess.check_call(['ssh-keygen', '-t', 'ed25519', '-f', id_key_path,
'-P', '', '-N', ''], stdout=open(os.devnull))
with open(ssh_config_path, "w") as ssh_config:
ssh_config.write(
_SSH_CONFIG_TEMPLATE.format(identity=id_key_path,
known_hosts=known_hosts_path))
if os.path.exists(known_hosts_path):
os.remove(known_hosts_path)
return (
ssh_config_path,
(('ssh/ssh_host_ed25519_key', host_key_path),
('ssh/ssh_host_ed25519_key.pub', host_pubkey_path),
('ssh/authorized_keys', id_pubkey_path))
)
def _MakeQcowDisk(output_dir, disk_path):
"""Creates a QEMU copy-on-write version of |disk_path| in the output
directory."""
qimg_path = os.path.join(common.SDK_ROOT, 'qemu', 'bin', 'qemu-img')
output_path = os.path.join(output_dir,
os.path.basename(disk_path) + '.qcow2')
subprocess.check_call([qimg_path, 'create', '-q', '-f', 'qcow2',
'-b', disk_path, output_path])
return output_path
def GetTargetFile(target_arch, filename):
"""Computes a path to |filename| in the Fuchsia target directory specific to
|target_arch|."""
return os.path.join(_TargetCpuToSdkBinPath(target_arch), filename)
def GetSSHConfigPath(output_dir):
return output_dir + '/ssh_config'
def ConfigureDataFVM(output_dir, output_type):
"""Builds the FVM image for the /data volume and prepopulates it
with SSH keys.
output_dir: Path to the output directory which will contain the FVM file.
output_type: If FVM_TYPE_QCOW, then returns a path to the qcow2 FVM file,
used for QEMU.
If FVM_TYPE_SPARSE, then returns a path to the
sparse/compressed FVM file."""
logging.debug('Building /data partition FVM file.')
with tempfile.NamedTemporaryFile() as data_file:
# Build up the minfs partition data and install keys into it.
ssh_config, ssh_data = _ProvisionSSH(output_dir)
with tempfile.NamedTemporaryFile() as manifest:
for dest, src in ssh_data:
manifest.write('%s=%s\n' % (dest, src))
manifest.flush()
minfs_path = os.path.join(common.SDK_ROOT, 'tools', 'minfs')
subprocess.check_call([minfs_path, '%s@1G' % data_file.name, 'create'])
subprocess.check_call([minfs_path, data_file.name, 'manifest',
manifest.name])
# Wrap the minfs partition in a FVM container.
fvm_path = os.path.join(common.SDK_ROOT, 'tools', 'fvm')
fvm_output_path = os.path.join(output_dir, 'fvm.data.blk')
if os.path.exists(fvm_output_path):
os.remove(fvm_output_path)
if output_type == FVM_TYPE_SPARSE:
cmd = [fvm_path, fvm_output_path, 'sparse', '--compress', 'lz4',
'--data', data_file.name]
else:
cmd = [fvm_path, fvm_output_path, 'create', '--data', data_file.name]
logging.debug(' '.join(cmd))
subprocess.check_call(cmd)
if output_type == FVM_TYPE_SPARSE:
return fvm_output_path
elif output_type == FVM_TYPE_QCOW:
return _MakeQcowDisk(output_dir, fvm_output_path)
else:
raise Exception('Unknown output_type: %r' % output_type)
def GetNodeName(output_dir):
"""Returns the cached Zircon node name, or generates one if it doesn't
already exist. The node name is used by Discover to find the prior
deployment on the LAN."""
nodename_file = os.path.join(output_dir, 'nodename')
if not os.path.exists(nodename_file):
nodename = uuid.uuid4()
f = open(nodename_file, 'w')
f.write(str(nodename))
f.flush()
f.close()
return str(nodename)
else:
f = open(nodename_file, 'r')
return f.readline()
def GetKernelArgs(output_dir):
return ['devmgr.epoch=%d' % time.time(),
'zircon.nodename=' + GetNodeName(output_dir)]