Skip to content

Commit d7bf872

Browse files
committed
Merge pull request #46 from xcp-ng/ydi/10.10.29-8.3
Rebase on 10.10.29
2 parents 1bf7cc9 + f2c4e96 commit d7bf872

20 files changed

+844
-243
lines changed

answerfile.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ def processAnswerfile(self):
9090
else:
9191
raise AnswerfileException("Unknown mode, %s" % install_type)
9292

93+
results['repo-gpgcheck'] = getBoolAttribute(self.top_node, ['repo-gpgcheck'], default=True)
94+
results['gpgcheck'] = getBoolAttribute(self.top_node, ['gpgcheck'], default=True)
9395
results.update(self.parseCommon())
9496
elif self.operation == 'restore':
9597
results = self.parseRestore()
@@ -131,6 +133,7 @@ def parseFreshInstall(self):
131133
results['preserve-settings'] = False
132134
results['backup-existing-installation'] = False
133135

136+
results.update(self.parseRaid())
134137
results.update(self.parseDisks())
135138
results.update(self.parseInterface())
136139
results.update(self.parseRootPassword())
@@ -269,7 +272,21 @@ def parseSource(self):
269272
if rtype == 'url':
270273
address = util.URL(address)
271274

272-
results['sources'].append({'media': rtype, 'address': address})
275+
# workaround getBoolAttribute() not allowing "None" as
276+
# default, by using a getStrAttribute() call first to
277+
# handle the default situation where the attribute is not
278+
# specified
279+
repo_gpgcheck = (None if getStrAttribute(i, ['repo-gpgcheck'], default=None) is None
280+
else getBoolAttribute(i, ['repo-gpgcheck']))
281+
gpgcheck = (None if getStrAttribute(i, ['gpgcheck'], default=None) is None
282+
else getBoolAttribute(i, ['gpgcheck']))
283+
284+
results['sources'].append({
285+
'media': rtype, 'address': address,
286+
'repo_gpgcheck': repo_gpgcheck,
287+
'gpgcheck': gpgcheck,
288+
})
289+
logger.log("parsed source %s" % results['sources'][-1])
273290

274291
return results
275292

@@ -296,6 +313,16 @@ def parseDriverSource(self):
296313
results['extra-repos'].append((rtype, address))
297314
return results
298315

316+
def parseRaid(self):
317+
results = {}
318+
for raid_node in getElementsByTagName(self.top_node, ['raid']):
319+
disk_device = normalize_disk(getStrAttribute(raid_node, ['device'], mandatory=True))
320+
disks = [normalize_disk(getText(node)) for node in getElementsByTagName(raid_node, ['disk'])]
321+
if 'raid' not in results:
322+
results['raid'] = {}
323+
results['raid'][disk_device] = disks
324+
return results
325+
299326
def parseDisks(self):
300327
results = {}
301328

@@ -339,7 +366,7 @@ def parseDisks(self):
339366
if SR_TYPE_LARGE_BLOCK and len(large_block_disks) > 0:
340367
default_sr_type = SR_TYPE_LARGE_BLOCK
341368
else:
342-
default_sr_type = SR_TYPE_LVM
369+
default_sr_type = SR_TYPE_EXT
343370

344371
sr_type = getMapAttribute(self.top_node,
345372
['sr-type', 'srtype'],

backend.py

Lines changed: 137 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import version
3636
from version import *
3737
from constants import *
38+
from diskutil import getRemovableDeviceList
3839

3940
MY_PRODUCT_BRAND = PRODUCT_BRAND or PLATFORM_NAME
4041

@@ -106,6 +107,7 @@ def getPrepSequence(ans, interactive):
106107
Task(util.getUUID, As(ans), ['installation-uuid']),
107108
Task(util.getUUID, As(ans), ['control-domain-uuid']),
108109
Task(util.randomLabelStr, As(ans), ['disk-label-suffix']),
110+
Task(diskutil.create_raid, A(ans, 'raid'), []),
109111
Task(partitionTargetDisk, A(ans, 'primary-disk', 'installation-to-overwrite', 'preserve-first-partition','sr-on-primary', 'target-platform'),
110112
['target-boot-mode', 'primary-partnum', 'backup-partnum', 'storage-partnum', 'boot-partnum', 'logs-partnum', 'swap-partnum']),
111113
]
@@ -158,7 +160,7 @@ def getPrepSequence(ans, interactive):
158160

