#-*- coding: utf-8 -*- # Copyright 2010 Calculate Ltd. http://www.calculate-linux.org # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import re import sys from cl_install import cl_install, InstallError, __app__, __version__,\ get_install_errors from cl_vars_share import varsShare from cl_opt import opt from cl_share_cmd import share_cmd from utils import ip as iputils from cl_utils import isPkgInstalled from cl_lang import lang lang().setLanguage(sys.modules[__name__]) OSSYSTEM_LIST=sorted(varsShare.dictNameSystem.keys()) NM_LIST=sorted(varsShare.dictNetworkManagers.keys()) DESCRIPTION = _("The Calculate Linux installation and configuration utility") CMD_OPTIONS = [{'shortOption':"d", 'longOption':"disk", 'optVal':"DISK[:[DIR:FILESYSTEM:OPTIONS]]", 'action':'append', 'help':_("the DISK for installation, which mounted to DIR") + ". " + _("DIR with value 'none' will cancels the mount point transfer") + ". " +_("For creating bind mount point need specify source directory as DISK") }, {'shortOption':"w", 'longOption':"swap", 'optVal':"SWAP_DISK", 'action':'append', 'help':_("swap disk for installation") }, {'longOption':"mbr", 'optVal':"DEVICE", 'help':_("boot disk for the installed system \ (to be recorded MBR), off - disable MBR writing") }, {'longOption':"type", 'optVal':"DISKTYPE", 'type':'choice', 'choices':['flash','hdd','usb-hdd'], 'help':_("device type for installed system") }, {'longOption':'iso', 'optVal':"ISO", 'help':_("ISO image for installation")}, {'shortOption':"s", 'longOption':"os", 'optVal':"SYSTEM", 'type':'choice', 'choices_regignore': OSSYSTEM_LIST, 'help':_("select operation system") }, {'longOption':"march", 'optVal':"ARCH", 'type':'choice', 'choices':['i686','x86_64'], 'help':_("select processor architecture") }, {'longOption':"build", 'help':_("installation for assembling") }, {'shortOption':"l", 'longOption':"lang", 'optVal':"LANG", 'help':_("set language") }, {'shortOption':"u", 'longOption':"user", 'optVal':"USER", 'action':'append', 'help':_("add user to installed system") }, {'longOption':"hostname", 'optVal':"HOSTNAME", 'help':_("set short hostname of full hostname") }, {'longOption':"netconf", 'optVal':"NETMANAGER", 'type':'choice', 'choices_regignore': NM_LIST, 'help':_("network manager (%s)"% "%s or %s" % (",".join(NM_LIST[0:-1]),NM_LIST[-1]))}, {'longOption':"dhcp", 'optVal':"IFACE", 'action':'append', 'help':_("get ip address by %s for specified interface")%"DHCP" }, {'longOption':"ip", 'optVal':"[IFACE:]IP", 'action':'append', 'help':_("ip address with net (example:%s)")%"192.168.1.1/24" }, {'longOption':"route", 'optVal':"NETWORK:[GATEWAY][:DEV[:SOURCE]]", 'action':'append', 'help':_("add routing rule") }, {'longOption':"dns", 'optVal':"DNS", 'help':_("domain name servers (comma delimeter)") }, {'longOption':"proxy", 'optVal':"PROXY", 'help':_("set proxy server for system") }, {'longOption':"ntp", 'optVal':"NTP", 'help':_("set ntp server for system") }, {'longOption':"nouuid", 'help':_("do not use UUID") }, {'longOption':'set'}, {'shortOption':"U", 'longOption':"update", 'help':_("install only newer images")}, {'shortOption':"f", 'longOption':"force", 'help':_("no questions during the install process") }, {'longOption':"live", 'help':_("configure current system") }, {'longOption':"install", 'help':_("install package") }, {'longOption':"uninstall", 'help':_("uninstall package") }, {'longOption':"nospinner", 'help':_("disable spinner") }, {'shortOption':"P", 'help':_("use passwords for the users accounts \ (from standard input)") }, {'shortOption':'p', 'longOption':"show-partitions", 'help':_("display all partitions")} ] class OptException(Exception): pass class install_cmd(share_cmd): """Class for work with cl_install by console""" def __init__(self): setpos = \ filter(lambda x:x[1].get('longOption')=="set", enumerate(CMD_OPTIONS))[0][0] CMD_OPTIONS[setpos] = opt.variable_set[0] self.optobj = opt(package=__app__, version=__version__, description=DESCRIPTION, option_list= CMD_OPTIONS + opt.variable_view + opt.color_control, check_values=self.checkOpts) self.logicObj = cl_install() # names incompatible options with --live self.optionsLiveIncompatible = ["type","d", "b", "mbr", "w", "f","U", "s","install","uninstall","build","u"] self.optionsDhcpIncompatible = ["ip","gateway","dns"] def _getNamesAllSetOptions(self): """Get list set options""" setOptDict = self.optobj.values.__dict__.items() defaultOptDict = self.optobj.get_default_values().__dict__.items() return reduce(lambda x,y: x+[y[0][0]], filter(lambda x:x[0][1] != x[1][1], zip(setOptDict,defaultOptDict)), []) def getStringIncompatibleOptions(self,listOpt): """Форматированная строка несовместимых опций разделенных ','""" return ", ".join(map(lambda x: len(x) == 1 and "'-%s'"%x or "'--%s'"%x, listOpt)) def checkIncompatibleLive(self): """Check incompatible options for option --live""" incompatible = list(set(self._getNamesAllSetOptions()) & set(self.optionsLiveIncompatible)) if incompatible: self.optobj.error(_("incompatible options")+":"+" %s"\ %self.getStringIncompatibleOptions(incompatible+["live"])) def checkIncompatibleDhcp(self): """Check incompatible options for option --dchp""" incompatible = list(set(self._getNamesAllSetOptions()) & set(self.optionsDhcpIncompatible)) if incompatible: self.optobj.error(_("incompatible options")+":"+" %s"\ %self.getStringIncompatibleOptions(incompatible+["dhcp"])) def checkIncompatibleInstallUninstall(self): """Check incompatible options for options --install and --uninstall""" opts = self._getNamesAllSetOptions() if len(opts)>1: self.optobj.error(_("incompatible options")+":"+" %s"\ %self.getStringIncompatibleOptions(opts)) def checkOpts(self, values, args): """Check values all specified options.""" if len(args) > 0: self.optobj.error(_("unrecognized option") + ": %s"% "".join(args)) if values.install or values.uninstall: self.checkIncompatibleInstallUninstall() if values.live: self.checkIncompatibleLive() if values.dhcp: self.checkIncompatibleDhcp() if not values.v: if values.filter: errMsg = _("incorrect option") + ":" + " %s" %"--filter" +\ ": " + _("use with option '-v'") self.optobj.error(errMsg) if values.xml: errMsg = _("incorrect option") + ":" + " %s" %"--xml" +\ ": " + _("use with option '-v'") self.optobj.error(errMsg) if values.ip: for ipaddr in values.ip: if not re.match("^(\w+:)?%s$"%iputils.IP_ADDR_NET,ipaddr): self.optobj.error(_("option %s:") %"--ip" +\ " " + _("ip specifing error: '{ip}' " "(example: '{example}')").format(\ ip=ipaddr,example="eth0:192.168.0.21/16")) if not values.dns is None: if not re.match("(^{0}(,{0})*|)$".format(iputils.IP_ADDR),values.dns): self.optobj.error(_("option %s:") %"--dns" +\ " " + _("dns specifing error: '%s'") %\ values.dns) if values.route: for route in values.route: if not re.match("^({net}|default):(({ipaddr})?" "(:\w+(:{ipaddr})?)?)?$".format( net=iputils.IP_ADDR_NET,ipaddr=iputils.IP_ADDR),route): self.optobj.error(_("option %s:") %"--route" +\ " " + _("route specifing error: '{route}'" "(example: '{example}'").format( route=route, example="default:192.168.1.1")) if not (values.install or values.uninstall or values.live): if values.v is False and \ not values.p and \ values.d is None and \ not self.logicObj.clVars.Get('os_install_dev_from'): self.optobj.error(_("need specify disk by '-d' option")) # check syntax DISK:DIR:FS:'Do I need to format the disk' if values.d: reTrueDisk = re.compile("^[^:]+(:[^:]*){0,3}$") wrongValue = filter(lambda x: not reTrueDisk.match(x),values.d) if wrongValue: self.optobj.error(_("option %s:") %"d" +\ " " + _("disk specifing error: '%s'")\ %", ".join(wrongValue)) # check syntax SWAP_DISK if values.w: reTrueBind = re.compile("^[^:]+$") wrongValue = filter(lambda x: not reTrueBind.match(x),values.w) if wrongValue: self.optobj.error(_("option %s:") %"w" +\ " " + _("mount bind specifing error: '%s'")\ %", ".join(wrongValue)) #check boot device if values.mbr: bootDisk = values.mbr reBootDisk = re.compile("^/dev/.+[^\d]|off$") if not reBootDisk.match(bootDisk): self.optobj.error(_("option %s:") %"mbr" + " " +\ _("disk specifing error: '%s'")%bootDisk) # check syntax --set self.optobj.checkVarSyntax(values) return (values, args) def setLang(self,lang): """Process set locales by lang""" if self.logicObj.setAllLocaleByLang(lang): return True else: self.printERROR(_("specified lang %s is unsupported")%lang) return False def setProxyNtpHostname(self,proxy,ntp,hostname): """Process set locales by lang""" getVar = self.logicObj.clVars.Get setVar = self.logicObj.clVars.Set setWriteVar = self.logicObj.clVars.SetWriteVar if proxy: if not setWriteVar('os_install_proxy',proxy): self.printERROR(get_install_errors(),printBR=False) return False if ntp: if not setWriteVar('os_install_ntp',ntp): self.printERROR(get_install_errors(),printBR=False) return False if hostname: if "." in hostname: hostname, op, domain = hostname.partition('.') setVar('os_install_net_hostname', hostname,True) setVar('os_install_net_domain', domain,True) dns_search = getVar("os_install_net_dns_search") if not domain in dns_search: setVar("os_install_net_dns_search",domain,True) else: setVar('os_install_net_hostname', hostname,True) return True def setNetworkParams(self,ipaddrs,routes,dns,setup,dhcps): """Set nework params""" if not dns is None: self.logicObj.clVars.Set("os_install_net_dns",dns,True) ipaddrs = map(lambda x:x.split(":"),ipaddrs or []) routes = map(lambda x:x+[""]*(4-len(x)), map(lambda x:x.split(":"),routes or [])) dhcps = dhcps or [] if not self.logicObj.setNetwork(ipaddrs,dhcps,routes): return False if setup: if not isPkgInstalled(setup): self.printERROR(_("Network manager %s is not installed")%setup) return False self.logicObj.clVars.Set("os_install_net_conf",setup,True) return True def setVars(self,options): """Process setting values for variables""" if options.set: for vals in options.set: for val in vals.split(','): k,o,v = val.partition('=') if self.logicObj.clVars.exists(k): if self.logicObj.clVars.SetWriteVar(k,v) == False: self.printERROR(get_install_errors(),printBR=False) return False else: self.printERROR(_('variable %s not found')%k) return False return True def setAction(self,live): """Set action by configuration or install system""" self.logicObj.clVars.Set('cl_action', "merge" if live else "system",True) def checkAndSetInstallOptions(self,diskOptions, swapOptions, usersOptions): """Check and set disk, swap and bind cmd options""" if self.optobj.values.s: self.logicObj.setLinuxName(self.optobj.values.s.upper()) if self.optobj.values.march: self.logicObj.clVars.Set('os_install_arch_machine', self.optobj.values.march,True) if self.optobj.values.nouuid: self.logicObj.clVars.Set('cl_uuid_set','off',True) if self.optobj.values.build: self.logicObj.clVars.Set('os_install_scratch',"on",True) else: self.logicObj.clVars.Set('os_install_scratch',"off",True) if self.optobj.values.type: self.logicObj.clVars.Set('os_install_root_type', self.optobj.values.type, True) if self.optobj.values.iso: if not self.logicObj.setIso(self.optobj.values.iso): return False listDiskOptions = [] listBindOptions = [] listSwapOptions = [] if diskOptions: listDiskOptions = self._parseOptDisk(diskOptions) if listDiskOptions is False: return False listBindOptions = self._parseOptBind(diskOptions) if listBindOptions is False: return False if swapOptions: listSwapOptions = self._parseOptSwap(swapOptions) if listSwapOptions is False: return False if not self.logicObj.setInstallOptions(listDiskOptions, listBindOptions, listSwapOptions, usersOptions, self.optobj.values.mbr): return False return True def _parseOptSwap(self, listOpt): """Parse value cmd option --swap""" listNameOptions = ["dev"] lenOptions = len(listNameOptions) itemOptions = map(lambda x: (x,''), listNameOptions) rawListOpt = map(lambda x: filter(lambda y: y, x.split(':')), listOpt) sameLenListOpt = [] for listData in rawListOpt: lenListData = len(listData) if lenListData>1: errOpt = ":".join(filter(lambda x: x, listData)) self.printERROR(_("incorrect '%s'")%errOpt) return False dictOpt = {} dictOpt.update(zip(map(lambda x:x[0],itemOptions),listData)) sameLenListOpt.append(dictOpt) return sameLenListOpt def _parseOptBind(self, listOpt): """Parse value cmd option --bind""" listOpt = filter(lambda x:not x.startswith('/dev/'),listOpt) listNameOptions = ["srcMountPoint", "destMountPoint"] lenOptions = len(listNameOptions) itemOptions = map(lambda x: (x,''), listNameOptions) rawListOpt = map(lambda x: filter(lambda y: y, x.split(':')), listOpt) sameLenListOpt = [] for listData in rawListOpt: lenListData = len(listData) if lenListData < lenOptions: listData += ['']*(lenOptions-lenListData) dictOpt = {} dictOpt.update(zip(map(lambda x: x[0], itemOptions),listData)) srcDir = dictOpt["srcMountPoint"] destDir = dictOpt["destMountPoint"] if not (srcDir and destDir): errOpt = ":".join(filter(lambda x: x, listData)) self.printERROR(_("incorrect '%s'")%errOpt) return False sameLenListOpt.append(dictOpt) return sameLenListOpt def _parseOptDisk(self, listOpt): """Parse value cmd option --disk""" listOpt = filter(lambda x:x.startswith('/dev/'),listOpt) listNameOptions = ["dev","mountPoint","fileSystem","options"] lenOptions = len(listNameOptions) itemOptions = map(lambda x: (x,''), listNameOptions) rawListOpt = map(lambda x: x.split(':'), listOpt) sameLenListOpt = [] for listData in rawListOpt: lenListData = len(listData) if lenListData < lenOptions: listData += ['']*(lenOptions-lenListData) dictOpt = {} dictOpt.update(zip(map(lambda x: x[0],itemOptions), listData)) options = [] strOptions = dictOpt["options"] if strOptions: options = filter(lambda x: x, strOptions.split(',')) dictOpt["options"] = options sameLenListOpt.append(dictOpt) return sameLenListOpt def templateSelect(self,template): """Process template appling""" if self.logicObj.applyTemplatesForSystem(): return True else: return False def installSystem(self, force=False, bootDisk=None, users=[], stdinReadPwd=False, builder=False, flagSpinner=True, update=False): """Run install system""" if self.logicObj.installSystem(force=force, bootDisk=bootDisk, stdinReadPwd=stdinReadPwd, builder=builder, flagSpinner=flagSpinner,update=update): if self.logicObj.clVars.Get('os_install_root_type') != "flash": self.defaultPrint("\n") self.defaultPrint(_("To apply changes you have to reboot")+ ".\n") return True else: return False def showPartitions(self): self.logicObj.printAllPartitonsTable() def configureSystem(self): """Run configure system""" if self.logicObj.configureSystem(): return True else: return False def installPackage(self): """Run package installation""" if self.logicObj.installPackage(): return True else: return False def uninstallPackage(self): """Run package uninstallation""" if self.logicObj.uninstallPackage(): return True else: return False