We create a universal Install Server for automatic network installation of Linux and Windows based on Cobbler

    Cobbler is a tool in the Linux world that can be used as Install Server, creating many network installation scripts based on one or more Linux distributions. There is also support for installations of FreeBSD, VMware, Xen, and Nexenta.

    With his help, I would also like to flexibly and universally create my own network installation scripts from different Windows distributions (XP, 2003, 7, 8, 2008, 2012).

    About how to configure and use cobbler to install Linux is exhaustively written on its official website - https://cobbler.github.io . I will focus here on my version of the solution to the problem regarding Windows.

    The main problem for creating your own network installation script was preparing the necessary Windows boot files in Linux. The generation of the necessary binaries for me occurs when I run the cobbler post-trigger on the command cobbler sync:
    • some files are created from standard ones by directly replacing one line with another right in the binary.
    • in the process of changing the bootmgr.exe file, the checksum of the PE file will change and it needs to be recalculated. The trigger does this with python-pefile.
    • hivex was used to create BCD boot information registries.
    • for working with wim images - wimlib
    • Windows response files are generated using cobbler templates using all its capabilities for conditional code generation depending on the version of Windows, architecture (32 or 64 bits), installation profile, etc.
    • startup scripts for wim images (startnet.cmd) and a script that runs after the installation of OS (post_install.cmd) are also generated from templates.

    In my case, response files are generated mainly based on the version of Windows and quite rarely, additionally based on the profile name. Those. I chose for myself the most suitable and in most cases used installation options that can be specified in the answer file for each version of Windows.

    The main work is to configure the installation option, install additional software, etc. lies on the script that I specify as the kickstart file for the cobbler profile (win.ks) in the case of installing Windows and downloading the post_install.cmd script already at the time of installation. Cobbler at the time of download dynamically generates code based on the win.ks template. And in this template, most code is already generated based on the profile name.

    As a result, in most cases, to change the installation script during debugging, it is enough for me to simply edit the win.ks text file on the server and restart the installation.

    Simplifying the logical relationship of the files that need to be changed for each scenario, the network installation for Xp and 2003 can be schematically depicted as follows: For Windos 7 and newer: Not the most, of course, a short way from the item in the PXE menu to running the post_install.cmd script with the profile name as a parameter. In addition, this script should also take the kickstart server cobbler corresponding to this profile and run it.
    pxeboot.n12 → setupldr.exe → winnt.sif → post_install.cmd profile_name



    pxeboot.n12 → bootmgr.exe → BCD → winpe.wim → startnet.cmd → autounattended.xml → post_install.cmd profile_name




    Now, first things first


    We believe that cobbler 2.6.9 and everything you need for its operation have already been installed, configured (for example iptables, SElinux) and does an excellent job of installing Linux.

    Install everything you need:
    • # dnf install python-pefile hivex ntfs-3g fuse
    • wimlib I collected from source

    Using hivex directly is not convenient - here (a good installation option, but I didn’t like it because it uses tftp log analysis, but I don’t need to tweak the binaries) I found a ready-made script (bcdedit.pl) that makes the necessary changes in the standard BCD . In fact, it is a replacement for the Windows utility bcdedit. It is written in Perl, I extracted it from the archive and placed it in / usr / local / bin, having modified it beforehand:
    # diff -c bcdedit.pl.orig bcdedit.pl 
    *** bcdedit.pl.orig
    --- bcdedit.pl
    ***************
    *** 232,237 ****
    --- 232,238 ----
      &AddElement($BCDFILE,$guids{bootmgr},"25000004","hex:3:1e,00,00,00,00,00,00,00");
      &AddElement($BCDFILE,$guids{bootmgr},"12000004","string:Windows Boot Manager");
      &AddElement($BCDFILE,$guids{bootmgr},"24000001",&Guids2MultiSZ($newguid));
    + &AddElement($BCDFILE,$guids{bootmgr},"16000048","hex:3:01");
      print "Creating New Object\n";
      &CreateGuid($BCDFILE,$newguid,"0x10200003");
    

    The added line is similar to executing the command:
    
    bcdedit -set {bootmgr} nointegritychecks Yes
    

    In the tftp server directory, create a folder where we will have Windows distributions:
    
    # mkdir /var/lib/tftpboot/winos
    

    Now we create directories for each distribution there and copy its contents there.
    It looks like this for me:
    
    # ls -l /var/lib/tftpboot/winos
    dr-xr-xr-x.  6 root    root               4096 Nov 29  2014 Win2012-Server_EN-x64
    dr-xr-xr-x.  6 root    root               4096 Jun  1  2014 Win2012-Server_RU-x64
    dr-xr-xr-x. 10 root    root               4096 May  6 19:41 Win2K3-Server_EN-x64
    dr-xr-xr-x.  7 root    root               4096 Nov 13  2013 Win2k8-Server_EN-x64
    dr-xr-xr-x.  4 root    root               4096 Oct 28  2013 Win7_EN-x64
    dr-xr-xr-x.  5 root    root               4096 Sep 25  2014 Win7_RU-x64
    dr-xr-xr-x.  6 root    root               4096 Jun 25 10:29 Win8_RU-x64
    dr-xr-xr-x.  7 root    root               4096 Dec  8  2011 WinXp_EN-i386
    dr-xr-xr-x.  8 root    root               4096 Jul 31 17:12 WinXp_RU-i386
    

    If you plan to install such rarities as Xp or 2003

    Preparing boot files


    In the directories with the distributions WinXp_EN-i386, WinXp_RU-i386 and Win2K3-Server_EN-x64, execute the following commands:
    
    [root@is WinXp_EN-i386]# cabextract i386/startrom.n1_
    [root@is WinXp_EN-i386]# mv startrom.n12 pxeboot.n12
    [root@is WinXp_EN-i386]# cabextract i386/setupldr.ex_
    

    In distribution directories with Xp:
    
    [root@is WinXp_EN-i386]# sed -i 's/ntdetect\.com/ntdetect.wxp/gi setupldr.exe
    

    In distribution directories since 2003:
    
    [root@is Win2K3-Server_EN-x64]# sed -i 's/ntdetect\.com/ntdetect.2k3/gi setupldr.exe
    

    
    # cp /var/lib/tftpboot/winos/WinXp_EN-i386/i386/ntdetect.com /var/lib/tftpboot/winos/ntdetect.wxp
    # cp /var/lib/tftpboot/winos/Win2K3-Server_EN-x64/i386/ntdetect.com /var/lib/tftpboot/winos/ntdetect.wxp
    

    Configure RIS


    We will need ris-linux. In my distribution, it is put like this:
    
    # dnf install ris-linux
    

    So that RIS does not interfere with the installation of Win 7, you need to comment out one line in the /usr/share/ris-linux/binlsrv.py file:
    
    # cd /usr/share/ris-linux
    # cp binlsrv.py binlsrv.py.orig
    # sed -i "s/p = p + chr(252) + chr(len(/#&/gi" binlsrv.py
    

    # diff binlsrv.py.orig binlsrv.py 
    571c571
    <     p = p + chr(252) + chr(len('boot\\bcd')) + 'boot\\bcd'
    ---
    >     #p = p + chr(252) + chr(len('boot\\bcd')) + 'boot\\bcd'
    

    I was not able to find an easy way, without analyzing tftpd logs, the ability to tell RIS which BCD is needed. Therefore, I just turned it off.

    Create the folder / var / lib / tftpboot / winos / inf and copy there all the .inf files of the 32-bit network card drivers that you need and run the command:
    
    # /usr/share/ris-linux/infparser.py /var/lib/tftpboot/winos/inf
    

    I need RIS to simultaneously serve 32-bit Xp and 64-bit Server 2003. To do this, create another instance of the service on another port:
    
    # cd /etc/sysconfig
    # cp ris-linuxd ris-linuxd64
    # sed -i 's/\/inf/&64/gi' ris-linuxd64
    # sed -i 's/linuxd/&64/gi' ris-linuxd64
    # sed -i 's/BINLSRV_OPTS=/&--port=4012/gi' ris-linuxd64
    

    # diff ris-linuxd ris-linuxd64
    2c2
    < # ris-linuxd service.
    ---
    > # ris-linuxd64 service.
    6c6
    < BINLSRV_INFPATH=/var/lib/tftpboot/winos/inf
    ---
    > BINLSRV_INFPATH=/var/lib/tftpboot/winos/inf64
    9c9
    < BINLSRV_OPTS=
    ---
    > BINLSRV_OPTS=--port=4012
    12c12
    < BINLSRV_LOGFILE=/var/log/ris-linuxd.log
    ---
    > BINLSRV_LOGFILE=/var/log/ris-linuxd64.log
    15c15
    < BINLSRV_PIDFILE=/var/run/ris-linuxd.pid
    ---
    > BINLSRV_PIDFILE=/var/run/ris-linuxd64.pid
    

    
    # cd /usr/sbin
    # ln -s ris-linuxd ris-linuxd64
    

    No one bothered to rewrite the ris-linux service under systemd, however no one needs it anymore.
    
    # cd /etc/rc.d/init.d
    # cp ris-linuxd ris-linuxd64
    # sed -i 's/ris-linuxd/&64/gi' ris-linuxd64
    

    Create the folder / var / lib / tftpboot / winos / inf64 and copy there all the .inf files of the 64-bit network card drivers that you need and run the command:
    
    /usr/share/ris-linux/infparser.py /var/lib/tftpboot/winos/inf64
    

    We turn on and start the services:
    
    # systemctl enable  ris-linuxd
    # systemctl start  ris-linuxd
    # systemctl enable  ris-linuxd64
    # systemctl start  ris-linuxd64
    

    Additionally, you need to change the port in the Win2K3-Server_EN-x64 bootloader to the one that we pointed to 64-bit RIS.
    You can do this using the modldr.py utility from the ris-linux package:
    
    # /usr/share/ris-linux/modldr.py -p 4012 /var/lib/tftpboot/winos/Win2K3-Server_EN-x64/setupldr.exe
    

    If the utility operation fails, then in the text of the script modldr.py replace the line:
    ppattern = re.compile(r'\x6a\x04\x68(..)\x00\x00\xff\x35', re.DOTALL)
    

    per line:
    ppattern = re.compile(r'\x6a\x64\x68(..)\x00\x00\xff\x35', re.DOTALL)
    


    SAMBA Preparation


    We need Samba to make two folders public
    
    # vi /etc/samba/smb.conf
    # Дистрибутивы для инсталляции
    [WINOS]
    path = /var/lib/tftpboot/winos
    guest ok = yes
    browseable = yes
    writeable = no
    public = yes
    blocking locks = no
    oplocks = no
    level2 oplocks = no
    # Разный публично доступный софт
    [public]
    comment = Public Stuff
    path = /var/www/html/Distr
    public = yes
    writable = no
    printable = no
    guest ok = yes
    blocking locks = no
    oplocks = no
    level2 oplocks = no
    


    We create a user with minimal rights, which we will use during installation. I have: install / install

    Preparing Files for a Network Installation Win 7, 8, 2008, 2012


    The situation with Win 7, 8, 2008, 2012 is much worse than in Xp. It’s not so easy to dig into binaries here - you need to then count the checksum in it.

    The answer file for automatic installation here also has a more modern look - in xml format. It has many improvements, for example, you can specify how to cut disks into partitions, etc. But there is also a big fly in the ointment - the name of the answer file is sewn into the startup script, which actually starts the installation. In turn, the script itself is wired into the wim image.

    Those. there were still big inconveniences with fixing the binaries, and in addition, for each installation scenario based on one distribution, I now have to have a separate wim image that differs from another same image only in the name of the response file in the start script.

    In the directories with distributions you need to put the following files:
    • pxeboot.n12
    • bootmgr.exe
    • boot / bcd
    • boot / boot.sdi
    • boot / fonts and the font set in it

    Creating Cobbler Templates


    Create file name translation rules for tftp:
    File Contents /etc/tftpd.rules
    
    #vi /etc/tftpd.rules
    rg	\\					/ # Convert backslashes to slashes
    r	(BOOTFONT\.BIN)				/winos/\1
    r	(/Boot/Fonts/)(.*)			/winos/Win8_RU-x64/boot/Fonts/\2
    r	(ntdetect\.wxp)				/winos/\1
    r	(ntdetect\.2k3)				/winos/\1
    r	(wine.\.sif)				/WinXp_EN-i386/\1
    r	(xple.)					/WinXp_EN-i386/\1
    r	(winr.\.sif)				/WinXp_RU-i386/\1
    r	(xplr.)					/WinXp_RU-i386/\1
    r	(wi2k.\.sif)				/Win2K3-Server_EN-x64/\1
    r	(w2k3.)					/Win2K3-Server_EN-x64/\1
    r	(/Win2K3-Server_EN-x64/)(.*)		/winos\1\L\2
    r	(boot7r..exe)				/winos/Win7_RU-x64/\1
    r	(/Boot/)(7R.)				/winos/Win7_RU-x64/boot/\2
    r	(boot7e.\.exe)				/winos/Win7_EN-x64/\1
    r	(/Boot/)(7E.)				/winos/Win7_EN-x64/boot/\2
    r	(boot28.\.exe)				/winos/Win2k8-Server_EN-x64/\1
    r	(/Boot/)(28.)				/winos/Win2k8-Server_EN-x64/boot/\2
    r	(boot2e.\.exe)				/winos/Win2012-Server_EN-x64/\1
    r	(/Boot/)(2e.)				/winos/Win2012-Server_EN-x64/boot/\2
    r	(boot2r.\.exe)				/winos/Win2012-Server_RU-x64/\1
    r	(/Boot/)(2r.)				/winos/Win2012-Server_RU-x64/boot/\2
    r	(boot81.\.exe)				/winos/Win8_RU-x64/\1
    r	(/Boot/)(B8.)				/winos/Win8_RU-x64/boot/\2
    r	(/WinXp...-i386/)(.*)			/winos\1\L\2
    


    We specify conversion rules in the cobbler template for tftp:
    
    # vi /etc/cobbler/tftpd.template
    service tftp
    {
            disable                 = no
            socket_type             = dgram
            protocol                = udp
            wait                    = yes
            user                    = $user
            server                  = $binary
            server_args             = -m /etc/tftpd.rules --port-range 25000:25030 -v -v -v -s $args
            per_source              = 11
            cps                     = 100 2
            flags                   = IPv4
    }
    

    We add version information in the /var/lib/cobbler/distro_signatures.json file in the windows section so that they can be used in the templates through cobbler metadata.
    
    # vi /var/lib/cobbler/distro_signatures.json
      "windows": {
       "2003": {
       },
       "2008": {
       },
       "2012": {
       },
       "XP": {
       },
       "7": {
       },
       "8": {
       }
      },
    

    When setting up the Windows installation, it was possible to preserve the main advantages of installing Linux through cobbler by generating an answer file and a post-installation script from the templates.

    The composition of the templates for Windows includes the following files:
    1. post_inst_cmd.template - script template run after OS installation
    2. win.ks - plays the role of the post section in kiskstart linux for windows
    3. win_sif.template - response file template
    4. startnet.template - start script template in wim image
    5. winpe7.template - wim file for Win 7 and Win 2008 Server
    6. winpe8.template - wim file for Win 8 and Win 2012 Server

    Post_inst_cmd.template and win.ks templates


    Create a template for the script that runs after installing windows (no matter which version).
    At startup, the name of the profile (installation option) cobbler is passed as a parameter.
    
    # cat /var/lib/tftpboot/winos/post_inst_cmd.template
    %systemdrive%
    CD %systemdrive%\TMP >nul 2>&1
    $SNIPPET('my/win_wait_network_online')
    wget.exe http://@@http_server@@/cblr/svc/op/ks/profile/%1
    MOVE %1 install.cmd
    todos.exe install.cmd
    start /wait install.cmd
    DEL /F /Q libeay32.dll >nul 2>&1
    DEL /F /Q libiconv2.dll >nul 2>&1
    DEL /F /Q libintl3.dll >nul 2>&1
    DEL /F /Q libssl32.dll >nul 2>&1
    DEL /F /Q wget.exe >nul 2>&1
    DEL /F /Q %0 >nul 2>&1
    

    A few explanations on the template:
    In each directory with the distribution kit there is a folder $ OEM $ / $ 1 / TMP in which lies wget.exe, todos.exe and dlls that they need to run. More precisely, they lie in one folder, while others are just symlink to it.
    
    # ls -l '/var/lib/tftpboot/winos/Win2K3-Server_EN-x64/$OEM$/$1/TMP'
    -rwxr-xr-x. 1 root root 1177600 Sep  4  2008 libeay32.dll
    -rwxr-xr-x. 1 root root 1008128 Mar 15  2008 libiconv2.dll
    -rwxr-xr-x. 1 root root  103424 May  7  2005 libintl3.dll
    -rwxr-xr-x. 1 root root  232960 Sep  4  2008 libssl32.dll
    -rwxr-xr-x. 1 root root    4880 Oct 26  1999 sleep.exe
    -rwxr-xr-x. 1 root root   52736 Oct 27  2013 todos.exe
    -rwxr-xr-x. 1 root root  449024 Dec 31  2008 wget.exe
    # ls -l '/var/lib/tftpboot/winos/Win8_RU-x64/sources/$OEM$/$1/TMP'
    lrwxrwxrwx. 1 root root 45 Oct 28  2013 /var/lib/tftpboot/winos/Win8_RU-x64/sources/$OEM$/$1/TMP -> ../../../../Win2K3-Server_EN-x64/$OEM$/$1/TMP
    # ls -l '/var/lib/tftpboot/winos/WinXp_RU-i386/$OEM$/$1/TMP'
    lrwxrwxrwx. 1 root root 42 May 31  2014 /var/lib/tftpboot/winos/WinXp_RU-i386/$OEM$/$1/TMP -> ../../../Win2K3-Server_EN-x64/$OEM$/$1/TMP
    

    • When installing OS, these files are copied to C: \ TMP.
    • Snipet win_wait_network_online waits until the network is ready, pinging the IP address of the server where cobbler is installed.
      Snippet file contents / var / lib / cobbler / snippets / my / win_wait_network_online
      
      :wno10
      set n=0
      :wno20
      ping @@http_server@@ -n 3
      set exit_code=%ERRORLEVEL%
      IF %exit_code% EQU 0 GOTO wno_exit
      set /a n=n+1
      IF %n% lss 30 goto wno20
      pause
      goto wno10
      :wno_exit
      

      When the script is generated, the variable name @@ http_server @@ from the cobbler metadata is replaced with the real IP address of the server on which cobbler is installed.
    • Next, wget.exe downloads the kickstart file. Unlike Linux kickstart, this one contains a .cmd file, but also generated on the fly by the cobbler server from the win.ks template.
    • This file is renamed, then transcoded to the windows text file format and run as a regular cmd script.
    • After working out the script in C: \ TMP, the files used to obtain it and the script itself, too, are deleted.

    The file that we downloaded with wget and which plays the role of the post-installation part of kickstart Linux looks like this in a simplified form:
    File Contents /var/lib/cobbler/kickstarts/win.ks
    # cat /var/lib/cobbler/kickstarts/win.ks
    $SNIPPET('my/win_wait_network_online')
    set n=0
    :mount_y
    net use y: \\@@http_server@@\Public /user:install install
    set exit_code=%ERRORLEVEL%
    IF %exit_code% EQU 0 GOTO mount_z
    set /a n=n+1
    IF %n% lss 20 goto mount_y
    PAUSE
    goto mount_y
    set n=0
    :mount_z
    net use z: \\@@http_server@@\winos /user:install install
    set exit_code=%ERRORLEVEL%
    IF %exit_code% EQU 0 GOTO mount_exit
    set /a n=n+1
    IF %n% lss 20 goto mount_z
    PAUSE
    goto mount_z
    :mount_exit
    if exist %systemdrive%\TMP\stage.dat goto flag005
    echo 0 > %systemdrive%\TMP\stage.dat
    $SNIPPET('my/win_check_virt')
    #if $distro_name in ( 'WinXp_EN-i386', 'WinXp_RU-i386', 'Win2K3-Server_EN-x64' )
    z:\Drivers\wsname.exe /N:$DNS /NOREBOOT
    #else
    REM pause
    #end if
    echo Windows Registry Editor Version 5.00 > %systemdrive%\TMP\install.reg
    echo [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce] >> %systemdrive%\TMP\install.reg
    echo "DD"="C:\\TMP\\install.cmd" >> %systemdrive%\TMP\install.reg
    $SNIPPET('my/win_install_drivers')
    #if $distro_name == 'Win2K3-Server_EN-x64'
    start /wait z:\Win2K3-Server_EN-x64\cmpnents\r2\setup2.exe /q /a /sr
    start /wait y:\Windows\Win2003\IE8-WindowsServer2003-x64-ENU.exe /passive /update-no /norestart
    if %virt% equ NO REG IMPORT y:\Windows\Win2003\vm.reg
    #end if
    REG IMPORT %systemdrive%\TMP\install.reg
    net use Y: /delete
    net use Z: /delete
    %systemdrive%\TMP\sleep.exe 10
    exit
    :flag005
    for /f "tokens=*" %%i in (%systemdrive%\TMP\stage.dat) do set stage=%%i
    echo 1 > %systemdrive%\TMP\stage.dat
    REG IMPORT %systemdrive%\TMP\install.reg
    if %stage% neq 0 goto flag010
    net use Y: /delete
    net use Z: /delete
    shutdown -r -f -t 5
    exit
    :flag010
    if %stage% gtr 1 goto flag020
    echo 2 > %systemdrive%\TMP\stage.dat
    $SNIPPET('my/winzip')
    $SNIPPET('my/winrar')
    $SNIPPET('my/win_install_chrome')
    $SNIPPET('my/win_install_ffox')
    $SNIPPET('my/win_install_adacr')
    $SNIPPET('my/win_install_jdk7-x86')
    $SNIPPET('my/win_install_jdk7-x86_64')
    $SNIPPET('my/win_install_UltraVNC')
    #if $distro_name in ( 'WinXp_EN-i386', 'WinXp_RU-i386', 'Win2K3-Server_EN-x64' )
    $SNIPPET('my/win_install_office_2007_ru')
    #else if $distro_name in (  'Win7_RU-x64', 'Win2012-Server_RU-x64', 'Win8_RU-x64' )
    $SNIPPET('my/win_install_office_2010_ru')
    #else
    $SNIPPET('my/win_install_office_2010_en')
    #end if
    Title Cleaning Temp files
    DEL "%systemroot%\*.bmp" >nul 2>&1
    DEL "%systemroot%\Web\Wallpaper\*.jpg" >nul 2>&1
    DEL "%systemroot%\system32\dllcache\*.scr" >nul 2>&1
    DEL "%systemroot%\system32\*.scr" >nul 2>&1
    DEL "%AllUsersProfile%\Start Menu\Windows Update.lnk" >nul 2>&1
    DEL "%AllUsersProfile%\Start Menu\Set Program Access and Defaults.lnk" >nul 2>&1
    DEL "%AllUsersProfile%\Start Menu\Windows Catalog.lnk" >nul 2>&1
    DEL "%systemdrive%\Microsoft Office*.txt" >nul 2>&1
    net user aspnet /delete >nul 2>&1
    REM %systemdrive%\TMP\sleep.exe 60
    net use Y: /delete
    net use Z: /delete
    shutdown -r -f -t 30
    RD /S /Q %systemdrive%\DRIVERS\ >nul 2>&1
    if not defined stage DEL /F /Q %systemdrive%\post_install.cmd
    DEL /F /S /Q %systemdrive%\TMP\*.*
    exit
    


    Snippets are used here so as not to clutter up the code. They carry out checks on the hypervisor, install the necessary drivers, depending on the distribution and profile, this or that software is installed or not installed.

    Win_sif.template template


    This is a single template from which the response files of all versions of windows are formed. In it, as in other templates, all cobbler meta-variables can be used.
    Such as $ arch, $ distro_name, $ profile_name.
    File Contents /var/lib/tftpboot/winos/win_sif.template
    
    #if $distro_name in ( 'WinXp_EN-i386', 'WinXp_RU-i386', 'Win2K3-Server_EN-x64' )
    #if $arch == 'x86_64'
    	#set $win_arch = 'amd64'
    #else if $arch == 'i386'
    	#set $win_arch = 'i386'
    #end if
    #set $OriSrc = '\\\\' + $http_server + '\\WINOS\\' + $distro_name + '\\' + $win_arch
    #set $DevSrc = '\\Device\\LanmanRedirector\\' + $http_server + '\\WINOS\\' + $distro_name
    [Data]
    floppyless = "1"
    msdosinitiated = "1"
    ; Needed for second stage
    OriSrc="$OriSrc"
    OriTyp="4"
    LocalSourceOnCD=1
    DisableAdminAccountOnDomainJoin=0
    AutomaticUpdates="No"
    Autopartition="0"
    UnattendedInstall="Yes"
    [SetupData]
    OsLoadOptions = "/noguiboot /fastdetect"
    ; Needed for first stage
    SetupSourceDevice = "$DevSrc"
    [Unattended]
    CrashDumpSetting=0
    FactoryMode=No
    UnattendMode=FullUnattended
    UnattendSwitch="Yes"
    OemPreinstall="Yes"
    OemSkipEula="Yes"
    Repartition=No
    FileSystem=*
    WaitForReboot="No"
    NoWaitAfterTextMode=1
    NoWaitAfterGUIMode=1
    DriverSigningPolicy=Ignore
    NonDriverSigningPolicy=Ignore
    UpdateInstalledDrivers=Yes
    TargetPath=\WINDOWS
    OemPnPDriversPath=DRIVERS\NIC;DRIVERS\ACPI;DRIVERS\CHIPSET\5520\All;DRIVERS\CHIPSET\C200\All;DRIVERS\Storage;DRIVERS\Virt
    #if $os_version == '2003'
    [LicenseFilePrintData]
    AutoMode = PerSeat
    #end if
    [Display]
    BitsPerPel=32
    XResolution=1440
    YResolution=900
    Vrefresh=60
    [WindowsFirewall]
    Profiles = WindowsFirewall.TurnOffFirewall
    [WindowsFirewall.TurnOffFirewall]
    Mode = 0
    [PCHealth]
    RA_AllowToGetHelp=0
    [GuiRunOnce]
    "%Systemdrive%\post_install.cmd @@profile_name@@"
    [GuiUnattended]
    AdminPassword=*
    TimeZone=195
    OEMSkipRegional=1
    OemSkipWelcome=1
    #if $os_version != '2003'
    AutoLogon = Yes
    AutoLogonCount=1
    #end if
    [RemoteInstall]
    Repartition=Yes
    UseWholeDisk=Yes
    [Components]
    msmsgs=Off
    msnexplr=Off
    zonegames=Off
    Paint=Off
    #if $os_version == '2003'
    ; Iis_common=On
    ; Iis_inetmgr=On
    ComPlusNetwork=On
    ; Iis_www=On
    ; Iis_asp=On
    IEHardenAdmin=Off
    IEHardenUser=Off
    #end if
    [TerminalServices]
    AllowConnections=1
    [UserData]
    #if $os_version == '2003'
    ProductKey="XXXXX-XXXXX-XXXXX-XXXXX-XXXXX"
    #else if $distro_name == 'WinXp_EN-i386'
    ProductKey="XXXXX-XXXXX-XXXXX-XXXXX-XXXXX"
    #else if $distro_name == 'WinXp_RU-i386'
    ProductKey="XXXXX-XXXXX-XXXXX-XXXXX-XXXXX"
    #end if
    ComputerName=*
    FullName="Admin"
    OrgName="Microsoft"
    [RegionalSettings]
    LanguageGroup=1,2,3,4,5
    #if $distro_name == 'WinXp_RU-i386'
    SystemLocale=00000419
    UserLocale=00000419
    #else
    SystemLocale=00000409
    UserLocale=00000409
    #end if
    InputLocale=0409:00000409,0419:00000419
    [Shell]
    CustomDefaultThemeFile="%WinDir%\Resources\Themes\Windows Classic.Theme"
    [Networking]
    InstallDefaultComponents="Yes"
    

    #else if $distro_name in ( 'Win7_RU-x64', 'Win7_EN-x64', 'Win2k8-Server_EN-x64', 'Win2012-Server_EN-x64', 'Win2012-Server_RU-x64', 'Win8_RU-x64' )
    
    
    #if $distro_name in ( 'Win2012-Server_EN-x64', 'Win2012-Server_RU-x64' )
        
    #end if
        
    #if $distro_name in ( 'Win7_RU-x64', 'Win2012-Server_RU-x64', 'Win8_RU-x64' )
                0409:00000409;0419:00000419ru-RUru-RUru-RUru-RU
    #else
                0409:00000409en-USen-USen-USen-US
    #end if
            OnError1truePrimary0true/IMAGE/NAME
    #if $profile_name == 'IOS'
                                Windows8_VmVare_MacOS_xCode
    #else if $distro_name in ( 'Win7_RU-x64', 'Win7_EN-x64' )
                                Windows 7 PROFESSIONAL
    #else if $distro_name in (  'Win2k8-Server_EN-x64' )
                                Windows Server 2008 R2 SERVERENTERPRISE
    #else if $distro_name in (  'Win2012-Server_EN-x64', 'Win2012-Server_RU-x64' )
                                Windows Server 2012 R2 SERVERDATACENTER
    #else if $distro_name in (  'Win8_RU-x64' )
                                Windows 8.1 Pro
    #else if $distro_name in (  'Win8_EN-x64' )
                                Windows 8.1 Enterprise
    #end if
                            01
    #if $distro_name in ( 'Win2012-Server_EN-x64', 'Win2012-Server_RU-x64' )
                        XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
    #end if
                        NevertrueUserMy Organizationfalse
    #if $distro_name in ( 'Win2012-Server_EN-x64', 'Win2012-Server_RU-x64', 'Win8_RU-x64' )
                    \\@@http_server@@\WINOS\Drivers\CHIPSET\Win8
    #else
                    \\@@http_server@@\WINOS\Drivers\CHIPSET\5520\Vista\\@@http_server@@\WINOS\Drivers\CHIPSET\C200\WIN7
    #end if
                    
    #if $distro_name in ( 'Win2012-Server_EN-x64', 'Win2012-Server_RU-x64', 'Win8_RU-x64' )
                        \\@@http_server@@\WINOS\Drivers\NIC\Win8
    #else
                        \\@@http_server@@\WINOS\Drivers\NIC
    #end if
                    
    #if $distro_name in ( 'Win7_RU-x64', 'Win7_EN-x64', 'Win2k8-Server_EN-x64' )
                    \\@@http_server@@\WINOS\Drivers\ACPI\64\WIN7
    #end if
                    \\@@http_server@@\WINOS\Drivers\Storage\64
    #if $distro_name in ( 'Win8_RU-x64' )
                        \\@@http_server@@\WINOS\Drivers\Virt\Win8
    #else if $distro_name in (  'Win2012-Server_EN-x64', 'Win2012-Server_RU-x64' )
                        \\@@http_server@@\WINOS\Drivers\Virt\2012
    #else if $distro_name in ( 'Win2k8-Server_EN-x64' )
                        \\@@http_server@@\WINOS\Drivers\Virt\2008
    #else
                        \\@@http_server@@\WINOS\Drivers\Virt\Win7
    #end if
                    false*My OrganizationInstructor
    #if $distro_name == 'Win7_RU-x64'
                XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
    #else if $distro_name == 'Win7_EN-x64'
                XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
    #else if $distro_name == 'Win2k8-Server_EN-x64'
                XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
    #else if $distro_name in ( 'Win2012-Server_EN-x64', 'Win2012-Server_RU-x64' )
                XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
    #end if
            WORKGROUPfalsetrueRemote Desktopall03WorkXXXXfalse</PlainText></AdministratorPassword><LocalAccounts><LocalAccount wcm:action="add"><Password><Value>XXXXX</Value><PlainText>false</PlainText></Password><Name>User</Name><Group>Administrators</Group></LocalAccount></LocalAccounts><DomainAccounts><DomainAccountList wcm:action="add"><Domain>WORKGOUP</Domain><DomainAccount wcm:action="add"><Name>Domain Admins</Name><Group>Administrators</Group></DomainAccount><DomainAccount wcm:action="add"><Name>User</Name><Group>Administrators</Group></DomainAccount></DomainAccountList></DomainAccounts></UserAccounts><TimeZone>Central Asia Standard Time</TimeZone><RegisteredOrganization>My Organization</RegisteredOrganization><RegisteredOwner>User</RegisteredOwner><FirstLogonCommands><SynchronousCommand wcm:action="add"><RequiresUserInput>false</RequiresUserInput><Order>1</Order><CommandLine>cmd /C wmic useraccount where "name='user'" set PasswordExpires=FALSE</CommandLine></SynchronousCommand><SynchronousCommand wcm:action="add"><RequiresUserInput>false</RequiresUserInput><Order>2</Order><CommandLine>c:\post_install.cmd @@profile_name@@</CommandLine></SynchronousCommand></FirstLogonCommands><AutoLogon><Password><Value>XXXX</Value><PlainText>false</PlainText></Password><Enabled>true</Enabled><Domain>WORKGOUP</Domain><Username>User</Username><LogonCount>10000</LogonCount></AutoLogon></component></settings>
    #if $distro_name in ( 'Win7_RU-x64', 'Win7_EN-x64' )
        <cpi:offlineImage cpi:source="catalog:d:/sources/install_windows 7 professional.clg" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
    #else if $distro_name in ( 'Win2k8-Server_EN-x64' )
        <cpi:offlineImage cpi:source="catalog:d:/sources/install_windows server 2008 r2 serverenterprise.clg" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
    #else if $distro_name in ( 'Win2012-Server_EN-x64', 'Win2012-Server_RU-x64' )
        <cpi:offlineImage cpi:source="wim:c:/netboot/install.wim#Windows Server 2012 R2 SERVERDATACENTER" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
    #else if $distro_name in ( 'Win8_RU-x64' )
        <cpi:offlineImage cpi:source="catalog:d:/sources/install_windows 8.1 Pro.clg" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
    #end if
    </unattend>
    #end if
    </code></pre><br></div></div><br>
    Ключевой момент здесь в строках, при помощи которых стартует скрипт с именем профиля в качестве параметра:<br><ul><li>для Xp, 2003<br><pre><code class="bash">
    [GuiRunOnce]
    "%Systemdrive%\post_install.cmd @@profile_name@@"
    </code></pre></li><li>для 7, 8, 2008, 2012<br><pre><code class="xml"><SynchronousCommand wcm:action="add"><RequiresUserInput>false</RequiresUserInput><Order>2</Order><CommandLine>c:\post_install.cmd @@profile_name@@</CommandLine></SynchronousCommand></code></pre></li></ul><br><h4>Шаблон startnet.template</h4><br>
    Шаблон на основе которого формируются стартовые скрипты для wim образов.<br><ul><li>После инициализации сетевого стека скрипт находит IP адрес DHCP сервера (там же у нас находится и cobbler с samba).</li><li>Монтируются необходимые сетевые ресурсы samba.</li><li>Из метаданных cobbler извлекается имя файла ответов ($kernel_options[«sif»]) для данного профиля.</li><li>Запросом к DNS серверу находим имя инсталлируемого компьютера в DNS и заносим его в файл ответов.</li><li>Запускаем инсталляцию с этим файлом ответов.</li></ul><br><div class="spoiler"><b class="spoiler_title">Содержимое файла /var/lib/cobbler/kickstarts/startnet.template</b><div class="spoiler_text"><pre><code class="dos">wpeinit
    ping 127.0.0.1 -n 10 >nul
    md \tmp
    cd \tmp
    ipconfig /all | find "DHCP Server" > dhcp
    ipconfig /all | find "IPv4 Address" > ipaddr
    FOR /F "eol=- tokens=2 delims=:" %%i in (dhcp) do set dhcp=%%i
    FOR  %%i in (%dhcp%) do set dhcp=%%i
    FOR /F "eol=- tokens=2 delims=:(" %%i in (ipaddr) do set ipaddr=%%i
    net use y: \\%dhcp%\Public /user:install install
    net use z: \\%dhcp%\WINOS\@@distro_name@@ /user:install install
    set exit_code=%ERRORLEVEL%
    IF %exit_code% EQU 0 GOTO GETNAME
    echo "Can't mount network drive
    goto EXIT
    :GETNAME
    y:\windows\bind\nslookup.exe %ipaddr% | find "name =" > wsname
    for /f "eol=- tokens=2 delims==" %%i in (wsname) do echo %%i > ws
    for /f "eol=- tokens=1 delims=." %%i in (ws) do set wsname=%%i
    FOR  %%i in (%wsname%) do set wsname=%%i
    #set $unattended = "set UNATTENDED_ORIG=Z:\\sources\\" + $kernel_options["sif"]
    $unattended
    set UNATTENDED=X:\tmp\autounattended.xml
    echo off
    FOR /F "tokens=1 delims=!" %%l in (%UNATTENDED_ORIG%) do (
       IF "%%l"=="            <ComputerName>*</ComputerName>" (
         echo             ^<ComputerName^>%wsname%^<^/ComputerName^>>> %UNATTENDED%
       ) else (
         echo %%l>> %UNATTENDED%
       )
    )
    echo on
    :INSTALL
    set n=0
    z:\sources\setup.exe /unattend:%UNATTENDED%
    set /a n=n+1
    ping 127.0.0.1 -n 5 >nul
    IF %n% lss 20 goto INSTALL
    :EXIT
    </code></pre><br></div></div><br><h4>Шаблоны winpe7.template и winpe8.template</h4><br>
    Стандартные образы для сетевой инсталляции Win 7 и Win 8 с интегрированными драйверами.<br>
    По команде <code>cobbler sync</code> их копируют в место указанное в каждом профиле.<br>
    Каждую такую копию монтируют, в нее копируют стартовый скрипт созданный на основе шаблона startnet.template и демонтируют в триггере на post-sync.<br><br>
    Для работы с wim образами в Linux я использую <a href="http://sourceforge.net/projects/wimlib">wimlib</a><br>
    Для ее работы нужно установить пакет ntfs-3g, который в свою очередь работает через fuse.<br>
    Поэтому если как я, будете ставить cobbler в linux контейнере lxc под управлением libvirt, то нужно прописать в xml определения домена строки:<br><pre><code class="xml"><hostdev mode='capabilities' type='misc'><source><char>/dev/fuse</char></source></hostdev></code></pre><br><h4>Создаем определения дистрибутивов и профилей</h4><br><u>Создаем определение дистрибутивов в cobbler:</u><br><pre><code class="bash">
    # systemctl restart cobblerd
    # cobbler distro add --name=WinXp_RU-i386 --kernel=/var/lib/tftpboot/winos/WinXp_RU-i386/pxeboot.n12 --initrd=/var/lib/tftpboot/winos/add_ram.dat --arch=i386 --breed=windows --os-version=XP --kopts='post_install=/var/lib/tftpboot/winos/WinXp_RU-i386/$OEM$/$1/post_install.cmd'
    # cobbler distro add --name=Win7_RU-x64 --kernel=/var/lib/tftpboot/winos/Win7_RU-x64/pxeboot.n12 --initrd=/var/lib/tftpboot/winos/add_ram.dat --arch=x86_64 --breed=windows --os-version=7 --kopts='post_install=/var/lib/tftpboot/winos/Win7_RU-x64/sources/$OEM$/$1/post_install.cmd'
    # cobbler distro add --name=Win8_RU-x64 --kernel=/var/lib/tftpboot/winos/Win8_RU-x64/pxeboot.n12 --initrd=/var/lib/tftpboot/winos/add_ram.dat --arch=x86_64 --breed=windows --os-version=8 --kopts='post_install=/var/lib/tftpboot/winos/Win8_RU-x64/sources/$OEM$/$1/post_install.cmd'
    и т.д.
    </code></pre><br>
    Здесь kernel фиктивный — PXE будет загружать файл созданный на основе этого pxeboot.n12 по команде <code>cobbler sync</code>.<br>
    /var/lib/tftpboot/winos/add_ram.dat тоже фиктивный initrd, в смысле можно подсунуть любой— иначе distro не создать.<br><br><u>Создаем определение профилей в cobbler:</u><br><pre><code class="bash">
    # cobbler profile add --name=WinXp_RU-i386 --distro=WinXp_RU-i386 --kickstart=/var/lib/cobbler/kickstarts/win.ks --kopts='pxeboot=winr0.0,bootmgr=xplr0,sif=winr0.sif'
    # cobbler profile add --name=Win7_RU-x64 --distro=Win7_RU-x64 --kickstart=/var/lib/cobbler/kickstarts/win.ks --kopts='pxeboot=win7ra.0,bootmgr=boot7ra.exe,bcd=7Ra,winpe=winpe.wim,sif=autounattended.xml'
    # cobbler profile add --name=Win8_RU-x64 --distro=Win8_RU-x64 --kickstart=/var/lib/cobbler/kickstarts/win.ks --kopts='pxeboot=win81a.0,bootmgr=boot81a.exe,bcd=B8a,winpe=winpe.wim,sif=autounattended.xml'
    </code></pre><br>
    Создадим профили для двух тестовых вариантов инсталляции на основе дистрибутива Win8_RU-x64.<br><pre><code class="bash">
    # cobbler profile add --name=Win8-test1 --distro=Win8_RU-x64 --kickstart=/var/lib/cobbler/kickstarts/win.ks --kopts='pxeboot=win81b.0,bootmgr=boot81b.exe,bcd=B8b,winpe=winpb.wim,sif=autounattended01.xml'
    # cobbler profile add --name=Win8-test2 --distro=Win8_RU-x64 --kickstart=/var/lib/cobbler/kickstarts/win.ks --kopts='pxeboot=win81c.0,bootmgr=boot81c.exe,bcd=B8c,winpe=winpc.wim,sif=autounattended02.xml'
    </code></pre><br>
    Теперь в win.ks можно вставлять конструкции типа:<br><pre><code class="bash">
    #if $profile_name == ' Win8-test1'
    <code>
    #end if
    #if $profile_name == ' Win8-test2'
    <code>
    #end if</code></pre><br>
    Имена файлов в профилях реальные и должны соответствовать шаблонам, определенным в /etc/tftpd.rules для данного дистрибутива. Только вот самих файлов пока еще нет, их создаст триггер в момент выполнения команды <code>cobbler sync</code>.<br><br><h4>Шаблон меню PXE загрузки</h4><br>
    Cobbler по умолчанию создает меню в виде списка и чтобы сделать его более удобным нужно еще немного потрудиться.<br><br>
    Почему-то разработчики cobbler при генерации pxe меню из шаблона в качестве значения переменной http_server устанавливают фиксированное значение <code>server.example.org</code>, а не берут его из конфигурационного файла. Windows пунктам меню это не мешает, а вот для Linux при помощи этого значения можно указать местоположение kickstart файла. Исправляем это:<br><pre><code class="bash">
    # cd /usr/lib/python2.7/site-packages/cobbler
    # cp templar.py templar.py.orig
    # sed -i 's/"server.example.org"/self.settings.server/gi'  templar.py
    </code></pre><br>
    Теперь из файла /etc/cobbler/pxe/pxedefault.template строку<br><pre><code class="bash">
    $pxe_menu_items
    </code></pre><br>
    можно убрать, а вместо нее нарисовать что-нибудь свое:<br><pre><code class="bash">
    menu begin Linux
    MENU TITLE Linux
    	label Fedora-latest-x86_64
    		MENU INDENT 5
    		MENU LABEL Fedora-latest-x86_64
    		kernel /images/Fedora-22-x86_64/vmlinuz
    		append initrd=/images/Fedora-22-x86_64/initrd.img ks.device=bootif ks.sendmac lang=en text ks=http://@@http_server@@/cblr/svc/op/ks/profile/Fedora-latest-x86_64
    		ipappend 2
    	label returntomain
    		menu label Return to ^main menu.
    		menu exit
    menu end
    menu begin Windows
    MENU TITLE Windows
    	label  Win8-test1
    		MENU INDENT 5
    		MENU LABEL  Win8-test1
    		kernel /winos/Win8_RU-x64/win81b.0
    	label  Win8-test2
    		MENU INDENT 5
    		MENU LABEL  Win8-test2
    		kernel /winos/Win8_RU-x64/win81c.0
    	label returntomain
    		menu label Return to ^main menu.
    		menu exit
    menu end
    </code></pre><br><h4>Собираем все вместе</h4><br>
    Теперь из шаблонов будем создавать загрузочные файлы, скрипты, образы wim для загрузки и поместим все это в нужные места.<br><br>
    Для этого я решил воспользоваться триггером cobbler на post sync:<br><div class="spoiler"><b class="spoiler_title">Содержимое файла /usr/lib/python2.7/site-packages/cobbler/modules/sync_post_wingen.py</b><div class="spoiler_text"><pre><code class="python">import distutils.sysconfig
    import sys
    import os
    import traceback
    import cexceptions
    import os
    import re
    import xmlrpclib
    import pefile
    import cobbler.module_loader as module_loader
    import cobbler.utils as utils
    import cobbler.config as config
    import cobbler.templar as templar
    template_dir = "/var/lib/tftpboot/winos/"
    sif_template_name = template_dir + "win_sif.template"
    post_inst_cmd_template_name = template_dir + "post_inst_cmd.template"
    startnet_template_name = template_dir + "startnet.template"
    wim7_template_name = template_dir + "winpe7.template"
    wim8_template_name = template_dir + "winpe8.template"
    wimlib = "/usr/bin/wimlib-imagex"
    wimlib_mount = wimlib + " mountrw"
    wimlib_umount = wimlib + " unmount"
    mount_point = "/mnt/wim"
    bcdedit = "/usr/local/bin/bcdedit.pl"
    plib = distutils.sysconfig.get_python_lib()
    mod_path="%s/cobbler" % plib
    sys.path.insert(0, mod_path)
    def register():
        # this pure python trigger acts as if it were a legacy shell-trigger, but is much faster.
        # the return of this method indicates the trigger type
        return "/var/lib/cobbler/triggers/sync/post/*"
    def run( api, args, logger ):
        settings = api.settings()
        images =  api.images()
        distros = api.distros()
        profiles = api.profiles()
        conf = config.Config( api )
        templ = templar.Templar( conf )
        rc = 0
        template_win = open( post_inst_cmd_template_name )
        tmpl_data = template_win.read()
        template_win.close()
        for distro in distros:
            if distro.breed == "windows":
                meta = utils.blender( api, False, distro )
                if distro.kernel_options.has_key( "post_install" ):
                    data = templ.render( tmpl_data, meta, None, distro )
                    pi_file = open( distro.kernel_options["post_install"], "w+" )
                    pi_file.write( data )
                    pi_file.close()
        template_win = open( sif_template_name )
        tmpl_data = template_win.read()
        template_win.close()
        template_start = open( startnet_template_name )
        tmplstart_data = template_start.read()
        template_start.close()
        logger.info( "\nWindows profiles:" )
        for profile in profiles:
            distro = profile.get_conceptual_parent()
            if distro.breed == "windows":
                logger.info( 'Profile: ' + profile.name )
                meta = utils.blender( api, False, profile )
                (distro_path, pxeboot_name) = os.path.split( distro.kernel )
                if profile.kernel_options.has_key( "sif" ):
                    data = templ.render( tmpl_data, meta, None, profile )
                    if distro.os_version in ( "7", "2008", "8", "2012" ):
                        sif_file_name = os.path.join( distro_path, 'sources', profile.kernel_options["sif"] )
                    else:
                        sif_file_name = os.path.join( distro_path, profile.kernel_options["sif"] )
                    sif_file = open(sif_file_name, "w+" )
                    sif_file.write( data )
                    sif_file.close()
                    logger.info( 'Build answer file: ' + sif_file_name )
                if profile.kernel_options.has_key( "pxeboot" ) and profile.kernel_options.has_key( "bootmgr" ):
                    wk_file_name = os.path.join( distro_path, profile.kernel_options["pxeboot"] )
                    wl_file_name = os.path.join( distro_path, profile.kernel_options["bootmgr"] )
                    logger.info( "Build PXEBoot: " + wk_file_name )
                    if distro.os_version in ( "7", "2008", "8", "2012" ):
                        if len(profile.kernel_options["bootmgr"]) != 11:
                            logger.error( "The loader  name should be EXACTLY 11 character" )
                            return 1
                        if profile.kernel_options.has_key( "bcd" ):
                            if len(profile.kernel_options["bcd"]) != 3:
                                logger.error( "The BCD name should be EXACTLY 5 character" )
                                return 1
                        tl_file_name = os.path.join( distro_path, 'bootmgr.exe' )
                        pat1 = re.compile( r'bootmgr\.exe', re.IGNORECASE )
                        pat2 = re.compile( r'(\\.B.o.o.t.\\.)(B)(.)(C)(.)(D)', re.IGNORECASE )
                        bcd_name = 'BCD'
                        if profile.kernel_options.has_key( "bcd" ):
                            bcd_name = profile.kernel_options["bcd"]
                        bcd_name = "\\g<1>" + bcd_name[0] + "\\g<3>" + bcd_name[1] + "\\g<5>" + bcd_name[2]
                        data = open( tl_file_name, 'rb').read()
                        out = pat2.sub( bcd_name, data )
                    else:
                        if len(profile.kernel_options["bootmgr"]) != 5:
                            logger.error( "The loader name should be EXACTLY 5 character" )
                            return 1
                        if len(profile.kernel_options["sif"]) != 9:
                            logger.error( "The response should be EXACTLY 9 character" )
                            return 1
                        tl_file_name = os.path.join( distro_path, 'setupldr.exe' )
                        pat1 = re.compile( r'NTLDR', re.IGNORECASE )
                        pat2 = re.compile( r'winnt\.sif', re.IGNORECASE)
                        data = open( tl_file_name, 'rb').read()
                        out = pat2.sub( profile.kernel_options["sif"], data )
                    logger.info( 'Build Loader: ' + wl_file_name )
                    if out != data:
                        open(wl_file_name, 'wb+').write(out)
                    if distro.os_version in ( "7", "2008", "8", "2012" ):
                        pe =  pefile.PE( wl_file_name, fast_load=True )
                        pe.OPTIONAL_HEADER.CheckSum = pe.generate_checksum()
                        pe.write( filename=wl_file_name )
                    data = open(distro.kernel, 'rb').read()
                    out = pat1.sub( profile.kernel_options["bootmgr"], data )
                    if out != data:
                        open(wk_file_name, 'wb+').write(out)
                if profile.kernel_options.has_key( "bcd" ):
                    obcd_file_name = os.path.join( distro_path, 'boot', 'BCD' )
                    bcd_file_name = os.path.join( distro_path, 'boot', profile.kernel_options["bcd"] )
                    wim_file_name = 'winpe.wim'
                    if profile.kernel_options.has_key( "winpe" ):
                        wim_file_name = profile.kernel_options["winpe"]
                    wim_file_name = os.path.join( '/winos', distro.name, 'boot', wim_file_name )
                    sdi_file_name = os.path.join( '/winos', distro.name, 'boot', 'boot.sdi' )
                    logger.info( 'Build BCD: ' + bcd_file_name + ' for ' + wim_file_name )
                    cmd = "/usr/bin/cp " + obcd_file_name + " " + bcd_file_name
                    rc = utils.subprocess_call( logger, cmd, shell=True )
                    cmd = bcdedit + " " + bcd_file_name + " " +  wim_file_name + " " + sdi_file_name
                    rc = utils.subprocess_call( logger, cmd, shell=True )
                    ps_file_name = os.path.join( distro_path, "boot",  profile.kernel_options["winpe"] )
                    if distro.os_version in ( "7", "2008" ):
                        wim_pl_name = wim7_template_name
                    elif distro.os_version in ( "8", "2012" ):
                        wim_pl_name = wim8_template_name
                    cmd = "/usr/bin/cp " + wim_pl_name + " " + ps_file_name
                    rc = utils.subprocess_call( logger, cmd, shell=True )
                    if os.path.exists( wimlib ):
                        cmd = wimlib_mount + " " + ps_file_name + " " + mount_point
                        rc = utils.subprocess_call( logger, cmd, shell=True )
                        data = templ.render( tmplstart_data, meta, None, profile )
                        pi_file = open( mount_point + "/Windows/System32/startnet.cmd", "w+" )
                        pi_file.write( data )
                        pi_file.close()
                        cmd = wimlib_umount + " " + mount_point + " --commit --rebuild"
                        rc = utils.subprocess_get( logger, cmd, shell=True )
        return 0
    </code></pre><br></div></div><br>
    Это обычный скрипт на python использующий Cobbler API. Для своей работы использует pefile для пересчета контрольной суммы в bootmgr.exe, bcdedit.pl для внесения изменений в BCD и библиотеку wimlib для монтирования образа и копирования созданного из шаблона файла startnet.cmd в папку Windows/System32.<br><br>
    Выполняем команды:<br><pre><code class="bash">
    # mkdir /mnt/wim
    # systemctl restart  cobblerd
    # cobbler sync
    # systemctl restart xinetd
    </code></pre><br>
    Теперь создаем виртуалку с инсталляцией по сети, либо на обычном компьютере жмем кнопки Reset, F12 (ну или что у вас в BIOS для этих целей прописано), выбираем нужный пункт в меню и наслаждаемся автоматической сетевой инсталляцией как Linux так и Windows.<br><br>
    При необходимости внести изменения в сценарий пользуемся условной генерацией кода в win.ks. У меня в шапке этого файла сначала идет общая часть нужная при любом варианте инсталляции, в потом длинный if/else:<br><pre><code class="bash">
    #if $profile_name == '<profile_name1>'
    <code>
    #elseif $profile_name == '<profile_name2>'
    <code>
    . . .
    #elseif $profile_name == '<profile_nameN>'
    <code>
    #end if
    </code></pre><br>
    Дублирующейся, либо логически замкнутый код вынесен в снипеты.<br><br><h4>Чего не хватает</h4><br><ol><li>Не будет работать импорт Windows дистрибутива. Новые версии Microsoft выпускает не так уж и часто, так что это можно пережить.</li><li>Не будет работать (даже не пытался проверять) создание ISO образа Windows дистрибутива.</li><li>Автоматическая генерация PXE меню в cobbler в случае Windows профилей будет работать неправильно.</li><li>Если не соблюдать правила формирования имен загрузочных файлов, придется изменять /etc/tftpd.rules. А при совпадении имен загрузочных файлов в рамках одного дистрибутива, файлы одного профиля могут перезаписаться файлами другого профиля.</li></ol></div></div><dl class="post__tags"><dt class="post__tags-label">Теги:</dt><dd class="post__tags-list"><ul class="inline-list inline-list_fav-tags js-post-tags"><li class="inline-list__item inline-list__item_tag"><a href="/?q=Cobbler">Cobbler</a></li><li class="inline-list__item inline-list__item_tag"><a href="/?q=Linux">Linux</a></li><li class="inline-list__item inline-list__item_tag"><a href="/?q=Windows">Windows</a></li><li class="inline-list__item inline-list__item_tag"><a href="/?q=Install Server">Install Server</a></li></ul></dd></dl></div></div></div></main></body></html></plaintext></administratorpassword></useraccounts></component></settings></unattend></code></pre></div></div></div></div></div></article>
    <div class="other-post c">
        <hr>
        <h4>Also popular now: </h4>
        <ul>
            <li>
                <a href="/post/207779-The-Art-Of-Programming-Issue-1">The Art Of Programming - Issue # 1</a>
            </li>
            <li>
                <a href="/post/207780-The-Art-Of-Programming-Issue-1">The Art Of Programming - Issue # 1</a>
            </li>
            <li>
                <a href="/post/207781-SocialTrend-07">SocialTrend # 07</a>
            </li>
            <li>
                <a href="/post/207782-Habracast-18">Habracast # 18</a>
            </li>
            <li>
                <a href="/post/207783-PodStuff-40">PodStuff # 40</a>
            </li>
            <li>
                <a href="/post/207784-SocialTrend-06">SocialTrend # 06</a>
            </li>
            <li>
                <a href="/post/207785-W3Cast-2">W3Cast # 2</a>
            </li>
            <li>
                <a href="/post/207787-Umputun-Weekly-Podcast-US-Chicago-181">Umputun Weekly Podcast (US, Chicago) # 181</a>
            </li>
            <li>
                <a href="/post/207788-IT-Thoughts-Issue-22">IT Thoughts, Issue 22</a>
            </li>
            <li>
                <a href="/post/207789-Radio-T-91">Radio T # 91</a>
            </li>
        </ul>
        <hr>
    </div>
    </main>
    <footer>
        <div class="c">
            <div class="copyright">Copyright &copy; Sudo Null company 2019</div>
            <a class="email-link" href="mailto:sudonull@yahoo.com">sudonull@yahoo.com</a>
        </div>
    </footer>
        <script async src="https://www.googletagmanager.com/gtag/js?id=UA-107715614-9"></script>
        <script>
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}
            gtag('js', new Date());gtag('config', 'UA-107715614-9');
        </script>
        <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
        <script>
            (adsbygoogle = window.adsbygoogle || [])
                .push({ google_ad_client: 'ca-pub-2887131369606284', enable_page_level_ads: true });
        </script>
    </body>
    </html>