Merge lp:~nuclearbob/utah/cobbler-desktop into lp:utah
- cobbler-desktop
- Merge into dev
Proposed by
Max Brustkern
Status: | Merged |
---|---|
Merged at revision: | 693 |
Proposed branch: | lp:~nuclearbob/utah/cobbler-desktop |
Merge into: | lp:utah |
Diff against target: |
245 lines (+103/-32) 2 files modified
utah/config.py (+6/-2) utah/provisioning/baremetal/cobbler.py (+97/-30) |
To merge this branch: | bzr merge lp:~nuclearbob/utah/cobbler-desktop |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Joe Talbott (community) | Approve | ||
Review via email: mp+123281@code.launchpad.net |
Commit message
Description of the change
This branch adds support for mini and desktop images to the CobblerMachine class. Desktop images currently require nfs, and are configured via the nfs options in the updated config.py. I haven't yet incorporated a dependency or check for nfs, so feedback on the ideal way to do that would be welcome. I've tested both types in magners-orchestra. Currently, the desktop install requires this latecommand:
chroot /target apt-get update
To get python-jsonschema from the PPA correctly in magners-orchestra, but once we have a different fix in place for that, we won't need that in the preseed anymore.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'utah/config.py' | |||
2 | --- utah/config.py 2012-08-22 15:20:25 +0000 | |||
3 | +++ utah/config.py 2012-09-07 14:03:40 +0000 | |||
4 | @@ -42,6 +42,9 @@ | |||
5 | 42 | machineid=None, | 42 | machineid=None, |
6 | 43 | name=None, | 43 | name=None, |
7 | 44 | new=False, | 44 | new=False, |
8 | 45 | nfscommand=['sudo', os.path.join('/', 'etc', 'init.d', 'nfs-kernel-server'), 'reload'], | ||
9 | 46 | nfsconfigfile=os.path.join('/', 'etc', 'exports'), | ||
10 | 47 | nfsoptions='*(ro,async,no_root_squash,no_subtree_check)', | ||
11 | 45 | powertimeout=5, | 48 | powertimeout=5, |
12 | 46 | preseed=None, | 49 | preseed=None, |
13 | 47 | qemupath='qemu:///system', | 50 | qemupath='qemu:///system', |
14 | @@ -55,6 +58,7 @@ | |||
15 | 55 | """ | 58 | """ |
16 | 56 | LOCALDEFAULTS = dict( | 59 | LOCALDEFAULTS = dict( |
17 | 57 | bridge=getbridge(), | 60 | bridge=getbridge(), |
18 | 61 | hostname=socket.gethostname(), | ||
19 | 58 | sshprivatekey=os.path.join(os.path.expanduser('~'), '.ssh', 'utah'), | 62 | sshprivatekey=os.path.join(os.path.expanduser('~'), '.ssh', 'utah'), |
20 | 59 | sshpublickey=os.path.join(os.path.expanduser('~'), '.ssh', 'utah.pub'), | 63 | sshpublickey=os.path.join(os.path.expanduser('~'), '.ssh', 'utah.pub'), |
21 | 60 | sshknownhosts=os.path.join(os.path.expanduser('~'), '.ssh', 'known_hosts'), | 64 | sshknownhosts=os.path.join(os.path.expanduser('~'), '.ssh', 'known_hosts'), |
22 | @@ -66,9 +70,9 @@ | |||
23 | 66 | These depend on other config options, so they're added last. | 70 | These depend on other config options, so they're added last. |
24 | 67 | """ | 71 | """ |
25 | 68 | LOCALDEFAULTS['logfile'] = os.path.join(DEFAULTS['logpath'], | 72 | LOCALDEFAULTS['logfile'] = os.path.join(DEFAULTS['logpath'], |
27 | 69 | socket.gethostname() + '.log') | 73 | LOCALDEFAULTS['hostname'] + '.log') |
28 | 70 | LOCALDEFAULTS['debuglog'] = os.path.join(DEFAULTS['logpath'], | 74 | LOCALDEFAULTS['debuglog'] = os.path.join(DEFAULTS['logpath'], |
30 | 71 | socket.gethostname() + '-debug.log') | 75 | LOCALDEFAULTS['hostname'] + '-debug.log') |
31 | 72 | 76 | ||
32 | 73 | DEFAULTS.update(LOCALDEFAULTS) | 77 | DEFAULTS.update(LOCALDEFAULTS) |
33 | 74 | 78 | ||
34 | 75 | 79 | ||
35 | === modified file 'utah/provisioning/baremetal/cobbler.py' | |||
36 | --- utah/provisioning/baremetal/cobbler.py 2012-08-30 08:34:34 +0000 | |||
37 | +++ utah/provisioning/baremetal/cobbler.py 2012-09-07 14:03:40 +0000 | |||
38 | @@ -2,7 +2,9 @@ | |||
39 | 2 | Support bare metal provisioning through cobbler. | 2 | Support bare metal provisioning through cobbler. |
40 | 3 | """ | 3 | """ |
41 | 4 | 4 | ||
42 | 5 | import netifaces | ||
43 | 5 | import os | 6 | import os |
44 | 7 | import pipes | ||
45 | 6 | import shutil | 8 | import shutil |
46 | 7 | import tempfile | 9 | import tempfile |
47 | 8 | import time | 10 | import time |
48 | @@ -42,17 +44,14 @@ | |||
49 | 42 | 'Image file required for cobbler installation') | 44 | 'Image file required for cobbler installation') |
50 | 43 | self._custominit(arch=self.arch, boot=boot, | 45 | self._custominit(arch=self.arch, boot=boot, |
51 | 44 | installtype=self.installtype, series=self.series) | 46 | installtype=self.installtype, series=self.series) |
54 | 45 | # TODO: support mini, | 47 | # TODO: verify we have nfs support for desktop image |
55 | 46 | # then desktop once we can figure out how | 48 | |
56 | 49 | # TODO: Rework cinitrd to be less of a confusing collection of kludges | ||
57 | 47 | self.cinitrd = None | 50 | self.cinitrd = None |
58 | 48 | if self.installtype in ['alternate', 'server']: | 51 | if self.installtype in ['alternate', 'server']: |
59 | 49 | self.cinitrd = os.path.join('install', 'netboot', | 52 | self.cinitrd = os.path.join('install', 'netboot', |
60 | 50 | 'ubuntu-installer', self.arch, | 53 | 'ubuntu-installer', self.arch, |
61 | 51 | 'initrd.gz') | 54 | 'initrd.gz') |
62 | 52 | else: | ||
63 | 53 | raise UTAHBMProvisioningException( | ||
64 | 54 | 'Only alternate and server images currently supported ' | ||
65 | 55 | 'for cobbler provisioning') | ||
66 | 56 | if self.arch == 'amd64': | 55 | if self.arch == 'amd64': |
67 | 57 | self.carch = 'x86_64' | 56 | self.carch = 'x86_64' |
68 | 58 | else: | 57 | else: |
69 | @@ -72,19 +71,34 @@ | |||
70 | 72 | 71 | ||
71 | 73 | # TODO: try to remove this step, mount the iso and import that | 72 | # TODO: try to remove this step, mount the iso and import that |
72 | 74 | # with a specified kernel/preseed/initrd from the tmpdir | 73 | # with a specified kernel/preseed/initrd from the tmpdir |
75 | 75 | self.logger.info('Extracting image to ' + self.tmpdir) | 74 | if self.installtype in ['alternate', 'desktop', 'server']: |
76 | 76 | self.image.extractall() | 75 | self.logger.info('Extracting image to ' + self.tmpdir) |
77 | 76 | self.image.extractall() | ||
78 | 77 | 77 | ||
81 | 78 | self._preparekernel() | 78 | kernel = self._preparekernel() |
82 | 79 | cinitrd = os.path.join(self.tmpdir, self.cinitrd) | 79 | if self.cinitrd is None: |
83 | 80 | cinitrd = None | ||
84 | 81 | else: | ||
85 | 82 | cinitrd = os.path.join(self.tmpdir, self.cinitrd) | ||
86 | 80 | initrd = self._prepareinitrd(initrd=cinitrd) | 83 | initrd = self._prepareinitrd(initrd=cinitrd) |
87 | 81 | self._unpackinitrd(initrd=initrd) | 84 | self._unpackinitrd(initrd=initrd) |
88 | 82 | self._setuplatecommand() | 85 | self._setuplatecommand() |
89 | 86 | if self.installtype == 'desktop': | ||
90 | 87 | self.logger.info('Configuring latecommand for desktop') | ||
91 | 88 | myfile = open(os.path.join(self.tmpdir, 'initrd.d', | ||
92 | 89 | 'utah-latecommand'), 'a') | ||
93 | 90 | myfile.write( | ||
94 | 91 | """ | ||
95 | 92 | chroot /target sh -c 'sed "/eth[0-9]/d" -i /etc/network/interfaces' | ||
96 | 93 | cp /etc/resolv.conf /target/etc/resolv.conf | ||
97 | 94 | """) | ||
98 | 95 | myfile.close() | ||
99 | 83 | self._setuppreseed() | 96 | self._setuppreseed() |
100 | 84 | initrd = self._repackinitrd() | 97 | initrd = self._repackinitrd() |
101 | 85 | 98 | ||
104 | 86 | os.chmod(cinitrd, 0755) | 99 | if cinitrd is not None and initrd != cinitrd: |
105 | 87 | shutil.copyfile(initrd, cinitrd) | 100 | os.chmod(cinitrd, 0755) |
106 | 101 | shutil.copyfile(initrd, cinitrd) | ||
107 | 88 | 102 | ||
108 | 89 | self.logger.info('Setting up system with cobbler') | 103 | self.logger.info('Setting up system with cobbler') |
109 | 90 | self.logger.debug('Removing old system') | 104 | self.logger.debug('Removing old system') |
110 | @@ -93,28 +107,67 @@ | |||
111 | 93 | self._cobble(['profile', 'remove', '--name=' + self.cname]) | 107 | self._cobble(['profile', 'remove', '--name=' + self.cname]) |
112 | 94 | self.logger.info('Removing old distro') | 108 | self.logger.info('Removing old distro') |
113 | 95 | self._cobble(['distro', 'remove', '--name=' + self.cname]) | 109 | self._cobble(['distro', 'remove', '--name=' + self.cname]) |
119 | 96 | # TODO: support more image types, maybe do this without unpacking ISO | 110 | |
120 | 97 | self.logger.info('Importing image') | 111 | preseed = os.path.join(self.tmpdir, 'initrd.d', 'preseed.cfg') |
121 | 98 | self._cobble(['import', '--name=' + self.cname, | 112 | |
122 | 99 | '--path=' + self.tmpdir, '--arch=' + self.carch]) | 113 | if self.installtype in ['alternate', 'server']: |
123 | 100 | # TODO: figure out if I need kopts in both places | 114 | # TODO: support more image types, |
124 | 115 | # maybe do this without unpacking ISO | ||
125 | 116 | self.logger.info('Importing image') | ||
126 | 117 | self._cobble(['import', '--name=' + self.cname, | ||
127 | 118 | '--path=' + self.tmpdir, '--arch=' + self.carch]) | ||
128 | 119 | elif self.installtype in ['mini', 'desktop']: | ||
129 | 120 | self.logger.info('Creating distro') | ||
130 | 121 | self._cobble(['distro', 'add', '--name=' + self.cname, | ||
131 | 122 | '--kernel=' + kernel, '--initrd=' + initrd]) | ||
132 | 123 | self.logger.info('Creating profile') | ||
133 | 124 | self._cobble(['profile', 'add', '--name=' + self.cname, | ||
134 | 125 | '--distro=' + self.cname]) | ||
135 | 126 | |||
136 | 127 | if self.installtype == 'desktop': | ||
137 | 128 | self.logger.info('Setting up NFS for desktop install') | ||
138 | 129 | self.logger.debug('Adding export to NFS config file') | ||
139 | 130 | # nfsfile = open(config.nfsconfigfile, 'a') | ||
140 | 131 | # nfsfile.write("\n" + self.tmpdir + ' ' + config.nfsoptions + "\n") | ||
141 | 132 | # nfsfile.close() | ||
142 | 133 | pipe = pipes.Template() | ||
143 | 134 | pipe.append('sudo tee -a ' + config.nfsconfigfile + ' >/dev/null', | ||
144 | 135 | '-.') | ||
145 | 136 | pipe.open('/dev/null', 'w').write(self.tmpdir + ' ' + | ||
146 | 137 | config.nfsoptions + "\n") | ||
147 | 138 | self.logger.debug('Reloading NFS config') | ||
148 | 139 | self._runargs(config.nfscommand) | ||
149 | 140 | self.logger.debug('Adding NFS boot options') | ||
150 | 141 | try: | ||
151 | 142 | iface = config.nfsiface | ||
152 | 143 | except AttributeError: | ||
153 | 144 | iface = config.bridge | ||
154 | 145 | ip = netifaces.ifaddresses(iface)[netifaces.AF_INET][0]['addr'] | ||
155 | 146 | self.cmdline += (' root=/dev/nfs netboot=nfs nfsroot=' + | ||
156 | 147 | ip + ':' + self.tmpdir) | ||
157 | 148 | self.cmdline = self.cmdline.strip() | ||
158 | 149 | |||
159 | 101 | self.logger.info('Adding kernel boot options to distro') | 150 | self.logger.info('Adding kernel boot options to distro') |
160 | 102 | self._cobble(['distro', 'edit', '--name=' + self.cname, | 151 | self._cobble(['distro', 'edit', '--name=' + self.cname, |
161 | 103 | '--kopts=' + self.cmdline]) | 152 | '--kopts=' + self.cmdline]) |
165 | 104 | self.logger.info('Adding kernel boot options ' | 153 | |
166 | 105 | 'and kickstart to profile') | 154 | self.logger.info('Adding kickstart to profile') |
164 | 106 | preseed = os.path.join(self.tmpdir, 'initrd.d', 'preseed.cfg') | ||
167 | 107 | self._cobble(['profile', 'edit', '--name=' + self.cname, | 155 | self._cobble(['profile', 'edit', '--name=' + self.cname, |
170 | 108 | '--kickstart=' + preseed, | 156 | '--kickstart=' + preseed]) |
171 | 109 | '--kopts=' + self.cmdline]) | 157 | |
172 | 110 | self.logger.info('Adding system') | 158 | self.logger.info('Adding system') |
173 | 111 | self._cobble(['system', 'add', '--name=' + self.name, | 159 | self._cobble(['system', 'add', '--name=' + self.name, |
174 | 112 | '--profile=' + self.cname, '--netboot-enabled=Y'] | 160 | '--profile=' + self.cname, '--netboot-enabled=Y'] |
175 | 113 | + ['--' + arg + '=' + self.cargs[arg] | 161 | + ['--' + arg + '=' + self.cargs[arg] |
176 | 114 | for arg in self.cargs]) | 162 | for arg in self.cargs]) |
177 | 115 | 163 | ||
180 | 116 | self.logger.info('Syncing cobbler') | 164 | # Things seem to be working fine without this, but sometimes things still get broken |
181 | 117 | self._cobble(['sync']) | 165 | #self.logger.info('Syncing cobbler') |
182 | 166 | #self._cobble(['sync']) | ||
183 | 167 | try: | ||
184 | 168 | self._runargs(config.cobblerfix) | ||
185 | 169 | except AttributeError: | ||
186 | 170 | pass | ||
187 | 118 | self.restart() | 171 | self.restart() |
188 | 119 | 172 | ||
189 | 120 | self.logger.info('Waiting for installation to begin') | 173 | self.logger.info('Waiting for installation to begin') |
190 | @@ -122,11 +175,21 @@ | |||
191 | 122 | self._cobble(['system', 'edit', '--name=' + self.name, | 175 | self._cobble(['system', 'edit', '--name=' + self.name, |
192 | 123 | '--netboot-enabled=N']) | 176 | '--netboot-enabled=N']) |
193 | 124 | 177 | ||
194 | 178 | self.logger.info('System installing') | ||
195 | 179 | self.logger.info('Checking every ' + str(checktimeout) + | ||
196 | 180 | ' seconds until system is installed') | ||
197 | 181 | self.logger.info('Will time out after ' + str(installtimeout) | ||
198 | 182 | + ' seconds') | ||
199 | 183 | retry(self.sshcheck, checktimeout, logmethod=self.logger.info) | ||
200 | 184 | |||
201 | 125 | if self.debug: | 185 | if self.debug: |
202 | 126 | self.logger.info('Leaving temp directory ' | 186 | self.logger.info('Leaving temp directory ' |
203 | 127 | 'because debug is enabled: ' + self.tmpdir) | 187 | 'because debug is enabled: ' + self.tmpdir) |
204 | 128 | else: | 188 | else: |
205 | 129 | self.logger.info('Cleaning up temp directory') | 189 | self.logger.info('Cleaning up temp directory') |
206 | 190 | if self.installtype == 'desktop': | ||
207 | 191 | self.logger.info('Removing NFS share') | ||
208 | 192 | self._removenfs() | ||
209 | 130 | # Cribbed from http://svn.python.org | 193 | # Cribbed from http://svn.python.org |
210 | 131 | # /projects/python/trunk/Mac/BuildScript/build-installer.py | 194 | # /projects/python/trunk/Mac/BuildScript/build-installer.py |
211 | 132 | for dirpath, dirnames, filenames in os.walk(self.tmpdir): | 195 | for dirpath, dirnames, filenames in os.walk(self.tmpdir): |
212 | @@ -138,12 +201,6 @@ | |||
213 | 138 | os.chmod(filename, 0775) | 201 | os.chmod(filename, 0775) |
214 | 139 | shutil.rmtree(self.tmpdir) | 202 | shutil.rmtree(self.tmpdir) |
215 | 140 | 203 | ||
216 | 141 | self.logger.info('System installing') | ||
217 | 142 | self.logger.info('Checking every ' + str(checktimeout) + | ||
218 | 143 | ' seconds until system is installed') | ||
219 | 144 | self.logger.info('Will time out after ' + str(installtimeout) | ||
220 | 145 | + ' seconds') | ||
221 | 146 | retry(self.sshcheck, checktimeout, logmethod=self.logger.info) | ||
222 | 147 | self.provisioned = True | 204 | self.provisioned = True |
223 | 148 | self.active = True | 205 | self.active = True |
224 | 149 | uuid_check_command = ('[ "{uuid}" == "$(cat /etc/utah/uuid)" ]' | 206 | uuid_check_command = ('[ "{uuid}" == "$(cat /etc/utah/uuid)" ]' |
225 | @@ -204,10 +261,20 @@ | |||
226 | 204 | """ | 261 | """ |
227 | 205 | If the machine has an inventory, tell the inventory to release the | 262 | If the machine has an inventory, tell the inventory to release the |
228 | 206 | machine when the object is deleted. | 263 | machine when the object is deleted. |
229 | 264 | If we exported a directory over NFS, remove it. | ||
230 | 207 | """ | 265 | """ |
231 | 266 | if self.installtype == 'desktop': | ||
232 | 267 | self._removenfs() | ||
233 | 208 | if self.inventory is not None: | 268 | if self.inventory is not None: |
234 | 209 | self.inventory.release(machine=self) | 269 | self.inventory.release(machine=self) |
235 | 210 | 270 | ||
236 | 271 | def _removenfs(self): | ||
237 | 272 | self.logger.info('Removing NFS share') | ||
238 | 273 | self._runargs(['sudo', 'sed', '/' + self.tmpdir.replace('/', '\/') + | ||
239 | 274 | '/d', '-i', config.nfsconfigfile]) | ||
240 | 275 | self.logger.debug('Reloading NFS config') | ||
241 | 276 | self._runargs(config.nfscommand) | ||
242 | 277 | |||
243 | 211 | def activecheck(self): | 278 | def activecheck(self): |
244 | 212 | """ | 279 | """ |
245 | 213 | Start the machine if needed, and check for SSH login. | 280 | Start the machine if needed, and check for SSH login. |
Looks good to me.