159161
def getMainRepoSequence(ans, repos):
160162
seq = []
161-
seq.append(Task(repository.installFromRepos, lambda a: [repos] + [a.get('mounts')], [],
163+
seq.append(Task(repository.installFromRepos, lambda a: [repos] + [a.get('mounts'), a.get('kernel-alt')], [],
162164
progress_scale=100,
163165
pass_progress_callback=True,
164166
progress_text="Installing %s..." % (", ".join([repo.name() for repo in repos]))))
@@ -180,6 +182,7 @@ def getRepoSequence(ans, repos):
180182

181183
def getFinalisationSequence(ans):
182184
seq = [
185+
Task(importYumAndRpmGpgKeys, A(ans, 'mounts'), []),
183186
Task(writeResolvConf, A(ans, 'mounts', 'manual-hostname', 'manual-nameservers'), []),
184187
Task(writeMachineID, A(ans, 'mounts'), []),
185188
Task(writeKeyboardConfiguration, A(ans, 'mounts', 'keymap'), []),
@@ -201,6 +204,7 @@ def getFinalisationSequence(ans):
201204
'boot-partnum', 'primary-partnum', 'target-boot-mode', 'branding',
202205
'disk-label-suffix', 'bootloader-location', 'write-boot-entry', 'install-type',
203206
'serial-console', 'boot-serial', 'host-config', 'fcoe-interfaces'), []),
207+
Task(postInstallAltKernel, A(ans, 'mounts', 'kernel-alt'), []),
204208
Task(touchSshAuthorizedKeys, A(ans, 'mounts'), []),
205209
Task(setRootPassword, A(ans, 'mounts', 'root-password'), [], args_sensitive=True),
206210
Task(setTimeZone, A(ans, 'mounts', 'timezone'), []),
@@ -299,6 +303,50 @@ def progressCallback(x):
299303
doCleanup(answers['cleanup'])
300304
del answers['cleanup']
301305

306+
def determineRepositories(answers, answers_pristine, main_repositories, update_repositories):
307+
def add_repos(main_repositories, update_repositories, repos, repo_gpgcheck, gpgcheck):
308+
"""Add repositories to the appropriate list, ensuring no duplicates,
309+
that the main repository is at the beginning, and that the order of the
310+
rest is maintained."""
311+
312+
for repo in repos:
313+
if isinstance(repo, repository.UpdateYumRepository):
314+
repo_list = update_repositories
315+
else:
316+
repo_list = main_repositories
317+
318+
if repo not in repo_list:
319+
if repo.identifier() == MAIN_REPOSITORY_NAME:
320+
repo_list.insert(0, repo)
321+
else:
322+
repo_list.append(repo)
323+
324+
if repo_list is main_repositories: # i.e., if repo is a "main repository"
325+
repo.setRepoGpgCheck(repo_gpgcheck)
326+
repo.setGpgCheck(gpgcheck)
327+
328+
default_repo_gpgcheck = answers.get('repo-gpgcheck', True)
329+
default_gpgcheck = answers.get('gpgcheck', True)
330+
# A list of sources coming from the answerfile
331+
if 'sources' in answers_pristine:
332+
for i in answers_pristine['sources']:
333+
repos = repository.repositoriesFromDefinition(i['media'], i['address'])
334+
repo_gpgcheck = default_repo_gpgcheck if i['repo_gpgcheck'] is None else i['repo_gpgcheck']
335+
gpgcheck = default_gpgcheck if i['gpgcheck'] is None else i['gpgcheck']
336+
add_repos(main_repositories, update_repositories, repos, repo_gpgcheck, gpgcheck)
337+
338+
# A single source coming from an interactive install
339+
if 'source-media' in answers_pristine and 'source-address' in answers_pristine:
340+
repos = repository.repositoriesFromDefinition(answers_pristine['source-media'], answers_pristine['source-address'])
341+
add_repos(main_repositories, update_repositories, repos, default_repo_gpgcheck, default_gpgcheck)
342+
343+
for media, address in answers_pristine['extra-repos']:
344+
repos = repository.repositoriesFromDefinition(media, address)
345+
add_repos(main_repositories, update_repositories, repos, default_repo_gpgcheck, default_gpgcheck)
346+
347+
if not main_repositories or main_repositories[0].identifier() != MAIN_REPOSITORY_NAME:
348+
raise RuntimeError("No main repository found")
349+
302350
def performInstallation(answers, ui_package, interactive):
303351
logger.log("INPUT ANSWERS DICTIONARY:")
304352
prettyLogAnswers(answers)
@@ -367,9 +415,17 @@ def performInstallation(answers, ui_package, interactive):
367415
assert answers['net-admin-interface'].startswith("eth")
368416
answers['net-admin-bridge'] = "xenbr%s" % answers['net-admin-interface'][3:]
369417

418+
# A list needs to be used rather than a set since the order of updates is
419+
# important. However, since the same repository might exist in multiple
420+
# locations or the same location might be listed multiple times, care is
421+
# needed to ensure that there are no duplicates.
422+
main_repositories = []
423+
update_repositories = []
424+
answers_pristine = answers.copy()
425+
determineRepositories(answers, answers_pristine, main_repositories, update_repositories)
426+
370427
# perform installation:
371428
prep_seq = getPrepSequence(answers, interactive)
372-
answers_pristine = answers.copy()
373429
executeSequence(prep_seq, "Preparing for installation...", answers, ui_package, False)
374430

375431
# install from main repositories:
@@ -383,48 +439,6 @@ def handleRepos(repos, ans):
383439

384440
answers['installed-repos'] = {}
385441

386-
# A list needs to be used rather than a set since the order of updates is
387-
# important. However, since the same repository might exist in multiple
388-
# locations or the same location might be listed multiple times, care is
389-
# needed to ensure that there are no duplicates.
390-
main_repositories = []
391-
update_repositories = []
392-
393-
def add_repos(main_repositories, update_repositories, repos):
394-
"""Add repositories to the appropriate list, ensuring no duplicates,
395-
that the main repository is at the beginning, and that the order of the
396-
rest is maintained."""
397-
398-
for repo in repos:
399-
if isinstance(repo, repository.UpdateYumRepository):
400-
repo_list = update_repositories
401-
else:
402-
repo_list = main_repositories
403-
404-
if repo not in repo_list:
405-
if repo.identifier() == MAIN_REPOSITORY_NAME:
406-
repo_list.insert(0, repo)
407-
else:
408-
repo_list.append(repo)
409-
410-
# A list of sources coming from the answerfile
411-
if 'sources' in answers_pristine:
412-
for i in answers_pristine['sources']:
413-
repos = repository.repositoriesFromDefinition(i['media'], i['address'])
414-
add_repos(main_repositories, update_repositories, repos)
415-
416-
# A single source coming from an interactive install
417-
if 'source-media' in answers_pristine and 'source-address' in answers_pristine:
418-
repos = repository.repositoriesFromDefinition(answers_pristine['source-media'], answers_pristine['source-address'])
419-
add_repos(main_repositories, update_repositories, repos)
420-
421-
for media, address in answers_pristine['extra-repos']:
422-
repos = repository.repositoriesFromDefinition(media, address)
423-
add_repos(main_repositories, update_repositories, repos)
424-
425-
if not main_repositories or main_repositories[0].identifier() != MAIN_REPOSITORY_NAME:
426-
raise RuntimeError("No main repository found")
427-
428442
handleMainRepos(main_repositories, answers)
429443
if update_repositories:
430444
handleRepos(update_repositories, answers)
@@ -435,7 +449,18 @@ def add_repos(main_repositories, update_repositories, repos):
435449
if r.accessor().canEject():
436450
r.accessor().eject()
437451

438-
if interactive and constants.HAS_SUPPLEMENTAL_PACKS:
452+
# XCP-ng: so, very unfortunately we don't remember with precision why this was added and
453+
# no commit message or comment can help us here.
454+
# It may be related to the fact that the "all_repositories" above doesn't contain
455+
# the installation CD-ROM or USB stick in the case of a netinstall.
456+
# Question: why it is needed at all since there's no repository on the netinstall
457+
# installation media?
458+
if answers.get('netinstall'):
459+
for device in getRemovableDeviceList():
460+
util.runCmd2(['eject', device])
461+
462+
if interactive and (constants.HAS_SUPPLEMENTAL_PACKS or
463+
"driver-repos" in answers):
439464
# Add supp packs in a loop
440465
while True:
441466
media_ans = dict(answers_pristine)
@@ -1130,7 +1155,11 @@ def installBootLoader(mounts, disk, boot_partnum, primary_partnum, target_boot_m
11301155
setEfiBootEntry(mounts, disk, boot_partnum, install_type, branding)
11311156
else:
11321157
if location == constants.BOOT_LOCATION_MBR:
1133-
installGrub2(mounts, disk, False)
1158+
if diskutil.is_raid(disk):
1159+
for member in diskutil.getDeviceSlaves(disk):
1160+
installGrub2(mounts, member, False)
1161+
else:
1162+
installGrub2(mounts, disk, False)
11341163
else:
11351164
installGrub2(mounts, root_partition, True)
11361165

@@ -1505,15 +1534,15 @@ def configureNetworking(mounts, admin_iface, admin_bridge, admin_config, hn_conf
15051534
print("NETMASK='%s'" % admin_config.netmask, file=mc)
15061535
if admin_config.gateway:
15071536
print("GATEWAY='%s'" % admin_config.gateway, file=mc)
1508-
if manual_nameservers:
1509-
print("DNS='%s'" % (','.join(nameservers),), file=mc)
1510-
if domain:
1511-
print("DOMAIN='%s'" % domain, file=mc)
15121537
print("MODEV6='%s'" % netinterface.NetInterface.getModeStr(admin_config.modev6), file=mc)
15131538
if admin_config.modev6 == netinterface.NetInterface.Static:
15141539
print("IPv6='%s'" % admin_config.ipv6addr, file=mc)
15151540
if admin_config.ipv6_gateway:
15161541
print("IPv6_GATEWAY='%s'" % admin_config.ipv6_gateway, file=mc)
1542+
if manual_nameservers:
1543+
print("DNS='%s'" % (','.join(nameservers),), file=mc)
1544+
if domain:
1545+
print("DOMAIN='%s'" % domain, file=mc)
15171546
if admin_config.vlan:
15181547
print("VLAN='%d'" % admin_config.vlan, file=mc)
15191548
mc.close()
@@ -1556,12 +1585,18 @@ def configureNetworking(mounts, admin_iface, admin_bridge, admin_config, hn_conf
15561585
# now we need to write /etc/sysconfig/network
15571586
nfd = open("%s/etc/sysconfig/network" % mounts["root"], "w")
15581587
nfd.write("NETWORKING=yes\n")
1559-
if admin_config.modev6:
1588+
ipv6 = admin_config.modev6 is not None
1589+
if ipv6:
15601590
nfd.write("NETWORKING_IPV6=yes\n")
15611591
util.runCmd2(['chroot', mounts['root'], 'systemctl', 'enable', 'ip6tables'])
15621592
else:
15631593
nfd.write("NETWORKING_IPV6=no\n")
15641594
netutil.disable_ipv6_module(mounts["root"])
1595+
1596+
with open("%s/etc/sysctl.d/91-net-ipv6.conf" % mounts["root"], "w") as ipv6_conf:
1597+
for i in ['all', 'default']:
1598+
ipv6_conf.write('net.ipv6.conf.%s.disable_ipv6=%d\n' % (i, int(not ipv6)))
1599+
15651600
nfd.write("IPV6_AUTOCONF=no\n")
15661601
nfd.write('NTPSERVERARGS="iburst prefer"\n')
15671602
nfd.close()
@@ -1654,6 +1689,57 @@ def touchSshAuthorizedKeys(mounts):
16541689
fh = open("%s/root/.ssh/authorized_keys" % mounts['root'], 'a')
16551690
fh.close()
16561691

1692+
def importYumAndRpmGpgKeys(mounts):
1693+
# Python script that uses yum functions to import the GPG key for our repositories
1694+
import_yum_keys = """#!/bin/env python
1695+
from __future__ import print_function
1696+
from yum import YumBase
1697+
1698+
def retTrue(*args, **kwargs):
1699+
return True
1700+
1701+
base = YumBase()
1702+
for repo in base.repos.repos.itervalues():
1703+
if repo.id.startswith('xcp-ng'):
1704+
print("*** Importing GPG key for repository %s - %s" % (repo.id, repo.name))
1705+
base.getKeyForRepo(repo, callback=retTrue)
1706+
"""
1707+
internal_tmp_filepath = '/tmp/import_yum_keys.py'
1708+
external_tmp_filepath = mounts['root'] + internal_tmp_filepath
1709+
with open(external_tmp_filepath, 'w') as f:
1710+
f.write(import_yum_keys)
1711+
# bind mount /dev, necessary for NSS initialization without which RPM won't work
1712+
util.bindMount('/dev', "%s/dev" % mounts['root'])
1713+
try:
1714+
util.runCmd2(['chroot', mounts['root'], 'python', internal_tmp_filepath])
1715+
util.runCmd2(['chroot', mounts['root'], 'rpm', '--import', '/etc/pki/rpm-gpg/RPM-GPG-KEY-xcpng'])
1716+
finally:
1717+
util.umount("%s/dev" % mounts['root'])
1718+
os.unlink(external_tmp_filepath)
1719+
1720+
def postInstallAltKernel(mounts, kernel_alt):
1721+
""" Install our alternate kernel. Must be called after the bootloader installation. """
1722+
if not kernel_alt:
1723+
logger.log('kernel-alt not installed')
1724+
return
1725+
1726+
util.bindMount("/proc", "%s/proc" % mounts['root'])
1727+
util.bindMount("/sys", "%s/sys" % mounts['root'])
1728+
util.bindMount("/dev", "%s/dev" % mounts['root'])
1729+
1730+
try:
1731+
rc, out = util.runCmd2(['chroot', mounts['root'], 'rpm', '-q', 'kernel-alt', '--qf', '%{version}'],
1732+
with_stdout=True)
1733+
version = out
1734+
# Generate the initrd as it was disabled during initial installation
1735+
util.runCmd2(['chroot', mounts['root'], 'dracut', '-f', '/boot/initrd-%s.img' % version, version])
1736+
1737+
# Update grub
1738+
util.runCmd2(['chroot', mounts['root'], '/opt/xensource/bin/updategrub.py', 'add', 'kernel-alt', version])
1739+
finally:
1740+
util.umount("%s/dev" % mounts['root'])
1741+
util.umount("%s/sys" % mounts['root'])
1742+
util.umount("%s/proc" % mounts['root'])
16571743

16581744
################################################################################
16591745
# OTHER HELPERS

constants.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,7 @@ def error_string(error, logname, with_hd):
6363
if err[-1:] != '.':
6464
err += '.'
6565

66-
return ('An unrecoverable error has occurred. ' + err +
67-
'\n\nPlease refer to your user guide or contact a Technical Support Representative for more details.')
66+
return ('An unrecoverable error has occurred. ' + err)
6867

6968
# minimum hardware specs:
7069
# memory checks should be done against MIN_SYSTEM_RAM_MB since libxc
@@ -197,3 +196,6 @@ def error_string(error, logname, with_hd):
197196
# Error partitioning disk as in use
198197
PARTITIONING_ERROR = \
199198
'The disk appears to be in use and partition changes cannot be applied. Reboot and repeat the installation'
199+
200+
# crypto configuration
201+
MIN_KEY_SIZE = 2048

0 commit comments

Comments
 (0)