* Kickstart support enabled using json configuration file.
* Checked in sample_ks.cfg for reference.
* The ks conf can be served from:
* file system, or
* http server, or
* an attached cd.
... | ... |
@@ -11,19 +11,21 @@ import crypt |
11 | 11 |
import re |
12 | 12 |
import random |
13 | 13 |
import string |
14 |
-import time |
|
15 | 14 |
import shutil |
16 | 15 |
import fnmatch |
17 | 16 |
import signal |
18 | 17 |
import sys |
18 |
+import glob |
|
19 |
+import modules.commons |
|
19 | 20 |
from jsonwrapper import JsonWrapper |
20 | 21 |
from progressbar import ProgressBar |
21 | 22 |
from window import Window |
22 | 23 |
from actionresult import ActionResult |
23 | 24 |
|
24 | 25 |
class Installer(object): |
25 |
- def __init__(self, install_config, maxy = 0, maxx = 0, iso_installer = False, tools_path = "../stage", rpm_path = "../stage/RPMS", log_path = "../stage/LOGS"): |
|
26 |
+ def __init__(self, install_config, maxy = 0, maxx = 0, iso_installer = False, tools_path = "../stage", rpm_path = "../stage/RPMS", log_path = "../stage/LOGS", ks_config = None): |
|
26 | 27 |
self.install_config = install_config |
28 |
+ self.ks_config = ks_config |
|
27 | 29 |
self.iso_installer = iso_installer |
28 | 30 |
self.tools_path = tools_path |
29 | 31 |
self.rpm_path = rpm_path |
... | ... |
@@ -45,13 +47,6 @@ class Installer(object): |
45 | 45 |
self.photon_root = self.working_directory + "/photon-chroot"; |
46 | 46 |
|
47 | 47 |
self.restart_command = "shutdown" |
48 |
- self.hostname_file = self.photon_root + "/etc/hostname" |
|
49 |
- self.hosts_file = self.photon_root + "/etc/hosts" |
|
50 |
- self.passwd_filename = self.photon_root + "/etc/passwd" |
|
51 |
- self.shadow_filename = self.photon_root + "/etc/shadow" |
|
52 |
- self.authorized_keys_dir = self.photon_root + "/root/.ssh" |
|
53 |
- self.authorized_keys_filename = self.authorized_keys_dir + "/authorized_keys" |
|
54 |
- self.sshd_config_filename = self.photon_root + "/etc/ssh/sshd_config" |
|
55 | 48 |
|
56 | 49 |
if self.iso_installer: |
57 | 50 |
self.output = open(os.devnull, 'w') |
... | ... |
@@ -100,6 +95,8 @@ class Installer(object): |
100 | 100 |
self.progress_bar.initialize('Initializing installation...') |
101 | 101 |
self.progress_bar.show() |
102 | 102 |
|
103 |
+ self.execute_modules(modules.commons.PRE_INSTALL) |
|
104 |
+ |
|
103 | 105 |
self.initialize_system() |
104 | 106 |
|
105 | 107 |
#install packages |
... | ... |
@@ -121,26 +118,21 @@ class Installer(object): |
121 | 121 |
self.finalize_system() |
122 | 122 |
|
123 | 123 |
if not self.install_config['iso_system']: |
124 |
+ # Execute post installation modules |
|
125 |
+ self.execute_modules(modules.commons.POST_INSTALL) |
|
126 |
+ |
|
124 | 127 |
# install grub |
125 | 128 |
process = subprocess.Popen([self.setup_grub_command, '-w', self.photon_root, self.install_config['disk']['disk'], self.install_config['disk']['root']], stdout=self.output) |
126 | 129 |
retval = process.wait() |
127 | 130 |
|
128 |
- #update root password |
|
129 |
- self.update_root_password() |
|
130 |
- |
|
131 |
- #update hostname |
|
132 |
- self.update_hostname() |
|
133 |
- |
|
134 |
- #update openssh config |
|
135 |
- self.update_openssh_config() |
|
136 |
- |
|
137 | 131 |
process = subprocess.Popen([self.unmount_disk_command, '-w', self.photon_root], stdout=self.output) |
138 | 132 |
retval = process.wait() |
139 | 133 |
|
140 | 134 |
if self.iso_installer: |
141 | 135 |
self.progress_bar.hide() |
142 | 136 |
self.window.addstr(0, 0, 'Congratulations, Photon has been installed in {0} secs.\n\nPress any key to continue to boot...'.format(self.progress_bar.time_elapsed)) |
143 |
- self.window.content_window().getch() |
|
137 |
+ if self.ks_config == None: |
|
138 |
+ self.window.content_window().getch() |
|
144 | 139 |
|
145 | 140 |
return ActionResult(True, None) |
146 | 141 |
|
... | ... |
@@ -186,19 +178,6 @@ class Installer(object): |
186 | 186 |
process = subprocess.Popen(['cp', '-r', "../installer", self.photon_root], stdout=self.output) |
187 | 187 |
retval = process.wait() |
188 | 188 |
|
189 |
- # get the RPMS dir form the cd |
|
190 |
- if self.rpm_path == 'cdrom': |
|
191 |
- # Mount the cd to get the RPMS |
|
192 |
- process = subprocess.Popen(['mkdir', '-p', '/mnt/cdrom'], stdout=self.output) |
|
193 |
- retval = process.wait() |
|
194 |
- |
|
195 |
- process = subprocess.Popen(['mount', '/dev/cdrom', '/mnt/cdrom'], stdout=self.output) |
|
196 |
- retval = process.wait() |
|
197 |
- if retval != 0: |
|
198 |
- self.exit_gracefully(None, None) |
|
199 |
- self.rpm_path = '/mnt/cdrom/RPMS' |
|
200 |
- self.tools_path = '/mnt/cdrom' |
|
201 |
- |
|
202 | 189 |
self.copy_rpms() |
203 | 190 |
|
204 | 191 |
def initialize_system(self): |
... | ... |
@@ -235,47 +214,30 @@ class Installer(object): |
235 | 235 |
process = subprocess.Popen([self.chroot_command, '-w', self.photon_root, self.install_package_command, '-w', self.photon_root, package_name, rpm_params], stdout=self.output) |
236 | 236 |
return process.wait() |
237 | 237 |
|
238 |
- def replace_string_in_file(self, filename, search_string, replace_string): |
|
239 |
- with open(filename, "r") as source: |
|
240 |
- lines=source.readlines() |
|
241 |
- |
|
242 |
- with open(filename, "w") as destination: |
|
243 |
- for line in lines: |
|
244 |
- destination.write(re.sub(search_string, replace_string, line)) |
|
245 |
- |
|
246 |
- def update_root_password(self): |
|
247 |
- shadow_password = self.install_config['password'] |
|
248 |
- |
|
249 |
- #replace root blank password in passwd file to point to shadow file |
|
250 |
- self.replace_string_in_file(self.passwd_filename, "root::", "root:x:") |
|
251 |
- |
|
252 |
- if os.path.isfile(self.shadow_filename) == False: |
|
253 |
- with open(self.shadow_filename, "w") as destination: |
|
254 |
- destination.write("root:"+shadow_password+":") |
|
255 |
- else: |
|
256 |
- #add password hash in shadow file |
|
257 |
- self.replace_string_in_file(self.shadow_filename, "root::", "root:"+shadow_password+":") |
|
258 |
- |
|
259 |
- def update_hostname(self): |
|
260 |
- self.hostname = self.install_config['hostname'] |
|
261 |
- outfile = open(self.hostname_file, 'wb') |
|
262 |
- outfile.write(self.hostname) |
|
263 |
- outfile.close() |
|
264 |
- |
|
265 |
- self.replace_string_in_file(self.hosts_file, r'127\.0\.0\.1\s+localhost', '127.0.0.1\tlocalhost\n127.0.0.1\t' + self.hostname) |
|
266 |
- |
|
267 |
- def update_openssh_config(self): |
|
268 |
- if 'public_key' in self.install_config: |
|
269 |
- |
|
270 |
- # Adding the authorized keys |
|
271 |
- if not os.path.exists(self.authorized_keys_dir): |
|
272 |
- os.makedirs(self.authorized_keys_dir) |
|
273 |
- with open(self.authorized_keys_filename, "a") as destination: |
|
274 |
- destination.write(self.install_config['public_key'] + "\n") |
|
275 |
- os.chmod(self.authorized_keys_filename, 0600) |
|
276 |
- |
|
277 |
- # Change the sshd config to allow root login |
|
278 |
- process = subprocess.Popen(["sed", "-i", "s/^\\s*PermitRootLogin\s\+no/PermitRootLogin yes/", self.sshd_config_filename], stdout=self.output) |
|
279 |
- return process.wait() |
|
280 |
- |
|
281 |
- |
|
238 |
+ def execute_modules(self, phase): |
|
239 |
+ modules = glob.glob('modules/m_*.py') |
|
240 |
+ for mod_path in modules: |
|
241 |
+ module = mod_path.replace('/', '.', 1) |
|
242 |
+ module = os.path.splitext(module)[0] |
|
243 |
+ try: |
|
244 |
+ __import__(module) |
|
245 |
+ mod = sys.modules[module] |
|
246 |
+ except ImportError: |
|
247 |
+ print >> sys.stderr, 'Error importing module %s' % module |
|
248 |
+ continue |
|
249 |
+ |
|
250 |
+ # the module default is disabled |
|
251 |
+ if not hasattr(mod, 'enabled') or mod.enabled == False: |
|
252 |
+ print >> sys.stderr, "module %s is not enabled" % module |
|
253 |
+ continue |
|
254 |
+ # check for the install phase |
|
255 |
+ if not hasattr(mod, 'install_phase'): |
|
256 |
+ print >> sys.stderr, "Error: can not defind module %s phase" % module |
|
257 |
+ continue |
|
258 |
+ if mod.install_phase != phase: |
|
259 |
+ print >> sys.stderr, "Skipping module %s for phase %s" % (module, phase) |
|
260 |
+ continue |
|
261 |
+ if not hasattr(mod, 'execute'): |
|
262 |
+ print >> sys.stderr, "Error: not able to execute module %s" % module |
|
263 |
+ continue |
|
264 |
+ mod.execute(module, self.ks_config, self.install_config, self.photon_root) |
... | ... |
@@ -6,6 +6,12 @@ |
6 | 6 |
|
7 | 7 |
import curses |
8 | 8 |
import sys |
9 |
+import subprocess |
|
10 |
+import re |
|
11 |
+import requests |
|
12 |
+import json |
|
13 |
+import time |
|
14 |
+import os |
|
9 | 15 |
from diskpartitioner import DiskPartitioner |
10 | 16 |
from packageselector import PackageSelector |
11 | 17 |
from custompackageselector import CustomPackageSelector |
... | ... |
@@ -17,6 +23,48 @@ from license import License |
17 | 17 |
|
18 | 18 |
class IsoInstaller(object): |
19 | 19 |
|
20 |
+ def get_config(self, path, cd_path): |
|
21 |
+ if path.startswith("http"): |
|
22 |
+ # Do 3 trials to get the kick start |
|
23 |
+ # TODO: make sure the installer run after network is up |
|
24 |
+ for x in range(0,3): |
|
25 |
+ err_msg = "" |
|
26 |
+ try: |
|
27 |
+ response = requests.get(path, timeout=3) |
|
28 |
+ if response.ok: |
|
29 |
+ return json.loads(response.text) |
|
30 |
+ err_msg = response.text |
|
31 |
+ except Exception as e: |
|
32 |
+ err_msg = e |
|
33 |
+ print >> sys.stderr, "Failed to get the kickstart file at {0}, error msg: {1}".format(path, err_msg) |
|
34 |
+ print "Failed to get the kickstart file at {0}, retry in a second".format(path) |
|
35 |
+ time.sleep(1) |
|
36 |
+ |
|
37 |
+ |
|
38 |
+ # Something went wrong |
|
39 |
+ print "Failed to get the kickstart file at {0}, exiting the installer, check the logs for more details".format(path) |
|
40 |
+ raise Exception(err_msg) |
|
41 |
+ else: |
|
42 |
+ if path.startswith("cdrom:/"): |
|
43 |
+ path = os.path.join(cd_path, path.replace("cdrom:/", "", 1)) |
|
44 |
+ return (JsonWrapper(path)).read(); |
|
45 |
+ |
|
46 |
+ def mount_RPMS_cd(self): |
|
47 |
+ # Mount the cd to get the RPMS |
|
48 |
+ process = subprocess.Popen(['mkdir', '-p', '/mnt/cdrom']) |
|
49 |
+ retval = process.wait() |
|
50 |
+ |
|
51 |
+ # Retry mount the CD |
|
52 |
+ for i in range(0,3): |
|
53 |
+ process = subprocess.Popen(['mount', '/dev/cdrom', '/mnt/cdrom']) |
|
54 |
+ retval = process.wait() |
|
55 |
+ if retval == 0: |
|
56 |
+ return "/mnt/cdrom" |
|
57 |
+ print "Failed to mount the cd, retry in a second" |
|
58 |
+ time.sleep(1) |
|
59 |
+ print "Failed to mount the cd, exiting the installer, check the logs for more details" |
|
60 |
+ raise Exception("Can not mount the cd") |
|
61 |
+ |
|
20 | 62 |
def __init__(self, stdscreen): |
21 | 63 |
self.screen = stdscreen |
22 | 64 |
|
... | ... |
@@ -33,13 +81,20 @@ class IsoInstaller(object): |
33 | 33 |
|
34 | 34 |
curses.curs_set(0) |
35 | 35 |
|
36 |
- self.install_config = {} |
|
36 |
+ self.install_config = {'iso_system': False} |
|
37 | 37 |
|
38 |
- license_agreement = License(self.maxy, self.maxx) |
|
38 |
+ # Mount the cd for the RPM, tools, and may be the ks |
|
39 |
+ cd_path = self.mount_RPMS_cd() |
|
40 |
+ |
|
41 |
+ # check the kickstart params |
|
42 |
+ ks_config = None |
|
43 |
+ kernel_params = subprocess.check_output(['cat', '/proc/cmdline']) |
|
44 |
+ m = re.match(r".*ks=(\S+)\s*.*\s*", kernel_params) |
|
45 |
+ if m != None: |
|
46 |
+ ks_config = self.get_config(m.group(1), cd_path) |
|
39 | 47 |
|
40 |
- self.install_config['iso_system'] = False |
|
48 |
+ license_agreement = License(self.maxy, self.maxx) |
|
41 | 49 |
select_disk = SelectDisk(self.maxy, self.maxx, self.install_config) |
42 |
- |
|
43 | 50 |
package_selector = PackageSelector(self.maxy, self.maxx, self.install_config) |
44 | 51 |
custom_package_selector = CustomPackageSelector(self.maxy, self.maxx, self.install_config) |
45 | 52 |
hostname_reader = WindowStringReader(self.maxy, self.maxx, 10, 70, False, 'Choose the hostname for your system', |
... | ... |
@@ -48,18 +103,20 @@ class IsoInstaller(object): |
48 | 48 |
root_password_reader = WindowStringReader(self.maxy, self.maxx, 10, 70, True, 'Set up root password', |
49 | 49 |
'Root password:', |
50 | 50 |
2, self.install_config) |
51 |
- installer = Installer(self.install_config, self.maxy, self.maxx, True, tools_path=None, rpm_path='cdrom', log_path="/var/log") |
|
51 |
+ installer = Installer(self.install_config, self.maxy, self.maxx, True, tools_path=cd_path, rpm_path=os.path.join(cd_path, "RPMS"), log_path="/var/log", ks_config=ks_config) |
|
52 | 52 |
|
53 | 53 |
# This represents the installer screen, the bool indicated if I can go back to this window or not |
54 |
- items = [ |
|
54 |
+ items = [] |
|
55 |
+ if not ks_config: |
|
56 |
+ items = items + [ |
|
55 | 57 |
(license_agreement.display, False), |
56 | 58 |
(select_disk.display, True), |
57 | 59 |
(package_selector.display, True), |
58 | 60 |
(custom_package_selector.display, False), |
59 | 61 |
(hostname_reader.get_user_string, True), |
60 | 62 |
(root_password_reader.get_user_string, True), |
61 |
- (installer.install, False) |
|
62 |
- ] |
|
63 |
+ ] |
|
64 |
+ items = items + [(installer.install, False)] |
|
63 | 65 |
|
64 | 66 |
index = 0 |
65 | 67 |
params = None |
... | ... |
@@ -36,12 +36,13 @@ PACKAGE_LIST_FILE=$4 |
36 | 36 |
WORKINGDIR=${BUILDROOT} |
37 | 37 |
BUILDROOT=${BUILDROOT}/photon-chroot |
38 | 38 |
|
39 |
-mkdir ${WORKINGDIR}/isolinux |
|
40 |
-cp BUILD_DVD/isolinux/* ${WORKINGDIR}/isolinux/ |
|
39 |
+cp -r BUILD_DVD/isolinux ${WORKINGDIR}/ |
|
40 |
+cp sample_ks.cfg ${WORKINGDIR}/isolinux/ |
|
41 | 41 |
|
42 | 42 |
find ${BUILDROOT} -name linux-[0-9]*.rpm | head -1 | xargs rpm2cpio | cpio -iv --to-stdout ./boot/vmlinuz* > ${WORKINGDIR}/isolinux/vmlinuz |
43 | 43 |
|
44 | 44 |
rm -f ${BUILDROOT}/installer/*.pyc |
45 |
+rm -rf ${BUILDROOT}/installer/BUILD_DVD |
|
45 | 46 |
# replace default package_list with specific one |
46 | 47 |
cp $PACKAGE_LIST_FILE ${BUILDROOT}/installer/package_list.json |
47 | 48 |
|
49 | 50 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,12 @@ |
0 |
+import re |
|
1 |
+ |
|
2 |
+PRE_INSTALL = "pre-install" |
|
3 |
+POST_INSTALL = "post-install" |
|
4 |
+ |
|
5 |
+def replace_string_in_file(filename, search_string, replace_string): |
|
6 |
+ with open(filename, "r") as source: |
|
7 |
+ lines=source.readlines() |
|
8 |
+ |
|
9 |
+ with open(filename, "w") as destination: |
|
10 |
+ for line in lines: |
|
11 |
+ destination.write(re.sub(search_string, replace_string, line)) |
|
0 | 12 |
\ No newline at end of file |
1 | 13 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,24 @@ |
0 |
+import os |
|
1 |
+import commons |
|
2 |
+from jsonwrapper import JsonWrapper |
|
3 |
+ |
|
4 |
+install_phase = commons.PRE_INSTALL |
|
5 |
+enabled = True |
|
6 |
+ |
|
7 |
+def execute(name, ks_config, config, root): |
|
8 |
+ |
|
9 |
+ if ks_config: |
|
10 |
+ package_list = JsonWrapper("package_list.json").read() |
|
11 |
+ |
|
12 |
+ if ks_config['type'] == 'micro': |
|
13 |
+ packages = package_list["micro_packages"] |
|
14 |
+ elif ks_config['type'] == 'minimal': |
|
15 |
+ packages = package_list["minimal_packages"] |
|
16 |
+ elif ks_config['type'] == 'full': |
|
17 |
+ packages = package_list["minimal_packages"] + package_list["optional_packages"] |
|
18 |
+ else: |
|
19 |
+ #TODO: error |
|
20 |
+ packages = [] |
|
21 |
+ |
|
22 |
+ config['type'] = ks_config['type'] |
|
23 |
+ config["packages"] = packages |
0 | 24 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,35 @@ |
0 |
+import os |
|
1 |
+import subprocess |
|
2 |
+import commons |
|
3 |
+ |
|
4 |
+install_phase = commons.PRE_INSTALL |
|
5 |
+enabled = True |
|
6 |
+ |
|
7 |
+def partition_disk(disk): |
|
8 |
+ partitions_data = {} |
|
9 |
+ partitions_data['disk'] = disk |
|
10 |
+ partitions_data['root'] = disk + '2' |
|
11 |
+ |
|
12 |
+ output = open(os.devnull, 'w') |
|
13 |
+ |
|
14 |
+ # Clear the disk |
|
15 |
+ process = subprocess.Popen(['sgdisk', '-o', '-g', partitions_data['disk']], stdout = output) |
|
16 |
+ retval = process.wait() |
|
17 |
+ |
|
18 |
+ # 1: grub, 2: filesystem |
|
19 |
+ process = subprocess.Popen(['sgdisk', '-n', '1::+2M', '-n', '2:', '-p', partitions_data['disk']], stdout = output) |
|
20 |
+ retval = process.wait() |
|
21 |
+ |
|
22 |
+ # Add the grub flags |
|
23 |
+ process = subprocess.Popen(['sgdisk', '-t1:ef02', partitions_data['disk']], stdout = output) |
|
24 |
+ retval = process.wait() |
|
25 |
+ |
|
26 |
+ # format the file system |
|
27 |
+ process = subprocess.Popen(['mkfs', '-t', 'ext4', partitions_data['root']], stdout = output) |
|
28 |
+ retval = process.wait() |
|
29 |
+ return partitions_data |
|
30 |
+ |
|
31 |
+def execute(name, ks_config, config, root): |
|
32 |
+ |
|
33 |
+ if ks_config: |
|
34 |
+ config['disk'] = partition_disk(ks_config['disk']) |
0 | 35 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,24 @@ |
0 |
+import os |
|
1 |
+import subprocess |
|
2 |
+import commons |
|
3 |
+ |
|
4 |
+install_phase = commons.POST_INSTALL |
|
5 |
+enabled = True |
|
6 |
+ |
|
7 |
+def execute(name, ks_config, config, root): |
|
8 |
+ |
|
9 |
+ if ks_config and 'postinstall' in ks_config: |
|
10 |
+ config['postinstall'] = ks_config['postinstall'] |
|
11 |
+ if 'postinstall' not in config: |
|
12 |
+ return |
|
13 |
+ # run the script in the chroot environment |
|
14 |
+ script = config['postinstall'] |
|
15 |
+ |
|
16 |
+ script_file = os.path.join(root, 'tmp/postinstall.sh') |
|
17 |
+ |
|
18 |
+ with open(script_file, 'wb') as outfile: |
|
19 |
+ outfile.write("\n".join(script)) |
|
20 |
+ |
|
21 |
+ os.chmod(script_file, 0700); |
|
22 |
+ process = subprocess.Popen(["./mk-run-chroot.sh", '-w', root, "/tmp/postinstall.sh"]) |
|
23 |
+ process.wait() |
0 | 24 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,19 @@ |
0 |
+import os |
|
1 |
+import commons |
|
2 |
+ |
|
3 |
+install_phase = commons.POST_INSTALL |
|
4 |
+enabled = True |
|
5 |
+ |
|
6 |
+def execute(name, ks_config, config, root): |
|
7 |
+ |
|
8 |
+ if ks_config: |
|
9 |
+ config["hostname"] = ks_config["hostname"] |
|
10 |
+ hostname = config['hostname'] |
|
11 |
+ |
|
12 |
+ hostname_file = os.path.join(root, 'etc/hostname') |
|
13 |
+ hosts_file = os.path.join(root, 'etc/hosts') |
|
14 |
+ |
|
15 |
+ with open(hostname_file, 'wb') as outfile: |
|
16 |
+ outfile.write(hostname) |
|
17 |
+ |
|
18 |
+ commons.replace_string_in_file(hosts_file, r'127\.0\.0\.1\s+localhost', '127.0.0.1\tlocalhost\n127.0.0.1\t' + hostname) |
0 | 19 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,34 @@ |
0 |
+import os |
|
1 |
+import commons |
|
2 |
+import crypt |
|
3 |
+import random |
|
4 |
+import string |
|
5 |
+ |
|
6 |
+install_phase = commons.POST_INSTALL |
|
7 |
+enabled = True |
|
8 |
+ |
|
9 |
+def execute(name, ks_config, config, root): |
|
10 |
+ |
|
11 |
+ if ks_config: |
|
12 |
+ # crypt the password if needed |
|
13 |
+ if ks_config['password']['crypted']: |
|
14 |
+ config['password'] = ks_config['password']['text'] |
|
15 |
+ else: |
|
16 |
+ config['password'] = crypt.crypt(ks_config['password']['text'], |
|
17 |
+ "$6$" + "".join([random.choice(string.ascii_letters + string.digits) for _ in range(16)])) |
|
18 |
+ |
|
19 |
+ shadow_password = config['password'] |
|
20 |
+ |
|
21 |
+ passwd_filename = os.path.join(root, 'etc/passwd') |
|
22 |
+ shadow_filename = os.path.join(root, 'etc/shadow') |
|
23 |
+ |
|
24 |
+ #replace root blank password in passwd file to point to shadow file |
|
25 |
+ commons.replace_string_in_file(passwd_filename, "root::", "root:x:") |
|
26 |
+ |
|
27 |
+ if os.path.isfile(shadow_filename) == False: |
|
28 |
+ with open(shadow_filename, "w") as destination: |
|
29 |
+ destination.write("root:"+shadow_password+":") |
|
30 |
+ else: |
|
31 |
+ #add password hash in shadow file |
|
32 |
+ commons.replace_string_in_file(shadow_filename, "root::", "root:"+shadow_password+":") |
|
33 |
+ |
0 | 34 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,27 @@ |
0 |
+import os |
|
1 |
+import subprocess |
|
2 |
+import commons |
|
3 |
+ |
|
4 |
+install_phase = commons.POST_INSTALL |
|
5 |
+enabled = True |
|
6 |
+ |
|
7 |
+def execute(name, ks_config, config, root): |
|
8 |
+ if ks_config and 'public_key' in ks_config: |
|
9 |
+ config['public_key'] = ks_config['public_key'] |
|
10 |
+ if 'public_key' not in config: |
|
11 |
+ return |
|
12 |
+ |
|
13 |
+ authorized_keys_dir = os.path.join(root, "root/.ssh") |
|
14 |
+ authorized_keys_filename = os.path.join(authorized_keys_dir, "authorized_keys") |
|
15 |
+ sshd_config_filename = os.path.join(root, "etc/ssh/sshd_config") |
|
16 |
+ |
|
17 |
+ # Adding the authorized keys |
|
18 |
+ if not os.path.exists(authorized_keys_dir): |
|
19 |
+ os.makedirs(authorized_keys_dir) |
|
20 |
+ with open(authorized_keys_filename, "a") as destination: |
|
21 |
+ destination.write(config['public_key'] + "\n") |
|
22 |
+ os.chmod(authorized_keys_filename, 0600) |
|
23 |
+ |
|
24 |
+ # Change the sshd config to allow root login |
|
25 |
+ process = subprocess.Popen(["sed", "-i", "s/^\\s*PermitRootLogin\s\+no/PermitRootLogin yes/", sshd_config_filename]) |
|
26 |
+ return process.wait() |
... | ... |
@@ -11,7 +11,7 @@ |
11 | 11 |
"linux", "patch", "cpio", |
12 | 12 |
"Linux-PAM", "attr", "libcap", "systemd", "dbus", |
13 | 13 |
"elfutils-libelf", "elfutils-libelf-devel", "sqlite-autoconf", "nspr", "nss-devel", "nss", "popt", "lua", "rpm", |
14 |
- "which", "gptfdisk", "tar", "gzip", "python2", "python2-devel", "python2-libs", "python2-tools", |
|
14 |
+ "which", "gptfdisk", "tar", "gzip", "openssl", "python2", "python2-devel", "python2-libs", "python2-tools", "python-requests", |
|
15 | 15 |
"pcre", "pcre-devel", "glib", "glib-devel", "parted", "libsigc++", "XML-Parser", "glibmm", "dparted", |
16 | 16 |
"libgsystem", "ostree"], |
17 | 17 |
|
18 | 18 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,15 @@ |
0 |
+{ |
|
1 |
+ "hostname": "photon-machine", |
|
2 |
+ "password": |
|
3 |
+ { |
|
4 |
+ "crypted": false, |
|
5 |
+ "text": "VMware123!" |
|
6 |
+ }, |
|
7 |
+ "disk": "/dev/sda", |
|
8 |
+ "type": "minimal", |
|
9 |
+ "postinstall": [ |
|
10 |
+ "#!/bin/sh", |
|
11 |
+ "echo \"Hello World\" > /etc/postinstall" |
|
12 |
+ ], |
|
13 |
+ "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDNBCufEER/6lBwFp+osVc/2wIZZXRRjvQkA4dPXOTcSfJDIcvRoGDWbEREQK95GJ8BGQ3IJUDXzb2uSt9daPU0FiPqoFHh84pX8M6EVMpyVrEW0xRutt5KBshQ3BQ6Cmw8bstSFtNYZeB7RnR8cU5xMlhtwEkoECwQhJsFtDzVEfrjJLKdEzNMaFlGbX/QxmY434JhpDo+BYjTYMns5lsz+AORPpCmG/48UtsmqAWsWhndYgD9GK53ISMw3LOg2ocmiP0yVYIYNmFmAztzD8g3erqmVlI64HOcn8VYlUUienu0nPt1lX5uNEGXn8fTOBvuTVkHKGs/f8yzWETTytzTweurBI6EeQVt0QP0+XI6osCxNMMvnh/Vx/xl+dgv9EK61pgzttzA8IbWLGUQu9mLAmHa17R/xoygaGUDKGdcZk4EAtAY2RbhO+xBW0nwyt4977CJGIz672H/oSQ3HtNSdIZI/fA7mDWbymSiFZgAXNhnm/jCO7ZR2AjZ7hd9sQzX+mVOH9lGMBEbznYyVOK+Fy5rHqPtQfcAYCfuoVRhJHhwq90jPl4bXv1Z++Nw97M7DJ2uXy+OFu+3HOQ2HliOozwkDUeoUV7pBX9W7idRr6Xb1h3DEE7Xa5R4QEhmCLb0Hr5sg4+0IADIgg7wrBgkA+l+c6EqRNKFYOzy3xAQqw== mbassiouny@vmware.com" |
|
14 |
+} |