├── .docs-bot.yml ├── .gitignore ├── .gitlab-ci.yml ├── .gitmodules ├── .travis.yml ├── LICENSE-GPL3.txt ├── README.rst ├── doc ├── .gitignore ├── Makefile ├── README.rst ├── architecture.rst ├── autounattend.rst ├── bugs.rst ├── compatibility.rst ├── conf.py ├── goldimage.rst ├── index.rst ├── ipxe-image.rst ├── libusbredir-patching.rst ├── license.rst ├── misc.rst ├── quality_control.rst ├── screenshots │ └── spice-disconnect-actions.png ├── security.rst ├── sftp-floppy-upload.rst ├── spice-disconnect-actions.rst ├── start-and-stop-management.rst ├── stateless_and_snapshot_features.rst ├── switching-virtual-rooms.rst ├── tc-vm-mapping.rst ├── thinclients.rst ├── virtesk-infrastructure-server.rst ├── virtesk-tc-connectspice.rst ├── virtesk-tc-kickstart.rst ├── virtesk-tc-tools.rst ├── virtesk-vm-rollout-config.rst ├── virtesk-vm-rollout-install.rst ├── virtesk-vm-rollout.rst ├── virtual_rooms.rst └── visio │ ├── Makefile │ ├── virtesk-archidecture-overview-small.png │ ├── virtesk-archidecture-overview.pdf │ ├── virtesk-archidecture-overview.png │ ├── virtesk-archidecture-overview.vsdx │ ├── virtesk-sysadmin-features-small.png │ ├── virtesk-sysadmin-features.pdf │ ├── virtesk-sysadmin-features.png │ ├── virtesk-sysadmin-features.vsdx │ ├── virtesk-vdi-connection-setup-small.png │ ├── virtesk-vdi-connection-setup.pdf │ ├── virtesk-vdi-connection-setup.png │ ├── virtesk-vdi-connection-setup.vsdx │ ├── virtesk-vdi-tc-rollout-small.png │ ├── virtesk-vdi-tc-rollout.pdf │ ├── virtesk-vdi-tc-rollout.png │ ├── virtesk-vdi-tc-rollout.vsdx │ ├── virtesk-virtual-rooms-small.png │ ├── virtesk-virtual-rooms.pdf │ ├── virtesk-virtual-rooms.png │ └── virtesk-virtual-rooms.vsdx ├── misc └── vdsm-spice-disconnect-actions-backport │ ├── README.txt │ ├── vdsm-spice-disconnect-actions-backport.patch │ └── vdsm.spec ├── requirements.txt ├── sample_config ├── Autounattend-production.xml.template ├── database-layout-update-001.sql ├── database-layout.sql ├── generatesql-statements.sh ├── tc_rollout.ks ├── virtesk-tc-tools.conf └── virtesk-vm-rollout.conf ├── virtesk-tc-connectspice ├── Makefile ├── conftest.py ├── connect_spice_client.py ├── find_thinclient_identifier.py ├── find_thinclient_identifier_nmcli.py ├── spice_xpi_controller.py ├── test_find_thinclient_identifier_nmcli.py ├── test_mock_data │ ├── active_connection │ ├── hostnamectl_transient │ ├── hostnamectl_transient_empty │ ├── nmcli_con_show_adsy_eap │ ├── nmcli_con_show_adsy_eap_empty │ ├── nmcli_device_show │ └── nmcli_device_show_single_line ├── virtesk-tc-connectspice-main └── virtesk-tc-connectspice-shutdown-vm ├── virtesk-tc-tools ├── README.md ├── tc_rollout_kexec ├── tc_screenshot ├── tc_ssh └── utils.sh └── virtesk-vm-rollout ├── Makefile ├── constants.py ├── rhev.py ├── rhev_manager.py ├── utils.py ├── virtesk-virtroom-delete ├── virtesk-virtroom-reset ├── virtesk-virtroom-rollout ├── virtesk-virtroom-show ├── virtesk-virtroom-shutdown └── virtesk-virtroom-start /.docs-bot.yml: -------------------------------------------------------------------------------- 1 | docs: 2 | extract_to: /var/www/html/public/virtesk 3 | download_delay: 20 4 | stages: 5 | - docs 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | site/ 4 | .python-version 5 | .cache/ 6 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | 2 | before_script: 3 | - source /etc/profile 4 | - git submodule sync 5 | - git submodule update --init --recursive 6 | - pyenv local 2.7.11 7 | - pip install sphinx_rtd_theme 8 | 9 | stages: 10 | - docs 11 | 12 | make-docs: 13 | stage: docs 14 | script: 15 | - rm -rf html 16 | - rm -rf doc/_build 17 | - make html_adsy -C doc 18 | - cp -r doc/_build/html html 19 | artifacts: 20 | paths: 21 | - html/* 22 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "doc/adsy-sphinx-template.src"] 2 | path = doc/adsy-sphinx-template.src 3 | url = git@git.adfinis-sygroup.ch:ad-sy/adsy-sphinx-template.src 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | # Python Version 3 | python: 4 | - "2.7" 5 | # Ignore submodules 6 | git: 7 | submodules: false 8 | # Install requirements 9 | install: "pip install -r requirements.txt" 10 | # Run pytests 11 | script: pytest 12 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. |br| raw:: html 2 | 3 |
4 | 5 | 6 | Virtesk-VDI 7 | ============ 8 | 9 | .. image:: https://api.travis-ci.org/adfinis-sygroup/virtesk.svg?branch=master 10 | 11 | Virtesk-VDI is an Open Source VDI solution. It allows to run virtual desktops 12 | in a RHEV/Ovirt environment seamlessly. The virtual desktops are displayed on thin clients 13 | in physical rooms. You can manage both the virtual desktops and the physical thin clients 14 | efficiently using the well-aligned tool collection. 15 | 16 | 17 | It is well-suited to virtualize workplaces in educational environments. 18 | 19 | The technical building blocks are: 20 | 21 | * Red Hat Enterprise Virtualization (RHEV) / Ovirt Virtualization 22 | * Spice VDI protocol 23 | * RHEL / CentOS for infrastructure services 24 | * Fedora Linux for thin clients 25 | * Active Directory (or Samba4) for Windows domain services 26 | * Windows VDI desktops 27 | 28 | 29 | Documentation is available `here `__. 30 | 31 | Features 32 | --------- 33 | 34 | Thin client user experience 35 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 36 | 37 | Thin clients are very easy to use: 38 | 39 | 1. Turn thin client on 40 | 2. Login directly on virtual Windows desktop 41 | 3. Work 42 | 4. Turn thin client off 43 | 44 | Features: 45 | 46 | * Virtual Windows desktop - feels like a native Windows desktop 47 | * USB redirect 48 | * Audio: headphones, loudspeakers, microphones 49 | * One single login - no need to enter credentials twice 50 | * Comfortable thin client devices - small and silent 51 | 52 | Thin client administration 53 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 54 | 55 | Virtesk-VDI features a fully automated network rollout of thin clients. 56 | 57 | The following remote administration features for thin clients are available: 58 | 59 | * Remote control / remote scripting (Tool tc_ssh) 60 | * Screenshots (Tool tc_screenshot) 61 | * Unattended Upgrades / Re-Installations (Tool tc_rollout_kexec) 62 | 63 | Virtual Rooms 64 | ~~~~~~~~~~~~~~ 65 | Virtesk-VDI features virtual Windows desktops organized in virtual rooms. 66 | 67 | Virtual rooms are useful for educational institutions - physical rooms are mapped to virtual rooms. This is useful when combined with 3rd party classroom management and monitoring software like iTalc, UCS\@School, MasterEye, ... 68 | 69 | Instant switching of virtual rooms is possible. For example, one set of VMs can be used for normal teaching, and a dedicated set of secure VMs can be assigned for exams. 70 | 71 | The 1:1-mapping from thin clients to desktop VMs is controlled through a postgres database. 72 | 73 | 74 | Application and desktop maintenance 75 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 76 | A master VM (the "gold image") is used for application installation and desktop configuration. This master VM can then be cloned as often as necessary. 77 | 78 | A set of tools (virtesk-virtroom-rollout and friends) helps to simplify and automate the necessary tasks. Scripting and automation features like automatic Windows domain join are available. 79 | 80 | 81 | Nightly desktop reset 82 | ~~~~~~~~~~~~~~~~~~~~~ 83 | 84 | For situations where clearly-defined centrally managed workplaces are desired, the nightly desktop reset feature comes in handy: 85 | 86 | * A snapshot is created upon VM creation 87 | * Every night, the VMs is set back to snapshot state 88 | 89 | This is useful to reduce time and effort spent by your IT support team: Desktops are always in a well defined state, divergence of desktops is avoided, and leftovers from old user sessions are cleaned up. 90 | 91 | 92 | Requirements 93 | -------------- 94 | 95 | * Virtualization hardware (~ 4GB Ram per workplace), shared storage attached through iscsi or FibreChannel 96 | * RHEV/oVirt 3.5.x 97 | * Active Directory (or Samba 4) for Windows domain features 98 | * A supported OS for virtual Desktops ( stable: Windows 7; Windows 10 support is underway) 99 | * Thin clients: Any linux compatible (x86 or x86_64, must be supported by Fedora Linux) hardware can be used. Usually, small, silent and low power thin client devices are used; However, it is also possible to re-use old desktop computers as thin clients 100 | * Infrastructure server VM (part of Virtesk-VDI) 101 | 102 | Bird's eye view of operation / installation 103 | ------------------------------------------- 104 | 105 | The steps to introduce Virtesk-VDI are more or less: 106 | 107 | * Preparing RHEV/Ovirt for VDI operation 108 | * Thin clients: Seting up Virtesk-VDI infrastructure services, including a Fedora Linux mirror, a network rollout infrastructure, scripts for unattended Fedora installations based on Kickstart, and a postgres database for VM-to-thin-client-mapping. 109 | * Installing virtesk-tc-tools for thin client remote management 110 | * Installing a Windows 7 master VM ("gold image") 111 | * Setting up the Windows unattended setup process for VM creation and for automatic Windows domain join 112 | * Setting up virtesk-virtroom-tools for virtual room management 113 | * Creating a network concept, including naming standards and ip-address conventions 114 | 115 | 116 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.html 3 | /_build/ 4 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help 23 | help: 24 | @echo "Please use \`make ' where is one of" 25 | @echo " html to make standalone HTML files" 26 | @echo " html_adsy to make standalone HTML files using adsy theme" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " applehelp to make an Apple Help Book" 34 | @echo " devhelp to make HTML files and a Devhelp project" 35 | @echo " epub to make an epub" 36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 37 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 39 | @echo " text to make text files" 40 | @echo " man to make manual pages" 41 | @echo " texinfo to make Texinfo files" 42 | @echo " info to make Texinfo files and run them through makeinfo" 43 | @echo " gettext to make PO message catalogs" 44 | @echo " changes to make an overview of all changed/added/deprecated items" 45 | @echo " xml to make Docutils-native XML files" 46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 47 | @echo " linkcheck to check all external links for integrity" 48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 49 | @echo " coverage to run coverage check of the documentation (if enabled)" 50 | 51 | .PHONY: clean 52 | clean: 53 | rm -rf $(BUILDDIR)/* 54 | 55 | .PHONY: html 56 | html: 57 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 58 | @echo 59 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 60 | 61 | .PHONY: html_adsy 62 | html_adsy: 63 | $(SPHINXBUILD) -b html -D html_theme='adsy' $(ALLSPHINXOPTS) $(BUILDDIR)/html 64 | @echo 65 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 66 | 67 | .PHONY: dirhtml 68 | dirhtml: 69 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 70 | @echo 71 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 72 | 73 | .PHONY: singlehtml 74 | singlehtml: 75 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 76 | @echo 77 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 78 | 79 | .PHONY: pickle 80 | pickle: 81 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 82 | @echo 83 | @echo "Build finished; now you can process the pickle files." 84 | 85 | .PHONY: json 86 | json: 87 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 88 | @echo 89 | @echo "Build finished; now you can process the JSON files." 90 | 91 | .PHONY: htmlhelp 92 | htmlhelp: 93 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 94 | @echo 95 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 96 | ".hhp project file in $(BUILDDIR)/htmlhelp." 97 | 98 | .PHONY: qthelp 99 | qthelp: 100 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 101 | @echo 102 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 103 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 104 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/amoothei.qhcp" 105 | @echo "To view the help file:" 106 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/amoothei.qhc" 107 | 108 | .PHONY: applehelp 109 | applehelp: 110 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 111 | @echo 112 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 113 | @echo "N.B. You won't be able to view it unless you put it in" \ 114 | "~/Library/Documentation/Help or install it in your application" \ 115 | "bundle." 116 | 117 | .PHONY: devhelp 118 | devhelp: 119 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 120 | @echo 121 | @echo "Build finished." 122 | @echo "To view the help file:" 123 | @echo "# mkdir -p $$HOME/.local/share/devhelp/amoothei" 124 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/amoothei" 125 | @echo "# devhelp" 126 | 127 | .PHONY: epub 128 | epub: 129 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 130 | @echo 131 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 132 | 133 | .PHONY: latex 134 | latex: 135 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 136 | @echo 137 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 138 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 139 | "(use \`make latexpdf' here to do that automatically)." 140 | 141 | .PHONY: latexpdf 142 | latexpdf: 143 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 144 | sed -i 's/pdflatex/xelatex/g' $(BUILDDIR)/latex/Makefile 145 | sed -i '/^\\DeclareUnicodeCharacter/d' $(BUILDDIR)/latex/*.tex 146 | sed -i '/\\usepackage{hyperref}/d' $(BUILDDIR)/latex/sphinxmanual.cls 147 | sed -i '/\\usepackage\[Bjarne\]{fncychap}/d' $(BUILDDIR)/latex/*.tex 148 | 149 | @echo "Running LaTeX files through pdflatex..." 150 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 151 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 152 | 153 | .PHONY: latexpdfja 154 | latexpdfja: 155 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 156 | @echo "Running LaTeX files through platex and dvipdfmx..." 157 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 158 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 159 | 160 | .PHONY: text 161 | text: 162 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 163 | @echo 164 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 165 | 166 | .PHONY: man 167 | man: 168 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 169 | @echo 170 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 171 | 172 | .PHONY: texinfo 173 | texinfo: 174 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 175 | @echo 176 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 177 | @echo "Run \`make' in that directory to run these through makeinfo" \ 178 | "(use \`make info' here to do that automatically)." 179 | 180 | .PHONY: info 181 | info: 182 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 183 | @echo "Running Texinfo files through makeinfo..." 184 | make -C $(BUILDDIR)/texinfo info 185 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 186 | 187 | .PHONY: gettext 188 | gettext: 189 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 190 | @echo 191 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 192 | 193 | .PHONY: changes 194 | changes: 195 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 196 | @echo 197 | @echo "The overview file is in $(BUILDDIR)/changes." 198 | 199 | .PHONY: linkcheck 200 | linkcheck: 201 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 202 | @echo 203 | @echo "Link check complete; look for any errors in the above output " \ 204 | "or in $(BUILDDIR)/linkcheck/output.txt." 205 | 206 | .PHONY: doctest 207 | doctest: 208 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 209 | @echo "Testing of doctests in the sources finished, look at the " \ 210 | "results in $(BUILDDIR)/doctest/output.txt." 211 | 212 | .PHONY: coverage 213 | coverage: 214 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 215 | @echo "Testing of coverage in the sources finished, look at the " \ 216 | "results in $(BUILDDIR)/coverage/python.txt." 217 | 218 | .PHONY: xml 219 | xml: 220 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 221 | @echo 222 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 223 | 224 | .PHONY: pseudoxml 225 | pseudoxml: 226 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 227 | @echo 228 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 229 | -------------------------------------------------------------------------------- /doc/README.rst: -------------------------------------------------------------------------------- 1 | ../README.rst -------------------------------------------------------------------------------- /doc/architecture.rst: -------------------------------------------------------------------------------- 1 | Architecture 2 | ============= 3 | 4 | Virtesk-VDI is a `Open Source `__ VDI solution based on 5 | Red Hat Enterprise Virtualization (or Ovirt Virtualization) and 6 | the Spice VDI protocol. 7 | 8 | Overview 9 | ---------- 10 | 11 | Seemless user experience: Ready for work in just two steps 12 | 13 | 14 | .. image:: visio/virtesk-archidecture-overview-small.* 15 | 16 | :download:`Print version (PDF)`. 17 | 18 | 19 | Features for system administrators 20 | ---------------------------------- 21 | 22 | Features that make the live of a system administrator easier: 23 | 24 | 25 | .. image:: visio/virtesk-sysadmin-features-small.* 26 | 27 | :download:`Print version (PDF)`. 28 | 29 | 30 | VDI concept: virtual rooms 31 | -------------------------- 32 | 33 | Virtesk-VDI uses an 1:1-Mapping between virtual and physical rooms. 34 | This is well-suited for schools and other environments where existing 35 | software depends on the concept of a "classroom". 36 | 37 | 38 | .. image:: visio/virtesk-virtual-rooms-small.* 39 | 40 | :download:`Print version (PDF)`. 41 | 42 | Thinclient: Connection setup 43 | ---------------------------- 44 | 45 | How thinclients connect to their assiged virtual machine: 46 | 47 | 48 | .. image:: visio/virtesk-vdi-connection-setup-small.* 49 | 50 | :download:`Print version (PDF)`. 51 | 52 | 53 | Thinclient: Kickstart rollout 54 | ----------------------------- 55 | 56 | Automatic thinclient rollout using fully automated and unattended 57 | kickstart installation of fedora 22: 58 | 59 | 60 | .. image:: visio/virtesk-vdi-tc-rollout-small.* 61 | 62 | :download:`Print version (PDF)`. 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /doc/autounattend.rst: -------------------------------------------------------------------------------- 1 | Windows Unattended Setup 2 | ====================================== 3 | 4 | Introduction 5 | ------------ 6 | 7 | Virtesk-virtroom-rollout creates several hunderds VMs by cloneing a 8 | single `Goldimage VM `__. 9 | 10 | However, simply cloneing a windows VM usually isn't enough, it needs to 11 | be generalized/sysprepped and then it must run though the Windows 12 | Unattended Setup phase for setting ComputerName, ip-address and for 13 | joining into the Active Directory Domain. 14 | 15 | This article explains the XML-Configuration-File used to configure and 16 | automatize the Windows Unattended Setup phase. 17 | 18 | Please note: Windows Unattended Setup is not an easy topic. This article 19 | covers only the aspects important for virtesk-vdi. 20 | 21 | Mako Templating 22 | --------------- 23 | 24 | `Python-mako `__ is 25 | used for templating and variable substitution. 26 | 27 | Variables: 28 | 29 | - ``${ip}`` 30 | - ``${netmask_as_suffix}`` 31 | - ``${default_gw}`` 32 | - ``${ComputerName}`` 33 | - ``${scripttime}`` 34 | 35 | In general, all keys available in the ``vmconfig``-python-dict can be 36 | used as mako template variables. They can be displayed using 37 | ``virtesk-virtroom-show``: 38 | 39 | :: 40 | 41 | $ virtesk-virtroom-show test01 42 | [...] 43 | 2016-03-03 15:55:08,231 - rhev_manager - DEBUG - {'ComputerName': 'test01-vd01', 'tc_user': '...', 'description': 'LehrerVM', 'rhev_vm_name': 'test01-vd01', 'ip': '...', 'default_gw': '...', 'cluster': 'Default', 'netmask_as_suffix': '21', 'snapshot_description': 'Automatic snapshot after virtesk-vmrollout, IP=${ip}/${netmask_as_suffix}, scripttime=${scripttime}', 'scripttime': '2016-03-03-1555', 'reset_startvm': 'Always', 'timezone': 'W. Europe Standard Time', 'network_name': '...', 'reset_to_snapshot_regex': <_sre.SRE_Pattern object at 0x201d2f0>, 'workaround_os': 'rhel_7x64', 'autounattend_templatefile': '/etc/virtesk-vdi/Autounattend-production.xml.template', 'usb_enabled': True, 'rollout_startvm': True, 'template': '...', 'memory': 4294967296, 'workaround_timezone': 'Etc/GMT', 'os': 'windows_7x64', 'stateless': False} 44 | [...] 45 | 46 | Sample Config File 47 | ------------------ 48 | 49 | A sample config file is available here: 50 | ``sample_config/Autounattend-production.xml.template``. It was tested 51 | with Windows 7 Professional, German, SP1 52 | (``de_windows_7_professional_with_sp1_x64_dvd_u_676919.iso``). 53 | 54 | If your are using it with a different language (e.g. not German, not 55 | DE-CH, ...), then you'll have to make adjustments. For example, network 56 | adapters have different names in different languages. 57 | 58 | The sample config file **will not "just work"**. You'll have to adapt it 59 | to make it work in your environment. 60 | 61 | Important Config Sections 62 | ------------------------- 63 | 64 | Network 65 | ~~~~~~~ 66 | 67 | Works only for German Windows 7: 68 | 69 | :: 70 | 71 | LAN-Verbindung 72 | 73 | Setting IP and gateway... 74 | 75 | :: 76 | 77 | 78 | 79 | LAN-Verbindung 80 | 81 | false 82 | 10 83 | false 84 | 85 | 86 | ${ip}/${netmask_as_suffix} 87 | 88 | 89 | 90 | 0 91 | ${default_gw} 92 | 0.0.0.0/0 93 | 94 | 95 | 96 | 97 | 98 | **Adjust**: Setting DNS-Servers and DNS-Searchdomain... 99 | 100 | :: 101 | 102 | 103 | 104 | LAN-Verbindung 105 | 106 | 192.0.2.220 107 | 108 | false 109 | true 110 | your-dns-domain.tld 111 | 112 | 113 | 114 | **Adjust**: more DNS stuff... 115 | 116 | :: 117 | 118 | 120 | 121 | 122 | 123 | 192.0.2.220 124 | 125 | LAN-Verbindung 126 | 1 127 | 128 | 129 | 130 | 131 | Setting the computer name and timezone... 132 | 133 | :: 134 | 135 | 136 | 137 | 32 138 | 96 139 | 1920 140 | 75 141 | 1080 142 | 143 | ${ComputerName} 144 | W. Europe Standard Time 145 | 146 | 147 | **Adjust**: Syncing with timeserver (avoids timezone problems during 148 | Windows Domain Join)... 149 | 150 | :: 151 | 152 | 153 | 154 | w32tm /config /manualpeerlist:192.0.2.221 /syncfromflags:MANUAL 155 | 1 156 | 157 | 158 | w32tm /resync 159 | 2 160 | 161 | 162 | w32tm /query /peers 163 | 3 164 | 165 | 166 | 167 | **Adjust**: Windows Domain Join... 168 | 169 | :: 170 | 171 | 172 | 173 | 174 | YOUR-ACTIVEDIRECTORY-DOMAIN 175 | Administrator4Domainjoins 176 | PASSWORD 177 | 178 | YOUR-ACTIVEDIRECTORY-DOMAIN 179 | 180 | 181 | 182 | **Adjust**: Local Administrator Password, Account for 183 | FirstLogonCommands, ... 184 | 185 | :: 186 | 187 | 188 | 189 | PASSWORD 190 | 191 | Administrator 192 | true 193 | 1 194 | 195 | 196 | 197 | PASSWORD 198 | true</PlainText> 199 | </AdministratorPassword> 200 | </UserAccounts> 201 | 202 | **Adjust**: Run some custization commands as a last step in the Windows 203 | Unattended Setup... 204 | 205 | - see also: `Quality Control: Windows Unattended 206 | Setup <quality_control.html>`__. 207 | 208 | :: 209 | 210 | <FirstLogonCommands> 211 | <SynchronousCommand wcm:action="add"> 212 | <CommandLine>net use Y: \\someserver\scriptshare /persistent:no /user:username@windowsdomain password</CommandLine> 213 | <RequiresUserInput>false</RequiresUserInput> 214 | <Order>20</Order> 215 | </SynchronousCommand> 216 | <SynchronousCommand wcm:action="add"> 217 | <CommandLine>cmd /c Y:\autounattend-firstlogon.cmd ${scripttime} ${ComputerName} 1> C:\autounattend-firstlogon.log 2>&1 </CommandLine> 218 | <RequiresUserInput>false</RequiresUserInput> 219 | <Order>21</Order> 220 | </SynchronousCommand> 221 | 222 | <!-- Do not delete next item --> 223 | <SynchronousCommand wcm:action="add"> 224 | <CommandLine>shutdown /p</CommandLine> 225 | <RequiresUserInput>false</RequiresUserInput> 226 | <Order>50</Order> 227 | </SynchronousCommand> 228 | </FirstLogonCommands> 229 | 230 | IMPORTANT: Last step: VM shutdown 231 | --------------------------------- 232 | 233 | It is very important, that after all Windows Unattened Setup tasks run 234 | trough, the VM will shut down. If VMs do not shutdown, then 235 | ``virtesk-virtroom-rollout`` will wait forever. 236 | 237 | :: 238 | 239 | <SynchronousCommand wcm:action="add"> 240 | <CommandLine>shutdown /p</CommandLine> 241 | <RequiresUserInput>false</RequiresUserInput> 242 | <Order>50</Order> 243 | </SynchronousCommand> 244 | -------------------------------------------------------------------------------- /doc/bugs.rst: -------------------------------------------------------------------------------- 1 | .. |br| raw:: html 2 | 3 | <br /> 4 | 5 | Known bugs 6 | ======================== 7 | 8 | 9 | Harmless bugs 10 | ------------- 11 | 12 | VMSnapshotCdroms object comparison caused infinite loop [uncritical] 13 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 14 | 15 | Error messages while rolling out a virtual room: 16 | 17 | :: 18 | 19 | 2016-01-20 19:27:14,186 - root - DEBUG - Creating a snapshot(description: Automatic snapshot after virtesk-vmrollout, IP=XXXXXXXXXXXX, scripttime=2016-01-20-1853) of vm test03-vd01... done 20 | 2016-01-20 19:27:15,141 - root - DEBUG - Creating a snapshot(description: Automatic snapshot after virtesk-vmrollout, IP=XXXXXXXXXXXX, scripttime=2016-01-20-1853) of vm test03-vd03... done 21 | VMSnapshotCdroms object comparison caused infinite loop. 22 | 2016-01-20 19:27:16,034 - root - DEBUG - Creating a snapshot(description: Automatic snapshot after virtesk-vmrollout, IP=XXXXXXXXXXXX, scripttime=2016-01-20-1853) of vm test03-vd05... done 23 | VMSnapshotCdroms object comparison caused infinite loop. 24 | VMSnapshotCdroms object comparison caused infinite loop. 25 | 2016-01-20 19:27:17,363 - root - DEBUG - Creating a snapshot(description: Automatic snapshot after virtesk-vmrollout, IP=XXXXXXXXXXXX, scripttime=2016-01-20-1853) of vm test03-vd07... done 26 | VMSnapshotCdroms object comparison caused infinite loop. 27 | VMSnapshotCdroms object comparison caused infinite loop. 28 | VMSnapshotCdroms object comparison caused infinite loop. 29 | 2016-01-20 19:27:18,254 - root - DEBUG - Creating a snapshot(description: Automatic snapshot after virtesk-vmrollout, IP=XXXXXXXXXXXX, scripttime=2016-01-20-1853) of vm test03-vd09... done 30 | VMSnapshotCdroms object comparison caused infinite loop. 31 | VMSnapshotCdroms object comparison caused infinite loop. 32 | VMSnapshotCdroms object comparison caused infinite loop. 33 | VMSnapshotCdroms object comparison caused infinite loop. 34 | 2016-01-20 19:27:19,231 - root - DEBUG - Creating a snapshot(description: Automatic snapshot after virtesk-vmrollout, IP=XXXXXXXXXXXX, scripttime=2016-01-20-1853) of vm test03-vd11... done 35 | VMSnapshotCdroms object comparison caused infinite loop. 36 | VMSnapshotCdroms object comparison caused infinite loop. 37 | VMSnapshotCdroms object comparison caused infinite loop. 38 | VMSnapshotCdroms object comparison caused infinite loop. 39 | VMSnapshotCdroms object comparison caused infinite loop. 40 | 2016-01-20 19:27:22,519 - root - DEBUG - Creating a snapshot(description: Automatic snapshot after virtesk-vmrollout, IP=XXXXXXXXXXXX, scripttime=2016-01-20-1853) of vm test03-vd15... done 41 | 42 | Cause: unknown. Maybe a python phaenomen related to recursive object 43 | structures, like described in 44 | https://mail.python.org/pipermail/python-dev/2003-October/039445.html? 45 | 46 | Impact: **none**, the VMs and the snapshots are created successfully. 47 | -------------------------------------------------------------------------------- /doc/compatibility.rst: -------------------------------------------------------------------------------- 1 | Compatibility 2 | ============= 3 | 4 | Current versions: 5 | ----------------- 6 | 7 | The current versions of virtesk-vdi are tested in the following 8 | environment: 9 | 10 | - Virtualisation: Ovirt/RHEV 3.5.x 11 | - VMs: Windows 7 Professional 12 | - Thinclients: Fedora 22 x86\_64 13 | - VDI protocol: Spice 14 | - Virtualization storage: Block-Based, e.g. FibreChannel or iscsi 15 | - Virtualization hosts: based on RHEL 6.x / CentOS 6.x. A full Linux 16 | installation is needed, the RHEV-H image is not supported. 17 | 18 | Earlier versions: 19 | ----------------- 20 | 21 | Earlier versions successfully run in the following environment: 22 | 23 | - RHEV 3.1 / 3.2 / 3.4 24 | - VMs: Windows 7 Professional 25 | - Thinclients: Fedora 20 x86 and x86\_64 26 | - VDI Protocol: Spice 27 | - Virtualization storage: block-based, e.g. FibreChannel or iscsi 28 | - Virtualization hosts: based on RHEL 6.x / CentOS 6.x. 29 | -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Virtesk-VDI documentation build configuration file, created by 4 | # sphinx-quickstart2 on Thu Mar 31 12:24:51 2016. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | import sphinx_rtd_theme 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | #sys.path.insert(0, os.path.abspath('.')) 22 | 23 | # -- General configuration ------------------------------------------------ 24 | 25 | # If your documentation needs a minimal Sphinx version, state it here. 26 | #needs_sphinx = '1.0' 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [] 32 | 33 | # Add any paths that contain templates here, relative to this directory. 34 | templates_path = ['_templates'] 35 | 36 | # The suffix(es) of source filenames. 37 | # You can specify multiple suffix as a list of string: 38 | # source_suffix = ['.rst', '.md'] 39 | source_suffix = '.rst' 40 | 41 | # The encoding of source files. 42 | #source_encoding = 'utf-8-sig' 43 | 44 | # The master toctree document. 45 | master_doc = 'index' 46 | 47 | # General information about the project. 48 | project = u'Virtesk-VDI' 49 | copyright = u'2016, Adfinis SyGroup' 50 | author = u'Simon Neininger, Ueli Isch' 51 | 52 | # The version info for the project you're documenting, acts as replacement for 53 | # |version| and |release|, also used in various other places throughout the 54 | # built documents. 55 | # 56 | # The short X.Y version. 57 | version = u'1.0' 58 | # The full version, including alpha/beta/rc tags. 59 | release = u'1.0' 60 | 61 | # The language for content autogenerated by Sphinx. Refer to documentation 62 | # for a list of supported languages. 63 | # 64 | # This is also used if you do content translation via gettext catalogs. 65 | # Usually you set "language" from the command line for these cases. 66 | language = 'en' 67 | 68 | # There are two options for replacing |today|: either, you set today to some 69 | # non-false value, then it is used: 70 | #today = '' 71 | # Else, today_fmt is used as the format for a strftime call. 72 | #today_fmt = '%B %d, %Y' 73 | 74 | # List of patterns, relative to source directory, that match files and 75 | # directories to ignore when looking for source files. 76 | exclude_patterns = ['_build'] 77 | 78 | # The reST default role (used for this markup: `text`) to use for all 79 | # documents. 80 | #default_role = None 81 | 82 | # If true, '()' will be appended to :func: etc. cross-reference text. 83 | #add_function_parentheses = True 84 | 85 | # If true, the current module name will be prepended to all description 86 | # unit titles (such as .. function::). 87 | #add_module_names = True 88 | 89 | # If true, sectionauthor and moduleauthor directives will be shown in the 90 | # output. They are ignored by default. 91 | #show_authors = False 92 | 93 | # The name of the Pygments (syntax highlighting) style to use. 94 | pygments_style = 'sphinx' 95 | 96 | # A list of ignored prefixes for module index sorting. 97 | #modindex_common_prefix = [] 98 | 99 | # If true, keep warnings as "system message" paragraphs in the built documents. 100 | #keep_warnings = False 101 | 102 | # If true, `todo` and `todoList` produce output, else they produce nothing. 103 | todo_include_todos = True 104 | 105 | 106 | # -- Options for HTML output ---------------------------------------------- 107 | 108 | # The theme to use for HTML and HTML Help pages. See the documentation for 109 | # a list of builtin themes. 110 | 111 | # The Theme MUST BE AN OPEN SOURCE SPHINX THEME. Contributors outside of 112 | # Adfinis SyGroup AG must be able to build the documentation. 113 | # Developers of Adfinis SyGroup AG can build the documentation using 114 | # make html_adsy 115 | # .. or .. 116 | # make SPHINXOPTS="-D html_theme='adsy'" html 117 | html_theme = 'adsy' 118 | 119 | # Theme options are theme-specific and customize the look and feel of a theme 120 | # further. For a list of options available for each theme, see the 121 | # documentation. 122 | #html_theme_options = {} 123 | 124 | # Add any paths that contain custom themes here, relative to this directory. 125 | html_theme_path = [ 'adsy-sphinx-template.src/html', sphinx_rtd_theme.get_html_theme_path()] 126 | # The name for this set of Sphinx documents. If None, it defaults to 127 | # "<project> v<release> documentation". 128 | #html_title = None 129 | 130 | # A shorter title for the navigation bar. Default is the same as html_title. 131 | #html_short_title = None 132 | 133 | # The name of an image file (relative to this directory) to place at the top 134 | # of the sidebar. 135 | #html_logo = None 136 | 137 | # The name of an image file (relative to this directory) to use as a favicon of 138 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 139 | # pixels large. 140 | #html_favicon = None 141 | 142 | # Add any paths that contain custom static files (such as style sheets) here, 143 | # relative to this directory. They are copied after the builtin static files, 144 | # so a file named "default.css" will overwrite the builtin "default.css". 145 | html_static_path = ['_static'] 146 | 147 | # Add any extra paths that contain custom files (such as robots.txt or 148 | # .htaccess) here, relative to this directory. These files are copied 149 | # directly to the root of the documentation. 150 | #html_extra_path = [] 151 | 152 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 153 | # using the given strftime format. 154 | #html_last_updated_fmt = '%b %d, %Y' 155 | 156 | # If true, SmartyPants will be used to convert quotes and dashes to 157 | # typographically correct entities. 158 | #html_use_smartypants = True 159 | 160 | # Custom sidebar templates, maps document names to template names. 161 | #html_sidebars = {} 162 | 163 | # Additional templates that should be rendered to pages, maps page names to 164 | # template names. 165 | #html_additional_pages = {} 166 | 167 | # If false, no module index is generated. 168 | #html_domain_indices = True 169 | 170 | # If false, no index is generated. 171 | #html_use_index = True 172 | 173 | # If true, the index is split into individual pages for each letter. 174 | #html_split_index = False 175 | 176 | # If true, links to the reST sources are added to the pages. 177 | #html_show_sourcelink = True 178 | 179 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 180 | #html_show_sphinx = True 181 | 182 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 183 | #html_show_copyright = True 184 | 185 | # If true, an OpenSearch description file will be output, and all pages will 186 | # contain a <link> tag referring to it. The value of this option must be the 187 | # base URL from which the finished HTML is served. 188 | #html_use_opensearch = '' 189 | 190 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 191 | #html_file_suffix = None 192 | 193 | # Language to be used for generating the HTML full-text search index. 194 | # Sphinx supports the following languages: 195 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 196 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' 197 | #html_search_language = 'en' 198 | 199 | # A dictionary with options for the search language support, empty by default. 200 | # Now only 'ja' uses this config value 201 | #html_search_options = {'type': 'default'} 202 | 203 | # The name of a javascript file (relative to the configuration directory) that 204 | # implements a search results scorer. If empty, the default will be used. 205 | #html_search_scorer = 'scorer.js' 206 | 207 | # Output file base name for HTML help builder. 208 | htmlhelp_basename = 'virtesk-vdi-doc' 209 | 210 | # -- Options for LaTeX output --------------------------------------------- 211 | 212 | latex_additional_files = [ 213 | 'adsy-sphinx-template.src/latex/logo.png', 214 | 'adsy-sphinx-template.src/latex/sphinx.sty', 215 | 'adsy-sphinx-template.src/latex/adsy.sty' 216 | ] 217 | 218 | 219 | latex_elements = { 220 | # The paper size ('letterpaper' or 'a4paper'). 221 | 'papersize': 'a4paper', 222 | 223 | # The font size ('10pt', '11pt' or '12pt'). 224 | 'pointsize': '10pt', 225 | 226 | # Additional stuff for the LaTeX preamble. 227 | 'preamble' : r""" 228 | 229 | \usepackage{adsy} 230 | 231 | 232 | \renewcommand{\subtitle}{%s} 233 | 234 | """ % (project) 235 | 236 | } 237 | 238 | # Grouping the document tree into LaTeX files. List of tuples 239 | # (source start file, target name, title, 240 | # author, documentclass [howto, manual, or own class]). 241 | latex_documents = [ 242 | (master_doc, 'virtesk-vdi.tex', u'Virtesk-VDI Documentation', 243 | u'Simon Neininger, Ueli Isch', 'manual'), 244 | ] 245 | 246 | # The name of an image file (relative to this directory) to place at the top of 247 | # the title page. 248 | #latex_logo = None 249 | 250 | # For "manual" documents, if this is true, then toplevel headings are parts, 251 | # not chapters. 252 | #latex_use_parts = False 253 | 254 | # If true, show page references after internal links. 255 | #latex_show_pagerefs = False 256 | 257 | # If true, show URL addresses after external links. 258 | #latex_show_urls = False 259 | 260 | # Documents to append as an appendix to all manuals. 261 | #latex_appendices = [] 262 | 263 | # If false, no module index is generated. 264 | #latex_domain_indices = True 265 | 266 | 267 | # -- Options for manual page output --------------------------------------- 268 | 269 | # One entry per manual page. List of tuples 270 | # (source start file, name, description, authors, manual section). 271 | man_pages = [ 272 | (master_doc, 'Virtesk-VDI', u'Virtesk-VDI Documentation', 273 | [author], 1) 274 | ] 275 | 276 | # If true, show URL addresses after external links. 277 | #man_show_urls = False 278 | 279 | 280 | # -- Options for Texinfo output ------------------------------------------- 281 | 282 | # Grouping the document tree into Texinfo files. List of tuples 283 | # (source start file, target name, title, author, 284 | # dir menu entry, description, category) 285 | texinfo_documents = [ 286 | (master_doc, 'virtesk-vdi', u'Virtesk-VDI Documentation', 287 | author, 'virtesk-vdi', 'Virtesk-VDI is a Open Source VDI solution based on Red Hat Enterprise Virtualization (or Ovirt Virtualization) and the Spice VDI protocol.', 288 | 'Miscellaneous'), 289 | ] 290 | 291 | # Documents to append as an appendix to all manuals. 292 | #texinfo_appendices = [] 293 | 294 | # If false, no module index is generated. 295 | #texinfo_domain_indices = True 296 | 297 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 298 | #texinfo_show_urls = 'footnote' 299 | 300 | # If true, do not generate a @detailmenu in the "Top" node's menu. 301 | #texinfo_no_detailmenu = False 302 | -------------------------------------------------------------------------------- /doc/goldimage.rst: -------------------------------------------------------------------------------- 1 | Windows Goldimage 2 | =============================== 3 | 4 | How to create and seal a Windows Goldimage and use it as a RHEV/Ovirt 5 | VmTemplate. 6 | 7 | -------------- 8 | 9 | Introduction 10 | ------------ 11 | 12 | The goal is to use one master VM (the "Goldimage") as a blueprint for 13 | creating/cloneing alot of VMs. 14 | 15 | If a Windows 7 VM is simply cloned directly, all identifiers inside 16 | remain the same. This includes the Active Directory SID and GUID. Those 17 | identifiers should be unique - but cloneing violates the uniqueness 18 | property. Therefore, direct cloneing will cause problems in Active 19 | Directory environments. This can be avoided by using the 20 | Sysprep/Autounattend process described below. 21 | 22 | Prerequisites 23 | ------------- 24 | 25 | This article covers Goldimages based on Windows 7 x64 Professional in an 26 | Active Directory (or Samba4) environment. The Professional Edition of 27 | Windows 7 is nessesary for usage in Active Directory (or Samba4) 28 | domains. 29 | 30 | Initial Goldimage 31 | ----------------- 32 | 33 | Steps: 34 | 35 | - Create VM + Install Win7 x64 Professional 36 | 37 | - Using a thin-provisioned disk in RHEV/Ovirt is recommended 38 | 39 | - Install virtio + spice drivers, install guest agent 40 | - Install all Windows Updates 41 | - Configure as you like, install Programs 42 | 43 | Common Settings: 44 | ~~~~~~~~~~~~~~~~ 45 | 46 | Registry Keys: 47 | 48 | :: 49 | 50 | rem Tell Windows where to find its Unattended Setup Config file. Common values include a:\sysprep.inf, a:\Autounattend.xml, a:\Unattend.xml. For virtesk-vdi, a:\sysprep.inf should be used. 51 | rem See also: 52 | rem * /etc/ovirt-engine/osinfo.conf.d/ on RHEV/Ovirt Manager 53 | rem os.windows_7.sysprepFileName.value = sysprep.inf 54 | rem os.windows_7x64.sysprepFileName.value = sysprep.inf 55 | reg add "HKEY_LOCAL_MACHINE\SYSTEM\SETUP" /v UnattendFile /t REG_SZ /d a:\sysprep.inf /f 56 | 57 | rem Disable TCP Task-Offloading. 58 | rem Useful to avoid problems with incorrect udp checksums regarding virtio-net + dhcp 59 | reg add "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\TCPIP\Parameters" /v DisableTaskOffload /t REG_DWORD /d 1 /f 60 | 61 | Ovirt OS info settings 62 | ---------------------- 63 | 64 | Virtesk-VDI puts the Unattend Setup Config file into the file 65 | ``a:\sysprep.inf`` on a `virtual floppy disk <sftp-floppy-upload.html>`__. 66 | To make sure windows finds the floppy there, setting the windows 67 | registry key as described above is sufficient. However, if you wanna use 68 | the same Goldimage VmTemplate for other purposes (Ovirt UserPortal / 69 | PowerUserPortal, manual creation of VMs, manual creation of Ovirt 70 | VmPools), then the following settings on your RHEV / Ovirt Manager are 71 | needed to make sure that RHEV / Ovirt will put their version of the 72 | Unattended Setup Config file into the right location: 73 | 74 | :: 75 | 76 | mkdir -p /etc/ovirt-engine/osinfo.conf.d/ 77 | mkdir -p /etc/ovirt-engine/sysprep/ 78 | cp /usr/share/ovirt-engine/conf/sysprep/sysprep.w7x64 /etc/ovirt-engine/sysprep/sysprep.w7x64 79 | vim /etc/ovirt-engine/sysprep/sysprep.w7x64 # adjust settings 80 | 81 | /etc/ovirt-engine/osinfo.conf.d/10-sysprep.properties 82 | 83 | :: 84 | 85 | os.windows_7x64.sysprepPath.value = /etc/ovirt-engine/sysprep/sysprep.w7x64 86 | 87 | ## Workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1192495 88 | os.windows_7x64.sysprepFileName.value = sysprep.inf 89 | 90 | Restart ovirt-engine: 91 | 92 | :: 93 | 94 | service ovirt-engine restart 95 | 96 | Sysprep 97 | ------- 98 | 99 | The following step will generalize your Goldimage VM. You will 100 | "partially loose" your Goldimage VM, so might wanna create a VM 101 | snapshot beforehand. 102 | Sysprep can be run as local Administrator or as a Domain 103 | Administrator. Please disconnect all network shares - they might cause 104 | problems when generalizing. 105 | 106 | Programm: ``C:\Windows\system32\sysprep\sysprep.exe`` 107 | 108 | Please choose the following options: 109 | 110 | English: 111 | 112 | :: 113 | 114 | System Cleanup Action: 115 | Enter Out-of-Box-Experience (OOBE) 116 | [X] Generalize 117 | Shutdown Options: 118 | Shutdown 119 | 120 | German: 121 | 122 | :: 123 | 124 | Systembereinigungsaktion: 125 | Out-of-Box-Experience (OOBE) für System aktivieren 126 | [X] Veralgemeinern 127 | Optionen für Herunterfahren 128 | Herunterfahren 129 | 130 | Afterwards, the VM will be generalized and then turned off. Then the VM 131 | is ready to create a RHEV/Ovirt VmTemplate. 132 | 133 | RHEV/Ovirt VmTemplate 134 | --------------------- 135 | 136 | In RHEV/Ovirt WebAdmin, right-click on the VM and create a new 137 | VmTemplate. Then, adjust 138 | `virtesk-vm-rollout.conf <virtesk-vm-rollout-config.html#room-definitions-section-room-room01>`__ 139 | to use the new template: ``template_name = myNewVmTemplate``. 140 | 141 | Rolling out virtual rooms 142 | ------------------------- 143 | 144 | Now you can use the `tools for virtual rooms <virtesk-vm-rollout.html>`__ 145 | to create VMs based on this Goldimage. 146 | 147 | ``virtesk-vm-rollout`` will run `Windows Unattended 148 | Setup <autounattend.html>`__. 149 | 150 | Re-Use Goldimage 151 | ---------------- 152 | 153 | To Re-Use your Goldimage VM, take the following steps: 154 | 155 | - Restore Goldimage VM to the snapshot that was created before sysprep 156 | - Install Updates, adjust configuration, install programs as you like 157 | - Create a fresh Snapshot 158 | - Run sysprep again to create a new RHEV/Ovirt VmTemplate 159 | 160 | Long snapshot chains should be avoided, delete old snapshots from time 161 | to time. 162 | 163 | Alternative approach: 164 | 165 | - Roll out a virtual room based on the last Goldimage 166 | - Take a VM out of this virtual room and use it as new Goldimage 167 | - Install Updates, adjust configuration, install programs as you like 168 | - Run sysprep again to create a new RHEV/Ovirt VmTemplate 169 | - Optional: Delete Goldimage VM 170 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | .. Virtesk-VDI documentation master file, created by 2 | sphinx-quickstart2 on Thu Mar 31 12:24:51 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Virtesk-VDI 7 | ============ 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 4 13 | 14 | README.rst 15 | architecture.rst 16 | virtual_rooms.rst 17 | thinclients.rst 18 | misc.rst 19 | 20 | 21 | Indices and tables 22 | ================== 23 | 24 | * :ref:`genindex` 25 | * :ref:`modindex` 26 | * :ref:`search` 27 | 28 | -------------------------------------------------------------------------------- /doc/ipxe-image.rst: -------------------------------------------------------------------------------- 1 | .. |br| raw:: html 2 | 3 | <br /> 4 | 5 | iPXE image for cdroms and usb flash drives 6 | ======================================================== 7 | 8 | 9 | Introduction 10 | ------------ 11 | 12 | In some environments, it is not feasible to do a network install of thinclients using PXE. |br| 13 | For this cases, an iPXE image for cdroms / usb flash drives can be 14 | built. 15 | 16 | The iPXE image contains an embedded configuration file pointing to your infrastructure server. |br| 17 | It will chainload the network bootloader (pxelinux) configured on your infrastructure server using http. 18 | 19 | Creating an iPXE image: 20 | ----------------------- 21 | 22 | Upstream documentation: http://ipxe.org/embed 23 | 24 | virtesk-vdi.ipxe: 25 | 26 | :: 27 | 28 | #!ipxe 29 | 30 | echo 31 | echo Virtesk-VDI thinclient rollout iPXE compatibility image 32 | 33 | 34 | echo 35 | echo Configuring DHCP... 36 | dhcp 37 | 38 | # Option 209 pxelinux.configfile 39 | # Specify the initial PXELINUX configuration file name, 40 | # which may be qualified or unqualified. 41 | 42 | # Option 210 pxelinux.pathprefix 43 | # Specify the PXELINUX common path prefix, instead of deriving 44 | # it from the boot file name. This almost certainly needs to 45 | # end in whatever character the TFTP server OS uses as a 46 | # pathname separator, e.g. slash (/) for Unix. 47 | 48 | # Path Prefix for PXELINUX: 49 | # set 210:string tftp://infrastructure-server/pxelinux/ 50 | set 210:string http://infrastructure-server/tftpboot/pxelinux/ 51 | 52 | echo 53 | prompt --key 0x02 --timeout 2000 Press Ctrl-B for the iPXE command line... && shell || 54 | 55 | echo 56 | echo Chainloading pxelinux.0 ... 57 | chain http://infrastructure-server/tftpboot/pxelinux/pxelinux.0 58 | 59 | Fetching source and build the image: 60 | 61 | :: 62 | 63 | git clone git://git.ipxe.org/ipxe.git 64 | cd ipxe.git/src 65 | make bin/ipxe.usb EMBED=/path/to/virtesk-vdi.ipxe 66 | make bin/ipxe.iso EMBED=/path/to/virtesk-vdi.ipxe 67 | 68 | Afterwards, the images will be available at: 69 | 70 | :: 71 | 72 | ipxe.git/src/bin/ipxe.usb 73 | ipxe.git/src/bin/ipxe.iso 74 | 75 | Writing the images to cdrom / usb flash drive 76 | --------------------------------------------- 77 | 78 | Burning cdrom: 79 | 80 | :: 81 | 82 | wodim -dao ipxe.iso 83 | 84 | Writing image to usb flash drive 85 | 86 | :: 87 | 88 | dd if=ipxe.usb of=/dev/sdc bs=1M; sync 89 | 90 | Replace ``/dev/sdc`` with the device where the image shall be written 91 | to. Data on ``/dev/sdc`` will be destroyed. 92 | 93 | When using an usb flash drive to roll out thinclients, please make sure you remove it as soon as the pxelinux menu is displayed. Otherwise, the kickstart installation would overwrite your usb flash drive. 94 | 95 | 96 | -------------------------------------------------------------------------------- /doc/libusbredir-patching.rst: -------------------------------------------------------------------------------- 1 | .. |br| raw:: html 2 | 3 | <br /> 4 | 5 | USB Redirect: avoiding USB reset 6 | ================================ 7 | 8 | Solution to avoid problems that might occour when redirecting usb devices over Spice. |br| 9 | Only few devices are affected, most devices work without the patches below. 10 | 11 | 12 | Introduction 13 | ------------ 14 | 15 | Spice used libusbredir to redirect usb devices. 16 | 17 | Normally, a reset command is issued to the device before it is redirected. 18 | This works fine for 99% of all avaiable usb devices. |br| 19 | However, some problematic devices have issues with usb resets. Redirect fails with the following error message: 20 | 21 | :: 22 | 23 | Could not redirect Electronics For Imaging, Inc. [hex] 24 | Device: error resetting device: LIBUSB_ERROR_NOT_FOUND. 25 | 26 | By adding those devices to an internal blacklist of libusbredir, we can prevent usb reset for them. This way, virtesk-vdi can successfully redirect those devices. However, libusbredir must be patched on all thinclients. The following steps explain how the patching is done. 27 | 28 | 29 | Setting up an rpm build environment 30 | ----------------------------------- 31 | 32 | Build Machine: A test machine running the same fedora version as your 33 | thinclients. 34 | 35 | Installing build-tools: 36 | 37 | :: 38 | 39 | # dnf install rpm-build gcc make rpmdevtools rpmlint 40 | 41 | Installing build dependencies: 42 | 43 | :: 44 | 45 | # dnf install libusb1-devel 46 | 47 | Setting up rpm build environment 48 | 49 | :: 50 | 51 | $ mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} 52 | $ echo '%_topdir %(echo $HOME)/rpmbuild' > ~/.rpmmacros 53 | 54 | Downloading usbredir sources: 55 | 56 | :: 57 | 58 | $ dnf download --enablerepo=fedora-source --source usbredir 59 | 60 | Unpacking sources into your local RPM Build environment: 61 | 62 | :: 63 | 64 | $ rpm -i usbredir-0.7-3.fc22.src.rpm 65 | 66 | The rpm command above will unpack the following files into your rpm 67 | build environment: 68 | 69 | - ~/rpmbuild/SOURCES/usbredir-0.7.tar.bz2 70 | - ~/rpmbuild/SPECS/usbredir.spec 71 | 72 | 73 | Patching libusbredir 74 | -------------------- 75 | 76 | First, we add a patch file, then we tell rpmbuild to apply our patch 77 | before building the package. 78 | 79 | ~/rpmbuild/SOURCES/usbredir-blacklist.patch 80 | 81 | :: 82 | 83 | diff -urN usbredir-0.7/usbredirhost/usbredirhost.c foo/usbredirhost/usbredirhost.c 84 | --- usbredir-0.7/usbredirhost/usbredirhost.c 2013-11-19 13:52:36.000000000 +0100 85 | +++ foo/usbredirhost/usbredirhost.c 2015-12-21 15:19:16.277973330 +0100 86 | @@ -139,6 +139,7 @@ 87 | 88 | static const struct usbredirhost_dev_ids usbredirhost_reset_blacklist[] = { 89 | { 0x1210, 0x001c }, 90 | + { 0x2650, 0x1311 }, 91 | { -1, -1 } /* Terminating Entry */ 92 | }; 93 | 94 | In the Spec-File ``~/rpmbuild/SPECS/usbredir.spec`` we have to add the 95 | following line to make sure that rpmbuild will knows about our patch: 96 | 97 | :: 98 | 99 | Patch0: usbredir-blacklist.patch 100 | 101 | Context / location in file: 102 | 103 | :: 104 | 105 | URL: http://spice-space.org/page/UsbRedir 106 | Source0: http://spice-space.org/download/%{name}/%{name}-%{version}.tar.bz2 107 | Patch0: usbredir-blacklist.patch 108 | BuildRequires: libusb1-devel >= 1.0.9 109 | 110 | In the Spec-File ``~/rpmbuild/SPECS/usbredir.spec`` we have to add the 111 | following line to make sure that rpmbuild will apply patches: 112 | 113 | :: 114 | 115 | %patch0 -p1 116 | 117 | Context / location in file: 118 | 119 | :: 120 | 121 | %prep 122 | %setup -q 123 | 124 | %patch0 -p1 125 | 126 | %build 127 | %configure --disable-static 128 | make %{?_smp_mflags} V=1 129 | 130 | Changing the package version: change the spec-file from 131 | 132 | :: 133 | 134 | Name: usbredir 135 | Version: 0.7 136 | Release: 3%{?dist} 137 | 138 | To: 139 | 140 | :: 141 | 142 | Name: usbredir 143 | Version: 0.7 144 | Release: 99mypackage3%{?dist} 145 | 146 | 147 | 148 | Building the RPM package: 149 | ------------------------- 150 | 151 | :: 152 | 153 | rpmbuild -ba ~/rpmbuild/SPECS/usbredir.spec 154 | 155 | Afterwards, the RPMs will be available at: 156 | 157 | :: 158 | 159 | ~/rpmbuild/RPMS/x86_64/ 160 | 161 | Testing: Installing RPM manually: 162 | --------------------------------- 163 | 164 | On a thinclient, run the following command: 165 | 166 | :: 167 | 168 | # dnf install usbredir-0.7-99mypackage3.fc22.x86_64.rpm 169 | 170 | Now the thinclient should be able to redirect the device. 171 | -------------------------------------------------------------------------------- /doc/misc.rst: -------------------------------------------------------------------------------- 1 | Misc 2 | ==== 3 | 4 | .. toctree:: 5 | :maxdepth: 3 6 | 7 | compatibility.rst 8 | sftp-floppy-upload.rst 9 | spice-disconnect-actions.rst 10 | bugs.rst 11 | security.rst 12 | license.rst 13 | -------------------------------------------------------------------------------- /doc/quality_control.rst: -------------------------------------------------------------------------------- 1 | Quality Control of virtual rooms 2 | ============================================== 3 | 4 | Introduction 5 | ------------ 6 | 7 | A lot of problems can occour during Windows Unattended Setup: 8 | 9 | - Network, DNS, DHCP, Firewall-Problems 10 | - Network adapter: wrong name, wrong Windows-Firewall-Zone, ... 11 | - Problems with Active Directory SRV-Records 12 | - Problems with Windows Domain Join 13 | - Infrastructure overload can occour if many VMs get joined at the same 14 | time or many CIFS-Shares are accessed at once. 15 | - ... 16 | 17 | It is important to detect those problems early, so that a solution can 18 | be found before users are affected. 19 | A system administrator has to deal with alot of VMs, therefore an 20 | efficient way for quality control is necessary. 21 | 22 | Windows Unattended Setup: Copying logfiles 23 | ------------------------------------------ 24 | 25 | First, we wanna introduce a ``autounattend-firstlogon.cmd``-Script 26 | that will be run inside every Win7-VM that is rolled out. 27 | The script is running in the *Firstlogon-Phase* in Windows Unattended 28 | Setup. It will be used for mounting shares, copying logfiles, ... 29 | 30 | Parameters 31 | ~~~~~~~~~~ 32 | 33 | - **someserver**: CIFS-Server (Windows or Samba) containing two 34 | CIFS-Shares: 35 | 36 | - **scriptshare**: CIFS-Share, where ``autounattend-firstlogon.cmd`` 37 | is located. 38 | - **logfileshare**: CIFS-Share for putting logfiles. 39 | - **username@windowsdomain** and **password**: Credentials to 40 | access those CIFS-Shares 41 | 42 | Autounattend-production.xml.template 43 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 44 | 45 | We need to make sure that our new ``autounattend-firstlogon.cmd``-Script 46 | will be called at the right time: 47 | 48 | :: 49 | 50 | ... 51 | <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 52 | <FirstLogonCommands> 53 | <SynchronousCommand wcm:action="add"> 54 | <CommandLine>net use Y: \\someserver\scriptshare /persistent:no /user:username@windowsdomain password</CommandLine> 55 | <RequiresUserInput>false</RequiresUserInput> 56 | <Order>20</Order> 57 | </SynchronousCommand> 58 | <SynchronousCommand wcm:action="add"> 59 | <CommandLine>cmd /c Y:\autounattend-firstlogon.cmd ${scripttime} ${ComputerName} 1> C:\autounattend-firstlogon.log 2>&1 </CommandLine> 60 | <RequiresUserInput>false</RequiresUserInput> 61 | <Order>21</Order> 62 | </SynchronousCommand> 63 | <SynchronousCommand wcm:action="add"> 64 | <CommandLine>shutdown /p</CommandLine> 65 | <RequiresUserInput>false</RequiresUserInput> 66 | <Order>50</Order> 67 | </SynchronousCommand> 68 | </FirstLogonCommands> 69 | </component> 70 | ... 71 | 72 | ``autounattend-firstlogon.cmd`` will be called with two parameters, 73 | the *scripttime* and the *ComputerName*. They will be used later for 74 | storing the logfiles in a well-organized folder structure. 75 | Output will be redirected to ``C:\autounattend-firstlogon.log``. 76 | 77 | 78 | autounattend-firstlogon.cmd 79 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 80 | 81 | Location: ``\\someserver\scriptshare\autounattend-firstlogon.cmd`` 82 | 83 | :: 84 | 85 | rem Parameters passed by Autounattend.xml: 86 | set DATETIME=%1 87 | set OWSNAME=%2 88 | 89 | net use R: \\someserver\report 90 | 91 | mkdir R:\%DATETIME%-%OWSNAME% 92 | 93 | 94 | xcopy C:\windows\Panther\*.* R:\%DATETIME%-%OWSNAME% /K /E /I /H /Y 95 | 96 | copy C:\Windows\debug\NetSetup.LOG R:\%DATETIME%-%OWSNAME% 97 | 98 | copy C:\Windows\Temp\Domainjoin.log R:\%DATETIME%-%OWSNAME% 99 | copy C:\Windows\Temp\Domainjoin.log.2 R:\%DATETIME%-%OWSNAME% 100 | 101 | echo %COMPUTERNAME% > R:\%DATETIME%-%OWSNAME%\_SELF-FQDN-%OWSNAME% 102 | 103 | copy C:\autounattend-firstlogon.log R:\%DATETIME%-%OWSNAME% 104 | 105 | net use R: /delete /yes 106 | 107 | **Remark:** ``net use R: ...`` is the second time a CIFS-Share is mounted. 108 | Windows stores Credentials, this is why we do not need to pass 109 | username/password. 110 | 111 | Analyzing Logfiles 112 | ~~~~~~~~~~~~~~~~~~ 113 | 114 | Windows Domain Join 115 | ^^^^^^^^^^^^^^^^^^^ 116 | 117 | Logfile: ``C:\\Windows\panther\UnattendGC\setupact.log`` 118 | 119 | Search terms: 120 | 121 | :: 122 | 123 | DJOIN 124 | 0x54a 125 | Unattended Join: NetJoinDomain succeeded 126 | 127 | Checking alot of logfiles: commands to analyze windows domain joins using the logfiles 128 | archived on the *logfile*-CIFS-Share: 129 | 130 | Coarse overview: 131 | 132 | :: 133 | 134 | grep -c "Unattended Join: NetJoinDomain succeeded" 2014-08-04-*/UnattendGC/setupact.log 135 | 136 | Result: 1 if joined successfully, 0 otherwise. 137 | 138 | How many times did the windows client try to join into the domain? 139 | 140 | :: 141 | 142 | grep -c 0x54a 2014-08-04-*/UnattendGC/setupact.log 143 | 144 | In infrastructure overload situations, the windows client retries 80-85 145 | times and then gives up. 146 | 147 | Details for a single windows client: 148 | 149 | :: 150 | 151 | grep DJOIN 2014-08-04-1216-test05-vd01/UnattendGC/setupact.log 152 | -------------------------------------------------------------------------------- /doc/screenshots/spice-disconnect-actions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/screenshots/spice-disconnect-actions.png -------------------------------------------------------------------------------- /doc/security.rst: -------------------------------------------------------------------------------- 1 | .. |br| raw:: html 2 | 3 | <br /> 4 | 5 | Security considerations 6 | ===================================== 7 | 8 | 9 | Virtual machine isolation 10 | ------------------------- 11 | 12 | Virtual machines can be isolated by putting them into different Ovirt 13 | networks and by applying the usual network security principles. 14 | 15 | Thinclients 16 | ----------- 17 | 18 | **Ovirt REST-API**: ``Thinclient ---> Ovirt Manager (tcp/443)`` 19 | 20 | - HTTPs connection; Server certificate is verified. Ovirt AAA is used 21 | to authenticate the technical user ``ovirt.thinclient@domain`` used 22 | by thinclients. 23 | - Used to get access parameters to assigned VM. Only VDI-VMs can be 24 | accessed by thinclients, other VMs are protected by Ovirt access 25 | control rules. 26 | 27 | **Spice**: ``Thinclient ---> qemu-kvm (tcp/5900-6100)`` 28 | 29 | - SSL connections; Certificate fingerprint is retrieved using REST-API, 30 | fingerprint is verified. **Spice Ticketing** is used by ``qemu-kvm`` 31 | to determine if the client may access. 32 | 33 | **Postgres Database**: 34 | ``Thinclient ---> infrastructure server (tcp/5432)`` 35 | 36 | - SSL connection, client verifies server certificate if configured to 37 | do so. 38 | - Server verifies client identity using username/password. 39 | - Thinclients have only limited, read only database access. 40 | 41 | **Remote logging**: ``Thinclient ---> infrastructure server (tcp/512)`` 42 | 43 | - Remote logging is not secure. All logging is sent in plain-text over 44 | the network. 45 | - No authentication and no encryption is done. 46 | - The thinclients are logging sensitive data over the network. 47 | - Mitigation strategies: 48 | 49 | - Put thinclients in a protected network (see below). 50 | - Disable remote logging. Thinclients work fine with local logging 51 | only - however, remote logging is useful for problem analysis. 52 | 53 | Thinclients do need to store credentials. If those credentials leak, the security concept will break down. The thinclients themself won't leak their credentials (exception: remote logging). However, common network boot techiques usually require all code and configuration to be accessible unauthenticated, where an attacker might retrieve them - see below. 54 | 55 | 56 | PXE Rollout 57 | ----------- 58 | 59 | .. note:: PXE is insecure. 60 | 61 | Network booting is very convinient. However, PXE is completely insecure. 62 | 63 | Recommendations for situations where both security and PXE are needed: 64 | 65 | - **Physical network protection:** The network hardware (switches, 66 | servers, ...) should be physically protected. Only trusted persons 67 | should be allowed to administrate switches or to plug cables into 68 | switches. 69 | - **Dedicated rollout network:** A dedicated rollout network is used 70 | for rollout. System administrators need to switch VLANs (or cables) 71 | for rollouts. Only devices in the rollout network are allowed to 72 | access sensitive data on the infrastructure server. 73 | - **Mixed network, locked-down:** A mixed (trusted and untrusted 74 | devices, some switch ports accessible by users) is used for both 75 | daily operation and for rollout. Techiques like private VLANs, 76 | MAC-to-Swichport binding, physical protection of cables and 77 | switchports used by thinclients, static ARP table entries and static 78 | IP adresses are used for network protection. Then, access to the 79 | infrastructure server (PXE, kickstart) can be granted based on IP 80 | access rules. 81 | 82 | Kexec Rollout 83 | ------------- 84 | 85 | In the current implementation of ``tc_kexec``, many mechanisms used for 86 | PXE boot are re-used. Therefore, ``tc_kexec`` is affected by the same 87 | security problems. 88 | 89 | A secure variant of ``tc_kexec`` could be engineered upon request. The 90 | kernel and initramfs can be transported in a secure way; the kickstart 91 | file, GnuPG keys and SSL CA certificates can be appended to the 92 | initramfs. 93 | 94 | Multiple thinclient environments 95 | -------------------------------- 96 | 97 | If only some, but not all thinclients need to be protected, then the 98 | security recommendations listed above can be applied only for those 99 | thinclients. Please make sure that for this usecase, a dedicated 100 | technical user ``ovirt.secure-thinclients@domain`` is used for the 101 | thinclients that need protection. 102 | -------------------------------------------------------------------------------- /doc/start-and-stop-management.rst: -------------------------------------------------------------------------------- 1 | Start and Stop management of virtual rooms 2 | ======================================================== 3 | 4 | Introduction 5 | ------------ 6 | 7 | Running VMs do consume ressources, in particular memory. This article 8 | describes start/stop behavior and ways to save ressources by shutting 9 | down unused VMs. 10 | 11 | This article was written with Ovirt 3.4/3.5 in mind. 12 | 13 | Ovirt 3.6 introduces new features for shutting down VMs upon spice disconnect. 14 | Those features can be used as an alternative to the approach described 15 | here. 16 | 17 | There is a `backport <spice-disconnect-actions.html>`__ of the spice disconnect action features for Ovirt 3.5. 18 | 19 | Start/Stop behaviour 20 | -------------------- 21 | 22 | Thinclients startup: VM auto-launch 23 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24 | 25 | Whenever a TC is started, it checks if the assigned VM is already 26 | running. If not, it is started automatically. 27 | 28 | No configuration is necessary. 29 | 30 | Thinclient shutdown: VM auto-shutdown 31 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 32 | 33 | When a TC shuts down, it will also shutdown the assigned VM. 34 | 35 | This can be configured individually for each TC in the database table 36 | ``timed_thinclient_to_vm_mapping``. 37 | 38 | When a TC reboots, nothing special happens to the assigned VM. No 39 | shutdown or reboot signal is sent to the assigned VM. It is implemented 40 | this way because a TC reboot usually is alot faster than a VM reboot. 41 | 42 | Thinclient auto-shutdown 43 | ~~~~~~~~~~~~~~~~~~~~~~~~ 44 | 45 | Unimplemented: When there is no mouse/keyboard interaction for some 46 | time, the thinclient will automatically shut down. This feature isn't 47 | available yet, but can be implemented upon request. A more advanced 48 | solution would also contain a shutdown-inhibit-feature, to avoid 49 | shutdowns when watching movies or when presenting slides. 50 | 51 | Tools for start and shutdown 52 | ---------------------------- 53 | 54 | Virtual rooms: startup and shutdown 55 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 56 | 57 | Whole virtual rooms can be started and shut down using 58 | ``virtesk-virtroom-start`` and ``virtesk-virtroom-shutdown``. 59 | 60 | Those tools are well-suited for unattended use in cronjobs. Cronjobs can 61 | be configured similary to 62 | `vm-reset-cronjobs <stateless_and_snapshot_features.html#automatic-reset-every-night>`__. 63 | 64 | Virtual rooms: startup after rollout and after reset 65 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 66 | 67 | The startup behavior after rollout and after reset can be configured 68 | using the config directives ``rollout_startvm`` and ``reset_startvm`` as 69 | described 70 | `here <virtesk-vm-rollout-config.html#room-definitions-section-room-room01>`__. 71 | 72 | Thinclient remote-startup using Wake-On-LAN 73 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 74 | 75 | Thinclient remote-startup is supported using Ethernet Wake-On-Lan if the 76 | hardware supports it and if BIOS and network are configured properly. 77 | 78 | Standard wake-up-tools (e.g. ``ether-wake``, ``wol``, ...) can be used 79 | to trigger a thinclient startup. 80 | 81 | Thinclient remote-shutdown 82 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 83 | 84 | Thinclient remote-shutdown is supported using 85 | `virtesk-tc-tools <virtesk-tc-tools.html>`__. 86 | 87 | Suggestions for start and stop management 88 | ----------------------------------------- 89 | 90 | Workplaces used often 91 | ~~~~~~~~~~~~~~~~~~~~~ 92 | 93 | Configuration: 94 | 95 | - VM shutdown upon TC shutdown: disabled 96 | - VM startup after reset: ``reset_startvm = Auto`` 97 | - Every Friday night: shutdown VMs using ``virtesk-virtroom-shutdown`` 98 | 99 | Effect: 100 | 101 | - VMs will always be running ===> TC startup very fast (except monday 102 | morning) 103 | - Resource consumption / memory usage: mostly constant 104 | - Doesn't consume resources for unused workplaces 105 | - Electricity savings on weekends 106 | 107 | Workplaces for Power-Users 108 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 109 | 110 | Same as above, but without or with less resets. Because the VM will be 111 | up the whole week, power users can leave their programms running during 112 | the week. 113 | 114 | Workplaces used infrequently 115 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 116 | 117 | Configuration: 118 | 119 | - VM shutdown upon TC shutdown: enabled 120 | - VM startup after reset: ``reset_startvm = Never`` 121 | 122 | Effect: 123 | 124 | - Unused TCs won't consume any resources 125 | - Less resources used on average 126 | - Less electricity used on average 127 | - TC startup is slower (VM has to be started first) 128 | - More dynamic scenario. Requires carefull monitoring of RHEV/Ovirt 129 | events and resources. 130 | - Resource reserves are needed for peak situations 131 | -------------------------------------------------------------------------------- /doc/stateless_and_snapshot_features.rst: -------------------------------------------------------------------------------- 1 | Stateless and snapshot features 2 | ============================================= 3 | 4 | Features to reset virtual rooms to a well-defined, known state 5 | 6 | -------------- 7 | 8 | Introduction 9 | ------------ 10 | 11 | Situation: 12 | 13 | - Large number of clearly-defined managed workplaces 14 | - Users and programs might modify workplaces and leave some debris 15 | after work 16 | - Workplace modifications and debris shall be avoided 17 | - Divergence of workplaces shall be avoided 18 | 19 | Virtesk-VDI provides two solutions: 20 | 21 | - Snapshots: A snapshot will be created for every VM after initial 22 | rollout. A cronjob resets the virtual rooms to the snapshot state 23 | every night. 24 | - Stateless VMs: A snapshot will be created at VM startup; The VM will 25 | be reset to snapshot state after shutdown. 26 | 27 | The approach using snapshots is the preferred solution. 28 | 29 | Snapshots 30 | --------- 31 | 32 | How it works: 33 | 34 | - Virtual rooms are created using virtesk-virtroom-rollout. After 35 | rollout, virtesk-virtroom-rollout automatically creates a snapshot 36 | for every VM. 37 | - Every night, virtesk-virtroom-reset is run by cronjob. This tool 38 | will stop the VMs, reset them back to the snapshot that was created 39 | by virtesk-virtroom-rollout, and starts the VMs again. During the 40 | reset phase at night, the VMs / Workplaces are unavailable. 41 | - VMs are statefull, but they will be reset every night 42 | 43 | Background: Snapshot operations are expensive, in particular when doing 44 | alot of snapshot operations at once. Snaphosts in RHEV/Ovirt also 45 | sometimes were buggy and problematic in the past. During snapshot 46 | operations, the VM cannot be used, that is, the user has to wait for the 47 | operations to complete. 48 | 49 | This solutions avoids those problems: 50 | 51 | - Snapshot creation is only done once, after initial rollout. By 52 | inserting short breaks where necessary, the tools do avoid situations 53 | where RHEV/Ovirt is overloaded by to many parallel snapshot 54 | operations. 55 | - Reset to snapshot date is done during the night, when workplaces are 56 | unused. 57 | - No snapshot operations are done during daytime. Therefore, the users 58 | do not have to wait for snapshot operations. Statefull VMs start alot 59 | faster than stateless VMs, and they are also more reliable and less 60 | ressource-consuming. 61 | - VM startup is fast, even in peak situations in a classroom where 20 62 | VMs might start at once. 63 | 64 | Configuration: ``virtesk-vm-rollout.conf``: 65 | 66 | :: 67 | 68 | [room room01] 69 | [[student_vms]] 70 | # The snapshot and stateless features are mutually exclusive and cannot be used together. 71 | stateless = False 72 | 73 | # Used for snapshot creation after initial rollout 74 | snapshot_description = "Automatic snapshot after virtesk-vmrollout, IP=${ip}/${netmask_as_suffix}, scripttime=${scripttime}" 75 | 76 | # Used for reset 77 | reset_to_snapshot_regex = "Automatic snapshot after virtesk-vmrollout, .*" 78 | 79 | # Shall VMs be started after reset? 80 | reset_startvm = Auto 81 | 82 | To use the snapshot feature, ``snapshot_description`` and 83 | ``reset_to_snapshot_regex`` must be configured. The first parameter is 84 | used during rollout of virtual rooms, the second is used to identify the 85 | snapshot the VMs to. 86 | 87 | ``reset_startvm = Always/Auto/Never`` determines if VMs shall be started 88 | after reset. When set to ``Auto``, the VMs will be started if they were 89 | running before reset. 90 | 91 | To disable the snapshot feature, set ``snapshot_description = ""`` and 92 | deactivate the reset cronjob. 93 | 94 | Automatic reset every night 95 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 96 | 97 | Cronjob: ``/etc/cron.d/virtesk-vdi-reset-virtrooms``: Runs reset script 98 | at 01:00 AM every night: 99 | 100 | :: 101 | 102 | SHELL=/bin/sh 103 | PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/virtesk-vdi.src/virtesk-vm-rollout/ 104 | # m h dom mon dow command 105 | 00 01 * * * root /etc/virtesk-vdi/reset-virtrooms-every-night.sh > /dev/zero 106 | 107 | Please make sure to add your virtesk installation directory to 108 | ``$PATH``, like ``/opt/virtesk-vdi.src/virtesk-vm-rollout/`` above. 109 | 110 | See also: ``man 5 crontab``. 111 | 112 | Reset-Script: ``/etc/virtesk-vdi/reset-virtrooms-every-night.sh``: 113 | 114 | :: 115 | 116 | #!/bin/bash 117 | # Reset virtual rooms to snapshots 118 | 119 | virtesk-virtroom-reset test01 120 | virtesk-virtroom-reset test02 121 | virtesk-virtroom-reset test03 122 | virtesk-virtroom-reset test04 123 | 124 | Please add ``virtesk-virtroom-reset <myroom>`` for every virtual room 125 | that shall be reset. 126 | 127 | Making it executable: 128 | ``chmod +x /etc/virtesk-vdi/reset-virtrooms-every-night.sh``. 129 | 130 | Stateless 131 | --------- 132 | 133 | Stateless VMs are a built-in feature of RHEV/Ovirt: Before starting a 134 | stateless VM, a snapshot is created. After VM shutdown, the snapshot is 135 | discarded, e.g. the VM is reset to the state before launch. 136 | 137 | Virtesk-vdi supports stateless VMs by setting the stateless flag after 138 | rollout. Afterwards, RHEV/Ovirt is responsible for snapshot management. 139 | 140 | Advantages: 141 | 142 | - Builtin feature of RHEV/Ovirt 143 | 144 | Drawbacks: 145 | 146 | - VM are starting slower (50-80 seconds until a spice session can be 147 | opened, compared to 15-30 seconds for statefull VMs) 148 | - Consumes more resources 149 | - Somewhat error prone (Bugs in snapshot implementation of RHEV/Ovirt) 150 | - Other virtesk-vdi code (Thinclients, Start/Stop - Management, ...) 151 | handle stateless VMs like statefull VMs. No special error handling is 152 | implemented for stateless VMs. This might be necessary in the 153 | following areas: VM launch time, virtesk-virtroom-delete, 154 | virtesk-virtroom-start, virtesk-virtroom-shutdown, VM startup upon 155 | TC startup, VM shutdown upon TC shutdown. In general, stateless VMs 156 | should run fine, but problems might occour when starting/stopping 157 | stateless VMs too fast or too often in a row. 158 | - Peak situations: When starting or stopping alot of stateless VMs at 159 | once, then RHEV/Ovirt might handle some operations sequentially. For 160 | example in a classroom situation, if the teacher tells the whole 161 | class to start their thinclients, startup might take longer than when 162 | starting a single thinclient. 163 | 164 | Configuration: ``virtesk-vm-rollout.conf``: 165 | 166 | :: 167 | 168 | [room room01] 169 | [[student_vms]] 170 | stateless = True 171 | 172 | # The snapshot and stateless features are mutually exclusive and cannot be used together. 173 | snapshot_description = "" 174 | -------------------------------------------------------------------------------- /doc/switching-virtual-rooms.rst: -------------------------------------------------------------------------------- 1 | Switching virtual rooms 2 | ===================================== 3 | 4 | 5 | 6 | Introduction 7 | ------------ 8 | 9 | Switching the virtual room assigned to a physical set of thinclient (the physical room) is an important feature of virtesk-vdi. 10 | 11 | Uses: 12 | 13 | - **Quality control**: virtual rooms can be tested before assignement 14 | to physical rooms. 15 | - **Dedicated VMs for exams** 16 | - **Dedicated VMs for special applications** 17 | - **Win7 Desktops for one lesson, Linux Desktops for next lessons** 18 | 19 | - Thinclients can dispaly Linux VMs, however, no rollout tools for 20 | Linux desktops have been implemented in virtesk-vdi so far. 21 | 22 | - ... 23 | 24 | Database 25 | -------- 26 | 27 | Switching virtual rooms is implemented using the postgres database. 28 | 29 | Documentation: 30 | 31 | - `Database layout <tc-vm-mapping.html>`__ 32 | - `Database Installation + DB 33 | Access <virtesk-infrastructure-server.html#setting-up-postgres-database>`__ 34 | - `Defining virtual 35 | rooms <virtesk-vm-rollout-config.html#room-definitions-section-room-room01>`__ 36 | 37 | How to switch virtual rooms 38 | --------------------------- 39 | 40 | How to switch virtual rooms: simple cases 41 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 42 | 43 | For a single TC, the assigned VM can be changed like this: 44 | 45 | :: 46 | 47 | psql -U vdi-dbadmin -h localhost vdi -c " 48 | UPDATE timed_thinclient_to_vm_mapping \ 49 | SET vm='test01-vd01' WHERE thinclient='test01-tc01' \ 50 | AND start_date IS NULL AND end_date IS NULL;" 51 | 52 | Output: 53 | 54 | :: 55 | 56 | UPDATE 1 57 | 58 | It might be convinient to use an SQL-File for this: 59 | 60 | update.sql: 61 | 62 | :: 63 | 64 | UPDATE timed_thinclient_to_vm_mapping SET vm='test01-vd01' WHERE thinclient='test01-tc01' AND start_date IS NULL AND end_date IS NULL; 65 | UPDATE timed_thinclient_to_vm_mapping SET vm='test01-vd02' WHERE thinclient='test01-tc02' AND start_date IS NULL AND end_date IS NULL; 66 | UPDATE timed_thinclient_to_vm_mapping SET vm='test01-vd03' WHERE thinclient='test01-tc03' AND start_date IS NULL AND end_date IS NULL; 67 | 68 | Executing update.sql: 69 | 70 | :: 71 | 72 | psql -U vdi-dbadmin -h localhost vdi < room.sql 73 | UPDATE 1 74 | UPDATE 1 75 | UPDATE 1 76 | 77 | If you want to assign the VMs ``test02-vd*`` instead of ``test01-vd*``, 78 | then you can use ``vim`` or ``sed`` for changeing update.sql: 79 | 80 | Using vim: 81 | 82 | :: 83 | 84 | vim update.sql # Open file with vim 85 | :%s/test01-vd/test02-vd/ # Apply regex over whole file 86 | :wq # Write file, quit 87 | 88 | Using sed: 89 | 90 | :: 91 | 92 | sed -i 's/test01-vd/test02-vd/' update.sql 93 | 94 | The exact regexes needed for your environment do depend on your naming 95 | schema; In general, switching virtual rooms is alot easier when 96 | systematic naming is used. 97 | 98 | Please note that all examples only update records where 99 | ``start_date``/``end_date`` are ``NULL``. This is useful for most cases, but it 100 | might need adjustment for special cases. 101 | 102 | How to switch virtual rooms: advanced examples 103 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 104 | 105 | When changeing the assigned VMs for 300 thinclients (10 physical rooms 106 | with up to 30 thinclients each), then bash comes handy to generate the 107 | SQL statements: 108 | 109 | generatesql-statements.sh (available in ``sample_config/``): 110 | 111 | :: 112 | 113 | #!/bin/bash 114 | 115 | ROOMS="test01 test02 test03 test04 test05 test06 test07 test08 test09 test10" 116 | 117 | for ROOM in $ROOMS; do 118 | IDs=$(seq 1 30) 119 | echo "# Room ${ROOM}" 120 | for ID in ${IDs}; do 121 | ID_TWODIGIT=$(printf "%02d" ${ID}) 122 | TC_NAME="${ROOM}-tc${ID_TWODIGIT}" 123 | VM_NAME="${ROOM}-vd${ID_TWODIGIT}" 124 | cat <<-ENDofSQL 125 | UPDATE timed_thinclient_to_vm_mapping 126 | SET vm='${VM_NAME}' WHERE thinclient='${TC_NAME}' 127 | AND start_date IS NULL AND end_date IS NULL; 128 | 129 | ENDofSQL 130 | done 131 | echo 132 | done 133 | 134 | Please adjust ``ROOMS="test01 test02 ..."`` and ``TC_NAME="${ROOM}-tc${ID_TWODIGIT}"`` and 135 | ``VM_NAME="${ROOM}-vd${ID_TWODIGIT}"`` according to your naming scheme, 136 | and ``IDs=$(seq 1 30)`` according to the number of TCs in your rooms. 137 | Some rooms might have less than 30 thinclients - however, the generated 138 | SQL statements for the non-existing TCs won't hurt, and its easier 139 | to process all rooms in an uniform way. 140 | 141 | Running it directly: 142 | 143 | :: 144 | 145 | bash generatesql-statements.sh | psql -U vdi-dbadmin -h localhost vdi 146 | 147 | Putting SQL commands into a file first, and run them afterwards: 148 | 149 | :: 150 | 151 | bash generatesql-statements.sh > update.sql # Generate SQL statements 152 | less update.sql # Control SQL statements 153 | psql -U vdi-dbadmin -h localhost vdi < update.sql # Execute SQL statements 154 | 155 | The same bash script can be used to generate the mapping for new 156 | thinclients - only the SQL statement needs to be replaced: 157 | 158 | :: 159 | 160 | INSERT INTO timed_thinclient_to_vm_mapping (vm, thinclient) 161 | VALUES ('${VM_NAME}', '${TC_NAME}'); 162 | -------------------------------------------------------------------------------- /doc/tc-vm-mapping.rst: -------------------------------------------------------------------------------- 1 | .. |br| raw:: html 2 | 3 | <br /> 4 | 5 | Assigning VMs to thinclients: Database layout 6 | ============================================= 7 | 8 | 9 | Introduction 10 | ------------ 11 | 12 | Thinclients use a postgres database to determine the virtual machine 13 | that shall be displayed. 14 | 15 | The database determines: 16 | 17 | - The VM shall be determined on a TC 18 | - When shall it be displayed (to display different VMs for different 19 | courses, ...) 20 | - If the VM shall `shut down <start-and-stop-management.html>`__ when the 21 | TC is shutting down 22 | 23 | Accessing the database is documented 24 | `here <virtesk-infrastructure-server.html#accessing-database>`__, 25 | database setup is documented 26 | `there <virtesk-infrastructure-server.html#setting-up-postgres-database>`__, 27 | and switching virtual rooms is documented 28 | `here <switching-virtual-rooms.html>`__ 29 | 30 | 1:1-Mapping between TCs and VMs 31 | ------------------------------- 32 | 33 | Any any given point in time, there shall be a 1:1-mapping between TCs 34 | and VMs: A thinclient needs to uniquely determine the virtual machine 35 | assigned to it, and on the other hand, one VM can only be displayed on 36 | one TC at a time. 37 | 38 | However, it makes also sense to talk about a **1:2-mapping** (or 39 | 1:3-mapping, ...). One VM is mapped to the thinclient. The other VMs can 40 | be maintained (fresh rollout of virtual rooms, ...) without impacting 41 | users. This is also interesting for having dedicated VMs for special 42 | courses (exam VMs, VMs with special software, VMs with different 43 | operating systems, ...). 44 | 45 | This mapping is mandatory. Other VDI concepts, like pools of VMs, 46 | on-the-fly creation of VMs, per-User-VMs, or user-choosen VMs are not 47 | supported. 48 | 49 | Thinclient perspective: SQL Query 50 | --------------------------------- 51 | 52 | The following SQL Query is executed on a thinclient whenever virtesk-tc-connectspice wants to determine the virtual machine assigned to the thinclient, e.g. on thinclient startup, when re-connecting, and on thinclient shutdown, for shutting down the assined virtual machine if configured to do so. 53 | 54 | :: 55 | 56 | cur.execute("SELECT vm, thinclient, prio, id, shutdown_vm FROM thinclient_everything_view WHERE dhcp_hostname = ANY (%s) OR systemuuid = ANY (%s);", (dhcp_hostnames, sys_uuids)) 57 | 58 | There is only one database view that is accessed by thinclients: 59 | ``thinclient_everything_view``. This allows to adapt the database layout 60 | to individual needs, the only mandatory aspect is the view 61 | ``thinclient_everything_view`` which must be compatible with the query 62 | above. 63 | 64 | Thinclient identification criteria 65 | ---------------------------------- 66 | 67 | It is necessary to define a mapping from thinclients to virtual 68 | machines. For this mapping, some kind of thinclient identification is 69 | necessary. 70 | 71 | Virtesk-tc-connectspice supports two identifiers to uniquely identify 72 | the thinclient it is running on: 73 | 74 | **dhcp\_hostname**: 75 | 76 | - Determined by parsing the dhcp-leasefile on the thinclient. The 77 | leasefile is found by looking at the ``-lf`` Parameter of 78 | ``dhclient``. 79 | - Usefull to identify a workplace, independent of the thinclient device 80 | at that workplace. If the device needs replacement, the system 81 | administrator can simply adjust the dhcp configuration (new MAC 82 | adress). The system administrator does not need to adjust the 83 | thinclient-to-vm mapping, because the dhcp\_hostname will stay the 84 | same. 85 | 86 | **systemuuid**: 87 | 88 | - Determined by parsing the output of ``dmidecode -t1`` on the 89 | thinclient. 90 | - Usefull for uniquely identifying the thinclient device. It doesn't 91 | matter where or in what network the thinclient is located. The 92 | thinclient is able to connect to its assigned virtual machine, as 93 | long as it is able to connect to the postgres database and the 94 | virtualization manager/hosts. 95 | 96 | Sample database layout 97 | ---------------------- 98 | 99 | A sample database layout is provided in 100 | ``sample_config/database-layout.sql``. Instructions for loading the file 101 | are provided 102 | `here <virtesk-infrastructure-server.html#setting-up-postgres-database>`__. 103 | 104 | Thinclient DNS Domain 105 | --------------------- 106 | 107 | Depending on the DHCP server software used, **dhcp\_hostname** can be a 108 | simple hostname or a fully qualified domain name. 109 | 110 | Often it is handy to define the mapping from the 111 | *thinclient*-database-key to the dhcp\_hostname automatically. This is 112 | done in the view ``dhcphostname_to_thinclient_auto_mapping``. If FQDNs 113 | are used, then you need to adapt the dns domain in the definition of the 114 | view: 115 | 116 | :: 117 | 118 | (((timed_thinclient_to_vm_mapping.thinclient)::text || '.thinclients.yourdomain.site'::text))::character 119 | 120 | You can also change the view later, for example using pgadmin3. 121 | 122 | Important tables and views 123 | -------------------------- 124 | 125 | Table timed\_thinclient\_to\_vm\_mapping 126 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 127 | 128 | Defines the mapping between thinclients and VMs. 129 | 130 | +---------------+-------------+------------+---------------------------------------------+ 131 | | start\_date | end\_date | priority | comment | 132 | +===============+=============+============+=============================================+ 133 | | NULL | NULL | low | useful for permanent mapping | 134 | +---------------+-------------+------------+---------------------------------------------+ 135 | | defined | NULL | medium | switch to new VMs on start\_darte | 136 | +---------------+-------------+------------+---------------------------------------------+ 137 | | defined | defined | high | override assignment for a period of time. | 138 | +---------------+-------------+------------+---------------------------------------------+ 139 | 140 | The column ``shutdown_vm`` determines if the VM shall be shut down upon 141 | TC shutdown. 142 | 143 | The column ``thinclient`` is any arbitrary name to identify the thinclient. It does not need to be equal to the local host name or the dhcp host name. However, it is usefull if this database key and the dhcp hostname are equal, see `Thinclient DNS Domain <#thinclient-dns-domain>`__ and `dhcphostname\_to\_thinclient\_auto\_mapping <#view-dhcphostname-to-thinclient-auto-mapping>`__ 144 | 145 | Table dhcphostname\_to\_thinclient\_mapping 146 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 147 | 148 | Manual mapping from dhcp hostname to thinclient. Use this table to deal 149 | with apart thinclients that do not adhere to any naming convention. 150 | 151 | See also: 152 | `dhcphostname\_to\_thinclient\_auto\_mapping <#view-dhcphostname-to-thinclient-auto-mapping>`__ 153 | 154 | Table systemuuid\_to\_thinclient\_mapping 155 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 156 | 157 | Mapping from System UUID to thinclient database identifier: 158 | 159 | :: 160 | 161 | vdi=> select * from systemuuid_to_thinclient_mapping; 162 | systemuuid | thinclient 163 | --------------------------------------+------------- 164 | C7E99E73-5ADB-48B3-8B03-30FDF9E4B238 | test01-tc04 165 | (1 row) 166 | 167 | For every thinclient that you wanna identify by System UUID, one row 168 | needs to be added. 169 | 170 | View current\_thinclient\_to\_vm\_mapping 171 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 172 | 173 | Helping View. Used to filter and prioritize the entries in 174 | ``timed_thinclient_to_vm_mapping`` based on ``start_date`` and 175 | ``end_date``. 176 | 177 | View dhcphostname\_to\_thinclient\_auto\_mapping 178 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 179 | 180 | Helping View. Automatically creates a mapping 181 | ``myTC.thinclients.yourdomain.site ---> myTC`` for every myTC listed in 182 | ``timed_thinclient_to_vm_mapping``. 183 | 184 | See also: `Thinclient DNS Domain <#thinclient-dns-domain>`__. 185 | 186 | View sysinfo\_to\_thinclient\_mapping 187 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 188 | 189 | Helping View. Union of dhcphostname\_to\_thinclient\_auto\_mapping, 190 | dhcphostname\_to\_thinclient\_mapping, and 191 | systemuuid\_to\_thinclient\_mapping, with defined priorities. 192 | 193 | View thinclient\_everything\_view 194 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 195 | 196 | *"One view to rule them all, one view to find them, 197 | one view to connect them all and using Spice to bind them."* 198 | 199 | All information in the other tables and views is condensed in this one 200 | big view, ready for use by `virtesk-tc-connectspice <virtesk-tc-connectspice.html>`__. 201 | 202 | See also: `Thinclient perspective: SQL 203 | Query <#thinclient-perspective-sql-query>`__ 204 | -------------------------------------------------------------------------------- /doc/thinclients.rst: -------------------------------------------------------------------------------- 1 | Thinclients 2 | =========== 3 | 4 | .. toctree:: 5 | :maxdepth: 3 6 | 7 | virtesk-infrastructure-server.rst 8 | virtesk-tc-kickstart.rst 9 | ipxe-image.rst 10 | virtesk-tc-tools.rst 11 | virtesk-tc-connectspice.rst 12 | libusbredir-patching.rst 13 | tc-vm-mapping.rst 14 | -------------------------------------------------------------------------------- /doc/virtesk-tc-connectspice.rst: -------------------------------------------------------------------------------- 1 | .. |br| raw:: html 2 | 3 | <br /> 4 | 5 | VDI Software running on thinclients 6 | =================================== 7 | 8 | 9 | Introduction 10 | ------------ 11 | 12 | Virtesk-tc-connectspice is the software running on thinclients. 13 | 14 | It connects to the postgres database, determines the assigned VM, starts 15 | it if necessary, determines spice connection parameters, starts a 16 | spice-client (``remote-viewer``), and passes the connection parameters 17 | to the spice client using a unix domain socket (remote-control of 18 | spice-clients using SPICE-XPI API). 19 | 20 | Virtesk-tc-connectspice is not designed to run standalone. It needs to 21 | be run on properly configured thinclients. All necessary configuration 22 | is done when rolling out a thinclient using kickstart. 23 | 24 | It features a minimalistic GUI, based on gxmessage. The language is 25 | German, other languages are not available. 26 | 27 | The former name was ``connect_spice_client``. 28 | 29 | Virtesk-tc-connectspice consists of two programs: 30 | 31 | - **virtesk-tc-connectspice-main**: Main program as described above. 32 | - **virtesk-tc-connectspice-shutdown-vm**: Called by systemd on 33 | thinclient `shutdown <start-and-stop-management.html>`__. Will shutdown 34 | the VM asssigned to the thinclient if configured to do so. 35 | 36 | Configuration 37 | ------------- 38 | 39 | Main config file 40 | ~~~~~~~~~~~~~~~~ 41 | 42 | /etc/connect\_spice\_client/connect\_spice\_client.conf 43 | 44 | :: 45 | 46 | [general] 47 | log_config_file = connect_spice_client_logging.conf 48 | shutdown_command=/bin/sh -c "sudo shutdown -h now" 49 | reboot_command=/bin/sh -c "sudo shutdown -r now" 50 | 51 | # Postgres DB connection string 52 | # ADJUST 53 | postgres_db_connect=host=infrastructure-server sslmode=require connect_timeout=1 dbname=vdi user=vdi-readonly password=PASSWORD 54 | 55 | # Desktop notifications 56 | notify_cmd_waiting_for_dhcplease = notify-send -t 3000 -i /usr/share/icons/gnome/48x48/status/network-wired-disconnected.png "Waiting for network..." 57 | notify_cmd_waiting_for_db_connection = notify-send -t 3000 -i /usr/share/icons/gnome/48x48/status/network-wired-disconnected.png "Waiting for database..." 58 | notify_cmd_waiting_for_vm_launch = notify-send --hint string:transient:true -t 3000 -i /usr/share/icons/gnome/48x48/apps/preferences-desktop-remote-desktop.png "starting VM... please wait..." 59 | 60 | # GUI 61 | dialog_command_with_retry = gxmessage -buttons "neu verbinden:101,Thinclient herunterfahren:102,Thinclient neu starten:103,Support:104" -center -title "Nachricht" -default "neu verbinden" -ontop -noescape -wrap 62 | dialog_command_without_retry = gxmessage -buttons "Thinclient herunterfahren:102,Thinclient neu starten:103,Support:104" -center -title "Nachricht" -ontop -noescape -wrap 63 | dialog_command_support = gxmessage -center -name "Support Informationen" -title "Support Informationen" -wrap -buttons OK:0 -default OK 64 | 65 | support_message_file = support_message.txt 66 | 67 | config_tags_user_query=ovirt.thinclient* 68 | 69 | [connect] 70 | # ADJUST 71 | url=https://your-ovirt-manager.fqdn/api 72 | 73 | # ADJUST 74 | username=ovirt.thinclient@your-ovirt-authentication-domain 75 | # ADJUST 76 | password=PASSWORD 77 | ca_file=ovirt-manager.crt 78 | 79 | 80 | #insecure=True 81 | #filter=True 82 | #filter=False 83 | 84 | [spice] 85 | spice_ca_file=ovirt-manager.crt 86 | socket=/tmp/adsy-rhev-tools-spice-control-socket 87 | spice_client_command=/usr/bin/remote-viewer --spice-controller 88 | 89 | Technical user for accessing Ovirt REST API 90 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 91 | 92 | A technical user, ``ovirt.thinclient@your-ovirt-authentication-domain``, 93 | with appropriate permissions, is needed for accessing the Ovirt 94 | REST-API. 95 | 96 | Because of REST API limitations regarding unprivileged users, we grant 97 | minimalistic adminstrator permissions to our technical user. However, he 98 | only will be able to access VDI VMs and no other VMs. 99 | 100 | New Ovirt Role ``MinimalAdmin``: 101 | 102 | :: 103 | 104 | Navigation: Ovirt Webadmin ---> top right corner ---> configure ---> Roles ---> New 105 | 106 | Name: MinimalAdmin 107 | Description: Minimalistic Administrator Role 108 | Account Type: Admin 109 | 110 | Check Boxes: leave them all unchecked 111 | 112 | For adding new user ``ovirt.thinclient@your-ovirt-authentication-domain`` to Ovirt, it 113 | first needs to be created in ``your-ovirt-authentication-domain``. |br| 114 | Then, grant him the role ``MinimalAdmin`` using 115 | ``Ovirt Webadmin ---> top right corner ---> configure ---> System Permissions --> Add``. 116 | 117 | New Ovirt Role ``UserRoleWithReconnect``: 118 | 119 | :: 120 | 121 | Navigation: Ovirt Webadmin ---> top right corner ---> configure ---> Roles ---> New 122 | 123 | Name: UserRoleWithReconnect 124 | Description: Required for Ovirt VDI Thinclients 125 | Account Type: User 126 | 127 | Checkboxes: 128 | [X] Login Permissions (System --> Configure System) 129 | [X] Basic Operations (VM --> Basic Operations) 130 | [X] Remote Log In (VM --> Basic Operations) 131 | [X] Override opened console session (VM --> Administrative Operations) 132 | 133 | Virtesk-virtroom-rollout will grant ``UserRoleWithReconnect`` to 134 | ``ovirt.thinclient@your-ovirt-authentication-domain`` on freshly created 135 | VMs. 136 | 137 | See also: config-option ``tc_user`` in 138 | `virtesk-vm-rollout.conf <virtesk-vm-rollout-config.html>`__. 139 | 140 | Ovirt REST API: SSL CA 141 | ~~~~~~~~~~~~~~~~~~~~~~ 142 | 143 | The Ovirt SSL certificate authority needs to be configured for secure 144 | SSL communication. 145 | 146 | Fetch the CA file from http://your-ovirt-manager.fqdn/ca.crt, and put it 147 | into ``ovirt-manager.crt``: 148 | 149 | /etc/connect\_spice\_client/ovirt-manager.crt 150 | 151 | :: 152 | 153 | -----BEGIN CERTIFICATE----- 154 | # ADJUST 155 | ... 156 | -----END CERTIFICATE----- 157 | 158 | /etc/connect\_spice\_client/connect\_spice\_client\_logging.conf 159 | 160 | :: 161 | 162 | [formatters] 163 | keys=simpleFormatter,logFileFormatter 164 | 165 | [loggers] 166 | keys=root 167 | 168 | [handlers] 169 | #keys=consoleHandler,timedRotatingFileHandler,syslogDebugHandler 170 | keys=consoleHandler,timedRotatingFileHandler,syslogHandler 171 | 172 | [logger_root] 173 | level=DEBUG 174 | handlers=consoleHandler,timedRotatingFileHandler,syslogHandler 175 | 176 | [handler_consoleHandler] 177 | class=StreamHandler 178 | level=DEBUG 179 | formatter=simpleFormatter 180 | args=(sys.stderr,) 181 | 182 | [handler_syslogHandler] 183 | class=handlers.SysLogHandler 184 | level=DEBUG 185 | formatter=simpleFormatter 186 | args=('/dev/log',) 187 | 188 | # [handler_syslogDebugHandler] 189 | # class=connect_spice_client.syslog_debug_handler 190 | # level=DEBUG 191 | # formatter=simpleFormatter 192 | # args=('/dev/log',) 193 | # 194 | 195 | [handler_timedRotatingFileHandler] 196 | class=handlers.TimedRotatingFileHandler 197 | level=DEBUG 198 | formatter=logFileFormatter 199 | args=(os.path.expanduser('~/logs/connect_spice_client.log'), 'D', 1, 30) 200 | 201 | [formatter_simpleFormatter] 202 | format=%(asctime)s - %(levelname)s - %(message)s 203 | datefmt= 204 | 205 | [formatter_logFileFormatter] 206 | format=%(asctime)s - %(levelname)s - %(message)s 207 | datefmt= 208 | 209 | Support message 210 | ~~~~~~~~~~~~~~~ 211 | 212 | The following message is displayed whenever a user clicks on the the support-button on a thinclient: 213 | 214 | /etc/connect\_spice\_client/support\_message.txt 215 | 216 | :: 217 | 218 | =========================================================== 219 | Support 220 | =========================================================== 221 | 222 | For support, please call ... 223 | 224 | In addition, some system debug information will be displayed. 225 | -------------------------------------------------------------------------------- /doc/virtesk-tc-kickstart.rst: -------------------------------------------------------------------------------- 1 | .. |br| raw:: html 2 | 3 | <br /> 4 | 5 | Thinclient-Rollout using Kickstart 6 | ================================== 7 | 8 | 9 | **Goal - perfect rollout experience** 10 | 11 | 12 | #. Start a thinclient, boot from network 13 | #. Choose what you wanna do in a small graphical boot loader menu 14 | (optional) 15 | #. The thinclient installation will run completely unattended. 16 | #. After 10 minutes (30 minutes on very old hardware) the installation 17 | and configuration is completed 18 | #. The thinclient reboots itself 19 | #. The thinclient connects to the virtual machine 20 | `assigned <tc-vm-mapping.html>`__ to it 21 | #. The thinclient is ready, users can work 22 | 23 | The amount of data transferred over the network is quite small (1000M - 2000M, estimated value), so its perfectly possible to roll out several hundred thinclients at a time over a single gigabit uplink. |br| 24 | Everything is completely scripted (no disk images!), therefore, the rollout process and the thinclient system can be flexible adapted for new needs and new features. |br| 25 | Experienced system administrators dont need to be on-site for thinclient rollout, remote rollout using Wake-On-Lan is possible. Instead of PXE network booting, thinclients can also be re-installed using ssh and `kexec <virtesk-tc-tools.html#tc-rollout-kexec>`__. 26 | 27 | 28 | .. warning:: WARNING: DATA LOSS |br| The provided sample kickstart file will ERASE everything on all hard drives and on all usb drives on any computer where a thinclient rollout is attempted. This is indented this way (thinclients dont contain any data, so it's ok). 29 | 30 | Background 31 | ---------- 32 | 33 | There are several techiques for fully automated installation of an 34 | operating system: 35 | 36 | - **Unattended Setup** for Microsoft Windows 37 | - **Preseed** for Debian GNU/Linux 38 | - **Kickstart** for Red Hat Enterprise Linux, CentOS, Fedora, ... 39 | 40 | They all use a configuration file for scripting the installation process. Normally, the goal is to make sure the operating system gets installed without any user or sysadmin interaction. |br| 41 | In virtesk-vdi, a network-based kickstart-installation of Fedora 22 is used to deploy and configure thinclients. |br| 42 | The kickstart script file contains a very large post-section, written in bash. It contains all thinclient configuration files (inlined using bash-here-documents). It also makes sure the minimalistic desktop interface is completely locked down, to make sure that students cannot do anything else besides accessing the assigned virtual machine. 43 | 44 | 45 | PXE-Setup, Network Boot, Fedora Mirror, ... 46 | ------------------------------------------- 47 | 48 | Documented `here <virtesk-infrastructure-server.html>`__. 49 | 50 | Sample Kickstart File 51 | --------------------- 52 | 53 | Located at: ``sample_config/tc_rollout.ks`` 54 | 55 | Alot of adjustment needs to be done to make the kickstart file work in a new environment. |br| 56 | The sections needing adjustmend are marked with ``# ADJUST``. 57 | 58 | All references to ``infrastructure-server`` need to be replaced with the 59 | actual FQDN of your infrastructure server. 60 | 61 | The thinclient software, *virtesk-tc-connectspice*, needs to be 62 | configured as described `here <virtesk-tc-connectspice.html>`__. 63 | 64 | Adapting the sample kickstart file isn't easy, and if you are completely new to kickstart, it is actually quite hard. Don't give up, everyone can learn PXE / network installation / kickstart! |br| 65 | For analyzing kickstart problems, remote logging is useful. On a properly configured `infrastructure server <virtesk-infrastructure-server.html>`__, logfiles are available at ``/var/log/remote``. However, not everything is logged, because remote logging is only done in stage 2 of the Fedora installer. 66 | 67 | Kickstart literature 68 | -------------------- 69 | 70 | Red Hat provides alot of documentation for Red Hat Enterprise Linux 71 | `here <https://access.redhat.com/documentation/en/red-hat-enterprise-linux/>`__. 72 | 73 | The *Installation Guide*, the *System Administrator's Guide*, *Networking Guide* provide alot of information about network boot, network installation, systemd, etc. |br| 74 | Most of the documentation for RHEL7 is also valid for Fedora 22/23. 75 | 76 | Kickstart documentation is available 77 | `here <https://github.com/rhinstaller/pykickstart/blob/master/docs/kickstart-docs.rst>`__. 78 | 79 | Kickstart boot options are documented 80 | `here <https://rhinstaller.github.io/anaconda/boot-options.html#kickstart>`__. 81 | -------------------------------------------------------------------------------- /doc/virtesk-vm-rollout-install.rst: -------------------------------------------------------------------------------- 1 | Installing VM rollout tools 2 | ========================================= 3 | 4 | Overview 5 | -------- 6 | 7 | To run virtesk-vm-rollout, the system must be prepared first: 8 | 9 | - Installing virtesk-vm-rollout 10 | - Minimal configuration 11 | - Setting up `payload injection mechanism <sftp-floppy-upload.html>`__ 12 | - Configuring virtual rooms 13 | 14 | Installation 15 | ------------ 16 | 17 | Installation has been tested on CentOS 7. Fedora >= 20 should work as 18 | well. RHEL 6 / CentOS 6 might not work (Code requires python 2.7) 19 | 20 | Dependencies: 21 | 22 | :: 23 | 24 | yum install ovirt-engine-sdk-python python-mako mtools 25 | 26 | Cloneing git repository: 27 | 28 | :: 29 | 30 | cd /opt 31 | git clone http://path.to.git.repository/virtesk-vdi.src.git 32 | 33 | .. raw:: html 34 | 35 | <!--- 36 | FIXME: git path above 37 | --> 38 | 39 | Bash search path: ``/etc/profile.d/virtesk-vm-rollout.sh`` 40 | 41 | :: 42 | 43 | pathmunge /opt/virtesk-vdi.src/virtesk-vm-rollout after 44 | 45 | Minimal configuration 46 | --------------------- 47 | 48 | :: 49 | 50 | mkdir /etc/virtesk-vdi/ 51 | mkdir /var/log/virtesk-vdi/ 52 | 53 | # SSL Certificyte of Ovirt engine Certificate Authority 54 | vim /etc/virtesk-vdi/ca.crt 55 | # Template File for Windows Unattended Setup configuration file 56 | vim /etc/virtesk-vdi/Autounattend-production.xml.template 57 | # Logging settings 58 | vim /etc/virtesk-vdi/logging.conf 59 | # Definitions of virtual rooms: 60 | vim /etc/virtesk-vdi/virtesk-vm-rollout.conf 61 | 62 | Details are documented `here <virtesk-vm-rollout-config.html>`__. 63 | 64 | Setting up payload injection mechanism 65 | -------------------------------------- 66 | 67 | For rolling out virtual rooms, a payload injection is necessary. 68 | 69 | Payload injection is done by creating floppy images containing 70 | ``a:\sysprep.inf``, uploading them using SFTP, and attaching them to 71 | RHEV/Ovirt VMs using vdsm-hook-floppy. 72 | 73 | The initial setup is quite complicated, but it has to be done only once, 74 | afterwards it will "just work". 75 | 76 | Detailed instructions can be found `here <sftp-floppy-upload.html>`__. 77 | 78 | Testing 79 | ------- 80 | 81 | After initial installation and after the configuration of at least one 82 | virtual room, run the following command to check if the config is valid 83 | and if tools do work in general: 84 | 85 | :: 86 | 87 | virtesk-virtroom-show room01 88 | -------------------------------------------------------------------------------- /doc/virtesk-vm-rollout.rst: -------------------------------------------------------------------------------- 1 | Rollout tools for virtual rooms 2 | ============================================= 3 | 4 | Tools for creating and manageing VDI VMs, grouped into virtual rooms. 5 | 6 | -------------- 7 | 8 | Introduction 9 | ------------ 10 | 11 | virtesk-virtroom-\* is a set of tools for creating/cloneing a lot of windows 7 virtual machines. 12 | Those win7 VMs can then be assigned to and displayed on thinclients. 13 | 14 | The script creates new VMs based on an existing Ovirt VM Template, and 15 | assings hostname, static IP address, ... to it and makes sure the new VM 16 | is joined into an Active Directory (or Samba4) domain. 17 | 18 | The process is based on Windows Sysprep / Unattended.xml technologies. 19 | 20 | See also: `Compatibility <compatibility.html>`__ 21 | 22 | -------------- 23 | 24 | Tools 25 | ----- 26 | 27 | virtesk-virtroom-show 28 | ~~~~~~~~~~~~~~~~~~~~~~ 29 | 30 | Parses and validates the room configuration, checks if the VMs exists, 31 | and lists their snapshosts. 32 | 33 | virtesk-virtroom-rollout 34 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 35 | 36 | Roll out a virtual room. 37 | 38 | Prerequisites: 39 | 40 | - VMs of the virtual room shall not exist 41 | 42 | How it works: 43 | 44 | #. Configuration parsing and validation 45 | #. Create VMs from Ovirt Template 46 | #. Attach Network to VM 47 | #. Create individual ``Autounattend.xml`` using a template mechanism 48 | #. Generate payload floppy, containing individual ``Autounattend.xml`` 49 | (filename: ``A:\sysprep.inf``) 50 | #. Run VM with payload attached. This configures Windows according to 51 | the settings in ``Autounattend.xml`` 52 | #. Wait until all VMs of a virtual room are shut down 53 | #. Postprocess VMs (USB settings, stateless feature, add permissions for 54 | technical vdi accounts, create snapshots, ...) 55 | #. Start VMs 56 | 57 | virtesk-virtroom-delete 58 | ~~~~~~~~~~~~~~~~~~~~~~~~ 59 | 60 | Deletes all VMs of a virtual room. 61 | 62 | virtesk-virtroom-reset 63 | ~~~~~~~~~~~~~~~~~~~~~~~ 64 | 65 | Reset VMs of a virtual room to a snapshot state. 66 | 67 | How it works: 68 | 69 | #. Configuration parsing and validation 70 | #. Stop VMs if they are running 71 | #. Restore state to snapshot 72 | #. Optional: Start VMs again 73 | 74 | Reset to snapshot state is not supported for stateless VMs. 75 | 76 | virtesk-virtroom-start 77 | ~~~~~~~~~~~~~~~~~~~~~~~ 78 | 79 | Starts all VMs in a virtual room. 80 | 81 | Details: 82 | 83 | - Ignores already running VMs 84 | - No validation is done - returns as soon as the start signal has been 85 | sent to all VMs. Does not wait for the VMs to launch. 86 | 87 | virtesk-virtroom-shutdown 88 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 89 | 90 | Shut down all VMs in a virtual room. 91 | 92 | Details: 93 | 94 | - Clean shutdown: A signal (ACPI shutdown or guest agent shutdown) is 95 | sent to the operating system of the VM. 96 | Assumes that ACPI daemon and/or guest agent is properly configured 97 | inside the VM and that the VM does a clean shutdown when told to do 98 | so. 99 | - Ignores already stopped VMs 100 | - No validation is done - returns as soon as the shutdown signal has 101 | been sent to all VMs. Does not wait for the VMs to shut down. 102 | 103 | Usage 104 | ----- 105 | 106 | .. code:: bash 107 | 108 | virtesk-virtroom-show [--config CONFIG] myroom 109 | virtesk-virtroom-rollout [--config CONFIG] myroom 110 | virtesk-virtroom-delete [--config CONFIG] myroom 111 | virtesk-virtroom-reset [--config CONFIG] myroom 112 | 113 | ``myroom`` is the virtual room to act on, e.g. the room to 114 | show/rollout/delete/reset. 115 | 116 | The following config file locations are used, first match wins: 117 | 118 | - Command line argument 119 | (``--config /path/to/virtesk-vm-rollout.conf``) 120 | - ``~/.config/virtesk-vdi/virtesk-vm-rollout.conf`` 121 | - ``/etc/virtesk-vdi/virtesk-vm-rollout.conf`` 122 | 123 | Many virtual rooms 124 | ------------------ 125 | 126 | When manageing alot of virtual rooms, bash features can be handy: 127 | 128 | :: 129 | 130 | for room in room{01..10}; do virtesk-virtroom-show $room; done 131 | for room in room01 room02 room03; do virtesk-virtroom-show $room; done 132 | for room in $(cat room-list.txt); do virtesk-virtroom-show $room; done 133 | 134 | See also 135 | -------- 136 | 137 | - `Installing virtesk-vm-rollout <virtesk-vm-rollout-install.html>`__ 138 | - `Defining and configuring virtual 139 | rooms <virtesk-vm-rollout-config.html>`__ 140 | - `Windows Goldimage <goldimage.html>`__ 141 | - `Windows Unattended Setup <autounattend.html>`__ 142 | - `Quality control after rollout <quality_control.html>`__ 143 | -------------------------------------------------------------------------------- /doc/virtual_rooms.rst: -------------------------------------------------------------------------------- 1 | Virtual Rooms 2 | ============= 3 | 4 | .. toctree:: 5 | :maxdepth: 3 6 | 7 | virtesk-vm-rollout.rst 8 | virtesk-vm-rollout-install.rst 9 | virtesk-vm-rollout-config.rst 10 | switching-virtual-rooms.rst 11 | stateless_and_snapshot_features.rst 12 | quality_control.rst 13 | autounattend.rst 14 | goldimage.rst 15 | start-and-stop-management.rst 16 | -------------------------------------------------------------------------------- /doc/visio/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | bash -c 'for i in *.pdf; do { convert -density 600 -alpha deactivate $$i $$(basename $$i .pdf).png; convert -resize 800x565 $$(basename $$i .pdf).png $$(basename $$i .pdf)-small.png; } & done; echo -n "Waiting for childs..."; wait; echo done.' 3 | -------------------------------------------------------------------------------- /doc/visio/virtesk-archidecture-overview-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-archidecture-overview-small.png -------------------------------------------------------------------------------- /doc/visio/virtesk-archidecture-overview.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-archidecture-overview.pdf -------------------------------------------------------------------------------- /doc/visio/virtesk-archidecture-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-archidecture-overview.png -------------------------------------------------------------------------------- /doc/visio/virtesk-archidecture-overview.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-archidecture-overview.vsdx -------------------------------------------------------------------------------- /doc/visio/virtesk-sysadmin-features-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-sysadmin-features-small.png -------------------------------------------------------------------------------- /doc/visio/virtesk-sysadmin-features.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-sysadmin-features.pdf -------------------------------------------------------------------------------- /doc/visio/virtesk-sysadmin-features.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-sysadmin-features.png -------------------------------------------------------------------------------- /doc/visio/virtesk-sysadmin-features.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-sysadmin-features.vsdx -------------------------------------------------------------------------------- /doc/visio/virtesk-vdi-connection-setup-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-vdi-connection-setup-small.png -------------------------------------------------------------------------------- /doc/visio/virtesk-vdi-connection-setup.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-vdi-connection-setup.pdf -------------------------------------------------------------------------------- /doc/visio/virtesk-vdi-connection-setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-vdi-connection-setup.png -------------------------------------------------------------------------------- /doc/visio/virtesk-vdi-connection-setup.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-vdi-connection-setup.vsdx -------------------------------------------------------------------------------- /doc/visio/virtesk-vdi-tc-rollout-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-vdi-tc-rollout-small.png -------------------------------------------------------------------------------- /doc/visio/virtesk-vdi-tc-rollout.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-vdi-tc-rollout.pdf -------------------------------------------------------------------------------- /doc/visio/virtesk-vdi-tc-rollout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-vdi-tc-rollout.png -------------------------------------------------------------------------------- /doc/visio/virtesk-vdi-tc-rollout.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-vdi-tc-rollout.vsdx -------------------------------------------------------------------------------- /doc/visio/virtesk-virtual-rooms-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-virtual-rooms-small.png -------------------------------------------------------------------------------- /doc/visio/virtesk-virtual-rooms.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-virtual-rooms.pdf -------------------------------------------------------------------------------- /doc/visio/virtesk-virtual-rooms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-virtual-rooms.png -------------------------------------------------------------------------------- /doc/visio/virtesk-virtual-rooms.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adfinis/virtesk/2a9f9fcb705172663efbff996df675f511a49f3f/doc/visio/virtesk-virtual-rooms.vsdx -------------------------------------------------------------------------------- /misc/vdsm-spice-disconnect-actions-backport/README.txt: -------------------------------------------------------------------------------- 1 | Documentation is avaliable in: 2 | ../../doc/spice-disconnect-actions.rst 3 | -------------------------------------------------------------------------------- /misc/vdsm-spice-disconnect-actions-backport/vdsm-spice-disconnect-actions-backport.patch: -------------------------------------------------------------------------------- 1 | diff -pru vdsm-4.16.30/vdsm/clientIF.py vdsm-4.16.30-vdsm-spice-disconnect-actions-backport/vdsm/clientIF.py 2 | --- vdsm-4.16.30/vdsm/clientIF.py 2015-11-23 16:01:24.000000000 +0100 3 | +++ vdsm-4.16.30-vdsm-spice-disconnect-actions-backport/vdsm/clientIF.py 2016-04-05 11:45:16.000000000 +0200 4 | @@ -556,9 +556,10 @@ class clientIF(object): 5 | 'authScheme %s subject %s', 6 | phase, localAddr, remoteAddr, authScheme, subject) 7 | if phase == libvirt.VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE: 8 | - v.onConnect(remoteAddr['node']) 9 | + v.onConnect(remoteAddr['node'], remoteAddr['service']) 10 | elif phase == libvirt.VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT: 11 | - v.onDisconnect() 12 | + v.onDisconnect(clientIp=remoteAddr['node'], 13 | + clientPort=remoteAddr['service']) 14 | elif eventid == libvirt.VIR_DOMAIN_EVENT_ID_WATCHDOG: 15 | action, = args[:-1] 16 | v._onWatchdogEvent(action) 17 | diff -pru vdsm-4.16.30/vdsm/virt/vm.py vdsm-4.16.30-vdsm-spice-disconnect-actions-backport/vdsm/virt/vm.py 18 | --- vdsm-4.16.30/vdsm/virt/vm.py 2015-11-23 16:01:24.000000000 +0100 19 | +++ vdsm-4.16.30-vdsm-spice-disconnect-actions-backport/vdsm/virt/vm.py 2016-04-05 11:43:31.000000000 +0200 20 | @@ -1960,6 +1960,7 @@ class Vm(object): 21 | self._powerDownEvent = threading.Event() 22 | self._liveMergeCleanupThreads = {} 23 | self._shutdownReason = None 24 | + self._clientPort = '' 25 | 26 | def _get_lastStatus(self): 27 | # note that we don't use _statusLock here. One of the reasons is the 28 | @@ -2471,15 +2472,51 @@ class Vm(object): 29 | except Exception: 30 | self.log.error("Reboot event failed", exc_info=True) 31 | 32 | - def onConnect(self, clientIp=''): 33 | + def onConnect(self, clientIp='', clientPort=''): 34 | if clientIp: 35 | self.conf['clientIp'] = clientIp 36 | + self._clientPort = clientPort 37 | 38 | def _timedDesktopLock(self): 39 | - if not self.conf.get('clientIp', ''): 40 | - self.guestAgent.desktopLock() 41 | + # This is not a definite fix, we're aware that there is still the 42 | + # possibility of a race condition, however this covers more cases 43 | + # than before and a quick gain 44 | + 45 | + if not self.conf.get('clientIp', '') and not self.destroyed: 46 | + delay = config.get('vars', 'user_shutdown_timeout') 47 | + timeout = config.getint('vars', 'sys_shutdown_timeout') 48 | + daction = 'undef' 49 | + 50 | + if 'spice_disconnect_action' in self.conf['custom']: 51 | + daction = ( 52 | + self.conf['custom']['spice_disconnect_action'].lower() 53 | + ) 54 | + 55 | + if daction == 'lock' or daction == 'undef': 56 | + self.guestAgent.desktopLock() 57 | + elif daction == 'logoff': 58 | + self.guestAgent.desktopLogoff(True) 59 | + elif daction == 'reboot': 60 | + self.shutdown(delay=delay, reboot=True, timeout=timeout, 61 | + message='Scheduled reboot on disconnect', 62 | + force=True) 63 | + elif daction == 'shutdown': 64 | + self.shutdown(delay=delay, reboot=False, timeout=timeout, 65 | + message='Scheduled shutdown on disconnect', 66 | + force=True) 67 | + elif daction == 'noop': 68 | + pass 69 | + else: 70 | + self.guestAgent.desktopLock() 71 | + 72 | + def onDisconnect(self, detail=None, clientIp='', clientPort=''): 73 | + if self.conf['clientIp'] != clientIp: 74 | + self.log.debug('Ignoring disconnect event because ip differs') 75 | + return 76 | + if self._clientPort and self._clientPort != clientPort: 77 | + self.log.debug('Ignoring disconnect event because ports differ') 78 | + return 79 | 80 | - def onDisconnect(self, detail=None): 81 | self.conf['clientIp'] = '' 82 | # This is a hack to mitigate the issue of spice-gtk not respecting the 83 | # configured secure channels. Spice-gtk is always connecting first to 84 | @@ -2494,6 +2531,22 @@ class Vm(object): 85 | # Multiple desktopLock calls won't matter if we're really disconnected 86 | # It is not harmful. And the threads will exit after 2 seconds anyway. 87 | _DESKTOP_LOCK_TIMEOUT = 2 88 | + 89 | + if 'spice_disconnect_waittime_seconds' in self.conf['custom']: 90 | + try: 91 | + _DESKTOP_LOCK_TIMEOUT = int( 92 | + self.conf['custom']['spice_disconnect_waittime_seconds'] 93 | + ) 94 | + except ValueError: 95 | + self.log.error( 96 | + "Cannot convert spice_disconnect_waittime_seconds={0} to " 97 | + "int. Proceeding with default value.".format( 98 | + self.conf['custom'][ 99 | + 'spice_disconnect_waittime_seconds' 100 | + ] 101 | + ), 102 | + exc_info=True) 103 | + 104 | timer = threading.Timer(_DESKTOP_LOCK_TIMEOUT, self._timedDesktopLock) 105 | timer.start() 106 | 107 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pytest-mock 2 | -------------------------------------------------------------------------------- /sample_config/database-layout-update-001.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Update database layout from previous version 3 | -- 4 | 5 | ALTER TABLE timed_thinclient_to_vm_mapping ADD resolution character varying NULL; 6 | 7 | DROP VIEW current_thinclient_to_vm_mapping; 8 | CREATE VIEW current_thinclient_to_vm_mapping AS 9 | SELECT 10 | f.id, 11 | f.thinclient, 12 | f.vm, 13 | f.resolution, 14 | f.start_date, 15 | f.end_date, 16 | f.prio, 17 | f.shutdown_vm 18 | FROM ( 19 | SELECT 20 | timed_thinclient_to_vm_mapping.id, 21 | timed_thinclient_to_vm_mapping.thinclient, 22 | timed_thinclient_to_vm_mapping.vm, 23 | timed_thinclient_to_vm_mapping.resolution, 24 | timed_thinclient_to_vm_mapping.start_date, 25 | timed_thinclient_to_vm_mapping.end_date, 26 | 0 AS prio, 27 | timed_thinclient_to_vm_mapping.shutdown_vm 28 | FROM 29 | timed_thinclient_to_vm_mapping 30 | WHERE ( 31 | (timed_thinclient_to_vm_mapping.start_date IS NULL) 32 | AND 33 | (timed_thinclient_to_vm_mapping.end_date IS NULL) 34 | ) 35 | UNION SELECT 36 | timed_thinclient_to_vm_mapping.id, 37 | timed_thinclient_to_vm_mapping.thinclient, 38 | timed_thinclient_to_vm_mapping.vm, 39 | timed_thinclient_to_vm_mapping.resolution, 40 | timed_thinclient_to_vm_mapping.start_date, 41 | timed_thinclient_to_vm_mapping.end_date, 42 | 1 AS prio, 43 | timed_thinclient_to_vm_mapping.shutdown_vm 44 | FROM 45 | timed_thinclient_to_vm_mapping 46 | WHERE ( 47 | (timed_thinclient_to_vm_mapping.start_date <= now()) 48 | AND 49 | (timed_thinclient_to_vm_mapping.end_date IS NULL)) 50 | UNION SELECT 51 | timed_thinclient_to_vm_mapping.id, 52 | timed_thinclient_to_vm_mapping.thinclient, 53 | timed_thinclient_to_vm_mapping.vm, 54 | timed_thinclient_to_vm_mapping.resolution, 55 | timed_thinclient_to_vm_mapping.start_date, 56 | timed_thinclient_to_vm_mapping.end_date, 57 | 2 AS prio, 58 | timed_thinclient_to_vm_mapping.shutdown_vm 59 | FROM 60 | timed_thinclient_to_vm_mapping 61 | WHERE ( 62 | (now() >= timed_thinclient_to_vm_mapping.start_date) 63 | AND 64 | (now() <= timed_thinclient_to_vm_mapping.end_date) 65 | ) 66 | ) f 67 | ORDER BY 68 | f.prio DESC, 69 | f.start_date, 70 | f.id; 71 | 72 | 73 | ALTER TABLE public.current_thinclient_to_vm_mapping OWNER TO "vdi-dbadmin"; 74 | 75 | DROP VIEW thinclient_everything_view; 76 | SELECT DISTINCT 77 | s.thinclient, 78 | c.vm, 79 | c.resolution, 80 | s.dhcp_hostname, 81 | s.systemuuid, 82 | c.id, 83 | c.start_date, 84 | c.end_date, 85 | c.prio, 86 | c.shutdown_vm 87 | FROM ( 88 | sysinfo_to_thinclient_mapping s 89 | LEFT JOIN current_thinclient_to_vm_mapping c ON (((s.thinclient)::text = (c.thinclient)::text)) 90 | ) 91 | ORDER BY 92 | c.prio DESC, 93 | c.start_date, 94 | c.id; 95 | -------------------------------------------------------------------------------- /sample_config/database-layout.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- This is the main table containing configuration of thin clients 3 | -- 4 | 5 | CREATE TABLE timed_thinclient_to_vm_mapping ( 6 | vm character varying NOT NULL, 7 | thinclient character varying NOT NULL, 8 | resolution character varying NULL, 9 | start_date TIMESTAMP WITH TIME ZONE, 10 | end_date TIMESTAMP WITH TIME ZONE, 11 | id bigint NOT NULL, 12 | shutdown_vm boolean DEFAULT FALSE NOT NULL 13 | ); 14 | 15 | 16 | -- 17 | -- mapping between thinclient and vm depending 18 | -- on start time and priority 19 | -- 20 | 21 | CREATE VIEW current_thinclient_to_vm_mapping AS 22 | SELECT 23 | f.id, 24 | f.thinclient, 25 | f.vm, 26 | f.resolution, 27 | f.start_date, 28 | f.end_date, 29 | f.prio, 30 | f.shutdown_vm 31 | FROM ( 32 | SELECT 33 | timed_thinclient_to_vm_mapping.id, 34 | timed_thinclient_to_vm_mapping.thinclient, 35 | timed_thinclient_to_vm_mapping.vm, 36 | timed_thinclient_to_vm_mapping.resolution, 37 | timed_thinclient_to_vm_mapping.start_date, 38 | timed_thinclient_to_vm_mapping.end_date, 39 | 0 AS prio, 40 | timed_thinclient_to_vm_mapping.shutdown_vm 41 | FROM 42 | timed_thinclient_to_vm_mapping 43 | WHERE ( 44 | (timed_thinclient_to_vm_mapping.start_date IS NULL) 45 | AND 46 | (timed_thinclient_to_vm_mapping.end_date IS NULL) 47 | ) 48 | UNION SELECT 49 | timed_thinclient_to_vm_mapping.id, 50 | timed_thinclient_to_vm_mapping.thinclient, 51 | timed_thinclient_to_vm_mapping.vm, 52 | timed_thinclient_to_vm_mapping.resolution, 53 | timed_thinclient_to_vm_mapping.start_date, 54 | timed_thinclient_to_vm_mapping.end_date, 55 | 1 AS prio, 56 | timed_thinclient_to_vm_mapping.shutdown_vm 57 | FROM 58 | timed_thinclient_to_vm_mapping 59 | WHERE ( 60 | (timed_thinclient_to_vm_mapping.start_date <= now()) 61 | AND 62 | (timed_thinclient_to_vm_mapping.end_date IS NULL)) 63 | UNION SELECT 64 | timed_thinclient_to_vm_mapping.id, 65 | timed_thinclient_to_vm_mapping.thinclient, 66 | timed_thinclient_to_vm_mapping.vm, 67 | timed_thinclient_to_vm_mapping.resolution, 68 | timed_thinclient_to_vm_mapping.start_date, 69 | timed_thinclient_to_vm_mapping.end_date, 70 | 2 AS prio, 71 | timed_thinclient_to_vm_mapping.shutdown_vm 72 | FROM 73 | timed_thinclient_to_vm_mapping 74 | WHERE ( 75 | (now() >= timed_thinclient_to_vm_mapping.start_date) 76 | AND 77 | (now() <= timed_thinclient_to_vm_mapping.end_date) 78 | ) 79 | ) f 80 | ORDER BY 81 | f.prio DESC, 82 | f.start_date, 83 | f.id; 84 | 85 | -- 86 | -- add domain to thinclient dhcp hostname 87 | -- 88 | 89 | CREATE VIEW dhcphostname_to_thinclient_auto_mapping AS 90 | SELECT DISTINCT 91 | (((timed_thinclient_to_vm_mapping.thinclient)::text || '.thinclients.yourdomain.site'::text))::character varying AS dhcp_hostname, 92 | timed_thinclient_to_vm_mapping.thinclient 93 | FROM 94 | timed_thinclient_to_vm_mapping 95 | UNION SELECT DISTINCT 96 | timed_thinclient_to_vm_mapping.thinclient AS dhcp_hostname, 97 | timed_thinclient_to_vm_mapping.thinclient 98 | FROM 99 | timed_thinclient_to_vm_mapping; 100 | 101 | -- 102 | -- assign dhcp hostname to thinclient 103 | -- 104 | 105 | CREATE TABLE dhcphostname_to_thinclient_mapping ( 106 | dhcp_hostname character varying NOT NULL, 107 | thinclient character varying NOT NULL 108 | ); 109 | 110 | -- 111 | -- assign systemuid to thinclient 112 | -- 113 | 114 | CREATE TABLE systemuuid_to_thinclient_mapping ( 115 | systemuuid character varying NOT NULL, 116 | thinclient character varying NOT NULL 117 | ); 118 | 119 | -- 120 | -- view combining dhcp host, system id and thinclient 121 | -- 122 | 123 | CREATE VIEW sysinfo_to_thinclient_mapping AS 124 | SELECT 125 | f.dhcp_hostname, 126 | f.systemuuid, 127 | f.thinclient, 128 | f.prio 129 | FROM ( 130 | SELECT 131 | dhcphostname_to_thinclient_auto_mapping.dhcp_hostname, 132 | NULL::character varying AS systemuuid, 133 | dhcphostname_to_thinclient_auto_mapping.thinclient, 134 | 100 AS prio 135 | FROM 136 | dhcphostname_to_thinclient_auto_mapping 137 | UNION SELECT 138 | dhcphostname_to_thinclient_mapping.dhcp_hostname, 139 | NULL::character varying AS systemuuid, 140 | dhcphostname_to_thinclient_mapping.thinclient, 141 | 200 AS prio 142 | FROM 143 | dhcphostname_to_thinclient_mapping 144 | UNION SELECT 145 | NULL::character varying AS dhcp_hostname, 146 | systemuuid_to_thinclient_mapping.systemuuid, 147 | systemuuid_to_thinclient_mapping.thinclient, 148 | 300 AS prio 149 | FROM 150 | systemuuid_to_thinclient_mapping 151 | ) f 152 | ORDER BY 153 | f.prio DESC; 154 | 155 | -- 156 | -- View combining all tables concerning thin client 157 | -- 158 | 159 | CREATE VIEW thinclient_everything_view AS 160 | SELECT DISTINCT 161 | s.thinclient, 162 | c.vm, 163 | c.resolution, 164 | s.dhcp_hostname, 165 | s.systemuuid, 166 | c.id, 167 | c.start_date, 168 | c.end_date, 169 | c.prio, 170 | c.shutdown_vm 171 | FROM ( 172 | sysinfo_to_thinclient_mapping s 173 | LEFT JOIN current_thinclient_to_vm_mapping c ON (((s.thinclient)::text = (c.thinclient)::text)) 174 | ) 175 | ORDER BY 176 | c.prio DESC, 177 | c.start_date, 178 | c.id; 179 | 180 | CREATE SEQUENCE timed_thinclient_to_vm_mapping_id_seq 181 | START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; 182 | 183 | ALTER TABLE ONLY timed_thinclient_to_vm_mapping 184 | ALTER COLUMN id 185 | SET DEFAULT nextval('timed_thinclient_to_vm_mapping_id_seq'::regclass); 186 | 187 | ALTER TABLE ONLY dhcphostname_to_thinclient_mapping 188 | ADD CONSTRAINT dhcphostname_to_thinclient_mapping_pkey PRIMARY KEY (dhcp_hostname); 189 | 190 | ALTER TABLE ONLY systemuuid_to_thinclient_mapping 191 | ADD CONSTRAINT systemuuid_to_thinclient_mapping_pkey PRIMARY KEY (systemuuid); 192 | 193 | ALTER TABLE ONLY timed_thinclient_to_vm_mapping 194 | ADD CONSTRAINT timed_thinclient_to_vm_mapping_pkey PRIMARY KEY (id); 195 | -------------------------------------------------------------------------------- /sample_config/generatesql-statements.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ROOMS="test01 test02 test03 test04 test05 test06 test07 test08 test09 test10" 4 | 5 | for ROOM in $ROOMS; do 6 | IDs=$(seq 1 30) 7 | echo "# Room ${ROOM}" 8 | for ID in ${IDs}; do 9 | ID_TWODIGIT=$(printf "%02d" ${ID}) 10 | TC_NAME="${ROOM}-tc${ID_TWODIGIT}" 11 | VM_NAME="${ROOM}-vd${ID_TWODIGIT}" 12 | cat <<-ENDofSQL 13 | UPDATE timed_thinclient_to_vm_mapping 14 | SET vm='${VM_NAME}' WHERE thinclient='${TC_NAME}' 15 | AND start_date IS NULL AND end_date IS NULL; 16 | 17 | ENDofSQL 18 | done 19 | echo 20 | done 21 | 22 | -------------------------------------------------------------------------------- /sample_config/virtesk-tc-tools.conf: -------------------------------------------------------------------------------- 1 | # Configuration for virtesk-tc-tools. 2 | 3 | # Documentation: 4 | # * see doc/virtesk-tc-tools.rst 5 | 6 | # DEVELOPER_MODE=0 =====> quiet mode 7 | # DEVELOPER_MODE=1 =====> verbose debug messages 8 | DEVELOPER_MODE=0 9 | 10 | # Domain appended to thinclient short names 11 | TC_DOMAIN=myorganization.mydomain 12 | 13 | # SSH options for tc_ssh 14 | SSH_GLOBAL_OPTS="-q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=4 -i /etc/virtesk-vdi/virtesk-thinclient-ssh-private-key-id_rsa" 15 | 16 | # Commands to run on a thinclient in order 17 | # to re-install itself using kexec and kickstart 18 | # Please adjust the URLs to point to the correct locations 19 | # on your infrastructure server. 20 | # Kernel commandline should be the same as when doing PXE rollout. 21 | ROLLOUT_CMDLINE='rm vmlinuz; rm initrd.img; wget http://infrastructure-server/tftpboot/fedora22-x86_64-pxeboot/vmlinuz; wget http://infrastructure-server/tftpboot/fedora22-x86_64-pxeboot/initrd.img; kexec -l vmlinuz --initrd=initrd.img --reset-vga --append="net.ifnames=0 enforcing=0 inst.ks=http://infrastructure-server/mirror/private/thinclients/kickstart/tc_rollout.ks"; shutdown -r now' 22 | 23 | # Commands to run on a thinclient in order to generate a screenshot 24 | # and to write it to standart output. 25 | SCREENSHOT_CMDLINE="DISPLAY=:0 xwd -root | convert - png:-" 26 | 27 | # Directory where screenshots shall be stored. 28 | SCREENSHOT_DIR=/screenshot 29 | 30 | -------------------------------------------------------------------------------- /sample_config/virtesk-vm-rollout.conf: -------------------------------------------------------------------------------- 1 | # Section holding general settings 2 | [general] 3 | # ADJUST: sftp-server 4 | sftp_floppy_upload_cmd = "(echo put {0}; echo chmod 666 {1}; echo ls -l {1}) | sftp sftp-floppy-upload@sftp-server:/floppy/" 5 | # ADJUST: sftp-server 6 | sftp_floppy_cleanup_cmd = "echo rm {0} | sftp sftp-floppy-upload@sftp-server:/floppy/" 7 | # ADJUST: nfs-mount-point 8 | ovirt_worker_floppy_prefix = "/rhev/data-center/mnt/nfs-mount-point/floppy" 9 | [[connect]] 10 | # ADJUST: ovirt-manager hostname 11 | url = "https://ovirt-manager/api" 12 | # ADJUST: Username (any admin account is fine) 13 | username = "admin@internal" 14 | # ADJUST: Password 15 | password = "PASSWORD" 16 | ca_file = "ca.crt" 17 | persistent_auth = True 18 | renew_session = True 19 | 20 | [logging] 21 | config_file=logging.conf 22 | log_file=/var/log/virtesk-vdi/virtesk-virtroom.log 23 | 24 | # ADJUST: everything. 25 | [room test01] 26 | [[teacher_vms]] 27 | ids = "[1]" 28 | names = "${roomname}-vd${id}" 29 | ip_addresses_suffix = 2 30 | ip_addresses = "192.0.2.$suffix" 31 | template_name = "vdi-teachers-010" 32 | description = "LehrerVM" 33 | tc_user = "ovirt.thinclient@mydomain.site" 34 | memory = 4 * 1024 * 1024 * 1024 35 | cluster = Default 36 | workaround_os="rhel_7x64" 37 | workaround_timezone="Etc/GMT" 38 | os="windows_7x64" 39 | timezone="W. Europe Standard Time" 40 | autounattend_templatefile = "Autounattend-production.xml.template" 41 | netmask_suffix=24 42 | network_name="mynetwork" 43 | default_gateway=192.0.2.1 44 | usb = enabled 45 | rollout_startvm = True 46 | reset_startvm = Always 47 | snapshot_description = "Automatic snapshot after virtesk-vmrollout, IP=${ip}/${netmask_as_suffix}, scripttime=${scripttime}" 48 | reset_to_snapshot_regex = "Automatic snapshot after virtesk-vmrollout, .*" 49 | stateless = False 50 | 51 | [[student_vms]] 52 | ids = "range(2,4+1)" 53 | names = "${roomname}-vd${id}" 54 | ip_addresses_suffix = 2 55 | ip_addresses = "192.0.2.$suffix" 56 | template_name = "vdi-students-010" 57 | description = "SchuelerVM" 58 | tc_user = "ovirt.thinclient@mydomain.site" 59 | memory = 4 * 1024 * 1024 * 1024 60 | cluster = Default 61 | workaround_os="rhel_7x64" 62 | workaround_timezone="Etc/GMT" 63 | os="windows_7x64" 64 | timezone="W. Europe Standard Time" 65 | autounattend_templatefile = "Autounattend-production.xml.template" 66 | netmask_suffix=21 67 | network_name="mynetwork" 68 | default_gateway=192.0.2.1 69 | usb = enabled 70 | rollout_startvm = True 71 | reset_startvm = Auto 72 | snapshot_description = "Automatic snapshot after virtesk-vmrollout, IP=${ip}/${netmask_as_suffix}, scripttime=${scripttime}" 73 | reset_to_snapshot_regex = "Automatic snapshot after virtesk-vmrollout, .*" 74 | stateless = False 75 | 76 | -------------------------------------------------------------------------------- /virtesk-tc-connectspice/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | echo no default make target... 3 | 4 | clean: 5 | rm -f *.pyc 6 | 7 | #release: clean 8 | # tar -C /root -czf /var/www/mirror/private/thinclients/thinclient-software/connect_spice_client-new.tar.gz connect_spice_client 9 | # mv -f /var/www/mirror/private/thinclients/thinclient-software/connect_spice_client-new.tar.gz /var/www/mirror/private/thinclients/thinclient-software/connect_spice_client-dev.tar.gz 10 | 11 | release: clean 12 | tar --transform 's,^,virtesk-tc-connectspice/,S' -czf /var/www/mirror/private/thinclients/thinclient-software/connect_spice_client-new.tar.gz *.py virtesk-tc-connectspice-shutdown-vm virtesk-tc-connectspice-main 13 | echo mv -f /var/www/mirror/private/thinclients/thinclient-software/connect_spice_client-new.tar.gz /var/www/mirror/private/thinclients/thinclient-software/connect_spice_client-dev.tar.gz 14 | 15 | flake8: 16 | flake8 --doctests -j auto --ignore=E221,E222,E251,E272,E241,E203 *.py virtesk-tc-connectspice-* 17 | flake8-strict: 18 | flake8 --doctests -j auto *.py virtesk-tc-connectspice-* 19 | indention_report: 20 | autopep8 . --recursive --select=E101,E121 --diff 21 | indention_fix: 22 | autopep8 . --recursive --select=E101,E121 --in-place 23 | autopep8_report: 24 | autopep8 . --recursive --diff 25 | 26 | 27 | -------------------------------------------------------------------------------- /virtesk-tc-connectspice/conftest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # vim: autoindent expandtab tabstop=4 sw=4 sts=4 filetype=python 4 | 5 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 6 | # 7 | # This file is part of Virtesk VDI. 8 | # 9 | # Virtesk VDI is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # Virtesk VDI is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with Virtesk VDI. If not, see <http://www.gnu.org/licenses/>. 21 | 22 | # System Imports 23 | import pytest 24 | import os.path 25 | 26 | 27 | MOCK_DATA_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'test_mock_data') 28 | 29 | 30 | @pytest.yield_fixture(scope='module') 31 | def active_connection(): 32 | yield readfile('active_connection') 33 | 34 | 35 | @pytest.yield_fixture(scope='module') 36 | def nmcli_con_show_empty(): 37 | yield readfile('nmcli_con_show_adsy_eap_empty') 38 | 39 | 40 | @pytest.yield_fixture(scope='module') 41 | def nmcli_con_show(): 42 | yield readfile('nmcli_con_show_adsy_eap') 43 | 44 | 45 | @pytest.yield_fixture(scope='module') 46 | def nmcli_device_show_single_line(): 47 | yield readfile('nmcli_device_show_single_line') 48 | 49 | 50 | @pytest.yield_fixture(scope='module') 51 | def nmcli_device_show(): 52 | yield readfile('nmcli_device_show') 53 | 54 | 55 | @pytest.yield_fixture(scope='module') 56 | def hostnamectl_transient(): 57 | yield readfile('hostnamectl_transient') 58 | 59 | @pytest.yield_fixture(scope='module') 60 | def hostnamectl_transient_empty(): 61 | yield readfile('hostnamectl_transient_empty') 62 | 63 | def readfile(filename): 64 | with open(os.path.join(MOCK_DATA_PATH, filename), 'r') as f: 65 | return f.read() 66 | -------------------------------------------------------------------------------- /virtesk-tc-connectspice/find_thinclient_identifier.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # vim: autoindent expandtab tabstop=4 sw=4 sts=4 filetype=python 4 | 5 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 6 | # 7 | # This file is part of Virtesk VDI. 8 | # 9 | # Virtesk VDI is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # Virtesk VDI is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with Virtesk VDI. If not, see <http://www.gnu.org/licenses/>. 21 | 22 | # System Imports 23 | import os 24 | import re 25 | import logging 26 | import subprocess 27 | from find_thinclient_identifier_nmcli import extract_identifiers_from_nmcli 28 | 29 | 30 | def get_dmidecode_sysuuid(): 31 | result = [] 32 | 33 | dmi = subprocess.check_output("sudo dmidecode -t1", shell=True) 34 | for line in dmi.splitlines(): 35 | match_uuid = re.search('UUID:\s+([0-9A-Za-z_-]+)', line) 36 | if match_uuid: 37 | result.append(match_uuid.group(1).upper()) 38 | return result 39 | 40 | 41 | def process_ip(ip): 42 | # RHEV-tags cannot contain dots in their names. 43 | # so we replace dots by hyphens. 44 | return ip.replace('.', '-') 45 | 46 | 47 | def get_thinclient_identifiers(): 48 | (hostnames, fixedips) = extract_identifiers_from_nmcli() 49 | identifiers = (["thinclient-hostname-%s" % x for x in hostnames] + 50 | ["thinc*ient-hostname-%s" % x for x in hostnames] + 51 | ["thinclient-ip-%s" % process_ip(x) for x in fixedips] + 52 | ["thinc*ient-ip-%s" % process_ip(x) for x in fixedips]) 53 | logging.debug("Found the following thinclient identifiers: %s", 54 | ", ".join(identifiers)) 55 | return identifiers 56 | 57 | 58 | def get_dhcp_hostnames(): 59 | hostnames, _ = extract_identifiers_from_nmcli() 60 | return hostnames 61 | 62 | 63 | def main(): 64 | print get_thinclient_identifiers() 65 | 66 | if __name__ == "__main__": 67 | main() 68 | -------------------------------------------------------------------------------- /virtesk-tc-connectspice/find_thinclient_identifier_nmcli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # vim: autoindent expandtab tabstop=4 sw=4 sts=4 filetype=python 4 | 5 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 6 | # 7 | # This file is part of Virtesk VDI. 8 | # 9 | # Virtesk VDI is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # Virtesk VDI is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with Virtesk VDI. If not, see <http://www.gnu.org/licenses/>. 21 | 22 | # System Imports 23 | import subprocess 24 | import re 25 | 26 | 27 | def extract_identifiers_from_nmcli(): 28 | fixedips = [] 29 | hostnames = [] 30 | nmcli_output = subprocess.check_output(['nmcli', 'device', 'show'], env={'LC_ALL': 'C'}) 31 | for line in nmcli_output.splitlines(): 32 | if re.match('^IP4\.ADDRESS.', line): 33 | key, value = get_line_key_value(line) 34 | value = value[:value.index("/")].strip() 35 | fixedips.append(value) 36 | elif re.match('^GENERAL\.CONNECTION.', line): 37 | key, value = get_line_key_value(line) 38 | dhcp_hostname = get_dhcp_hostname_from_connection(value) 39 | if dhcp_hostname: 40 | hostnames.append(dhcp_hostname) 41 | hostnamectl_hostname = extract_hostname_from_hostnamectl() 42 | if hostnamectl_hostname and hostnamectl_hostname not in hostnames: 43 | hostnames.append(hostnamectl_hostname) 44 | return (hostnames, fixedips) 45 | 46 | 47 | def extract_hostname_from_hostnamectl(): 48 | hostnamectl_output = subprocess.check_output(['hostnamectl'], env={'LC_ALL': 'C'}) 49 | for line in hostnamectl_output.splitlines(): 50 | if re.match('^Transient', line): 51 | key, value = get_line_key_value(line) 52 | return value 53 | 54 | 55 | def get_dhcp_hostname_from_connection(name): 56 | if name and name != '--': 57 | nmcli_output = subprocess.check_output(['nmcli', 'con', 'show', name], env={'LC_ALL': 'C'}) 58 | for line in nmcli_output.splitlines(): 59 | if re.match('^ipv4\.dhcp-hostname.', line): 60 | key, value = get_line_key_value(line) 61 | if value != '--': 62 | return value 63 | 64 | 65 | def get_line_key_value(line): 66 | if line: 67 | values = str(line).split(':') 68 | if len(values) == 2: 69 | key, value = values 70 | value = value.strip() 71 | return (key, value) 72 | 73 | 74 | def get_active_connections(): 75 | nmcli_output = subprocess.check_output(['nmcli', '-t', '-f', 'state', 'con', 'show', '--active'], env={'LC_ALL': 'C'}) 76 | return len(nmcli_output.splitlines()) 77 | 78 | def main(): 79 | print(extract_identifiers_from_nmcli()) 80 | 81 | 82 | if __name__ == "__main__": 83 | main() 84 | -------------------------------------------------------------------------------- /virtesk-tc-connectspice/spice_xpi_controller.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # vim: autoindent expandtab tabstop=4 sw=4 sts=4 filetype=python 4 | 5 | """ 6 | Implements the binary protocol [0] over a unix domain socket necessary for 7 | parameter passing, configuration and controlling a spice client. 8 | 9 | Tested with remote-viewer. 10 | 11 | Only the sending side is implemented. 12 | The receiving side has been omitted, because it wasn't necessary. 13 | 14 | Please be aware that the specification [0] contains many errors, so for real 15 | hacking, you might want to consult the source code of spice-xpi: 16 | git clone git://anongit.freedesktop.org/spice/spice-xpi 17 | 18 | [0]: http://spice-space.org/page/Whiteboard/ControllerProtocol 19 | """ 20 | 21 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 22 | # 23 | # This file is part of Virtesk VDI 24 | # 25 | # Virtesk VDI is free software: you can redistribute it and/or modify 26 | # it under the terms of the GNU General Public License as published by 27 | # the Free Software Foundation, either version 3 of the License, or 28 | # (at your option) any later version. 29 | # 30 | # Virtesk VDI is distributed in the hope that it will be useful, 31 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 32 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 33 | # GNU General Public License for more details. 34 | # 35 | # You should have received a copy of the GNU General Public License 36 | # along with Virtesk VDI. If not, see <http://www.gnu.org/licenses/>. 37 | 38 | 39 | import socket 40 | import struct 41 | import logging 42 | 43 | 44 | CONTROLLER_HOST = 1 45 | CONTROLLER_PORT = 2 46 | CONTROLLER_SPORT = 3 47 | CONTROLLER_PASSWORD = 4 48 | 49 | CONTROLLER_SECURE_CHANNELS = 5 50 | CONTROLLER_DISABLE_CHANNELS = 6 51 | 52 | CONTROLLER_TLS_CIPHERS = 7 53 | CONTROLLER_CA_FILE = 8 54 | CONTROLLER_HOST_SUBJECT = 9 55 | 56 | CONTROLLER_FULL_SCREEN = 10 57 | CONTROLLER_SET_TITLE = 11 58 | 59 | CONTROLLER_CREATE_MENU = 12 60 | CONTROLLER_DELETE_MENU = 13 61 | 62 | CONTROLLER_HOTKEYS = 14 63 | CONTROLLER_SEND_CAD = 15 64 | 65 | CONTROLLER_CONNECT = 16 66 | CONTROLLER_SHOW = 17 67 | CONTROLLER_HIDE = 18 68 | 69 | CONTROLLER_ENABLE_SMARTCARD = 19 70 | 71 | CONTROLLER_COLOR_DEPTH = 20 72 | CONTROLLER_DISABLE_EFFECTS = 21 73 | 74 | CONTROLLER_ENABLE_USB = 22 75 | CONTROLLER_ENABLE_USB_AUTOSHARE = 23 76 | CONTROLLER_USB_FILTER = 24 77 | 78 | 79 | def ControllerValue(message_id, arg): 80 | result = struct.pack("=III", message_id, 12, arg) 81 | logging.debug("INT: message_id: %s, ARG: %s, STRUCT: %s" % 82 | (message_id, arg, result)) 83 | return result 84 | 85 | 86 | def ControllerValueBoolean(message_id, arg): 87 | arg_boolean = 1 if arg else 0 88 | result = struct.pack("=III", message_id, 12, arg_boolean) 89 | logging.debug("Bool: message_id: %s, ARG: %s, STRUCT: %s" % 90 | (message_id, arg, result)) 91 | return result 92 | 93 | 94 | def ControllerDataString(message_id, arg): 95 | argplusnull = arg + '\0' 96 | b = argplusnull.encode('ascii') 97 | fmt = "=II%ds" % len(b) 98 | size = len(b) + 8 99 | 100 | result = struct.pack(fmt, message_id, size, argplusnull) 101 | logging.debug("STRING: message_id: %s, ARG: %s, LEN: %d/%d, STRUCT: %s" % 102 | (message_id, arg, size, len(result), result)) 103 | return result 104 | 105 | 106 | def ControllerMsg(message_id): 107 | result = struct.pack("=II", message_id, 8) 108 | return result 109 | 110 | 111 | def connect( 112 | socket_filename, host, port, secport, ticket, spice_ca_file, 113 | secure_channels=None, disable_channels=None, tls_ciphers=None, 114 | host_subject=None, window_title=None, hotkeys=None, 115 | disable_effects=None, ctrl_alt_delete=None, enable_usb=None, 116 | enable_usb_autoshare=None, usb_filter=None, **rest 117 | ): 118 | s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 119 | s.connect(socket_filename) 120 | 121 | # send CONTROLLER_INIT 122 | 123 | magic = 0x4C525443 124 | version = 1 125 | size = 24 126 | 127 | credentials = 0 128 | flags = 1 129 | 130 | s.send(struct.pack("=IIIQI", magic, version, size, credentials, flags)) 131 | 132 | # send CONTROLLER_HOST 133 | s.send(ControllerDataString(CONTROLLER_HOST, host)) 134 | 135 | # send CONTROLLER_PORT 136 | if port is not None: 137 | s.send(ControllerValue(CONTROLLER_PORT, port)) 138 | 139 | # send CONTROLLER_SPORT 140 | if secport is not None: 141 | s.send(ControllerValue(CONTROLLER_SPORT, secport)) 142 | 143 | # send CONTROLLER_PASSWORD 144 | s.send(ControllerDataString(CONTROLLER_PASSWORD, ticket)) 145 | 146 | # send CONTROLLER_FULL_SCREEN 147 | s.send(ControllerValue(CONTROLLER_FULL_SCREEN, 1)) 148 | 149 | # CA_FILE 150 | s.send(ControllerDataString(CONTROLLER_CA_FILE, spice_ca_file)) 151 | 152 | # CONTROLLER_HOST_SUBJECT 153 | if host_subject is not None: 154 | s.send(ControllerDataString(CONTROLLER_HOST_SUBJECT, host_subject)) 155 | 156 | # TLS_CIPHERS 157 | if tls_ciphers is not None: 158 | s.send(ControllerDataString(CONTROLLER_TLS_CIPHERS, tls_ciphers)) 159 | 160 | # SECURE_CHANNELS 161 | if secure_channels is not None: 162 | s.send(ControllerDataString( 163 | CONTROLLER_SECURE_CHANNELS, secure_channels)) 164 | 165 | if disable_channels is not None: 166 | s.send(ControllerDataString( 167 | CONTROLLER_DISABLE_CHANNELS, disable_channels)) 168 | 169 | # CONTROLLER_SET_TITLE 170 | if window_title is not None: 171 | s.send(ControllerDataString(CONTROLLER_SET_TITLE, window_title)) 172 | 173 | if hotkeys is not None: 174 | s.send(ControllerDataString(CONTROLLER_HOTKEYS, hotkeys)) 175 | 176 | if ctrl_alt_delete is not None: 177 | s.send(ControllerValueBoolean(CONTROLLER_SEND_CAD, ctrl_alt_delete)) 178 | 179 | if disable_effects is not None: 180 | s.send(ControllerDataString( 181 | CONTROLLER_DISABLE_EFFECTS, disable_effects)) 182 | 183 | if enable_usb is not None: 184 | s.send(ControllerValueBoolean(CONTROLLER_ENABLE_USB, enable_usb)) 185 | 186 | if enable_usb_autoshare is not None: 187 | s.send(ControllerValueBoolean( 188 | CONTROLLER_ENABLE_USB_AUTOSHARE, enable_usb_autoshare)) 189 | 190 | if usb_filter is not None: 191 | s.send(ControllerDataString(CONTROLLER_USB_FILTER, usb_filter)) 192 | 193 | # CONNECT 194 | s.send(ControllerMsg(CONTROLLER_CONNECT)) 195 | 196 | s.send(ControllerMsg(CONTROLLER_SHOW)) 197 | 198 | s.close() 199 | -------------------------------------------------------------------------------- /virtesk-tc-connectspice/test_find_thinclient_identifier_nmcli.py: -------------------------------------------------------------------------------- 1 | import find_thinclient_identifier_nmcli as find_nmcli 2 | 3 | 4 | def test_extract_identifiers_from_nmcli(mock, nmcli_device_show, nmcli_con_show, nmcli_con_show_empty, hostnamectl_transient): 5 | def my_check_output(cmd, **kwargs): 6 | if cmd[:4] == ['nmcli', 'con', 'show', 'ADSY-EAP']: 7 | return nmcli_con_show 8 | elif cmd[:3] == ['nmcli', 'con', 'show']: 9 | return nmcli_con_show_empty 10 | elif cmd[:3] == ['nmcli', 'device', 'show']: 11 | return nmcli_device_show 12 | elif cmd[:1] == ['hostnamectl']: 13 | return hostnamectl_transient 14 | mock.patch('find_thinclient_identifier_nmcli.subprocess.check_output', my_check_output) 15 | return_value = find_nmcli.extract_identifiers_from_nmcli() 16 | 17 | assert return_value == (['a-hostname-appeared', 'transient-hostname'], ['172.17.0.1', '10.9.5.185', '127.0.0.1']) 18 | 19 | 20 | def test_get_dhcp_hostname_from_connection(mock, nmcli_con_show): 21 | patched_check_output = mock.patch('find_thinclient_identifier_nmcli.subprocess.check_output') 22 | patched_check_output.return_value = nmcli_con_show 23 | return_value = find_nmcli.get_dhcp_hostname_from_connection('SOME-NAME') 24 | assert return_value == 'a-hostname-appeared' 25 | 26 | 27 | def test_get_dhcp_hostname_from_connection_empty(mock, nmcli_con_show_empty): 28 | patched_check_output = mock.patch('find_thinclient_identifier_nmcli.subprocess.check_output') 29 | patched_check_output.return_value = nmcli_con_show_empty 30 | return_value = find_nmcli.get_dhcp_hostname_from_connection('SOME-NAME') 31 | assert return_value is None 32 | 33 | 34 | def test_get_line_key_value(nmcli_device_show_single_line): 35 | return_value = find_nmcli.get_line_key_value(nmcli_device_show_single_line) 36 | key, value = return_value 37 | assert key == 'IP4.ADDRESS[1]' 38 | assert value == '10.9.5.185/16' 39 | 40 | 41 | def test_get_line_key_value_none(): 42 | return_value = find_nmcli.get_line_key_value('') 43 | assert return_value is None 44 | 45 | 46 | def test_get_active_connections(mock, active_connection): 47 | patched_check_output = mock.patch('find_thinclient_identifier_nmcli.subprocess.check_output') 48 | patched_check_output.return_value = active_connection 49 | return_value = find_nmcli.get_active_connections() 50 | assert return_value == 2 51 | 52 | 53 | def test_get_hostname_from_hostnamectl(mock, hostnamectl_transient): 54 | patched_check_output = mock.patch('find_thinclient_identifier_nmcli.subprocess.check_output') 55 | patched_check_output.return_value = hostnamectl_transient 56 | return_value = find_nmcli.extract_hostname_from_hostnamectl() 57 | assert return_value == 'transient-hostname' 58 | 59 | 60 | def test_get_hostname_from_hostnamectl_empty(mock, hostnamectl_transient_empty): 61 | patched_check_output = mock.patch('find_thinclient_identifier_nmcli.subprocess.check_output') 62 | patched_check_output.return_value = hostnamectl_transient_empty 63 | return_value = find_nmcli.extract_hostname_from_hostnamectl() 64 | assert return_value == None 65 | -------------------------------------------------------------------------------- /virtesk-tc-connectspice/test_mock_data/active_connection: -------------------------------------------------------------------------------- 1 | activated 2 | activated 3 | -------------------------------------------------------------------------------- /virtesk-tc-connectspice/test_mock_data/hostnamectl_transient: -------------------------------------------------------------------------------- 1 | Static hostname: static-hostname 2 | Transient hostname: transient-hostname 3 | Icon name: computer-laptop 4 | Chassis: laptop 5 | Machine ID: 11111111111111111111111111111111 6 | Boot ID: 11111111111111111111111111111111 7 | Operating System: Ubuntu 8 | Kernel: Linux 4.4.0-53-generic 9 | Architecture: x86-64 10 | -------------------------------------------------------------------------------- /virtesk-tc-connectspice/test_mock_data/hostnamectl_transient_empty: -------------------------------------------------------------------------------- 1 | Static hostname: static-hostname 2 | Icon name: computer-laptop 3 | Chassis: laptop 4 | Machine ID: 11111111111111111111111111111111 5 | Boot ID: 11111111111111111111111111111111 6 | Operating System: Ubuntu 7 | Kernel: Linux 4.4.0-53-generic 8 | Architecture: x86-64 9 | -------------------------------------------------------------------------------- /virtesk-tc-connectspice/test_mock_data/nmcli_con_show_adsy_eap: -------------------------------------------------------------------------------- 1 | connection.id: ADSY-EAP 2 | connection.uuid: f3008792-bd40-4bbe-9b4d-106eac03c9f9 3 | connection.interface-name: -- 4 | connection.type: 802-11-wireless 5 | connection.autoconnect: yes 6 | connection.autoconnect-priority: 0 7 | connection.timestamp: 1481184404 8 | connection.read-only: no 9 | connection.permissions: user:john 10 | connection.zone: -- 11 | connection.master: -- 12 | connection.slave-type: -- 13 | connection.autoconnect-slaves: -1 (default) 14 | connection.secondaries: 15 | connection.gateway-ping-timeout: 0 16 | connection.metered: unknown 17 | connection.lldp: -1 (default) 18 | 802-1x.eap: ttls 19 | 802-1x.identity: johnw 20 | 802-1x.anonymous-identity: -- 21 | 802-1x.pac-file: -- 22 | 802-1x.ca-cert: -- 23 | 802-1x.ca-path: -- 24 | 802-1x.subject-match: -- 25 | 802-1x.altsubject-matches: 26 | 802-1x.domain-suffix-match: -- 27 | 802-1x.client-cert: -- 28 | 802-1x.phase1-peapver: -- 29 | 802-1x.phase1-peaplabel: -- 30 | 802-1x.phase1-fast-provisioning: -- 31 | 802-1x.phase2-auth: mschapv2 32 | 802-1x.phase2-autheap: -- 33 | 802-1x.phase2-ca-cert: -- 34 | 802-1x.phase2-ca-path: -- 35 | 802-1x.phase2-subject-match: -- 36 | 802-1x.phase2-altsubject-matches: 37 | 802-1x.phase2-domain-suffix-match: -- 38 | 802-1x.phase2-client-cert: -- 39 | 802-1x.password: <hidden> 40 | 802-1x.password-flags: 1 (agent-owned) 41 | 802-1x.password-raw: <hidden> 42 | 802-1x.password-raw-flags: 0 (none) 43 | 802-1x.private-key: -- 44 | 802-1x.private-key-password: <hidden> 45 | 802-1x.private-key-password-flags: 0 (none) 46 | 802-1x.phase2-private-key: -- 47 | 802-1x.phase2-private-key-password: <hidden> 48 | 802-1x.phase2-private-key-password-flags:0 (none) 49 | 802-1x.pin: <hidden> 50 | 802-1x.pin-flags: 0 (none) 51 | 802-1x.system-ca-certs: no 52 | 802-11-wireless.ssid: ADSY-EAP 53 | 802-11-wireless.mode: infrastructure 54 | 802-11-wireless.band: -- 55 | 802-11-wireless.channel: 0 56 | 802-11-wireless.bssid: -- 57 | 802-11-wireless.rate: 0 58 | 802-11-wireless.tx-power: 0 59 | 802-11-wireless.mac-address: -- 60 | 802-11-wireless.cloned-mac-address: -- 61 | 802-11-wireless.mac-address-blacklist: 62 | 802-11-wireless.mac-address-randomization:default 63 | 802-11-wireless.mtu: auto 64 | 802-11-wireless.seen-bssids: 06:27:22:F1:EE:54 65 | 802-11-wireless.hidden: no 66 | 802-11-wireless.powersave: default (0) 67 | 802-11-wireless-security.key-mgmt: wpa-eap 68 | 802-11-wireless-security.wep-tx-keyidx: 0 69 | 802-11-wireless-security.auth-alg: -- 70 | 802-11-wireless-security.proto: 71 | 802-11-wireless-security.pairwise: 72 | 802-11-wireless-security.group: 73 | 802-11-wireless-security.leap-username: -- 74 | 802-11-wireless-security.wep-key0: <hidden> 75 | 802-11-wireless-security.wep-key1: <hidden> 76 | 802-11-wireless-security.wep-key2: <hidden> 77 | 802-11-wireless-security.wep-key3: <hidden> 78 | 802-11-wireless-security.wep-key-flags: 0 (none) 79 | 802-11-wireless-security.wep-key-type: 0 (unknown) 80 | 802-11-wireless-security.psk: <hidden> 81 | 802-11-wireless-security.psk-flags: 0 (none) 82 | 802-11-wireless-security.leap-password: <hidden> 83 | 802-11-wireless-security.leap-password-flags:0 (none) 84 | ipv4.method: auto 85 | ipv4.dns: 86 | ipv4.dns-search: 87 | ipv4.dns-options: (default) 88 | ipv4.addresses: 89 | ipv4.gateway: -- 90 | ipv4.routes: 91 | ipv4.route-metric: -1 92 | ipv4.ignore-auto-routes: no 93 | ipv4.ignore-auto-dns: no 94 | ipv4.dhcp-client-id: -- 95 | ipv4.dhcp-timeout: 0 96 | ipv4.dhcp-send-hostname: yes 97 | ipv4.dhcp-hostname: a-hostname-appeared 98 | ipv4.dhcp-fqdn: -- 99 | ipv4.never-default: no 100 | ipv4.may-fail: yes 101 | ipv4.dad-timeout: -1 (default) 102 | ipv6.method: auto 103 | ipv6.dns: 104 | ipv6.dns-search: 105 | ipv6.dns-options: (default) 106 | ipv6.addresses: 107 | ipv6.gateway: -- 108 | ipv6.routes: 109 | ipv6.route-metric: -1 110 | ipv6.ignore-auto-routes: no 111 | ipv6.ignore-auto-dns: no 112 | ipv6.never-default: no 113 | ipv6.may-fail: yes 114 | ipv6.ip6-privacy: -1 (unknown) 115 | ipv6.addr-gen-mode: stable-privacy 116 | ipv6.dhcp-send-hostname: yes 117 | ipv6.dhcp-hostname: -- 118 | GENERAL.NAME: ADSY-EAP 119 | GENERAL.UUID: f3008792-bd40-4bbe-9b4d-106eac03c9f9 120 | GENERAL.DEVICES: wlp3s0 121 | GENERAL.STATE: activated 122 | GENERAL.DEFAULT: yes 123 | GENERAL.DEFAULT6: no 124 | GENERAL.VPN: no 125 | GENERAL.ZONE: -- 126 | GENERAL.DBUS-PATH: /org/freedesktop/NetworkManager/ActiveConnection/4 127 | GENERAL.CON-PATH: /org/freedesktop/NetworkManager/Settings/3 128 | GENERAL.SPEC-OBJECT: /org/freedesktop/NetworkManager/AccessPoint/12 129 | GENERAL.MASTER-PATH: -- 130 | IP4.ADDRESS[1]: 10.9.5.185/16 131 | IP4.GATEWAY: 10.9.1.1 132 | IP4.DNS[1]: 10.9.2.1 133 | IP4.DNS[2]: 10.1.2.8 134 | IP4.DNS[3]: 10.1.2.22 135 | IP4.DOMAIN[1]: adfinis-int.ch 136 | DHCP4.OPTION[1]: requested_ms_classless_static_routes = 1 137 | DHCP4.OPTION[2]: requested_domain_search = 1 138 | DHCP4.OPTION[3]: requested_host_name = 1 139 | DHCP4.OPTION[4]: requested_time_offset = 1 140 | DHCP4.OPTION[5]: requested_domain_name = 1 141 | DHCP4.OPTION[6]: filename = pxelinux.0 142 | DHCP4.OPTION[7]: requested_rfc3442_classless_static_routes = 1 143 | DHCP4.OPTION[8]: requested_wpad = 1 144 | DHCP4.OPTION[9]: requested_broadcast_address = 1 145 | DHCP4.OPTION[10]: next_server = 10.9.2.1 146 | DHCP4.OPTION[11]: requested_netbios_scope = 1 147 | DHCP4.OPTION[12]: requested_interface_mtu = 1 148 | DHCP4.OPTION[13]: requested_subnet_mask = 1 149 | DHCP4.OPTION[14]: routers = 10.9.1.1 150 | DHCP4.OPTION[15]: dhcp_message_type = 5 151 | DHCP4.OPTION[16]: ip_address = 10.9.5.185 152 | DHCP4.OPTION[17]: requested_static_routes = 1 153 | DHCP4.OPTION[18]: expiry = 1481225250 154 | DHCP4.OPTION[19]: requested_domain_name_servers = 1 155 | DHCP4.OPTION[20]: broadcast_address = 10.9.255.255 156 | DHCP4.OPTION[21]: requested_ntp_servers = 1 157 | DHCP4.OPTION[22]: domain_name = adfinis-int.ch 158 | DHCP4.OPTION[23]: dhcp_lease_time = 43200 159 | DHCP4.OPTION[24]: domain_name_servers = 10.9.2.1 10.1.2.8 10.1.2.22 160 | DHCP4.OPTION[25]: requested_netbios_name_servers = 1 161 | DHCP4.OPTION[26]: subnet_mask = 255.255.0.0 162 | DHCP4.OPTION[27]: network_number = 10.9.0.0 163 | DHCP4.OPTION[28]: requested_routers = 1 164 | DHCP4.OPTION[29]: dhcp_server_identifier = 10.9.2.1 165 | IP6.ADDRESS[1]: fe80::db41:83ec:a6e7:a7a4/64 166 | IP6.GATEWAY: 167 | -------------------------------------------------------------------------------- /virtesk-tc-connectspice/test_mock_data/nmcli_con_show_adsy_eap_empty: -------------------------------------------------------------------------------- 1 | connection.id: ADSY-EAP 2 | connection.uuid: f3008792-bd40-4bbe-9b4d-106eac03c9f9 3 | connection.interface-name: -- 4 | connection.type: 802-11-wireless 5 | connection.autoconnect: yes 6 | connection.autoconnect-priority: 0 7 | connection.timestamp: 1481184404 8 | connection.read-only: no 9 | connection.permissions: user:john 10 | connection.zone: -- 11 | connection.master: -- 12 | connection.slave-type: -- 13 | connection.autoconnect-slaves: -1 (default) 14 | connection.secondaries: 15 | connection.gateway-ping-timeout: 0 16 | connection.metered: unknown 17 | connection.lldp: -1 (default) 18 | 802-1x.eap: ttls 19 | 802-1x.identity: johnw 20 | 802-1x.anonymous-identity: -- 21 | 802-1x.pac-file: -- 22 | 802-1x.ca-cert: -- 23 | 802-1x.ca-path: -- 24 | 802-1x.subject-match: -- 25 | 802-1x.altsubject-matches: 26 | 802-1x.domain-suffix-match: -- 27 | 802-1x.client-cert: -- 28 | 802-1x.phase1-peapver: -- 29 | 802-1x.phase1-peaplabel: -- 30 | 802-1x.phase1-fast-provisioning: -- 31 | 802-1x.phase2-auth: mschapv2 32 | 802-1x.phase2-autheap: -- 33 | 802-1x.phase2-ca-cert: -- 34 | 802-1x.phase2-ca-path: -- 35 | 802-1x.phase2-subject-match: -- 36 | 802-1x.phase2-altsubject-matches: 37 | 802-1x.phase2-domain-suffix-match: -- 38 | 802-1x.phase2-client-cert: -- 39 | 802-1x.password: <hidden> 40 | 802-1x.password-flags: 1 (agent-owned) 41 | 802-1x.password-raw: <hidden> 42 | 802-1x.password-raw-flags: 0 (none) 43 | 802-1x.private-key: -- 44 | 802-1x.private-key-password: <hidden> 45 | 802-1x.private-key-password-flags: 0 (none) 46 | 802-1x.phase2-private-key: -- 47 | 802-1x.phase2-private-key-password: <hidden> 48 | 802-1x.phase2-private-key-password-flags:0 (none) 49 | 802-1x.pin: <hidden> 50 | 802-1x.pin-flags: 0 (none) 51 | 802-1x.system-ca-certs: no 52 | 802-11-wireless.ssid: ADSY-EAP 53 | 802-11-wireless.mode: infrastructure 54 | 802-11-wireless.band: -- 55 | 802-11-wireless.channel: 0 56 | 802-11-wireless.bssid: -- 57 | 802-11-wireless.rate: 0 58 | 802-11-wireless.tx-power: 0 59 | 802-11-wireless.mac-address: -- 60 | 802-11-wireless.cloned-mac-address: -- 61 | 802-11-wireless.mac-address-blacklist: 62 | 802-11-wireless.mac-address-randomization:default 63 | 802-11-wireless.mtu: auto 64 | 802-11-wireless.seen-bssids: 06:27:22:F1:EE:54 65 | 802-11-wireless.hidden: no 66 | 802-11-wireless.powersave: default (0) 67 | 802-11-wireless-security.key-mgmt: wpa-eap 68 | 802-11-wireless-security.wep-tx-keyidx: 0 69 | 802-11-wireless-security.auth-alg: -- 70 | 802-11-wireless-security.proto: 71 | 802-11-wireless-security.pairwise: 72 | 802-11-wireless-security.group: 73 | 802-11-wireless-security.leap-username: -- 74 | 802-11-wireless-security.wep-key0: <hidden> 75 | 802-11-wireless-security.wep-key1: <hidden> 76 | 802-11-wireless-security.wep-key2: <hidden> 77 | 802-11-wireless-security.wep-key3: <hidden> 78 | 802-11-wireless-security.wep-key-flags: 0 (none) 79 | 802-11-wireless-security.wep-key-type: 0 (unknown) 80 | 802-11-wireless-security.psk: <hidden> 81 | 802-11-wireless-security.psk-flags: 0 (none) 82 | 802-11-wireless-security.leap-password: <hidden> 83 | 802-11-wireless-security.leap-password-flags:0 (none) 84 | ipv4.method: auto 85 | ipv4.dns: 86 | ipv4.dns-search: 87 | ipv4.dns-options: (default) 88 | ipv4.addresses: 89 | ipv4.gateway: -- 90 | ipv4.routes: 91 | ipv4.route-metric: -1 92 | ipv4.ignore-auto-routes: no 93 | ipv4.ignore-auto-dns: no 94 | ipv4.dhcp-client-id: -- 95 | ipv4.dhcp-timeout: 0 96 | ipv4.dhcp-send-hostname: yes 97 | ipv4.dhcp-hostname: -- 98 | ipv4.dhcp-fqdn: -- 99 | ipv4.never-default: no 100 | ipv4.may-fail: yes 101 | ipv4.dad-timeout: -1 (default) 102 | ipv6.method: auto 103 | ipv6.dns: 104 | ipv6.dns-search: 105 | ipv6.dns-options: (default) 106 | ipv6.addresses: 107 | ipv6.gateway: -- 108 | ipv6.routes: 109 | ipv6.route-metric: -1 110 | ipv6.ignore-auto-routes: no 111 | ipv6.ignore-auto-dns: no 112 | ipv6.never-default: no 113 | ipv6.may-fail: yes 114 | ipv6.ip6-privacy: -1 (unknown) 115 | ipv6.addr-gen-mode: stable-privacy 116 | ipv6.dhcp-send-hostname: yes 117 | ipv6.dhcp-hostname: -- 118 | GENERAL.NAME: ADSY-EAP 119 | GENERAL.UUID: f3008792-bd40-4bbe-9b4d-106eac03c9f9 120 | GENERAL.DEVICES: wlp3s0 121 | GENERAL.STATE: activated 122 | GENERAL.DEFAULT: yes 123 | GENERAL.DEFAULT6: no 124 | GENERAL.VPN: no 125 | GENERAL.ZONE: -- 126 | GENERAL.DBUS-PATH: /org/freedesktop/NetworkManager/ActiveConnection/4 127 | GENERAL.CON-PATH: /org/freedesktop/NetworkManager/Settings/3 128 | GENERAL.SPEC-OBJECT: /org/freedesktop/NetworkManager/AccessPoint/12 129 | GENERAL.MASTER-PATH: -- 130 | IP4.ADDRESS[1]: 10.9.5.185/16 131 | IP4.GATEWAY: 10.9.1.1 132 | IP4.DNS[1]: 10.9.2.1 133 | IP4.DNS[2]: 10.1.2.8 134 | IP4.DNS[3]: 10.1.2.22 135 | IP4.DOMAIN[1]: adfinis-int.ch 136 | DHCP4.OPTION[1]: requested_ms_classless_static_routes = 1 137 | DHCP4.OPTION[2]: requested_domain_search = 1 138 | DHCP4.OPTION[3]: requested_host_name = 1 139 | DHCP4.OPTION[4]: requested_time_offset = 1 140 | DHCP4.OPTION[5]: requested_domain_name = 1 141 | DHCP4.OPTION[6]: filename = pxelinux.0 142 | DHCP4.OPTION[7]: requested_rfc3442_classless_static_routes = 1 143 | DHCP4.OPTION[8]: requested_wpad = 1 144 | DHCP4.OPTION[9]: requested_broadcast_address = 1 145 | DHCP4.OPTION[10]: next_server = 10.9.2.1 146 | DHCP4.OPTION[11]: requested_netbios_scope = 1 147 | DHCP4.OPTION[12]: requested_interface_mtu = 1 148 | DHCP4.OPTION[13]: requested_subnet_mask = 1 149 | DHCP4.OPTION[14]: routers = 10.9.1.1 150 | DHCP4.OPTION[15]: dhcp_message_type = 5 151 | DHCP4.OPTION[16]: ip_address = 10.9.5.185 152 | DHCP4.OPTION[17]: requested_static_routes = 1 153 | DHCP4.OPTION[18]: expiry = 1481225250 154 | DHCP4.OPTION[19]: requested_domain_name_servers = 1 155 | DHCP4.OPTION[20]: broadcast_address = 10.9.255.255 156 | DHCP4.OPTION[21]: requested_ntp_servers = 1 157 | DHCP4.OPTION[22]: domain_name = adfinis-int.ch 158 | DHCP4.OPTION[23]: dhcp_lease_time = 43200 159 | DHCP4.OPTION[24]: domain_name_servers = 10.9.2.1 10.1.2.8 10.1.2.22 160 | DHCP4.OPTION[25]: requested_netbios_name_servers = 1 161 | DHCP4.OPTION[26]: subnet_mask = 255.255.0.0 162 | DHCP4.OPTION[27]: network_number = 10.9.0.0 163 | DHCP4.OPTION[28]: requested_routers = 1 164 | DHCP4.OPTION[29]: dhcp_server_identifier = 10.9.2.1 165 | IP6.ADDRESS[1]: fe80::db41:83ec:a6e7:a7a4/64 166 | IP6.GATEWAY: 167 | -------------------------------------------------------------------------------- /virtesk-tc-connectspice/test_mock_data/nmcli_device_show: -------------------------------------------------------------------------------- 1 | GENERAL.DEVICE: docker0 2 | GENERAL.TYPE: bridge 3 | GENERAL.HWADDR: 8A:7F:3B:20:49:AE 4 | GENERAL.MTU: 1500 5 | GENERAL.STATE: 100 (connected) 6 | GENERAL.CONNECTION: docker0 7 | GENERAL.CON-PATH: /org/freedesktop/NetworkManager/ActiveConnection/0 8 | IP4.ADDRESS[1]: 172.17.0.1/16 9 | IP4.GATEWAY: 10 | IP4.ROUTE[1]: dst = 169.254.0.0/16, nh = 0.0.0.0, mt = 1000 11 | IP6.GATEWAY: 12 | 13 | GENERAL.DEVICE: wlp3s0 14 | GENERAL.TYPE: wifi 15 | GENERAL.HWADDR: 28:B2:BD:E6:35:7A 16 | GENERAL.MTU: 0 17 | GENERAL.STATE: 100 (connected) 18 | GENERAL.CONNECTION: ADSY-EAP 19 | GENERAL.CON-PATH: /org/freedesktop/NetworkManager/ActiveConnection/7 20 | IP4.ADDRESS[1]: 10.9.5.185/16 21 | IP4.GATEWAY: 10.9.1.1 22 | IP4.DNS[1]: 10.9.2.1 23 | IP4.DNS[2]: 10.1.2.8 24 | IP4.DNS[3]: 10.1.2.22 25 | IP4.DOMAIN[1]: adfinis-int.ch 26 | IP6.ADDRESS[1]: fe80::db41:83ec:a6e7:a7a4/64 27 | IP6.GATEWAY: 28 | 29 | GENERAL.DEVICE: enp0s25 30 | GENERAL.TYPE: ethernet 31 | GENERAL.HWADDR: 54:EE:75:2C:E4:D5 32 | GENERAL.MTU: 1500 33 | GENERAL.STATE: 20 (unavailable) 34 | GENERAL.CONNECTION: -- 35 | GENERAL.CON-PATH: -- 36 | WIRED-PROPERTIES.CARRIER: off 37 | 38 | GENERAL.DEVICE: lo 39 | GENERAL.TYPE: loopback 40 | GENERAL.HWADDR: 00:00:00:00:00:00 41 | GENERAL.MTU: 65536 42 | GENERAL.STATE: 10 (unmanaged) 43 | GENERAL.CONNECTION: -- 44 | GENERAL.CON-PATH: -- 45 | IP4.ADDRESS[1]: 127.0.0.1/8 46 | IP4.GATEWAY: 47 | IP6.ADDRESS[1]: ::1/128 48 | IP6.GATEWAY: 49 | -------------------------------------------------------------------------------- /virtesk-tc-connectspice/test_mock_data/nmcli_device_show_single_line: -------------------------------------------------------------------------------- 1 | IP4.ADDRESS[1]: 10.9.5.185/16 2 | -------------------------------------------------------------------------------- /virtesk-tc-connectspice/virtesk-tc-connectspice-main: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # vim: autoindent expandtab tabstop=4 sw=4 sts=4 filetype=python 4 | 5 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 6 | # 7 | # This file is part of Virtesk VDI. 8 | # 9 | # Virtesk VDI is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # Virtesk VDI is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with Virtesk VDI. If not, see <http://www.gnu.org/licenses/>. 21 | 22 | 23 | import connect_spice_client 24 | 25 | 26 | if __name__ == "__main__": 27 | # logging.basicConfig(level=logging.DEBUG) 28 | connect_spice_client.connect_spice_client().main() 29 | -------------------------------------------------------------------------------- /virtesk-tc-connectspice/virtesk-tc-connectspice-shutdown-vm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # vim: autoindent expandtab tabstop=4 sw=4 sts=4 filetype=python 4 | 5 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 6 | # 7 | # This file is part of Virtesk VDI. 8 | # 9 | # Virtesk VDI is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # Virtesk VDI is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with Virtesk VDI. If not, see <http://www.gnu.org/licenses/>. 21 | 22 | import connect_spice_client 23 | 24 | 25 | if __name__ == "__main__": 26 | # logging.basicConfig(level=logging.DEBUG) 27 | connect_spice_client.connect_spice_client().shutdown_vm_action_sequence() 28 | -------------------------------------------------------------------------------- /virtesk-tc-tools/README.md: -------------------------------------------------------------------------------- 1 | # Virtesk-tc-tools 2 | 3 | ------------------------------- 4 | 5 | Virtesk-tc-tools is a collection of shell scripts for manageing thinclients. 6 | See [virtesk-tc-tools.rst](../doc/virtesk-tc-tools.rst) for details. 7 | -------------------------------------------------------------------------------- /virtesk-tc-tools/tc_rollout_kexec: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 4 | # 5 | # This file is part of Virtesk VDI. 6 | # 7 | # Virtesk VDI is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # Virtesk VDI is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with Virtesk VDI. If not, see <http://www.gnu.org/licenses/>. 19 | 20 | # Global variables 21 | declare -a ARGV 22 | declare -a ssh_opts 23 | declare -a remote_cmdline 24 | declare -i CMDLINE_AVAILABLE=0 25 | 26 | source $(dirname $(readlink -e ${BASH_SOURCE}))/utils.sh 27 | 28 | display_usage() { 29 | cat 1>&2 <<-'EndOfHelpText' 30 | Usage: 31 | 32 | tc_rollout_kexec <thinclient> 33 | 34 | 35 | tc_rollout_kexec is used to roll out thinclients 36 | using ssh and kexec. 37 | 38 | It is useful for re-installation of already 39 | installed thinclients. 40 | 41 | tc_rollout_kexec is part of the virtesk-tc-tools collection. 42 | 43 | Amoothei-vdi is a virtual desktop infrastructure 44 | solution based on RHEV / Ovirt. 45 | 46 | Documentation: see doc/virtesk-tc-tools.rst . 47 | EndOfHelpText 48 | 49 | exit 1; 50 | } 51 | 52 | ARGV=( $* ) 53 | 54 | source_configfiles 55 | parse_cmdline_simple "$1" 56 | 57 | debug tc_ssh "$1" -- "$ROLLOUT_CMDLINE" 58 | tc_ssh "$1" -- "$ROLLOUT_CMDLINE" 59 | 60 | RETCODE=$? 61 | exit $RETCODE 62 | 63 | -------------------------------------------------------------------------------- /virtesk-tc-tools/tc_screenshot: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 4 | # 5 | # This file is part of Virtesk VDI. 6 | # 7 | # Virtesk VDI is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # Virtesk VDI is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with Virtesk VDI. If not, see <http://www.gnu.org/licenses/>. 19 | 20 | # Global variables 21 | declare -a ARGV 22 | declare -a ssh_opts 23 | declare -a remote_cmdline 24 | declare -i CMDLINE_AVAILABLE=0 25 | 26 | source $(dirname $(readlink -e ${BASH_SOURCE}))/utils.sh 27 | 28 | display_usage() { 29 | cat 1>&2 <<-'EndOfHelpText' 30 | Usage: 31 | 32 | tc_screenshot <thinclient> <session> 33 | 34 | tc_screenshot takes screenshots of thinclients. 35 | 36 | It is useful for quality control after deploying new 37 | VMs or after rolling out new thinclient software. 38 | 39 | <session> is an identifier. You can choose it freely. 40 | 41 | The screenshot will be put into the following file: 42 | ${SCREENSHOT_DIR}/<session>/<thinclient>.png 43 | 44 | tc_screenshot is part of the virtesk-tc-tools collection. 45 | 46 | Amoothei-vdi is a virtual desktop infrastructure 47 | solution based on RHEV / Ovirt. 48 | 49 | Documentation: see doc/virtesk-tc-tools.rst . 50 | EndOfHelpText 51 | 52 | exit 1; 53 | } 54 | 55 | ARGV=( $* ) 56 | 57 | source_configfiles 58 | parse_cmdline_twoargument 59 | 60 | if [[ -z "${SCREENSHOT_DIR}" ]]; then 61 | error_msg Please configure $SCREENSHOT_DIR . 62 | error_msg 63 | display_usage 64 | fi 65 | 66 | FILENAME="$1.png" 67 | SESSION="$2" 68 | 69 | SESSION_DIR="${SCREENSHOT_DIR}/${SESSION}" 70 | SCREENSHOT_DEST="${SESSION_DIR}/${FILENAME}" 71 | 72 | if [[ ! -e "$SESSION_DIR" ]]; then 73 | echo "The session directory '$SESSION_DIR' does not exist." 74 | echo "Trying to create it..." 75 | mkdir -p "$SESSION_DIR" 76 | if [[ $? -eq 0 ]]; then 77 | echo "Successfully created '$SESSION_DIR'." 78 | else 79 | error_msg "Failed to create '$SESSION_DIR'." 80 | error_msg "Aborting." 81 | exit -1 82 | fi 83 | fi 84 | 85 | debug tc_ssh $1 -l vdiclient -- "$SCREENSHOT_CMDLINE" > $SCREENSHOT_DEST 86 | tc_ssh $1 -l vdiclient -- "$SCREENSHOT_CMDLINE" > $SCREENSHOT_DEST 87 | RETCODE=$? 88 | 89 | if [[ $RETCODE -ne 0 ]]; then 90 | error_msg "Taking screenshot failed with return code $RETCODE." 91 | else 92 | echo "Successfully stored a screenshot at $SCREENSHOT_DEST." 93 | ls -lh $SCREENSHOT_DEST 94 | fi 95 | 96 | exit $RETCODE 97 | 98 | -------------------------------------------------------------------------------- /virtesk-tc-tools/tc_ssh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 4 | # 5 | # This file is part of Virtesk VDI. 6 | # 7 | # Virtesk VDI is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # Virtesk VDI is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with Virtesk VDI. If not, see <http://www.gnu.org/licenses/>. 19 | 20 | # Global variables 21 | declare -a ARGV 22 | declare -a ssh_opts 23 | declare -a remote_cmdline 24 | declare -i CMDLINE_AVAILABLE=0 25 | declare TC 26 | declare TC_RAW 27 | 28 | source $(dirname $(readlink -e ${BASH_SOURCE}))/utils.sh 29 | 30 | display_usage() { 31 | cat 1>&2 <<-'EndOfHelpText' 32 | Usage: 33 | 34 | # interactive shell: 35 | tc_ssh <thinclient> [ssh-args] 36 | 37 | # execute remote command 38 | tc_ssh <thinclient> [ssh-args] -- remote_command 39 | 40 | 41 | tc_ssh is used for ssh remote access to thinclients. 42 | 43 | It can be used to open an interactive shell, or 44 | to execute remote commands. 45 | 46 | tc_ssh is part of the virtesk-tc-tools collection. 47 | 48 | Amoothei-vdi is a virtual desktop infrastructure 49 | solution based on RHEV / Ovirt. 50 | 51 | Documentation: see doc/virtesk-tc-tools.rst . 52 | EndOfHelpText 53 | 54 | exit 1; 55 | } 56 | 57 | ARGV=( $* ) 58 | 59 | source_configfiles 60 | parse_cmdline 61 | process_thinclient_arg 62 | 63 | debug ssh $SSH_GLOBAL_OPTS ${ssh_opts[@]} $TC -- "${remote_cmdline[@]}" 64 | ssh $SSH_GLOBAL_OPTS ${ssh_opts[@]} $TC -- "${remote_cmdline[@]}" 65 | 66 | RETCODE=$? 67 | 68 | if [[ $RETCODE -ne 0 ]]; then 69 | error_msg "Thinclient $TC: SSH return code: $RETCODE" 70 | fi 71 | 72 | exit $RETCODE 73 | 74 | -------------------------------------------------------------------------------- /virtesk-tc-tools/utils.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 4 | # 5 | # This file is part of Virtesk VDI. 6 | # 7 | # Virtesk VDI is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # Virtesk VDI is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with Virtesk VDI. If not, see <http://www.gnu.org/licenses/>. 19 | 20 | 21 | debug() { 22 | if [[ $DEVELOPER_MODE -eq 1 ]]; then 23 | echo DEBUG: $* 1>&2 24 | fi 25 | } 26 | 27 | error_msg() { 28 | echo ERROR: $* 1>&2 29 | } 30 | 31 | parse_cmdline() { 32 | argc=${#ARGV[@]} 33 | 34 | [[ $argc -eq 0 ]] && display_usage 35 | [[ ${ARGV[1]} = "-h" ]] && display_usage 36 | [[ ${ARGV[1]} = "--help" ]] && display_usage 37 | 38 | TC_RAW="${ARGV[0]}" 39 | debug TC_RAW = $TC_RAW 40 | 41 | index=1 42 | 43 | while [[ $index -lt $argc ]]; do 44 | debug argument $index: ${ARGV[index]} 45 | if [[ "${ARGV[index]}" = "--" ]]; then 46 | CMDLINE_AVAILABLE=1 47 | ssh_opts=( ${ARGV[@]:1:$((index-1))} ) 48 | debug ssh_opts: ${ssh_opts[@]} 49 | 50 | # skip -- 51 | let index++ 52 | 53 | if [[ index -eq $argc ]]; then 54 | error_msg "-- specified, but no remote cmdline given." 55 | error_msg 56 | 57 | display_usage 58 | fi 59 | 60 | remote_cmdline=( ${ARGV[@]:index:$argc} ) 61 | debug remote_cmdline: ${remote_cmdline[@]} 62 | 63 | break 64 | fi 65 | let index++ 66 | done 67 | 68 | if [[ $CMDLINE_AVAILABLE -eq 0 ]]; then 69 | ssh_opts=( ${ARGV[@]:1:$argc} ) 70 | debug ssh_opts: ${ssh_opts[@]} 71 | fi 72 | } 73 | 74 | parse_cmdline_simple() { 75 | [[ $# -ne 1 ]] && display_usage 76 | [[ $1 = "-h" ]] && display_usage 77 | [[ $1 = "--help" ]] && display_usage 78 | } 79 | 80 | parse_cmdline_twoargument() { 81 | argc=${#ARGV[@]} 82 | 83 | [[ ${ARGV[1]} = "-h" ]] && display_usage 84 | [[ ${ARGV[1]} = "--help" ]] && display_usage 85 | [[ $argc -ne 2 ]] && display_usage 86 | } 87 | 88 | source_configfiles(){ 89 | if [[ "x${AMOOTHEI_TC_TOOLS_CONF_DIR}" = "x" ]]; then 90 | AMOOTHEI_TC_TOOLS_CONF_DIR="/etc/virtesk-vdi" 91 | fi 92 | 93 | # Sourcing main configuration file 94 | MAIN_CONF_FILE="${AMOOTHEI_TC_TOOLS_CONF_DIR}/virtesk-tc-tools.conf" 95 | 96 | [[ -r "${MAIN_CONF_FILE}" ]] || { 97 | error_msg Cannot read main config file "${MAIN_CONF_FILE}" 98 | error_msg Aborting... 99 | error_msg 100 | display_usage 101 | } 102 | 103 | source "${MAIN_CONF_FILE}" 104 | 105 | # PROGNAME=$(basename $BASH_SOURCE) 106 | PROGNAME=$(basename $0) 107 | debug PROGNAME=${PROGNAME} 108 | 109 | IND_CONF_FILE="${AMOOTHEI_TC_TOOLS_CONF_DIR}/virtesk-tc-tools.conf.dir/${PROGNAME}.conf" 110 | [[ -r "${IND_CONF_FILE}" ]] && source "${IND_CONF_FILE}" 111 | 112 | } 113 | 114 | # Append $TC_DOMAIN if a short name (e.g. contains no "." or ":") 115 | # is given. 116 | process_thinclient_arg() { 117 | echo "${TC_RAW}" | egrep -q '\.|:' 118 | if [[ $? -ne 0 ]]; then 119 | TC="${TC_RAW}.${TC_DOMAIN}" 120 | else 121 | TC="${TC_RAW}" 122 | fi 123 | } 124 | 125 | 126 | -------------------------------------------------------------------------------- /virtesk-vm-rollout/Makefile: -------------------------------------------------------------------------------- 1 | flake8: 2 | flake8 --doctests -j auto --ignore=E221,E222,E251,E272,E241,E203 *.py virtesk-virtroom-* 3 | flake8-strict: 4 | flake8 --doctests -j auto . *.py virtesk-virtroom-* 5 | indention_report: 6 | autopep8 . --recursive --select=E101,E121 --diff 7 | indention_fix: 8 | autopep8 . --recursive --select=E101,E121 --in-place 9 | autopep8_report: 10 | autopep8 . --recursive --diff 11 | clean: 12 | rm -f *.pyc 13 | -------------------------------------------------------------------------------- /virtesk-vm-rollout/constants.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # vim: autoindent expandtab tabstop=4 sw=4 sts=4 filetype=python 4 | 5 | """Defines constants for Ad-Sy RHEV tools""" 6 | 7 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 8 | # 9 | # This file is part of Amoothei-VDI. 10 | # 11 | # Amoothei-VDI is free software: you can redistribute it and/or modify 12 | # it under the terms of the GNU General Public License as published by 13 | # the Free Software Foundation, either version 3 of the License, or 14 | # (at your option) any later version. 15 | # 16 | # Amoothei-VDI is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # 21 | # You should have received a copy of the GNU General Public License 22 | # along with Amoothei-VDI. If not, see <http://www.gnu.org/licenses/>. 23 | 24 | import os 25 | 26 | DEFAULT_CONFIGURATION_FILE_NAME = 'adsy-rhev-tools.config' 27 | 28 | CONFIG_FILE_SEARCH_PATH = [ 29 | os.path.expanduser('~/.config/virtesk-vdi/virtesk-vm-rollout.conf'), 30 | '/etc/virtesk-vdi/virtesk-vm-rollout.conf' 31 | ] 32 | 33 | # Wait time in seconds after creating a new VM. 34 | VM_CREATION_SLEEP_TIME = 15 35 | 36 | # Wait time in seconds after various tasks concerning VMs 37 | VM_SLEEP_TIME = 10 38 | 39 | # Wait time in seconds after creating a new VM snapshot 40 | CREATE_VM_SNAPSHOT_SLEEP_TIME = 8 41 | 42 | # Wait time in seconds between attempts to check the state of freshly 43 | # created VM snapshots 44 | WAIT_FOR_SNAPSHOTS_READY_SLEEP_TIME = 4 45 | -------------------------------------------------------------------------------- /virtesk-vm-rollout/rhev_manager.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # vim: autoindent expandtab tabstop=4 sw=4 sts=4 filetype=python 4 | 5 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 6 | # 7 | # This file is part of Virtesk VDI. 8 | # 9 | # Virtesk VDI is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # Virtesk VDI is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with Virtesk VDI. If not, see <http://www.gnu.org/licenses/>. 21 | 22 | 23 | # System Imports 24 | import os 25 | import time 26 | import logging 27 | import logging.config 28 | import configobj 29 | import sys 30 | import argparse 31 | import textwrap 32 | 33 | # Project imports 34 | import rhev 35 | import utils 36 | import constants 37 | 38 | 39 | class RhevManager(): 40 | 41 | def __init__(self, configuration_file): 42 | assert(configuration_file) 43 | 44 | self.config_file = configuration_file 45 | self.logger = None 46 | self.rhev_lib = None 47 | self.base_configuration = None 48 | 49 | self.read_base_configuration() 50 | self.initialize_logging() 51 | 52 | self.rhev_lib = rhev.rhev(self.base_configuration) 53 | 54 | @staticmethod 55 | def construct_from_cmdargs(description): 56 | epilog = textwrap.dedent(''' 57 | The following config file locations are used, first match wins: 58 | * Command line argument 59 | * ~/.config/virtesk-vdi/virtesk-vm-rollout.conf 60 | * /etc/virtesk-vdi/virtesk-vm-rollout.conf 61 | ''') 62 | 63 | parser = argparse.ArgumentParser( 64 | formatter_class=argparse.RawDescriptionHelpFormatter, 65 | epilog=epilog, description=description) 66 | 67 | parser.add_argument( 68 | '--config', 69 | help="Absolute or relative path to configuration file." 70 | ) 71 | 72 | parser.add_argument( 73 | 'virtroom', help="virtual room to act on" 74 | ) 75 | 76 | args = parser.parse_args() 77 | 78 | config_file = utils.get_valid_config_file( 79 | args.config 80 | ) 81 | 82 | room = args.virtroom 83 | 84 | # Initialize RHEV manager 85 | rhev_manager = RhevManager( 86 | config_file 87 | ) 88 | 89 | return rhev_manager, room 90 | 91 | def cleanup(self): 92 | self.rhev_lib.cleanup() 93 | 94 | def read_base_configuration(self): 95 | try: 96 | self.base_configuration = configobj.ConfigObj( 97 | self.config_file, 98 | file_error=True 99 | ) 100 | self.base_configuration['general']['config_file'] = ( 101 | self.config_file 102 | ) 103 | logging.info('Successfully read base configuration') 104 | 105 | except (configobj.ConfigObjError, IOError), e: 106 | raise Exception( 107 | "Could not read '{0}': {1}".format( 108 | os.path.abspath(self.config_file), e 109 | ) 110 | ) 111 | 112 | def initialize_logging(self): 113 | assert(self.config_file) 114 | 115 | try: 116 | config_file_path, config_file_name = os.path.split( 117 | self.config_file 118 | ) 119 | 120 | log_config_file = os.path.join( 121 | config_file_path, 122 | self.base_configuration['logging']['config_file'] 123 | ) 124 | 125 | log_file = self.base_configuration['logging']['log_file'] 126 | 127 | logging.config.fileConfig(log_config_file, defaults={ 128 | 'log_file': log_file, 129 | 'file_mode': 'a' 130 | }) 131 | 132 | self.logger = logging.getLogger(__name__) 133 | self.base_configuration['logging']['config_file'] = log_config_file 134 | 135 | self.logger.debug('Successfully initialized logger') 136 | self.logger.debug( 137 | "Using configuration file '{0}'".format(log_config_file) 138 | ) 139 | 140 | except Exception as ex: 141 | logging.error( 142 | "Failed to initialize logging. " 143 | "Logfile: `{0}'. Error details: `{1}'".format(log_file, ex) 144 | ) 145 | sys.exit(-1) 146 | 147 | def cleanup_classroom(self, classroom): 148 | if not classroom: 149 | raise Exception('No classrom given') 150 | 151 | self.logger.info("cleaning up classroom '{0}'".format(classroom)) 152 | 153 | try: 154 | # Get VMs for given classroom 155 | self.logger.debug( 156 | "Getting VMs for classroom '{0}'".format(classroom) 157 | ) 158 | vms_for_classroom = self.rhev_lib.get_vms_for_classroom(classroom) 159 | 160 | if vms_for_classroom: 161 | # Teardown of VMs 162 | for vm in vms_for_classroom: 163 | self.rhev_lib.delete_vm(vm) 164 | 165 | self.logger.info( 166 | "Successfully cleaned up classroom '{0}'".format(classroom) 167 | ) 168 | 169 | except Exception, e: 170 | raise Exception( 171 | "Cleaning up classroom '{0}' failed: {1}".format(classroom, e) 172 | ) 173 | 174 | # reset vms to snapshot 175 | def reset_classroom(self, classroom): 176 | if not classroom: 177 | raise Exception('No classroom given') 178 | 179 | self.logger.info( 180 | "Starting to reset classroom '{0}'".format(classroom) 181 | ) 182 | 183 | # Get VMs for given classroom 184 | vms_for_classroom = self.rhev_lib.get_vms_for_classroom( 185 | classroom) 186 | 187 | for vm in vms_for_classroom: 188 | self.rhev_lib.reset_vm_to_snapshot(vm) 189 | 190 | def analyze_config_of_classroom(self, classroom): 191 | self.logger.info( 192 | "Analyzing configuration of classroom {0}".format(classroom) 193 | ) 194 | 195 | # Get VMs for given classroom 196 | vms_for_classroom = self.rhev_lib.get_vms_for_classroom( 197 | classroom) 198 | 199 | for vmconfig in vms_for_classroom: 200 | self.logger.debug(str(vmconfig)) 201 | 202 | if self.rhev_lib.check_if_vms_exist(vms_for_classroom): 203 | self.logger.info( 204 | "Some VMs already exist. Please delete them before rollout" 205 | ) 206 | self.rhev_lib.analyze_snapshots(vms_for_classroom) 207 | 208 | def start_all_vms_in_room(self, classroom): 209 | self.logger.info("Starting VMs in Room... {0}".format(classroom)) 210 | 211 | # Get VMs for given classroom 212 | vms_for_classroom = self.rhev_lib.get_vms_for_classroom( 213 | classroom 214 | ) 215 | 216 | for vmconfig in vms_for_classroom: 217 | self.logger.debug(str(vmconfig)) 218 | 219 | for vmconfig in vms_for_classroom: 220 | self.rhev_lib.start_vm_if_possible(vmconfig) 221 | 222 | def shutdown_all_vms_in_room(self, classroom): 223 | self.logger.info("Shutting down VMs in Room... {0}".format(classroom)) 224 | 225 | # Get VMs for given classroom 226 | vms_for_classroom = self.rhev_lib.get_vms_for_classroom( 227 | classroom) 228 | 229 | for vmconfig in vms_for_classroom: 230 | self.logger.debug(str(vmconfig)) 231 | 232 | for vmconfig in vms_for_classroom: 233 | self.rhev_lib.shutdown_vm_if_possible(vmconfig) 234 | 235 | def rollout_classroom(self, classroom): 236 | if not classroom: 237 | raise Exception('No classroom given') 238 | 239 | try: 240 | with utils.tempdir("virtesk-virtroom-rollout-") as temp_dir: 241 | self.logger.info( 242 | "Starting to roll out classroom '{0}'".format(classroom) 243 | ) 244 | 245 | # Get VMs for given classroom 246 | vms_for_classroom = self.rhev_lib.get_vms_for_classroom( 247 | classroom) 248 | 249 | for vmconfig in vms_for_classroom: 250 | self.logger.debug(str(vmconfig)) 251 | 252 | # Make sure the VMs don't exist (avoiding conflicts) 253 | if self.rhev_lib.check_if_vms_exist(vms_for_classroom): 254 | self.logger.error( 255 | "Some VMs already exist. Please delete them first" 256 | ) 257 | sys.exit(-1) 258 | 259 | # Create (template) VMs 260 | for vm in vms_for_classroom: 261 | self.rhev_lib.create_standalone_vm(vm) 262 | # sys.exit(0) 263 | time.sleep(constants.VM_CREATION_SLEEP_TIME) 264 | 265 | # Wait for VMs to shut donw 266 | self.rhev_lib.wait_for_vms_down( 267 | vmconfigs=vms_for_classroom, 268 | formatstring="VM {0} successfully created" 269 | ) 270 | 271 | # Add NIC and initiate sysprep 272 | for vm in vms_for_classroom: 273 | self.rhev_lib.add_vm_nic(vm) 274 | self.rhev_lib.sysprep_vm(vm, temp_dir) 275 | self.logger.info('Waiting for sysprep to finish') 276 | time.sleep(constants.VM_SLEEP_TIME) 277 | 278 | # Wait for VMs to shut down 279 | msg_formatstring = \ 280 | "VM {0} has been stopped after running Autounattend.xml." 281 | 282 | self.rhev_lib.wait_for_vms_down( 283 | vmconfigs=vms_for_classroom, 284 | formatstring=msg_formatstring 285 | ) 286 | 287 | # Postprocess VMs: 288 | # Eject ISOs, set statless and add (user-) group. 289 | # Create a snapshot of every VM. 290 | for vm in vms_for_classroom: 291 | self.rhev_lib.postprocess_vm(vm) 292 | 293 | # Wait for all VM snapshots to become ready. 294 | self.rhev_lib.wait_for_vm_snapshots_ready(vms_for_classroom) 295 | 296 | # Start VMs 297 | for vm in vms_for_classroom: 298 | self.rhev_lib.start_vm_after_rollout(vm) 299 | 300 | self.logger.info( 301 | "Finished rolling out classroom '{0}' successfully".format( 302 | classroom 303 | ) 304 | ) 305 | 306 | except Exception, e: 307 | logging.exception(e) 308 | raise Exception( 309 | "Rolling out classroom '{0}' failed: {1}".format(classroom, e) 310 | ) 311 | 312 | return True 313 | -------------------------------------------------------------------------------- /virtesk-vm-rollout/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # vim: autoindent expandtab tabstop=4 sw=4 sts=4 filetype=python 4 | 5 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 6 | # 7 | # This file is part of Amoothei-VDI. 8 | # 9 | # Amoothei-VDI is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # Amoothei-VDI is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with Amoothei-VDI. If not, see <http://www.gnu.org/licenses/>. 21 | 22 | 23 | # System imports 24 | import contextlib 25 | import errno 26 | import os 27 | import shutil 28 | import tempfile 29 | 30 | # Project imports 31 | import constants 32 | 33 | 34 | class Failure: 35 | 36 | def __init__(self, msg): 37 | self.msg = msg 38 | 39 | 40 | def get_valid_config_file(config_file): 41 | """ 42 | Checks if the given parameter 'config_file' 43 | exists as file and is readable. If not, tries 44 | to get one possible configuration file within 45 | the path under CONFIG_FILE_SEARCH_PATH. 46 | """ 47 | 48 | if config_file is not None: 49 | if os.path.exists(config_file): 50 | return config_file 51 | else: 52 | config_file = os.path.expanduser(config_file) 53 | if os.path.exists(config_file): 54 | return config_file 55 | else: 56 | raise Failure( 57 | "Config file `{0}' does not exist.".format(config_file) 58 | ) 59 | 60 | for config_file in constants.CONFIG_FILE_SEARCH_PATH: 61 | if os.path.exists(config_file): 62 | os.stat(config_file) 63 | return config_file 64 | 65 | raise Failure( 66 | "No config file specified on command line and no " 67 | "config file available at default locations." 68 | ) 69 | 70 | 71 | @contextlib.contextmanager 72 | def tempdir(prefix): 73 | try: 74 | tempdir = tempfile.mkdtemp(prefix=prefix) 75 | yield tempdir 76 | finally: 77 | try: 78 | shutil.rmtree(tempdir) 79 | except OSError as e: 80 | # Reraise unless ENOENT: No such file or directory 81 | # (ok if directory has already been deleted) 82 | if e.errno != errno.ENOENT: 83 | raise 84 | -------------------------------------------------------------------------------- /virtesk-vm-rollout/virtesk-virtroom-delete: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # vim: autoindent expandtab tabstop=4 sw=4 sts=4 filetype=python 4 | 5 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 6 | # 7 | # This file is part of Amoothei-VDI. 8 | # 9 | # Amoothei-VDI is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # Amoothei-VDI is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with Amoothei-VDI. If not, see <http://www.gnu.org/licenses/>. 21 | 22 | # System Imports 23 | import traceback 24 | import logging 25 | import sys 26 | 27 | # Project imports 28 | import rhev_manager 29 | import utils 30 | 31 | try: 32 | description = """Deletes all VMs in a virtual room.""" 33 | 34 | manager, room = rhev_manager.RhevManager.construct_from_cmdargs( 35 | description 36 | ) 37 | 38 | manager.cleanup_classroom(room) 39 | 40 | manager.cleanup() 41 | 42 | except utils.Failure as f: 43 | logging.error(f.msg) 44 | sys.exit(-1) 45 | except Exception as ex: 46 | logging.exception("Unexpected error: {0}".format(ex)) 47 | logging.exception(traceback.format_exc()) 48 | sys.exit(-1) 49 | -------------------------------------------------------------------------------- /virtesk-vm-rollout/virtesk-virtroom-reset: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # vim: autoindent expandtab tabstop=4 sw=4 sts=4 filetype=python 4 | 5 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 6 | # 7 | # This file is part of Amoothei-VDI. 8 | # 9 | # Amoothei-VDI is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # Amoothei-VDI is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with Amoothei-VDI. If not, see <http://www.gnu.org/licenses/>. 21 | 22 | 23 | # System Imports 24 | import traceback 25 | import logging 26 | import sys 27 | 28 | # Project imports 29 | import rhev_manager 30 | import utils 31 | 32 | try: 33 | description = """Reset a virtual room to snapshosts""" 34 | 35 | manager, room = rhev_manager.RhevManager.construct_from_cmdargs( 36 | description 37 | ) 38 | 39 | manager.reset_classroom(room) 40 | 41 | manager.cleanup() 42 | 43 | except utils.Failure as f: 44 | logging.error(f.msg) 45 | sys.exit(-1) 46 | except Exception as ex: 47 | logging.exception("Unexpected error: {0}".format(ex)) 48 | logging.exception(traceback.format_exc()) 49 | sys.exit(-1) 50 | -------------------------------------------------------------------------------- /virtesk-vm-rollout/virtesk-virtroom-rollout: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # vim: autoindent expandtab tabstop=4 sw=4 sts=4 filetype=python 4 | 5 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 6 | # 7 | # This file is part of Amoothei-VDI. 8 | # 9 | # Amoothei-VDI is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # Amoothei-VDI is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with Amoothei-VDI. If not, see <http://www.gnu.org/licenses/>. 21 | 22 | # System Imports 23 | import traceback 24 | import logging 25 | import sys 26 | 27 | # Project imports 28 | import rhev_manager 29 | import utils 30 | 31 | try: 32 | description = """Roll out VMs of a virtual room.""" 33 | 34 | manager, room = rhev_manager.RhevManager.construct_from_cmdargs( 35 | description 36 | ) 37 | 38 | manager.rollout_classroom(room) 39 | 40 | manager.cleanup() 41 | 42 | except utils.Failure as f: 43 | logging.error(f.msg) 44 | sys.exit(-1) 45 | except Exception as ex: 46 | logging.exception("Unexpected error: {0}".format(ex)) 47 | logging.exception(traceback.format_exc()) 48 | sys.exit(-1) 49 | -------------------------------------------------------------------------------- /virtesk-vm-rollout/virtesk-virtroom-show: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # vim: autoindent expandtab tabstop=4 sw=4 sts=4 filetype=python 4 | 5 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 6 | # 7 | # This file is part of Virtesk VDI. 8 | # 9 | # Virtesk VDI is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # Virtesk VDI is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with Virtesk VDI. If not, see <http://www.gnu.org/licenses/>. 21 | 22 | # System Imports 23 | import traceback 24 | import logging 25 | import sys 26 | 27 | # Project imports 28 | import rhev_manager 29 | import utils 30 | 31 | try: 32 | description = """Show information about the virtual room: 33 | * config dump 34 | * analyze vm status 35 | * show snapshots 36 | """ 37 | 38 | manager, room = rhev_manager.RhevManager.construct_from_cmdargs( 39 | description 40 | ) 41 | 42 | manager.analyze_config_of_classroom(room) 43 | 44 | manager.cleanup() 45 | 46 | except utils.Failure as f: 47 | logging.error(f.msg) 48 | sys.exit(-1) 49 | except Exception as ex: 50 | logging.exception("Unexpected error: {0}".format(ex)) 51 | logging.exception(traceback.format_exc()) 52 | sys.exit(-1) 53 | -------------------------------------------------------------------------------- /virtesk-vm-rollout/virtesk-virtroom-shutdown: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # vim: autoindent expandtab tabstop=4 sw=4 sts=4 filetype=python 4 | 5 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 6 | # 7 | # This file is part of Amoothei-VDI. 8 | # 9 | # Amoothei-VDI is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # Amoothei-VDI is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with Amoothei-VDI. If not, see <http://www.gnu.org/licenses/>. 21 | 22 | # System Imports 23 | import traceback 24 | import logging 25 | import sys 26 | 27 | # Project imports 28 | import rhev_manager 29 | import utils 30 | 31 | try: 32 | description = """Shutdown all VMs in a virtual room.""" 33 | 34 | manager, room = rhev_manager.RhevManager.construct_from_cmdargs( 35 | description 36 | ) 37 | 38 | manager.shutdown_all_vms_in_room(room) 39 | 40 | manager.cleanup() 41 | 42 | except utils.Failure as f: 43 | logging.error(f.msg) 44 | sys.exit(-1) 45 | except Exception as ex: 46 | logging.exception("Unexpected error: {0}".format(ex)) 47 | logging.exception(traceback.format_exc()) 48 | sys.exit(-1) 49 | -------------------------------------------------------------------------------- /virtesk-vm-rollout/virtesk-virtroom-start: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # vim: autoindent expandtab tabstop=4 sw=4 sts=4 filetype=python 4 | 5 | # Copyright (c) 2013-2016, Adfinis SyGroup AG 6 | # 7 | # This file is part of Virtesk VDI. 8 | # 9 | # Virtesk VDI is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # Virtesk VDI is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with Virtesk VDI. If not, see <http://www.gnu.org/licenses/>. 21 | 22 | 23 | # System Imports 24 | import traceback 25 | import logging 26 | import sys 27 | 28 | # Project imports 29 | import rhev_manager 30 | import utils 31 | 32 | try: 33 | description = """Starts all VMs in the virtual room.""" 34 | 35 | manager, room = rhev_manager.RhevManager.construct_from_cmdargs( 36 | description 37 | ) 38 | 39 | manager.start_all_vms_in_room(room) 40 | 41 | manager.cleanup() 42 | 43 | except utils.Failure as f: 44 | logging.error(f.msg) 45 | sys.exit(-1) 46 | except Exception as ex: 47 | logging.exception("Unexpected error: {0}".format(ex)) 48 | logging.exception(traceback.format_exc()) 49 | sys.exit(-1) 50 | --------------------------------------------------------------------------------