├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── support-windows-version.md ├── dco.yml ├── pull_request_template.md └── workflows │ └── powershell.yml ├── .gitignore ├── README.md ├── generate-all.sh ├── generate.sh ├── nssm ├── nssm-2.24 ├── README.txt ├── win32 │ └── nssm.exe └── win64 │ └── nssm.exe ├── package.wxs ├── src └── context.ps1 └── unattend.xml /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: 'Type: Bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Description** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Details:** 20 | - Windows Version: [ e.g. Windows 10 ] 21 | - Context release version: [ e.g. 6.6.0 ] 22 | - OpenNebula version: [ e.g. 6.6.1 ] 23 | 24 | **Additional context** 25 | Add any other context about the problem here. 26 | 27 | ## Progress Status 28 | - [ ] Code committed 29 | - [ ] Testing - QA 30 | - [ ] Documentation (Release notes - resolved issues, compatibility, known issues) 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: 'Type: Feature' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Description** 11 | Brief description of the new functionality 12 | 13 | **Use case** 14 | How are you going to use this new feature? Why do you need it? 15 | 16 | **Additional Context** 17 | Please feel free to add any other context or screenshots about the feature request here. Or any other alternative you have considered to address this new feature. 18 | 19 | ## Progress Status 20 | - [ ] Code committed 21 | - [ ] Testing - QA 22 | - [ ] Documentation (Release notes - resolved issues, compatibility, known issues) 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/support-windows-version.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Support Windows version 3 | about: Support a Windows version officially supported by Microsoft 4 | title: Support Windows 5 | labels: 'Type: Feature' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Progress 11 | - [ ] Image Built and operational 12 | - [ ] Testing - QA 13 | - [ ] [Platform notes](https://docs.opennebula.io/6.6/intro_release_notes/release_notes/platform_notes.html#windows-contextualization-packages) 14 | - [ ] [Windows KB](https://support.opennebula.pro/hc/en-us/articles/360042898271-How-to-Build-an-Image-for-Windows-Virtual-Machines) 15 | -------------------------------------------------------------------------------- /.github/dco.yml: -------------------------------------------------------------------------------- 1 | require: 2 | members: false 3 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Changes proposed in this pull request: 8 | - 9 | - 10 | - 11 | -------------------------------------------------------------------------------- /.github/workflows/powershell.yml: -------------------------------------------------------------------------------- 1 | name: PowerShell Linting 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | # https://docs.github.com/en/actions/guides/building-and-testing-powershell#using-psscriptanalyzer-to-lint-code 7 | lint-with-PSScriptAnalyzer: 8 | name: Install and run PSScriptAnalyzer 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Install PSScriptAnalyzer module 13 | shell: pwsh 14 | run: | 15 | Set-PSRepository PSGallery -InstallationPolicy Trusted 16 | Install-Module PSScriptAnalyzer -ErrorAction Stop 17 | - name: Lint with PSScriptAnalyzer 18 | shell: pwsh 19 | run: | 20 | Invoke-ScriptAnalyzer -Path src/*.ps1 -Recurse -Outvariable issues 21 | $errors = $issues.Where({$_.Severity -eq 'Error'}) 22 | $warnings = $issues.Where({$_.Severity -eq 'Warning'}) 23 | if ($errors) { 24 | Write-Error "There were $($errors.Count) errors and $($warnings.Count) warnings total." -ErrorAction Stop 25 | } else { 26 | Write-Output "There were $($errors.Count) errors and $($warnings.Count) warnings total." 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | *.msi 3 | rhsrvany.exe 4 | *.un~ 5 | .idea 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | ⚠️⚠️⚠️⚠️⚠️⚠️⚠️ 3 | 4 | >[!IMPORTANT] 5 | >This repository is being moved to a new location https://github.com/OpenNebula/one-apps 6 | >During the transition the repository is in read-only mode 7 | 8 | ---- 9 | 10 | 11 | # OpenNebula Windows VM Contextualization 12 | 13 | ## Description 14 | 15 | This addon provides contextualization package for the Windows 16 | guest virtual machines running in the OpenNebula cloud. Based 17 | on the provided contextualization parameters, the packages prepare the 18 | networking in the running guest virt. machine, set 19 | passwords, run custom start scripts, and many others. 20 | 21 | ## Download 22 | 23 | Latest versions can be downloaded from the 24 | [release page](https://github.com/OpenNebula/addon-context-windows/releases). 25 | Check the supported OpenNebula versions for each release. 26 | 27 | ## Install 28 | 29 | Documentation on packages installation and guest contextualization can 30 | be found in the latest stable 31 | [OpenNebula Operation Guide](http://docs.opennebula.org/stable/operation/vm_setup/context_overview.html). 32 | For beta releases, refer to the latest 33 | [development documentation](http://docs.opennebula.org/devel/operation/vm_setup/context_overview.html). 34 | 35 | ## Build own package 36 | 37 | ### Requirements 38 | 39 | * **Linux host** 40 | * latest [msitools](https://wiki.gnome.org/msitools) 41 | * binary [nssm.exe](https://nssm.cc/) [present] 42 | * binary [rhsrvany.exe](https://github.com/rwmjones/rhsrvany) [optional] 43 | * `mkisofs` 44 | 45 | The service manager **NSSM** is the preferred tool to manage services because 46 | it handles long running services better and more correctly (srvany/rhsrvany 47 | fails to terminate its child processes on stop). NSSM is in public domain and 48 | the binary is part of this repo. There are both 32bit and 64bit versions - 49 | currently 32bit version is used because it covers broader set of systems. 50 | 51 | If you wish to use rhsrvany instead then you must set the shell variable 52 | `SRV_MANAGER` to `rhsrvany` otherwise it will default to `nssm`. 53 | 54 | On RHEL (CentOS) and Fedora systems, the required binary 55 | [rhsrvany.exe](https://github.com/rwmjones/rhsrvany) is distributed as part 56 | of the package `virt-v2v` and placed into `/usr/share/virt-tools/rhsrvany.exe`. 57 | Please copy the EXE into your local repository clone before creating the MSI. 58 | 59 | ### Steps 60 | 61 | Script `generate.sh` builds the MSI package. It's a wrapper around 62 | the `wixl` command from `msitools`. It reads the `package.wxs`, a package 63 | definition in the WiX-like XML format. Package name or version can be 64 | overridden by env. variables `NAME` and `VERSION`. For example: 65 | 66 | ``` 67 | $ TARGET=msi ./generate.sh 68 | $ NAME=one-context TARGET=msi ./generate.sh 69 | $ VERSION=1.0.0 TARGET=msi ./generate.sh 70 | ``` 71 | 72 | New package is created as `${NAME}-${VERSION}.msi`, 73 | e.g. `one-context-1.0.0.msi` in the `out/` directory. 74 | 75 | You can also built both the iso and msi targets like this: 76 | 77 | ``` 78 | $ ./generate-all.sh 79 | ``` 80 | 81 | Or with a different service manager and explicit version: 82 | 83 | ``` 84 | $ env SRV_MANAGER=rhsrvany VERSION=5.13 ./generate-all.sh 85 | ``` 86 | 87 | Please ignore following assertion on package build, which is caused 88 | by skipping the attribute `Start` in tag `ServiceControl`. The parameter 89 | is optional in WiX specification, but the `msitools` still counts with it. 90 | Despite that, the package is built. 91 | 92 | ``` 93 | (wixl:22764): wixl-CRITICAL **: wixl_wix_builder_install_mode_to_event: assertion 'modeString != NULL' failed 94 | ``` 95 | 96 | ## Acknowledgements 97 | 98 | This addon is largely based upon the work by André Monteiro and Tiago Batista in the [DETI/IEETA Universidade de Aveiro](http://www.ua.pt/). The original guide is available here: [OpenNebula - IEETA](http://wiki.ieeta.pt/wiki/index.php/OpenNebula) 99 | 100 | ## License 101 | 102 | Copyright 2002-2021, OpenNebula Project, OpenNebula Systems (formerly C12G Labs) 103 | 104 | Licensed under the Apache License, Version 2.0 (the "License"); you may 105 | not use this file except in compliance with the License. You may obtain 106 | a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 107 | 108 | Unless required by applicable law or agreed to in writing, software 109 | distributed under the License is distributed on an "AS IS" BASIS, 110 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 111 | See the License for the specific language governing permissions and 112 | limitations under the License. 113 | -------------------------------------------------------------------------------- /generate-all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | export DATE=$(date +%Y%m%d) 6 | TARGETS='msi iso' 7 | 8 | for TARGET in $TARGETS; do 9 | TARGET="${TARGET}" ./generate.sh 10 | done 11 | 12 | echo 13 | echo "The packages are here:" 14 | echo "--------------------------------------------------------------------------------" 15 | find out -type f 16 | -------------------------------------------------------------------------------- /generate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # -------------------------------------------------------------------------- # 4 | # Copyright 2002-2021, OpenNebula Project, OpenNebula Systems # 5 | # # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may # 7 | # not use this file except in compliance with the License. You may obtain # 8 | # a copy of the License at # 9 | # # 10 | # http://www.apache.org/licenses/LICENSE-2.0 # 11 | # # 12 | # Unless required by applicable law or agreed to in writing, software # 13 | # distributed under the License is distributed on an "AS IS" BASIS, # 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # 15 | # See the License for the specific language governing permissions and # 16 | # limitations under the License. # 17 | #--------------------------------------------------------------------------- # 18 | 19 | if [ -z "${TARGET}" ]; then 20 | echo 'Error: env. variable TARGET not set' >&2 21 | exit 1 22 | fi 23 | 24 | ### 25 | 26 | if [ -z "${RELEASE}" ]; then 27 | if git describe --contains $(git rev-parse HEAD) &>/dev/null; then 28 | RELEASE=1 29 | else 30 | DATE=${DATE:-$(date +%Y%m%d)} 31 | GIT=$(git rev-parse --short HEAD) 32 | RELEASE="${DATE}git${GIT}" 33 | fi 34 | fi 35 | 36 | ### 37 | 38 | NAME=${NAME:-one-context} 39 | VERSION=${VERSION:-6.6.1} 40 | RELEASE=${RELEASE:-1} 41 | LABEL="${NAME}-${VERSION}" 42 | SRV_MANAGER="${SRV_MANAGER:-nssm}" 43 | 44 | if [ "${RELEASE}" = '1' ]; then 45 | FILENAME=${FILENAME:-${NAME}-${VERSION}.${TARGET}} 46 | else 47 | FILENAME=${FILENAME:-${NAME}-${VERSION}-${RELEASE}.${TARGET}} 48 | fi 49 | 50 | # cleanup 51 | if [ -z "${OUT}" ]; then 52 | OUT="out/${FILENAME}" 53 | mkdir -p $(dirname "${OUT}") 54 | rm -rf "${OUT}" 55 | fi 56 | 57 | set -e 58 | 59 | if [ "${TARGET}" = 'msi' ]; then 60 | case "${SRV_MANAGER}" in 61 | nssm) 62 | _SRV_BINARY_NAME='nssm.exe' 63 | _SRV_BINARY_FILE='nssm/win32/nssm.exe' 64 | _SRV_BINARY_ARGS='' 65 | ;; 66 | rhsrvany) 67 | _SRV_BINARY_NAME='rhsrvany.exe' 68 | _SRV_BINARY_FILE='rhsrvany.exe' 69 | _SRV_BINARY_ARGS='-s onecontext' 70 | 71 | # in the rhsrvany case we might be able to use some os package 72 | if [ ! -f rhsrvany.exe ]; then 73 | if [ -f /usr/share/virt-tools/rhsrvany.exe ]; then 74 | cp /usr/share/virt-tools/rhsrvany.exe . 75 | fi 76 | fi 77 | ;; 78 | esac 79 | 80 | if [ ! -f "${_SRV_BINARY_FILE}" ] ; then 81 | echo "Error: The service binary is missing: ${_SRV_BINARY_FILE}" 82 | exit 1 83 | else 84 | echo "Info: Using the binary: ${_SRV_BINARY_FILE}" 85 | fi >&2 86 | 87 | wixl -D Version="${VERSION}" \ 88 | -D SrvBinaryName="${_SRV_BINARY_NAME}" \ 89 | -D SrvBinaryFile="${_SRV_BINARY_FILE}" \ 90 | -D SrvBinaryArgs="${_SRV_BINARY_ARGS}" \ 91 | -o "${OUT}" package.wxs 92 | 93 | elif [ "${TARGET}" = 'iso' ]; then 94 | mkisofs -J -R -input-charset utf8 \ 95 | -m '*.iso' \ 96 | -V "${LABEL}" \ 97 | -o "${OUT}" \ 98 | $(dirname "${OUT}") 99 | 100 | else 101 | echo "Error: Invalid target '${TARGET}'" >&2 102 | exit 1 103 | fi 104 | 105 | echo $(basename ${OUT}) 106 | -------------------------------------------------------------------------------- /nssm: -------------------------------------------------------------------------------- 1 | nssm-2.24 -------------------------------------------------------------------------------- /nssm-2.24/README.txt: -------------------------------------------------------------------------------- 1 | NSSM: The Non-Sucking Service Manager 2 | Version 2.24, 2014-08-31 3 | 4 | NSSM is a service helper program similar to srvany and cygrunsrv. It can 5 | start any application as an NT service and will restart the service if it 6 | fails for any reason. 7 | 8 | NSSM also has a graphical service installer and remover. 9 | 10 | Full documentation can be found online at 11 | 12 | http://nssm.cc/ 13 | 14 | Since version 2.0, the GUI can be bypassed by entering all appropriate 15 | options on the command line. 16 | 17 | Since version 2.1, NSSM can be compiled for x64 platforms. 18 | Thanks Benjamin Mayrargue. 19 | 20 | Since version 2.2, NSSM can be configured to take different actions 21 | based on the exit code of the managed application. 22 | 23 | Since version 2.3, NSSM logs to the Windows event log more elegantly. 24 | 25 | Since version 2.5, NSSM respects environment variables in its parameters. 26 | 27 | Since version 2.8, NSSM tries harder to shut down the managed application 28 | gracefully and throttles restart attempts if the application doesn't run 29 | for a minimum amount of time. 30 | 31 | Since version 2.11, NSSM respects srvany's AppEnvironment parameter. 32 | 33 | Since version 2.13, NSSM is translated into French. 34 | Thanks François-Régis Tardy. 35 | 36 | Since version 2.15, NSSM is translated into Italian. 37 | Thanks Riccardo Gusmeroli. 38 | 39 | Since version 2.17, NSSM can try to shut down console applications by 40 | simulating a Control-C keypress. If they have installed a handler routine 41 | they can clean up and shut down gracefully on receipt of the event. 42 | 43 | Since version 2.17, NSSM can redirect the managed application's I/O streams 44 | to an arbitrary path. 45 | 46 | Since version 2.18, NSSM can be configured to wait a user-specified amount 47 | of time for the application to exit when shutting down. 48 | 49 | Since version 2.19, many more service options can be configured with the 50 | GUI installer as well as via the registry. 51 | 52 | Since version 2.19, NSSM can add to the service's environment by setting 53 | AppEnvironmentExtra in place of or in addition to the srvany-compatible 54 | AppEnvironment. 55 | 56 | Since version 2.22, NSSM can set the managed application's process priority 57 | and CPU affinity. 58 | 59 | Since version 2.22, NSSM can apply an unconditional delay before restarting 60 | an application which has exited. 61 | 62 | Since version 2.22, NSSM can rotate existing output files when redirecting I/O. 63 | 64 | Since version 2.22, NSSM can set service display name, description, startup 65 | type, log on details and dependencies. 66 | 67 | Since version 2.22, NSSM can manage existing services. 68 | 69 | 70 | Usage 71 | ----- 72 | In the usage notes below, arguments to the program may be written in angle 73 | brackets and/or square brackets. means you must insert the 74 | appropriate string and [] means the string is optional. See the 75 | examples below... 76 | 77 | Note that everywhere appears you may substitute the 78 | service's display name. 79 | 80 | 81 | Installation using the GUI 82 | -------------------------- 83 | To install a service, run 84 | 85 | nssm install 86 | 87 | You will be prompted to enter the full path to the application you wish 88 | to run and any command line options to pass to that application. 89 | 90 | Use the system service manager (services.msc) to control advanced service 91 | properties such as startup method and desktop interaction. NSSM may 92 | support these options at a later time... 93 | 94 | 95 | Installation using the command line 96 | ----------------------------------- 97 | To install a service, run 98 | 99 | nssm install [] 100 | 101 | NSSM will then attempt to install a service which runs the named application 102 | with the given options (if you specified any). 103 | 104 | Don't forget to enclose paths in "quotes" if they contain spaces! 105 | 106 | If you want to include quotes in the options you will need to """quote""" the 107 | quotes. 108 | 109 | 110 | Managing the service 111 | -------------------- 112 | NSSM will launch the application listed in the registry when you send it a 113 | start signal and will terminate it when you send a stop signal. So far, so 114 | much like srvany. But NSSM is the Non-Sucking service manager and can take 115 | action if/when the application dies. 116 | 117 | With no configuration from you, NSSM will try to restart itself if it notices 118 | that the application died but you didn't send it a stop signal. NSSM will 119 | keep trying, pausing between each attempt, until the service is successfully 120 | started or you send it a stop signal. 121 | 122 | NSSM will pause an increasingly longer time between subsequent restart attempts 123 | if the service fails to start in a timely manner, up to a maximum of four 124 | minutes. This is so it does not consume an excessive amount of CPU time trying 125 | to start a failed application over and over again. If you identify the cause 126 | of the failure and don't want to wait you can use the Windows service console 127 | (where the service will be shown in Paused state) to send a continue signal to 128 | NSSM and it will retry within a few seconds. 129 | 130 | By default, NSSM defines "a timely manner" to be within 1500 milliseconds. 131 | You can change the threshold for the service by setting the number of 132 | milliseconds as a REG_DWORD value in the registry at 133 | HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppThrottle. 134 | 135 | Alternatively, NSSM can pause for a configurable amount of time before 136 | attempting to restart the application even if it successfully ran for the 137 | amount of time specified by AppThrottle. NSSM will consult the REG_DWORD value 138 | at HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppRestartDelay 139 | for the number of milliseconds to wait before attempting a restart. If 140 | AppRestartDelay is set and the application is determined to be subject to 141 | throttling, NSSM will pause the service for whichever is longer of the 142 | configured restart delay and the calculated throttle period. 143 | 144 | If AppRestartDelay is missing or invalid, only throttling will be applied. 145 | 146 | NSSM will look in the registry under 147 | HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppExit for 148 | string (REG_EXPAND_SZ) values corresponding to the exit code of the application. 149 | If the application exited with code 1, for instance, NSSM will look for a 150 | string value under AppExit called "1" or, if it does not find it, will 151 | fall back to the AppExit (Default) value. You can find out the exit code 152 | for the application by consulting the system event log. NSSM will log the 153 | exit code when the application exits. 154 | 155 | Based on the data found in the registry, NSSM will take one of three actions: 156 | 157 | If the value data is "Restart" NSSM will try to restart the application as 158 | described above. This is its default behaviour. 159 | 160 | If the value data is "Ignore" NSSM will not try to restart the application 161 | but will continue running itself. This emulates the (usually undesirable) 162 | behaviour of srvany. The Windows Services console would show the service 163 | as still running even though the application has exited. 164 | 165 | If the value data is "Exit" NSSM will exit gracefully. The Windows Services 166 | console would show the service as stopped. If you wish to provide 167 | finer-grained control over service recovery you should use this code and 168 | edit the failure action manually. Please note that Windows versions prior 169 | to Vista will not consider such an exit to be a failure. On older versions 170 | of Windows you should use "Suicide" instead. 171 | 172 | If the value data is "Suicide" NSSM will simulate a crash and exit without 173 | informing the service manager. This option should only be used for 174 | pre-Vista systems where you wish to apply a service recovery action. Note 175 | that if the monitored application exits with code 0, NSSM will only honour a 176 | request to suicide if you explicitly configure a registry key for exit code 0. 177 | If only the default action is set to Suicide NSSM will instead exit gracefully. 178 | 179 | 180 | Application priority 181 | -------------------- 182 | NSSM can set the priority class of the managed application. NSSM will look in 183 | the registry under HKLM\SYSTEM\CurrentControlSet\Services\\Parameters 184 | for the REG_DWORD entry AppPriority. Valid values correspond to arguments to 185 | SetPriorityClass(). If AppPriority() is missing or invalid the 186 | application will be launched with normal priority. 187 | 188 | 189 | Processor affinity 190 | ------------------ 191 | NSSM can set the CPU affinity of the managed application. NSSM will look in 192 | the registry under HKLM\SYSTEM\CurrentControlSet\Services\\Parameters 193 | for the REG_SZ entry AppAffinity. It should specify a comma-separated listed 194 | of zero-indexed processor IDs. A range of processors may optionally be 195 | specified with a dash. No other characters are allowed in the string. 196 | 197 | For example, to specify the first; second; third and fifth CPUs, an appropriate 198 | AppAffinity would be 0-2,4. 199 | 200 | If AppAffinity is missing or invalid, NSSM will not attempt to restrict the 201 | application to specific CPUs. 202 | 203 | Note that the 64-bit version of NSSM can configure a maximum of 64 CPUs in this 204 | way and that the 32-bit version can configure a maxium of 32 CPUs even when 205 | running on 64-bit Windows. 206 | 207 | 208 | Stopping the service 209 | -------------------- 210 | When stopping a service NSSM will attempt several different methods of killing 211 | the monitored application, each of which can be disabled if necessary. 212 | 213 | First NSSM will attempt to generate a Control-C event and send it to the 214 | application's console. Batch scripts or console applications may intercept 215 | the event and shut themselves down gracefully. GUI applications do not have 216 | consoles and will not respond to this method. 217 | 218 | Secondly NSSM will enumerate all windows created by the application and send 219 | them a WM_CLOSE message, requesting a graceful exit. 220 | 221 | Thirdly NSSM will enumerate all threads created by the application and send 222 | them a WM_QUIT message, requesting a graceful exit. Not all applications' 223 | threads have message queues; those which do not will not respond to this 224 | method. 225 | 226 | Finally NSSM will call TerminateProcess() to request that the operating 227 | system forcibly terminate the application. TerminateProcess() cannot be 228 | trapped or ignored, so in most circumstances the application will be killed. 229 | However, there is no guarantee that it will have a chance to perform any 230 | tidyup operations before it exits. 231 | 232 | Any or all of the methods above may be disabled. NSSM will look for the 233 | HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppStopMethodSkip 234 | registry value which should be of type REG_DWORD set to a bit field describing 235 | which methods should not be applied. 236 | 237 | If AppStopMethodSkip includes 1, Control-C events will not be generated. 238 | If AppStopMethodSkip includes 2, WM_CLOSE messages will not be posted. 239 | If AppStopMethodSkip includes 4, WM_QUIT messages will not be posted. 240 | If AppStopMethodSkip includes 8, TerminateProcess() will not be called. 241 | 242 | If, for example, you knew that an application did not respond to Control-C 243 | events and did not have a thread message queue, you could set AppStopMethodSkip 244 | to 5 and NSSM would not attempt to use those methods to stop the application. 245 | 246 | Take great care when including 8 in the value of AppStopMethodSkip. If NSSM 247 | does not call TerminateProcess() it is possible that the application will not 248 | exit when the service stops. 249 | 250 | By default NSSM will allow processes 1500ms to respond to each of the methods 251 | described above before proceeding to the next one. The timeout can be 252 | configured on a per-method basis by creating REG_DWORD entries in the 253 | registry under HKLM\SYSTEM\CurrentControlSet\Services\\Parameters. 254 | 255 | AppStopMethodConsole 256 | AppStopMethodWindow 257 | AppStopMethodThreads 258 | 259 | Each value should be set to the number of milliseconds to wait. Please note 260 | that the timeout applies to each process in the application's process tree, 261 | so the actual time to shutdown may be longer than the sum of all configured 262 | timeouts if the application spawns multiple subprocesses. 263 | 264 | 265 | Console window 266 | -------------- 267 | By default, NSSM will create a console window so that applications which 268 | are capable of reading user input can do so - subject to the service being 269 | allowed to interact with the desktop. 270 | 271 | Creation of the console can be suppressed by setting the integer (REG_DWORD) 272 | HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppNoConsole 273 | registry value to 1. 274 | 275 | 276 | I/O redirection 277 | --------------- 278 | NSSM can redirect the managed application's I/O to any path capable of being 279 | opened by CreateFile(). This enables, for example, capturing the log output 280 | of an application which would otherwise only write to the console or accepting 281 | input from a serial port. 282 | 283 | NSSM will look in the registry under 284 | HKLM\SYSTEM\CurrentControlSet\Services\\Parameters for the keys 285 | corresponding to arguments to CreateFile(). All are optional. If no path is 286 | given for a particular stream it will not be redirected. If a path is given 287 | but any of the other values are omitted they will be receive sensible defaults. 288 | 289 | AppStdin: Path to receive input. 290 | AppStdout: Path to receive output. 291 | AppStderr: Path to receive error output. 292 | 293 | Parameters for CreateFile() are providing with the "AppStdinShareMode", 294 | "AppStdinCreationDisposition" and "AppStdinFlagsAndAttributes" values (and 295 | analogously for stdout and stderr). 296 | 297 | In general, if you want the service to log its output, set AppStdout and 298 | AppStderr to the same path, eg C:\Users\Public\service.log, and it should 299 | work. Remember, however, that the path must be accessible to the user 300 | running the service. 301 | 302 | 303 | File rotation 304 | ------------- 305 | When using I/O redirection, NSSM can rotate existing output files prior to 306 | opening stdout and/or stderr. An existing file will be renamed with a 307 | suffix based on the file's last write time, to millisecond precision. For 308 | example, the file nssm.log might be rotated to nssm-20131221T113939.457.log. 309 | 310 | NSSM will look in the registry under 311 | HKLM\SYSTEM\CurrentControlSet\Services\\Parameters for REG_DWORD 312 | entries which control how rotation happens. 313 | 314 | If AppRotateFiles is missing or set to 0, rotation is disabled. Any non-zero 315 | value enables rotation. 316 | 317 | If AppRotateSeconds is non-zero, a file will not be rotated if its last write 318 | time is less than the given number of seconds in the past. 319 | 320 | If AppRotateBytes is non-zero, a file will not be rotated if it is smaller 321 | than the given number of bytes. 64-bit file sizes can be handled by setting 322 | a non-zero value of AppRotateBytesHigh. 323 | 324 | Rotation is independent of the CreateFile() parameters used to open the files. 325 | They will be rotated regardless of whether NSSM would otherwise have appended 326 | or replaced them. 327 | 328 | NSSM can also rotate files which hit the configured size threshold while the 329 | service is running. Additionally, you can trigger an on-demand rotation by 330 | running the command 331 | 332 | nssm rotate 333 | 334 | On-demand rotations will happen after the next line of data is read from 335 | the managed application, regardless of the value of AppRotateBytes. Be aware 336 | that if the application is not particularly verbose the rotation may not 337 | happen for some time. 338 | 339 | To enable online and on-demand rotation, set AppRotateOnline to a non-zero 340 | value. 341 | 342 | Note that online rotation requires NSSM to intercept the application's I/O 343 | and create the output files on its behalf. This is more complex and 344 | error-prone than simply redirecting the I/O streams before launching the 345 | application. Therefore online rotation is not enabled by default. 346 | 347 | 348 | Environment variables 349 | --------------------- 350 | NSSM can replace or append to the managed application's environment. Two 351 | multi-valued string (REG_MULTI_SZ) registry values are recognised under 352 | HKLM\SYSTEM\CurrentControlSet\Services\\Parameters. 353 | 354 | AppEnvironment defines a list of environment variables which will override 355 | the service's environment. AppEnvironmentExtra defines a list of 356 | environment variables which will be added to the service's environment. 357 | 358 | Each entry in the list should be of the form KEY=VALUE. It is possible to 359 | omit the VALUE but the = symbol is mandatory. 360 | 361 | Environment variables listed in both AppEnvironment and AppEnvironmentExtra 362 | are subject to normal expansion, so it is possible, for example, to update the 363 | system path by setting "PATH=C:\bin;%PATH%" in AppEnvironmentExtra. Variables 364 | are expanded in the order in which they appear, so if you want to include the 365 | value of one variable in another variable you should declare the dependency 366 | first. 367 | 368 | Because variables defined in AppEnvironment override the existing 369 | environment it is not possible to refer to any variables which were previously 370 | defined. 371 | 372 | For example, the following AppEnvironment block: 373 | 374 | PATH=C:\Windows\System32;C:\Windows 375 | PATH=C:\bin;%PATH% 376 | 377 | Would result in a PATH of "C:\bin;C:\Windows\System32;C:\Windows" as expected. 378 | 379 | Whereas the following AppEnvironment block: 380 | 381 | PATH=C:\bin;%PATH% 382 | 383 | Would result in a path containing only C:\bin and probably cause the 384 | application to fail to start. 385 | 386 | Most people will want to use AppEnvironmentExtra exclusively. srvany only 387 | supports AppEnvironment. 388 | 389 | 390 | Managing services using the GUI 391 | ------------------------------- 392 | NSSM can edit the settings of existing services with the same GUI that is 393 | used to install them. Run 394 | 395 | nssm edit 396 | 397 | to bring up the GUI. 398 | 399 | NSSM offers limited editing capabilities for services other than those which 400 | run NSSM itself. When NSSM is asked to edit a service which does not have 401 | the App* registry settings described above, the GUI will allow editing only 402 | system settings such as the service display name and description. 403 | 404 | 405 | Managing services using the command line 406 | ---------------------------------------- 407 | NSSM can retrieve or set individual service parameters from the command line. 408 | In general the syntax is as follows, though see below for exceptions. 409 | 410 | nssm get 411 | 412 | nssm set 413 | 414 | Parameters can also be reset to their default values. 415 | 416 | nssm reset 417 | 418 | The parameter names recognised by NSSM are the same as the registry entry 419 | names described above, eg AppDirectory. 420 | 421 | NSSM offers limited editing capabilities for Services other than those which 422 | run NSSM itself. The parameters recognised are as follows: 423 | 424 | Description: Service description. 425 | DisplayName: Service display name. 426 | ImagePath: Path to the service executable. 427 | ObjectName: User account which runs the service. 428 | Name: Service key name. 429 | Start: Service startup type. 430 | Type: Service type. 431 | 432 | These correspond to the registry values under the service's key 433 | HKLM\SYSTEM\CurrentControlSet\Services\. 434 | 435 | 436 | Note that NSSM will concatenate all arguments passed on the command line 437 | with spaces to form the value to set. Thus the following two invocations 438 | would have the same effect. 439 | 440 | nssm set Description "NSSM managed service" 441 | 442 | nssm set Description NSSM managed service 443 | 444 | 445 | Non-standard parameters 446 | ----------------------- 447 | The AppEnvironment and AppEnvironmentExtra parameters recognise an 448 | additional argument when querying the environment. The following syntax 449 | will print all extra environment variables configured for a service 450 | 451 | nssm get AppEnvironmentExtra 452 | 453 | whereas the syntax below will print only the value of the CLASSPATH 454 | variable if it is configured in the environment block, or the empty string 455 | if it is not configured. 456 | 457 | nssm get AppEnvironmentExtra CLASSPATH 458 | 459 | When setting an environment block, each variable should be specified as a 460 | KEY=VALUE pair in separate command line arguments. For example: 461 | 462 | nssm set AppEnvironment CLASSPATH=C:\Classes TEMP=C:\Temp 463 | 464 | 465 | The AppExit parameter requires an additional argument specifying the exit 466 | code to get or set. The default action can be specified with the string 467 | Default. 468 | 469 | For example, to get the default exit action for a service you should run 470 | 471 | nssm get AppExit Default 472 | 473 | To get the exit action when the application exits with exit code 2, run 474 | 475 | nssm get AppExit 2 476 | 477 | Note that if no explicit action is configured for a specified exit code, 478 | NSSM will print the default exit action. 479 | 480 | To set configure the service to stop when the application exits with an 481 | exit code of 2, run 482 | 483 | nssm set AppExit 2 Exit 484 | 485 | 486 | The AppPriority parameter is used to set the priority class of the 487 | managed application. Valid priorities are as follows: 488 | 489 | REALTIME_PRIORITY_CLASS 490 | HIGH_PRIORITY_CLASS 491 | ABOVE_NORMAL_PRIORITY_CLASS 492 | NORMAL_PRIORITY_CLASS 493 | BELOW_NORMAL_PRIORITY_CLASS 494 | IDLE_PRIORITY_CLASS 495 | 496 | 497 | The DependOnGroup and DependOnService parameters are used to query or set 498 | the dependencies for the service. When setting dependencies, each service 499 | or service group (preceded with the + symbol) should be specified in 500 | separate command line arguments. For example: 501 | 502 | nssm set DependOnService RpcSs LanmanWorkstation 503 | 504 | 505 | The Name parameter can only be queried, not set. It returns the service's 506 | registry key name. This may be useful to know if you take advantage of 507 | the fact that you can substitute the service's display name anywhere where 508 | the syntax calls for . 509 | 510 | 511 | The ObjectName parameter requires an additional argument only when setting 512 | a username. The additional argument is the password of the user. 513 | 514 | To retrieve the username, run 515 | 516 | nssm get ObjectName 517 | 518 | To set the username and password, run 519 | 520 | nssm set ObjectName 521 | 522 | Note that the rules of argument concatenation still apply. The following 523 | invocation is valid and will have the expected effect. 524 | 525 | nssm set ObjectName correct horse battery staple 526 | 527 | The following well-known usernames do not need a password. The password 528 | parameter can be omitted when using them: 529 | 530 | "LocalSystem" aka "System" aka "NT Authority\System" 531 | "LocalService" aka "Local Service" aka "NT Authority\Local Service" 532 | "NetworkService" aka "Network Service" aka "NT Authority\Network Service" 533 | 534 | 535 | The Start parameter is used to query or set the startup type of the service. 536 | Valid service startup types are as follows: 537 | 538 | SERVICE_AUTO_START: Automatic startup at boot. 539 | SERVICE_DELAYED_START: Delayed startup at boot. 540 | SERVICE_DEMAND_START: Manual service startup. 541 | SERVICE_DISABLED: The service is disabled. 542 | 543 | Note that SERVICE_DELAYED_START is not supported on versions of Windows prior 544 | to Vista. NSSM will set the service to automatic startup if delayed start is 545 | unavailable. 546 | 547 | 548 | The Type parameter is used to query or set the service type. NSSM recognises 549 | all currently documented service types but will only allow setting one of two 550 | types: 551 | 552 | SERVICE_WIN32_OWN_PROCESS: A standalone service. This is the default. 553 | SERVICE_INTERACTIVE_PROCESS: A service which can interact with the desktop. 554 | 555 | Note that a service may only be configured as interactive if it runs under 556 | the LocalSystem account. The safe way to configure an interactive service 557 | is in two stages as follows. 558 | 559 | nssm reset ObjectName 560 | nssm set Type SERVICE_INTERACTIVE_PROCESS 561 | 562 | 563 | Controlling services using the command line 564 | ------------------------------------------- 565 | NSSM offers rudimentary service control features. 566 | 567 | nssm start 568 | 569 | nssm restart 570 | 571 | nssm stop 572 | 573 | nssm status 574 | 575 | 576 | Removing services using the GUI 577 | ------------------------------- 578 | NSSM can also remove services. Run 579 | 580 | nssm remove 581 | 582 | to remove a service. You will prompted for confirmation before the service 583 | is removed. Try not to remove essential system services... 584 | 585 | 586 | Removing service using the command line 587 | --------------------------------------- 588 | To remove a service without confirmation from the GUI, run 589 | 590 | nssm remove confirm 591 | 592 | Try not to remove essential system services... 593 | 594 | 595 | Logging 596 | ------- 597 | NSSM logs to the Windows event log. It registers itself as an event log source 598 | and uses unique event IDs for each type of message it logs. New versions may 599 | add event types but existing event IDs will never be changed. 600 | 601 | Because of the way NSSM registers itself you should be aware that you may not 602 | be able to replace the NSSM binary if you have the event viewer open and that 603 | running multiple instances of NSSM from different locations may be confusing if 604 | they are not all the same version. 605 | 606 | 607 | Example usage 608 | ------------- 609 | To install an Unreal Tournament server: 610 | 611 | nssm install UT2004 c:\games\ut2004\system\ucc.exe server 612 | 613 | To run the server as the "games" user: 614 | 615 | nssm set UT2004 ObjectName games password 616 | 617 | To configure the server to log to a file: 618 | 619 | nssm set UT2004 AppStdout c:\games\ut2004\service.log 620 | 621 | To restrict the server to a single CPU: 622 | 623 | nssm set UT2004 AppAffinity 0 624 | 625 | To remove the server: 626 | 627 | nssm remove UT2004 confirm 628 | 629 | To find out the service name of a service with a display name: 630 | 631 | nssm get "Background Intelligent Transfer Service" Name 632 | 633 | 634 | Building NSSM from source 635 | ------------------------- 636 | NSSM is known to compile with Visual Studio 2008 and later. Older Visual 637 | Studio releases may or may not work if you install an appropriate SDK and 638 | edit the nssm.vcproj and nssm.sln files to set a lower version number. 639 | They are known not to work with default settings. 640 | 641 | NSSM will also compile with Visual Studio 2010 but the resulting executable 642 | will not run on versions of Windows older than XP SP2. If you require 643 | compatiblity with older Windows releases you should change the Platform 644 | Toolset to v90 in the General section of the project's Configuration 645 | Properties. 646 | 647 | 648 | Credits 649 | ------- 650 | Thanks to Bernard Loh for finding a bug with service recovery. 651 | Thanks to Benjamin Mayrargue (www.softlion.com) for adding 64-bit support. 652 | Thanks to Joel Reingold for spotting a command line truncation bug. 653 | Thanks to Arve Knudsen for spotting that child processes of the monitored 654 | application could be left running on service shutdown, and that a missing 655 | registry value for AppDirectory confused NSSM. 656 | Thanks to Peter Wagemans and Laszlo Keresztfalvi for suggesting throttling 657 | restarts. 658 | Thanks to Eugene Lifshitz for finding an edge case in CreateProcess() and for 659 | advising how to build messages.mc correctly in paths containing spaces. 660 | Thanks to Rob Sharp for pointing out that NSSM did not respect the 661 | AppEnvironment registry value used by srvany. 662 | Thanks to Szymon Nowak for help with Windows 2000 compatibility. 663 | Thanks to François-Régis Tardy and Gildas le Nadan for French translation. 664 | Thanks to Emilio Frini for spotting that French was inadvertently set as 665 | the default language when the user's display language was not translated. 666 | Thanks to Riccardo Gusmeroli and Marco Certelli for Italian translation. 667 | Thanks to Eric Cheldelin for the inspiration to generate a Control-C event 668 | on shutdown. 669 | Thanks to Brian Baxter for suggesting how to escape quotes from the command 670 | prompt. 671 | Thanks to Russ Holmann for suggesting that the shutdown timeout be configurable. 672 | Thanks to Paul Spause for spotting a bug with default registry entries. 673 | Thanks to BUGHUNTER for spotting more GUI bugs. 674 | Thanks to Doug Watson for suggesting file rotation. 675 | Thanks to Арслан Сайдуганов for suggesting setting process priority. 676 | Thanks to Robert Middleton for suggestion and draft implementation of process 677 | affinity support. 678 | Thanks to Andrew RedzMax for suggesting an unconditional restart delay. 679 | Thanks to Bryan Senseman for noticing that applications with redirected stdout 680 | and/or stderr which attempt to read from stdin would fail. 681 | Thanks to Czenda Czendov for help with Visual Studio 2013 and Server 2012R2. 682 | Thanks to Alessandro Gherardi for reporting and draft fix of the bug whereby 683 | the second restart of the application would have a corrupted environment. 684 | Thanks to Hadrien Kohl for suggesting to disable the console window's menu. 685 | Thanks to Allen Vailliencourt for noticing bugs with configuring the service to 686 | run under a local user account. 687 | Thanks to Sam Townsend for noticing a regression with TerminateProcess(). 688 | 689 | Licence 690 | ------- 691 | NSSM is public domain. You may unconditionally use it and/or its source code 692 | for any purpose you wish. 693 | -------------------------------------------------------------------------------- /nssm-2.24/win32/nssm.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenNebula/addon-context-windows/40ce41bf915849e258d793105f720f09ba6198ec/nssm-2.24/win32/nssm.exe -------------------------------------------------------------------------------- /nssm-2.24/win64/nssm.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenNebula/addon-context-windows/40ce41bf915849e258d793105f720f09ba6198ec/nssm-2.24/win64/nssm.exe -------------------------------------------------------------------------------- /package.wxs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | INSTALLED OR NOT NEWERVERSIONDETECTED 44 | 45 | 46 | 47 | 52 | 53 | INSTALLED OR PSEXE 54 | 55 | 56 | 57 | 62 | 63 | 64 | 72 | 73 | 74 | 75 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 169 | NOT REMOVE AND FASTBOOT 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /src/context.ps1: -------------------------------------------------------------------------------- 1 | # -------------------------------------------------------------------------- # 2 | # Copyright 2002-2021, OpenNebula Project, OpenNebula Systems # 3 | # # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may # 5 | # not use this file except in compliance with the License. You may obtain # 6 | # a copy of the License at # 7 | # # 8 | # http://www.apache.org/licenses/LICENSE-2.0 # 9 | # # 10 | # Unless required by applicable law or agreed to in writing, software # 11 | # distributed under the License is distributed on an "AS IS" BASIS, # 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # 13 | # See the License for the specific language governing permissions and # 14 | # limitations under the License. # 15 | #--------------------------------------------------------------------------- # 16 | 17 | # Original work by: 18 | 19 | ################################################################# 20 | ##### Windows Powershell Script to configure OpenNebula VMs ##### 21 | ##### Created by andremonteiro@ua.pt and tsbatista@ua.pt ##### 22 | ##### DETI/IEETA Universidade de Aveiro 2011 ##### 23 | ################################################################# 24 | 25 | ################################################################################ 26 | # Functions 27 | ################################################################################ 28 | 29 | function logmsg($message) { 30 | # powershell 4 does not automatically add newline in the transcript so we 31 | # workaround it by adding it explicitly and using the NoNewline argument 32 | # we ensure that it will not be added twice 33 | Write-Host "[$(Get-Date -Format 'yyyy-MM-dd HH:mm K')] $message`r`n" -NoNewline 34 | } 35 | 36 | function logsuccess { 37 | logmsg " ... Success" 38 | } 39 | function logfail { 40 | logmsg " ... Failed" 41 | } 42 | 43 | function getContext($file) { 44 | 45 | # TODO: Improve regexp for multiple SSH keys on SSH_PUBLIC_KEY 46 | $context = @{} 47 | switch -regex -file $file { 48 | "^([^=]+)='(.+?)'$" { 49 | $name, $value = $matches[1..2] 50 | $context[$name] = $value 51 | } 52 | } 53 | return $context 54 | } 55 | 56 | function envContext($context) { 57 | ForEach ($h in $context.GetEnumerator()) { 58 | $name = "Env:" + $h.Name 59 | Set-Item $name $h.Value 60 | } 61 | } 62 | 63 | function contextChanged($file, $last_checksum) { 64 | $new_checksum = Get-FileHash -Algorithm SHA256 $file 65 | $ret = $last_checksum.Hash -ne $new_checksum.Hash 66 | return $ret 67 | } 68 | 69 | function waitForContext($checksum) { 70 | # This object will be set and returned at the end 71 | $contextPaths = New-Object PsObject -Property @{ 72 | contextScriptPath = $null 73 | contextPath = $null 74 | contextDrive = $null 75 | contextLetter = $null 76 | contextInitScriptPath = $null 77 | } 78 | 79 | # How long to wait before another poll (in seconds) 80 | $sleep = 30 81 | 82 | do { 83 | 84 | # Reset the contextPath 85 | $contextPaths.contextPath = "" 86 | 87 | # Get all drives and select only the one that has "CONTEXT" as a label 88 | $contextPaths.contextDrive = Get-WMIObject Win32_Volume | Where-Object { $_.Label -eq "CONTEXT" } 89 | 90 | if ($contextPaths.contextDrive) { 91 | 92 | # At this point we can obtain the letter of the contextDrive 93 | $contextPaths.contextLetter = $contextPaths.contextDrive.Name 94 | $contextPaths.contextPath = $contextPaths.contextLetter + "context.sh" 95 | $contextPaths.contextInitScriptPath = $contextPaths.contextLetter 96 | } 97 | else { 98 | 99 | # Try the VMware API 100 | foreach ($pf in ${env:ProgramFiles}, ${env:ProgramFiles(x86)}, ${env:ProgramW6432}) { 101 | $vmtoolsd = "${pf}\VMware\VMware Tools\vmtoolsd.exe" 102 | if (Test-Path $vmtoolsd) { 103 | break 104 | } 105 | } 106 | 107 | $vmwareContext = "" 108 | if (Test-Path $vmtoolsd) { 109 | $vmwareContext = & $vmtoolsd --cmd "info-get guestinfo.opennebula.context" | Out-String 110 | } 111 | 112 | if ("$vmwareContext" -ne "") { 113 | [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($vmwareContext)) | Out-File "$ctxDir\context.sh" "UTF8" 114 | $contextPaths.contextLetter = $env:SystemDrive + "\" 115 | $contextPaths.contextPath = "$ctxDir\context.sh" 116 | $contextPaths.contextInitScriptPath = "$ctxDir\.init-scripts\" 117 | 118 | if (!(Test-Path $contextPaths.contextInitScriptPath)) { 119 | mkdir $contextPaths.contextInitScriptPath 120 | } 121 | 122 | # Look for INIT_SCRIPTS 123 | $fileId = 0 124 | while ($true) { 125 | $vmwareInitFilename = & $vmtoolsd --cmd "info-get guestinfo.opennebula.file.${fileId}" | Select-Object -First 1 | Out-String 126 | 127 | $vmwareInitFilename = $vmwareInitFilename.Trim() 128 | 129 | if ($vmwareInitFilename -eq "") { 130 | # no file found 131 | break 132 | } 133 | 134 | $vmwareInitFileContent64 = & $vmtoolsd --cmd "info-get guestinfo.opennebula.file.${fileId}" | Select-Object -Skip 1 | Out-String 135 | 136 | # Sanitize the filenames (drop any path from them and instead use our directory) 137 | $vmwareInitFilename = $contextPaths.contextInitScriptPath + [System.IO.Path]::GetFileName("$vmwareInitFilename") 138 | 139 | [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($vmwareInitFileContent64)) | Out-File "${vmwareInitFilename}" "UTF8" 140 | 141 | $fileId++ 142 | } 143 | } 144 | 145 | } 146 | 147 | # Terminate the wait-loop only when context.sh is found and changed 148 | if (![string]::IsNullOrEmpty($contextPaths.contextPath) -and (Test-Path $contextPaths.contextPath)) { 149 | 150 | # Context must differ 151 | if (contextChanged $contextPaths.contextPath $checksum) { 152 | Break 153 | } 154 | } 155 | 156 | cleanup $contextPaths 157 | 158 | Write-Host "`r`n" -NoNewline 159 | Start-Sleep -Seconds $sleep 160 | } while ($true) 161 | 162 | # make a copy of the context.sh in the case another event would happen and 163 | # trigger a new context.sh while still working on the previous one which 164 | # would result in a mismatched checksum... 165 | $contextPaths.contextScriptPath = "$ctxDir\.opennebula-context.sh" 166 | Copy-Item -Path $contextPaths.contextPath -Destination $contextPaths.contextScriptPath -Force 167 | 168 | return $contextPaths 169 | } 170 | 171 | function addLocalUser($context) { 172 | # Create new user 173 | $username = $context["USERNAME"] 174 | $password = $context["PASSWORD"] 175 | $password64 = $context["PASSWORD_BASE64"] 176 | 177 | If ($password64) { 178 | $password = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($password64)) 179 | } 180 | 181 | if ($username -Or $password) { 182 | 183 | if ($null -eq $username) { 184 | # ATTENTION - Language/Regional settings have influence on the naming 185 | # of this user. Use the User SID instead (S-1-5-21domain-500) 186 | $username = (Get-WmiObject -Class "Win32_UserAccount" | 187 | Where-Object { $_.SID -like "S-1-5-21[0-9-]*-500" } | 188 | Select-Object -ExpandProperty Name | 189 | Get-Unique -AsString) 190 | } 191 | 192 | logmsg "* Creating Account for $username" 193 | 194 | $ADSI = [adsi]$ConnectionString 195 | 196 | if (!([ADSI]::Exists("WinNT://$computerName/$username"))) { 197 | # User does not exist, Create the User 198 | logmsg "- Creating account" 199 | $user = $ADSI.Create("user", $username) 200 | $user.setPassword($password) 201 | $user.SetInfo() 202 | } 203 | else { 204 | # User exists, Set Password 205 | logmsg "- Setting Password" 206 | $admin = [ADSI]"WinNT://$env:computername/$username" 207 | $admin.psbase.invoke("SetPassword", $password) 208 | } 209 | 210 | # Set Password to Never Expire 211 | logmsg "- Setting password to never expire" 212 | $admin = [ADSI]"WinNT://$env:computername/$username" 213 | $admin.UserFlags.value = $admin.UserFlags.value -bor 0x10000 214 | $admin.CommitChanges() 215 | 216 | # Add user to local Administrators 217 | # ATTENTION - Language/Regional settings have influence on the naming 218 | # of this group. Use the Group SID instead (S-1-5-32-544) 219 | $groups = (Get-WmiObject -Class "Win32_Group" | 220 | Where-Object { $_.SID -like "S-1-5-32-544" } | 221 | Select-Object -ExpandProperty Name) 222 | 223 | ForEach ($grp in $groups) { 224 | 225 | # Make sure the Group exists 226 | If ([ADSI]::Exists("WinNT://$computerName/$grp,group")) { 227 | 228 | # Check if the user is a Member of the Group 229 | $group = [ADSI] "WinNT://$computerName/$grp,group" 230 | $members = @($group.psbase.Invoke("Members")) 231 | 232 | $memberNames = @() 233 | $members | ForEach-Object { 234 | # https://p0w3rsh3ll.wordpress.com/2016/06/14/any-documented-adsi-changes-in-powershell-5-0/ 235 | $memberNames += ([ADSI]$_).psbase.InvokeGet('Name') 236 | } 237 | 238 | If (-Not ($memberNames -Contains $username)) { 239 | 240 | # Make sure the user exists, again 241 | if ([ADSI]::Exists("WinNT://$computerName/$username")) { 242 | 243 | # Add the user 244 | logmsg "- Adding to $grp" 245 | $group.Add("WinNT://$computerName/$username") 246 | } 247 | } 248 | } 249 | } 250 | } 251 | Write-Host "`r`n" -NoNewline 252 | } 253 | 254 | function configureNetwork($context) { 255 | 256 | # Get the NIC in the Context 257 | $nicIds = ($context.Keys | Where-Object { $_ -match '^ETH\d+_MAC$' } | ForEach-Object { $_ -replace '(^ETH|_MAC$)', '' } | Sort-Object -Unique) 258 | 259 | $nicId = 0 260 | 261 | foreach ($nicId in $nicIds) { 262 | $nicPrefix = "ETH" + $nicId + "_" 263 | 264 | $method = $context[$nicPrefix + 'METHOD'] 265 | $ip = $context[$nicPrefix + 'IP'] 266 | $netmask = $context[$nicPrefix + 'MASK'] 267 | $mac = $context[$nicPrefix + 'MAC'] 268 | $dns = (($context[$nicPrefix + 'DNS'] -split " " | Where-Object { $_ -match '^(([0-9]*).?){4}$' }) -join ' ') 269 | $dns6 = (($context[$nicPrefix + 'DNS'] -split " " | Where-Object { $_ -match '^(([0-9A-F]*):?)*$' }) -join ' ') 270 | $dnsSuffix = $context[$nicPrefix + 'SEARCH_DOMAIN'] 271 | $gateway = $context[$nicPrefix + 'GATEWAY'] 272 | $network = $context[$nicPrefix + 'NETWORK'] 273 | $mtu = $context[$nicPrefix + 'MTU'] 274 | $metric = $context[$nicPrefix + 'METRIC'] 275 | 276 | $ip6Method = $context[$nicPrefix + 'IP6_METHOD'] 277 | $ip6 = $context[$nicPrefix + 'IP6'] 278 | $ip6ULA = $context[$nicPrefix + 'IP6_ULA'] 279 | $ip6Prefix = $context[$nicPrefix + 'IP6_PREFIX_LENGTH'] 280 | $ip6Gw = $context[$nicPrefix + 'IP6_GATEWAY'] 281 | $ip6Metric = $context[$nicPrefix + 'IP6_METRIC'] 282 | 283 | $mac = $mac.ToUpper() 284 | if (!$netmask) { 285 | $netmask = "255.255.255.0" 286 | } 287 | if (!$ip6Prefix) { 288 | $ip6Prefix = "64" 289 | } 290 | if (!$ip6Gw) { 291 | # Backward compatibility, new context parameter 292 | # ETHx_IP6_GATEWAY introduced since 6.2 293 | $ip6Gw = $context[$nicPrefix + 'GATEWAY6'] 294 | } 295 | if (!$ip6Metric) { 296 | $ip6Metric = $metric 297 | } 298 | if (!$network) { 299 | $network = $ip -replace "\.[^.]+$", ".0" 300 | } 301 | if ($nicId -eq 0 -and !$gateway) { 302 | $gateway = $ip -replace "\.[^.]+$", ".1" 303 | } 304 | 305 | # default NIC configuration methods 306 | if (!$method) { 307 | $method = 'static' 308 | } 309 | if (!$ip6Method) { 310 | $ip6Method = $method 311 | } 312 | 313 | # Load the NIC Configuration Object 314 | $nic = $false 315 | $retry = 30 316 | do { 317 | $retry-- 318 | Start-Sleep -s 1 319 | $nic = Get-WMIObject Win32_NetworkAdapterConfiguration | ` 320 | Where-Object { $_.IPEnabled -eq "TRUE" -and $_.MACAddress -eq $mac } 321 | } while (!$nic -and $retry) 322 | 323 | If (!$nic) { 324 | logmsg ("* Configuring Network Settings: " + $mac) 325 | logmsg (" ... Failed: Interface with MAC not found") 326 | Continue 327 | } 328 | 329 | # We need the connection ID (i.e. "Local Area Connection", 330 | # which can be discovered from the NetworkAdapter object 331 | $na = Get-WMIObject Win32_NetworkAdapter | ` 332 | Where-Object { $_.deviceId -eq $nic.index } 333 | 334 | If (!$na) { 335 | logmsg ("* Configuring Network Settings: " + $mac) 336 | logmsg (" ... Failed: Network Adapter not found") 337 | Continue 338 | } 339 | 340 | logmsg ("* Configuring Network Settings: " + $nic.Description.ToString()) 341 | 342 | # Flag to indicate if any IPv4/6 configuration was placed 343 | $set_ip_conf = $false 344 | 345 | # IPv4 Configuration Methods 346 | Switch -Regex ($method) { 347 | '^\s*static\s*$' { 348 | if ($ip) { 349 | # Release the DHCP lease, will fail if adapter not DHCP Configured 350 | logmsg "- Release DHCP Lease" 351 | $ret = $nic.ReleaseDHCPLease() 352 | If ($ret.ReturnValue) { 353 | logmsg (" ... Failed: " + $ret.ReturnValue.ToString()) 354 | } 355 | Else { 356 | logmsg " ... Success" 357 | } 358 | 359 | # set static IP address and retry for few times if there was a problem 360 | # with acquiring write lock (2147786788) for network configuration 361 | # https://msdn.microsoft.com/en-us/library/aa390383(v=vs.85).aspx 362 | logmsg "- Set Static IP" 363 | $retry = 10 364 | do { 365 | $retry-- 366 | Start-Sleep -s 1 367 | $ret = $nic.EnableStatic($ip , $netmask) 368 | } while ($ret.ReturnValue -eq 2147786788 -and $retry) 369 | If ($ret.ReturnValue) { 370 | logmsg (" ... Failed: " + $ret.ReturnValue.ToString()) 371 | } 372 | Else { 373 | logmsg " ... Success" 374 | } 375 | 376 | # Set IPv4 MTU 377 | if ($mtu) { 378 | logmsg "- Set MTU: ${mtu}" 379 | netsh interface ipv4 set interface $nic.InterfaceIndex mtu=$mtu 380 | 381 | If ($?) { 382 | logmsg " ... Success" 383 | } 384 | Else { 385 | logmsg " ... Failed" 386 | } 387 | } 388 | 389 | # Set the Gateway 390 | if ($gateway) { 391 | if ($metric) { 392 | logmsg "- Set Gateway with metric" 393 | $ret = $nic.SetGateways($gateway, $metric) 394 | } 395 | Else { 396 | logmsg "- Set Gateway" 397 | $ret = $nic.SetGateways($gateway) 398 | } 399 | 400 | If ($ret.ReturnValue) { 401 | logmsg (" ... Failed: " + $ret.ReturnValue.ToString()) 402 | } 403 | Else { 404 | logmsg " ... Success" 405 | } 406 | } 407 | 408 | # Set DNS servers 409 | If ($dns) { 410 | $dnsServers = $dns -split " " 411 | 412 | # DNS Server Search Order 413 | logmsg "- Set DNS Server Search Order" 414 | $ret = $nic.SetDNSServerSearchOrder($dnsServers) 415 | If ($ret.ReturnValue) { 416 | logmsg (" ... Failed: " + $ret.ReturnValue.ToString()) 417 | } 418 | Else { 419 | logmsg " ... Success" 420 | } 421 | 422 | # Set Dynamic DNS Registration 423 | logmsg "- Set Dynamic DNS Registration" 424 | $ret = $nic.SetDynamicDNSRegistration("TRUE") 425 | If ($ret.ReturnValue) { 426 | logmsg (" ... Failed: " + $ret.ReturnValue.ToString()) 427 | } 428 | Else { 429 | logmsg " ... Success" 430 | } 431 | 432 | # WINS Addresses 433 | # $nic.SetWINSServer($DNSServers[0], $DNSServers[1]) 434 | } 435 | 436 | # Set DNS domain/search order 437 | if ($dnsSuffix) { 438 | $dnsSuffixes = $dnsSuffix -split " " 439 | 440 | # Set DNS Suffix Search Order 441 | logmsg "- Set DNS Suffix Search Order" 442 | $ret = ([WMIClass]"Win32_NetworkAdapterConfiguration").SetDNSSuffixSearchOrder(($dnsSuffixes)) 443 | If ($ret.ReturnValue) { 444 | logmsg (" ... Failed: " + $ret.ReturnValue.ToString()) 445 | } 446 | Else { 447 | logmsg " ... Success" 448 | } 449 | 450 | # Set Primary DNS Domain 451 | logmsg "- Set Primary DNS Domain" 452 | $ret = $nic.SetDNSDomain($dnsSuffixes[0]) 453 | If ($ret.ReturnValue) { 454 | logmsg (" ... Failed: " + $ret.ReturnValue.ToString()) 455 | } 456 | Else { 457 | logmsg " ... Success" 458 | } 459 | } 460 | 461 | $set_ip_conf = $true 462 | } 463 | else { 464 | logmsg "- No static IPv4 configuration provided, skipping" 465 | } 466 | } 467 | 468 | '^\s*dhcp\s*$' { 469 | # Enable DHCP 470 | logmsg "- Enable DHCP" 471 | $ret = $nic.EnableDHCP() 472 | # TODO: 1 ... Successful completion, reboot required 473 | If ($ret.ReturnValue) { 474 | logmsg (" ... Failed: " + $ret.ReturnValue.ToString()) 475 | } 476 | Else { 477 | logmsg " ... Success" 478 | } 479 | 480 | # Set IPv4 MTU 481 | if ($mtu) { 482 | logmsg "- Set MTU: ${mtu}" 483 | netsh interface ipv4 set interface $nic.InterfaceIndex mtu=$mtu 484 | 485 | If ($?) { 486 | logmsg " ... Success" 487 | } 488 | Else { 489 | logmsg " ... Failed" 490 | } 491 | } 492 | 493 | $set_ip_conf = $true 494 | } 495 | 496 | '\s*skip\s*$' { 497 | logmsg "- Skipped IPv4 configuration as requested in method (${nicPrefix}METHOD=${method})" 498 | } 499 | 500 | Default { 501 | logmsg "- Unknown IPv4 method (${nicPrefix}METHOD=${method}), skipping configuration" 502 | } 503 | } 504 | 505 | # IPv6 Configuration Methods 506 | Switch -Regex ($ip6Method) { 507 | '^\s*static\s*$' { 508 | if ($ip6) { 509 | enableIPv6 510 | disableIPv6Privacy 511 | 512 | # Disable router discovery 513 | logmsg "- Disable IPv6 router discovery" 514 | netsh interface ipv6 set interface $na.NetConnectionId ` 515 | advertise=disabled routerdiscover=disabled | Out-Null 516 | 517 | If ($?) { 518 | logmsg " ... Success" 519 | } 520 | Else { 521 | logmsg " ... Failed" 522 | } 523 | 524 | # Remove old IPv6 addresses 525 | logmsg "- Removing old IPv6 addresses" 526 | if (Get-Command Remove-NetIPAddress -ErrorAction SilentlyContinue) { 527 | # Windows 8.1 and Server 2012 R2 and up 528 | # we want to remove everything except the link-local address 529 | Remove-NetIPAddress -InterfaceAlias $na.NetConnectionId ` 530 | -AddressFamily IPv6 -Confirm:$false ` 531 | -PrefixOrigin Other, Manual, Dhcp, RouterAdvertisement ` 532 | -errorAction SilentlyContinue 533 | 534 | If ($?) { 535 | logmsg " ... Success" 536 | } 537 | Else { 538 | logmsg " ... Nothing to do" 539 | } 540 | } 541 | Else { 542 | logmsg " ... Not implemented" 543 | } 544 | 545 | # Set IPv6 Address 546 | logmsg "- Set IPv6 Address" 547 | netsh interface ipv6 add address $na.NetConnectionId $ip6/$ip6Prefix 548 | If ($? -And $ip6ULA) { 549 | netsh interface ipv6 add address $na.NetConnectionId $ip6ULA/64 550 | } 551 | 552 | If ($?) { 553 | logmsg " ... Success" 554 | } 555 | Else { 556 | logmsg " ... Failed" 557 | } 558 | 559 | # Set IPv6 Gateway 560 | if ($ip6Gw) { 561 | if ($ip6Metric) { 562 | logmsg "- Set IPv6 Gateway with metric" 563 | netsh interface ipv6 add route ::/0 $na.NetConnectionId $ip6Gw metric="${ip6Metric}" 564 | } 565 | else { 566 | logmsg "- Set IPv6 Gateway" 567 | netsh interface ipv6 add route ::/0 $na.NetConnectionId $ip6Gw 568 | } 569 | 570 | If ($?) { 571 | logmsg " ... Success" 572 | } 573 | Else { 574 | logmsg " ... Failed" 575 | } 576 | } 577 | 578 | # Set IPv6 MTU 579 | if ($mtu) { 580 | logmsg "- Set IPv6 MTU: ${mtu}" 581 | netsh interface ipv6 set interface $nic.InterfaceIndex mtu=$mtu 582 | 583 | If ($?) { 584 | logmsg " ... Success" 585 | } 586 | Else { 587 | logmsg " ... Failed" 588 | } 589 | } 590 | 591 | # Remove old IPv6 DNS Servers 592 | logmsg "- Removing old IPv6 DNS Servers" 593 | netsh interface ipv6 set dnsservers $na.NetConnectionId source=static address= 594 | 595 | If ($dns6) { 596 | # Set IPv6 DNS Servers 597 | logmsg "- Set IPv6 DNS Servers" 598 | $dns6Servers = $dns6 -split " " 599 | foreach ($dns6Server in $dns6Servers) { 600 | netsh interface ipv6 add dnsserver $na.NetConnectionId address=$dns6Server 601 | } 602 | } 603 | 604 | $set_ip_conf = $true 605 | 606 | doPing($ip6) 607 | } 608 | else { 609 | logmsg "- No static IPv6 configuration provided, skipping" 610 | } 611 | } 612 | 613 | '^\s*(auto|dhcp)\s*$' { 614 | enableIPv6 615 | disableIPv6Privacy 616 | 617 | # Enable router discovery 618 | logmsg "- Enable IPv6 router discovery" 619 | netsh interface ipv6 set interface $na.NetConnectionId ` 620 | advertise=disabled routerdiscover=enabled | Out-Null 621 | 622 | # Run of DHCPv6 client is controlled by RA managed/other 623 | # flags, we can't independently enable/disable DHCPv6 624 | # client. So at least we release the address allocated 625 | # through DHCPv6 in auto mode. See 626 | # https://serverfault.com/questions/692291/disable-dhcpv6-client-in-windows 627 | if ($ip6Method -match '^\s*auto\s*$') { 628 | logmsg "- Release DHCPv6 Lease (selected method auto, not dhcp!)" 629 | ipconfig /release6 $na.NetConnectionId 630 | 631 | If ($?) { 632 | logmsg " ... Success" 633 | } 634 | Else { 635 | logmsg " ... Failed" 636 | } 637 | } 638 | 639 | # Set IPv6 MTU 640 | if ($mtu) { 641 | logmsg "- Set IPv6 MTU: ${mtu}" 642 | logmsg "WARNING: MTU will be overwritten if announced as part of RA!" 643 | netsh interface ipv6 set interface $nic.InterfaceIndex mtu=$mtu 644 | 645 | If ($?) { 646 | logmsg " ... Success" 647 | } 648 | Else { 649 | logmsg " ... Failed" 650 | } 651 | } 652 | 653 | $set_ip_conf = $true 654 | } 655 | 656 | '^\s*disable\s*$' { 657 | disableIPv6 658 | } 659 | 660 | '\s*skip\s*$' { 661 | logmsg "- Skipped IPv6 configuration as requested in method (${nicPrefix}IP6_METHOD=${ip6Method})" 662 | } 663 | 664 | Default { 665 | logmsg "- Unknown IPv6 method (${nicPrefix}IP6_METHOD=${ip6Method}), skipping configuration" 666 | } 667 | } 668 | 669 | ### 670 | 671 | # If no IP configuration happened, we skip 672 | # configuring additional IP addresses (aliases) 673 | If ($set_ip_conf -eq $false) { 674 | logmsg "- Skipped IP aliases configuration due to missing main IP" 675 | Continue 676 | } 677 | 678 | # Get the aliases for the NIC in the Context 679 | $aliasIds = ($context.Keys | Where-Object { $_ -match "^ETH${nicId}_ALIAS\d+_IP6?$" } | ForEach-Object { $_ -replace '(^ETH\d+_ALIAS|_IP$|_IP6$)', '' } | Sort-Object -Unique) 680 | 681 | foreach ($aliasId in $aliasIds) { 682 | $aliasPrefix = "ETH${nicId}_ALIAS${aliasId}" 683 | $aliasIp = $context[$aliasPrefix + '_IP'] 684 | $aliasNetmask = $context[$aliasPrefix + '_MASK'] 685 | $aliasIp6 = $context[$aliasPrefix + '_IP6'] 686 | $aliasIp6ULA = $context[$aliasPrefix + '_IP6_ULA'] 687 | $aliasIp6Prefix = $context[$aliasPrefix + '_IP6_PREFIX_LENGTH'] 688 | $detach = $context[$aliasPrefix + '_DETACH'] 689 | $external = $context[$aliasPrefix + '_EXTERNAL'] 690 | 691 | if ($external -and ($external -eq "YES")) { 692 | continue 693 | } 694 | 695 | if (!$aliasNetmask) { 696 | $aliasNetmask = "255.255.255.0" 697 | } 698 | 699 | if (!$aliasIp6Prefix) { 700 | $aliasIp6Prefix = "64" 701 | } 702 | 703 | if ($aliasIp -and !$detach) { 704 | logmsg "- Set Additional Static IP (${aliasPrefix})" 705 | netsh interface ipv4 add address $nic.InterfaceIndex $aliasIp $aliasNetmask 706 | 707 | If ($?) { 708 | logmsg " ... Success" 709 | } 710 | Else { 711 | logmsg " ... Failed" 712 | } 713 | } 714 | 715 | if ($aliasIp6 -and !$detach) { 716 | logmsg "- Set Additional IPv6 Address (${aliasPrefix})" 717 | netsh interface ipv6 add address $nic.InterfaceIndex $aliasIp6/$aliasIp6Prefix 718 | If ($? -And $aliasIp6ULA) { 719 | netsh interface ipv6 add address $nic.InterfaceIndex $aliasIp6ULA/64 720 | } 721 | 722 | If ($?) { 723 | logmsg " ... Success" 724 | } 725 | Else { 726 | logmsg " ... Failed" 727 | } 728 | } 729 | } 730 | 731 | If ($ip) { 732 | doPing($ip) 733 | } 734 | } 735 | 736 | Write-Host "`r`n" -NoNewline 737 | } 738 | 739 | function setTimeZone($context) { 740 | $timezone = $context['TIMEZONE'] 741 | 742 | If ($timezone) { 743 | logmsg "* Configuring time zone '${timezone}'" 744 | 745 | tzutil /s "${timezone}" 746 | 747 | If ($?) { 748 | logmsg ' ... Success' 749 | } 750 | Else { 751 | logmsg ' ... Failed' 752 | } 753 | } 754 | } 755 | 756 | function renameComputer($context) { 757 | # Initialize Variables 758 | $current_hostname = hostname 759 | $context_hostname = $context["SET_HOSTNAME"] 760 | 761 | # SET_HOSTNAME was not set but maybe DNS_HOSTNAME was... 762 | if (! $context_hostname) { 763 | $dns_hostname = $context["DNS_HOSTNAME"] 764 | 765 | if ($null -ne $dns_hostname -and $dns_hostname.ToUpper() -eq "YES") { 766 | 767 | # we will set our hostname based on the reverse dns lookup - the IP 768 | # in question is the first one with a set default gateway 769 | # (as is done by get_first_ip in addon-context-linux) 770 | 771 | logmsg "* Requested change of Hostname via reverse DNS lookup (DNS_HOSTNAME=YES)" 772 | $first_ip = (Get-WmiObject -Class Win32_NetworkAdapterConfiguration | Where-Object { $null -ne $_.DefaultIPGateway }).IPAddress | Select-Object -First 1 773 | $context_hostname = [System.Net.Dns]::GetHostbyAddress($first_ip).HostName 774 | logmsg "- Resolved Hostname is: $context_hostname" 775 | } 776 | Else { 777 | 778 | # no SET_HOSTNAME nor DNS_HOSTNAME - skip setting hostname 779 | return 780 | } 781 | } 782 | 783 | $splitted_hostname = $context_hostname.split('.') 784 | $context_hostname = $splitted_hostname[0] 785 | $context_domain = $splitted_hostname[1..$splitted_hostname.length] -join '.' 786 | 787 | If ($context_domain) { 788 | logmsg "* Changing Domain to $context_domain" 789 | 790 | $networkConfig = Get-WmiObject Win32_NetworkAdapterConfiguration -filter "ipenabled = 'true'" 791 | $ret = $networkConfig.SetDnsDomain($context_domain) 792 | 793 | If ($ret.ReturnValue) { 794 | 795 | # Returned Non Zero, Failed, No restart 796 | logmsg (" ... Failed: " + $ret.ReturnValue.ToString()) 797 | } 798 | Else { 799 | 800 | # Returned Zero, Success 801 | logmsg " ... Success" 802 | } 803 | } 804 | 805 | # Check for the .opennebula-renamed file 806 | $logged_hostname = "" 807 | If (Test-Path "$ctxDir\.opennebula-renamed") { 808 | logmsg "- Using the JSON file: $ctxDir\.opennebula-renamed" 809 | 810 | # Grab the JSON content 811 | $json = Get-Content -Path "$ctxDir\.opennebula-renamed" ` 812 | | Out-String 813 | 814 | # Convert to a Hash Table and set the Logged Hostname 815 | try { 816 | $status = $json | ConvertFrom-Json 817 | $logged_hostname = $status.ComputerName 818 | } 819 | # Invalid JSON 820 | catch [System.ArgumentException] { 821 | logmsg " [!] Invalid JSON:" 822 | Write-Host $json.ToString() 823 | } 824 | } 825 | Else { 826 | 827 | # no renaming was ever done - we fallback to our current Hostname 828 | $logged_hostname = $current_hostname 829 | } 830 | 831 | If (($current_hostname -ne $context_hostname) -and ` 832 | ($context_hostname -eq $logged_hostname)) { 833 | 834 | # avoid rename->reboot loop - if we detect that rename attempt was done 835 | # but failed then we drop log message about it and finish... 836 | 837 | logmsg "* Computer Rename Attempted but failed:" 838 | logmsg "- Current: $current_hostname" 839 | logmsg "- Context: $context_hostname" 840 | } 841 | ElseIf ($context_hostname -ne $current_hostname) { 842 | 843 | # the current_name does not match the context_name, rename the computer 844 | 845 | logmsg "* Changing Hostname to $context_hostname" 846 | # Load the ComputerSystem Object 847 | $ComputerInfo = Get-WmiObject -Class Win32_ComputerSystem 848 | 849 | # Rename the computer 850 | $ret = $ComputerInfo.rename($context_hostname) 851 | 852 | $contents = @{} 853 | $contents["ComputerName"] = $context_hostname 854 | ConvertTo-Json $contents | Out-File "$ctxDir\.opennebula-renamed" 855 | 856 | # Check success 857 | If ($ret.ReturnValue) { 858 | 859 | # Returned Non Zero, Failed, No restart 860 | logmsg (" ... Failed: " + $ret.ReturnValue.ToString()) 861 | Write-Host " Check the computername. " 862 | Write-Host "Possible Issues: The name cannot include control " ` 863 | "characters, leading or trailing spaces, or any of " ` 864 | "the following characters: `" / \ [ ] : | < > + = ; , ?" 865 | 866 | } 867 | Else { 868 | 869 | # Returned Zero, Success 870 | logmsg " ... Success" 871 | 872 | # Restart the Computer 873 | logmsg " ... Rebooting" 874 | Restart-Computer -Force 875 | 876 | # Exit here so the script doesn't continue to run 877 | Exit 0 878 | } 879 | } 880 | Else { 881 | 882 | # Hostname is set and correct 883 | logmsg "* Computer Name already set: $context_hostname" 884 | } 885 | 886 | Write-Host "`r`n" -NoNewline 887 | } 888 | 889 | function enableRemoteDesktop() { 890 | logmsg "* Enabling Remote Desktop" 891 | # Windows 7 only - add firewall exception for RDP 892 | logmsg "- Enable Remote Desktop Rule Group" 893 | netsh advfirewall Firewall set rule group="Remote Desktop" new enable=yes 894 | 895 | # Enable RDP 896 | logmsg "- Enable Allow Terminal Services Connections" 897 | $ret = (Get-WmiObject -Class "Win32_TerminalServiceSetting" -Namespace root\cimv2\terminalservices).SetAllowTsConnections(1) 898 | If ($ret.ReturnValue) { 899 | logmsg (" ... Failed: " + $ret.ReturnValue.ToString()) 900 | } 901 | Else { 902 | logmsg " ... Success" 903 | } 904 | Write-Host "`r`n" -NoNewline 905 | } 906 | 907 | function enablePing() { 908 | logmsg "* Enabling Ping" 909 | #Create firewall manager object 910 | New-Object -com hnetcfg.fwmgr 911 | 912 | # Get current profile 913 | $pro = $fwmgcalPolicy.CurrentProfile 914 | 915 | logmsg "- Enable Allow Inbound Echo Requests" 916 | $ret = $pro.IcmpSettings.AllowInboundEchoRequest = $true 917 | If ($ret) { 918 | logmsg " ... Success" 919 | } 920 | Else { 921 | logmsg " ... Failed" 922 | } 923 | 924 | Write-Host "`r`n" -NoNewline 925 | } 926 | 927 | function doPing($ip, $retries = 20) { 928 | logmsg "- Ping Interface IP $ip" 929 | 930 | $ping = $false 931 | $retry = 0 932 | do { 933 | $retry++ 934 | Start-Sleep -s 1 935 | $ping = Test-Connection -ComputerName $ip -Count 1 -Quiet -ErrorAction SilentlyContinue 936 | } while (!$ping -and ($retry -lt $retries)) 937 | 938 | If ($ping) { 939 | logmsg " ... Success ($retry tries)" 940 | } 941 | Else { 942 | logmsg " ... Failed ($retry tries)" 943 | } 944 | } 945 | 946 | function disableIPv6Privacy() { 947 | # Disable Randomization of IPv6 addresses (use EUI-64) 948 | logmsg "- Globally disable IPv6 Identifiers Randomization" 949 | netsh interface ipv6 set global randomizeidentifiers=disable 950 | 951 | If ($?) { 952 | logmsg " ... Success" 953 | } 954 | Else { 955 | logmsg " ... Failed" 956 | } 957 | 958 | # Disable IPv6 Privacy Extensions (temporary addresses) 959 | logmsg "- Globally disable IPv6 Privacy Extensions" 960 | netsh interface ipv6 set privacy state=disabled 961 | 962 | If ($?) { 963 | logmsg " ... Success" 964 | } 965 | Else { 966 | logmsg " ... Failed" 967 | } 968 | } 969 | 970 | function enableIPv6() { 971 | logmsg '- Enabling IPv6' 972 | 973 | Enable-NetAdapterBinding -Name $na.NetConnectionId -ComponentID ms_tcpip6 974 | 975 | If ($?) { 976 | logmsg " ... Success" 977 | } 978 | Else { 979 | logmsg " ... Failed" 980 | } 981 | } 982 | 983 | function disableIPv6() { 984 | logmsg '- Disabling IPv6' 985 | 986 | Disable-NetAdapterBinding -Name $na.NetConnectionId -ComponentID ms_tcpip6 987 | 988 | If ($?) { 989 | logmsg " ... Success" 990 | } 991 | Else { 992 | logmsg " ... Failed" 993 | } 994 | } 995 | 996 | function runScripts($context, $contextPaths) { 997 | logmsg "* Running Scripts" 998 | 999 | # Get list of scripts to run, " " delimited 1000 | $initscripts = $context["INIT_SCRIPTS"] 1001 | 1002 | if ($initscripts) { 1003 | # Parse each script and run it 1004 | ForEach ($script in $initscripts.split(" ")) { 1005 | 1006 | # Sanitize the filename (drop any path from them and instead use our directory) 1007 | $script = $contextPaths.contextInitScriptPath + [System.IO.Path]::GetFileName($script.Trim()) 1008 | 1009 | if (Test-Path $script) { 1010 | logmsg "- $script" 1011 | envContext($context) 1012 | pswrapper "$script" 1013 | } 1014 | 1015 | } 1016 | } 1017 | else { 1018 | # Emulate the init.sh fallback behavior from Linux 1019 | $script = $contextPaths.contextInitScriptPath + "init.ps1" 1020 | 1021 | if (Test-Path $script) { 1022 | logmsg "- $script" 1023 | envContext($context) 1024 | pswrapper "$script" 1025 | } 1026 | } 1027 | 1028 | # Execute START_SCRIPT or START_SCRIPT_64 1029 | $startScript = $context["START_SCRIPT"] 1030 | $startScript64 = $context["START_SCRIPT_BASE64"] 1031 | 1032 | if ($startScript64) { 1033 | $startScript = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($startScript64)) 1034 | } 1035 | 1036 | if ($startScript) { 1037 | 1038 | # Save the script as .opennebula-startscript.ps1 1039 | $startScriptPS = "$ctxDir\.opennebula-startscript.ps1" 1040 | $startScript | Out-File $startScriptPS "UTF8" 1041 | 1042 | # Launch the Script 1043 | logmsg "- $startScriptPS" 1044 | envContext($context) 1045 | pswrapper "$startScriptPS" 1046 | removeFile "$startScriptPS" 1047 | } 1048 | Write-Host "`r`n" -NoNewline 1049 | } 1050 | 1051 | function extendPartition($disk, $part) { 1052 | "select disk $disk", "select partition $part", "extend" | diskpart | Out-Null 1053 | } 1054 | 1055 | function extendPartitions($context) { 1056 | logmsg "* Extend partitions" 1057 | 1058 | "rescan" | diskpart 1059 | 1060 | $disks = @() 1061 | 1062 | # Cmdlet 'Get-Partition' is not in older Windows/Powershell versions 1063 | if (Get-Command -ErrorAction SilentlyContinue -Name Get-Partition) { 1064 | if ([string]$context['GROW_ROOTFS'] -eq '' -or $context['GROW_ROOTFS'].ToUpper() -eq 'YES') { 1065 | # Add at least C: 1066 | $drives = "C: $($context['GROW_FS'])" 1067 | } 1068 | else { 1069 | $drives = "$($context['GROW_FS'])" 1070 | } 1071 | 1072 | $driveLetters = (-split $drives | Select-String -Pattern "^(\w):?[\/]?$" -AllMatches | ForEach-Object { $_.matches.groups[1].Value } | Sort-Object -Unique) 1073 | 1074 | ForEach ($driveLetter in $driveLetters) { 1075 | $disk = New-Object PsObject -Property @{ 1076 | name = $null 1077 | diskId = $null 1078 | partIds = @() 1079 | } 1080 | # TODO: in the future an AccessPath can be used instead of just DriveLetter 1081 | $drive = (Get-Partition -DriveLetter $driveLetter) 1082 | $disk.name = "$driveLetter" + ':' 1083 | $disk.diskId = $drive.DiskNumber 1084 | $disk.partIds += $drive.PartitionNumber 1085 | $disks += $disk 1086 | } 1087 | } 1088 | Else { 1089 | # always resize at least the disk 0 1090 | $disk = New-Object PsObject -Property @{ 1091 | name = $null 1092 | diskId = 0 1093 | partIds = @() 1094 | } 1095 | 1096 | # select all parts - preserve old behavior for disk 0 1097 | $disk.partIds = "select disk $($disk.diskId)", "list partition" | diskpart | Select-String -Pattern "^\s+\w+ (\d+)\s+" -AllMatches | ForEach-Object { $_.matches.groups[1].Value } 1098 | $disks += $disk 1099 | } 1100 | 1101 | # extend all requested disk/part 1102 | ForEach ($disk in $disks) { 1103 | ForEach ($partId in $disk.partIds) { 1104 | if ($disk.name) { 1105 | logmsg "- Extend ($($disk.name)) Disk: $($disk.diskId) / Part: $partId" 1106 | } 1107 | Else { 1108 | logmsg "- Extend Disk: $($disk.diskId) / Part: $partId" 1109 | } 1110 | extendPartition $disk.diskId $partId 1111 | } 1112 | } 1113 | } 1114 | 1115 | function reportReady($context, $contextLetter) { 1116 | $reportReady = $context['REPORT_READY'] 1117 | $oneGateEndpoint = $context['ONEGATE_ENDPOINT'] 1118 | $vmId = $context['VMID'] 1119 | $token = $context['ONEGATE_TOKEN'] 1120 | $retryCount = 3 1121 | $retryWaitPeriod = 10 1122 | 1123 | if ($reportReady -and $reportReady.ToUpper() -eq 'YES') { 1124 | logmsg '* Report Ready to OneGate' 1125 | 1126 | if (!$oneGateEndpoint) { 1127 | logmsg ' ... Failed: ONEGATE_ENDPOINT not set' 1128 | return 1129 | } 1130 | 1131 | if (!$vmId) { 1132 | logmsg ' ... Failed: VMID not set' 1133 | return 1134 | } 1135 | 1136 | if (!$token) { 1137 | logmsg " ... Token not set. Try file" 1138 | $tokenPath = $contextLetter + 'token.txt' 1139 | if (Test-Path $tokenPath) { 1140 | $token = Get-Content $tokenPath 1141 | } 1142 | else { 1143 | logmsg " ... Failed: Token file not found" 1144 | return 1145 | } 1146 | } 1147 | 1148 | $retryNumber = 1 1149 | while ($true) { 1150 | try { 1151 | $body = 'READY=YES' 1152 | $target = $oneGateEndpoint + '/vm' 1153 | 1154 | [System.Net.HttpWebRequest] $webRequest = [System.Net.WebRequest]::Create($target) 1155 | $webRequest.Timeout = 10000 1156 | $webRequest.Method = 'PUT' 1157 | $webRequest.Headers.Add('X-ONEGATE-TOKEN', $token) 1158 | $webRequest.Headers.Add('X-ONEGATE-VMID', $vmId) 1159 | $buffer = [System.Text.Encoding]::UTF8.GetBytes($body) 1160 | $webRequest.ContentLength = $buffer.Length 1161 | 1162 | if ($oneGateEndpoint -ilike "https://*") { 1163 | #For reporting on HTTPS OneGateEndpoint 1164 | logmsg " ... Use HTTPS for OneGateEndpoint report: $oneGateEndpoint" 1165 | $AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12' 1166 | [System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols 1167 | [System.Net.ServicePointManager]::Expect100Continue = $false 1168 | [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } 1169 | } 1170 | 1171 | $requestStream = $webRequest.GetRequestStream() 1172 | $requestStream.Write($buffer, 0, $buffer.Length) 1173 | $requestStream.Flush() 1174 | $requestStream.Close() 1175 | 1176 | $response = $webRequest.getResponse() 1177 | if ($response.StatusCode -eq 'OK') { 1178 | logmsg ' ... Success' 1179 | break 1180 | } 1181 | else { 1182 | logmsg " ... Failed: $($response.StatusCode)" 1183 | } 1184 | } 1185 | catch { 1186 | $errorMessage = $_.Exception.Message 1187 | logmsg " ... Failed: $errorMessage" 1188 | } 1189 | 1190 | logmsg " ... Report ready failed (${retryNumber}. try out of ${retryCount})" 1191 | $retryNumber++ 1192 | if ($retryNumber -le $retryCount) { 1193 | logmsg " ... sleep for ${retryWaitPeriod} seconds and try again..." 1194 | Start-Sleep -Seconds $retryWaitPeriod 1195 | } 1196 | else { 1197 | logmsg " ... All retries failed!" 1198 | break 1199 | } 1200 | } 1201 | } 1202 | } 1203 | 1204 | function ejectContextCD($cdrom_drive) { 1205 | if (-Not $cdrom_drive) { 1206 | return 1207 | } 1208 | 1209 | $eject_cdrom = $context['EJECT_CDROM'] 1210 | 1211 | if ($null -ne $eject_cdrom -and $eject_cdrom.ToUpper() -eq 'YES') { 1212 | logmsg '* Ejecting context CD' 1213 | try { 1214 | #https://learn.microsoft.com/en-us/windows/win32/api/shldisp/ne-shldisp-shellspecialfolderconstants 1215 | $ssfDRIVES = 0x11 1216 | $sh = New-Object -ComObject "Shell.Application" 1217 | $sh.Namespace($ssfDRIVES).Items() | Where-Object { $_.Type -eq "CD Drive" -and $_.Path -eq $cdrom_drive.Name } | ForEach-Object { 1218 | $_.InvokeVerb("Eject") 1219 | logmsg " ... Ejected $($cdrom_drive.Name)" 1220 | } 1221 | } 1222 | catch { 1223 | logmsg " ... Failed to eject the CD: $_" 1224 | } 1225 | } 1226 | } 1227 | 1228 | function removeFile($file) { 1229 | if (![string]::IsNullOrEmpty($file) -and (Test-Path $file)) { 1230 | logmsg "* Removing the file: ${file}" 1231 | Remove-Item -Path $file -Force 1232 | } 1233 | } 1234 | 1235 | function removeDir($dir) { 1236 | if (![string]::IsNullOrEmpty($dir) -and (Test-Path $dir)) { 1237 | logmsg "* Removing the directory: ${dir}" 1238 | Remove-Item -Path $dir -Recurse -Force 1239 | } 1240 | } 1241 | 1242 | function cleanup($contextPaths) { 1243 | if ($contextPaths.contextDrive) { 1244 | # Eject CD with 'context.sh' if requested 1245 | ejectContextCD $contextPaths.contextDrive 1246 | } 1247 | else { 1248 | # Delete 'context.sh' if not on CD-ROM 1249 | removeFile $contextPaths.contextPath 1250 | 1251 | # and downloaded init scripts 1252 | removeDir $contextPaths.contextInitScriptPath 1253 | } 1254 | } 1255 | 1256 | function pswrapper($path) { 1257 | # source: 1258 | # - http://cosmonautdreams.com/2013/09/03/Getting-Powershell-to-run-in-64-bit.html 1259 | # - https://ss64.com/nt/syntax-64bit.html 1260 | If ($env:PROCESSOR_ARCHITEW6432 -eq "AMD64") { 1261 | # This is only set in a x86 Powershell running on a 64bit Windows 1262 | 1263 | $realpath = [string]$(Resolve-Path "$path") 1264 | 1265 | # Run 64bit powershell as a subprocess and there execute the command 1266 | # 1267 | # NOTE: virtual subdir 'sysnative' exists only when running 32bit binary under 64bit system 1268 | & "$env:WINDIR\sysnative\windowspowershell\v1.0\powershell.exe" -NonInteractive -NoProfile -Command "$realpath" 1269 | } 1270 | Else { 1271 | & "$path" 1272 | } 1273 | } 1274 | 1275 | function authorizeSSHKeyAdmin { 1276 | param ( 1277 | $authorizedKeys 1278 | ) 1279 | 1280 | $authorizedKeysPath = "$env:ProgramData\ssh\administrators_authorized_keys" 1281 | 1282 | 1283 | 1284 | # whitelisting 1285 | Set-Content $authorizedKeysPath $authorizedKeys 1286 | 1287 | if ($?) { 1288 | # permissions 1289 | icacls.exe $authorizedKeysPath /inheritance:r /grant Administrators:F /grant SYSTEM:F 1290 | 1291 | logsuccess 1292 | } 1293 | else { 1294 | logfail 1295 | } 1296 | 1297 | } 1298 | 1299 | function authorizeSSHKeyStandard { 1300 | param ( 1301 | $authorizedKeys 1302 | ) 1303 | 1304 | $authorizedKeysPath = "$env:USERPROFILE\.ssh" 1305 | 1306 | New-Item -Force -ItemType Directory -Path $authorizedKeysPath 1307 | Set-Content $authorized_keys_path $authorizedKeys 1308 | 1309 | if ($?) { 1310 | logsuccess 1311 | } 1312 | else { 1313 | logfail 1314 | } 1315 | } 1316 | 1317 | function authorizeSSHKey { 1318 | param ( 1319 | $authorizedKeys, 1320 | $winadmin 1321 | ) 1322 | 1323 | logmsg "* Authorizing SSH_PUBLIC_KEY: ${authorizedKeys}" 1324 | 1325 | if ($winadmin -ieq "no") { 1326 | authorizeSSHKeyStandard $authorizedKeys 1327 | } 1328 | else { 1329 | authorizeSSHKeyAdmin $authorizedKeys 1330 | } 1331 | 1332 | } 1333 | 1334 | ################################################################################ 1335 | # Main 1336 | ################################################################################ 1337 | 1338 | # global variable pointing to the private .contextualization directory 1339 | $global:ctxDir = "$env:SystemDrive\.onecontext" 1340 | 1341 | # Check, if above defined context directory exists 1342 | If ( !(Test-Path "$ctxDir") ) { 1343 | mkdir "$ctxDir" 1344 | } 1345 | 1346 | # Move old logfile away - so we have a current log containing the output of the last boot 1347 | If ( Test-Path "$ctxDir\opennebula-context.log" ) { 1348 | mv "$ctxDir\opennebula-context.log" "$ctxDir\opennebula-context-old.log" 1349 | } 1350 | m 1351 | # Start now logging to logfile 1352 | Start-Transcript -Append -Path "$ctxDir\opennebula-context.log" | Out-Null 1353 | 1354 | logmsg "* Running Script: $($MyInvocation.MyCommand.Path)" 1355 | 1356 | Set-ExecutionPolicy unrestricted -Force # not needed if already done once on the VM 1357 | [string]$computerName = "$env:computername" 1358 | [string]$ConnectionString = "WinNT://$computerName" 1359 | 1360 | # Check the working WMI 1361 | if (-Not (Get-WMIObject -ErrorAction SilentlyContinue Win32_Volume)) { 1362 | logmsg "- WMI not ready, exiting" 1363 | Stop-Transcript | Out-Null 1364 | exit 1 1365 | } 1366 | 1367 | Write-Host "`r`n" -NoNewline 1368 | Write-Host "*********************************`r`n" -NoNewline 1369 | Write-Host "*** ENTERING THE SERVICE LOOP ***`r`n" -NoNewline 1370 | Write-Host "*********************************`r`n" -NoNewline 1371 | Write-Host "`r`n" -NoNewline 1372 | 1373 | # infinite loop 1374 | $checksum = "" 1375 | do { 1376 | # Stay in this wait-loop until context.sh emerges and its path is stored 1377 | $contextPaths = waitForContext($checksum) 1378 | 1379 | # Parse context file 1380 | $context = getContext $contextPaths.contextScriptPath 1381 | 1382 | # Execute the contextualization actions 1383 | extendPartitions $context 1384 | setTimeZone $context 1385 | addLocalUser $context 1386 | enableRemoteDesktop 1387 | enablePing 1388 | configureNetwork $context 1389 | renameComputer $context 1390 | runScripts $context $contextPaths 1391 | authorizeSSHKey $context["SSH_PUBLIC_KEY"] $context["WINADMIN"] 1392 | reportReady $context $contextPaths.contextLetter 1393 | 1394 | # Save the 'applied' context.sh checksum for the next recontextualization 1395 | logmsg "* Calculating the checksum of the file: $($contextPaths.contextScriptPath)" 1396 | $checksum = Get-FileHash -Algorithm SHA256 $contextPaths.contextScriptPath 1397 | logmsg " ... $($checksum.Hash)" 1398 | # and remove the file itself 1399 | removeFile $contextPaths.contextScriptPath 1400 | 1401 | # Cleanup at the end 1402 | cleanup $contextPaths 1403 | 1404 | Write-Host "`r`n" -NoNewline 1405 | 1406 | } while ($true) 1407 | 1408 | Stop-Transcript | Out-Null 1409 | -------------------------------------------------------------------------------- /unattend.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | true 7 | 13 | 14 | 15 | 16 | 17 | en-US 18 | 19 | en-US 20 | en-US 21 | en-US 22 | en-US 23 | 24 | 25 | 26 | 27 | 28 | ClearType 29 | 30 | 31 | true 32 | 38 | 3 39 | Work 40 | true 41 | 42 | true 43 | true 44 | 48 | 49 | 50 | 58 | 59 | 69 | 70 | 81 | * 82 | 83 | 84 | en-US 85 | en-US 86 | en-us 87 | en-US 88 | en-US 89 | 90 | 91 | 92 | 93 | false 94 | 95 | 96 | 0 97 | 98 | 99 | 100 | 101 | true 102 | all 103 | @FirewallAPI.dll,-28752 104 | 105 | 106 | 107 | 108 | UTC 109 | OpenNebulaVM 110 | 113 | 114 | 115 | 0 116 | 117 | 118 | 119 | --------------------------------------------------------------------------------