├── .gitignore ├── .gitmodules ├── README.md ├── create-user-daniel.sh ├── demo.sh ├── elasticsearch ├── demo.sh ├── generate-yaml.sh └── kibana.data-view.sh ├── generate-data ├── .gitignore ├── Containerfile ├── demo.js ├── generate-data.js ├── mappings.curl ├── package.json ├── public │ └── index.html └── rebuild.sh ├── hybrid-virt-elasticsearch.webp ├── net.yaml ├── rbac.yaml ├── setup.template.yaml ├── vm.template.yaml └── windows ├── .gitignore ├── autounattended.xml ├── vm.yaml.template └── windows2019.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | subscription.txt 2 | *id_rsa* 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "format"] 2 | path = format 3 | url = https://github.com/purefield/format.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Openshift Virtualization Demo running Elasticsearch on Mixed Pods (VMs and Containers) 2 | 3 | ![Elasticsearch Cluster Overview](hybrid-virt-elasticsearch.webp) 4 | 5 | ### Dependencies: 6 | * OpenShift Baremetal cluster with ODF 7 | * ODF is default storage class set and profile applied 8 | * Installed Operators 9 | * OpenShift Virtualization Operator 10 | * Cert Utils Operator 11 | * RHEL Subscription [Activation Keys](https://console.redhat.com/insights/connector/activation-keys) (for Pod software installation) 12 | * Subscription Org 13 | * Subscription Key 14 | 15 | ### Setup 16 | NOTE: Setup is ran from the desired bastion host for the OCP cluster(s) 17 | ```sh 18 | sudo dnf install -y git 19 | git clone https://github.com/purefield/opc-virt.git ~/demo 20 | cd ~/demo/ 21 | ./setup.sh 22 | ``` 23 | 24 | ### Demo Prep 25 | Log into the OpenShift Cluster 26 | 27 | Run the demo script from the same bastion to create a fresh elasticsearch cluster in a new namespace (first argument or prompt, defaults to ```ocp-virt```) 28 | ```sh 29 | ./demo.sh next-gen-virt 30 | ``` 31 | Wait for installation (approximately 4 mins in normal circumstances) 32 | 33 | ### Demo Highlights 34 | Under Administrator Perspective: 35 | * Operators -> OperatorHub 36 | * OpenShift Virtualization Operator (installed) 37 | * Migration Toolkit for Virtualization Operator (available) 38 | * Virtualization 39 | * Overview 40 | * Templates 41 | * DataStore (RWX) 42 | * Catalog 43 | * VirtualMachines 44 | * Create 45 | * With Wizard 46 | * Create 47 | * Select Red Hat Enterprise Linux 9.0 VM 48 | * Next 49 | * Create virtual machine 50 | * [... menu] Migrate Node to Node 51 | * [New VM] Overview 52 | * Virtual Machine options 53 | * Metrics 54 | * Snapshots 55 | * Run demo steps 56 | * Pick random/suggested name for namespace to pass as first argument or when prompted 57 | * Execute generate and apply steps in shared terminal window showing VirtualMachines screen in the background 58 | 59 | Under the Developer Perspective: 60 | * Topology 61 | * Show the mixed environment and dig into details 62 | * 3 Elasticsearch VMs for data and control plane 63 | * 1 Elasticsearch container as coordinator node 64 | * 1 Kibana container for data visualization 65 | * 1 RHEL 9 container for data generation 66 | * 1 RHEL 9 container for utilities to explore with ssh keys to the VMs 67 | 68 | Under Administrator Perspective: 69 | * Networking 70 | * Routes 71 | * Select es-master00 Cockpit route 72 | * Log-in using ```elasticsearch```:```redhat``` 73 | * View services and search for elasticsearch (will appear when ready) 74 | * Select elasticsearch route 75 | * Verify ```cluster_uuid``` is populated 76 | * Append ```/_cat/nodes``` to elasticsearch url and verify ```coordinate``` is a member 77 | * Open kibana route to show connected web application 78 | * In Kibana create a new discovery for the "generated" index with timestamp 79 | * Virtualization 80 | * VirtualMachines 81 | * es-master00 82 | * Environment 83 | * Configuration Maps mounted inside the VM 84 | * 00yaml - Elasticsearch Node specific configuration 85 | * 0000sh - Multiple scripts used during VM installation 86 | * Secret mounted inside the VM 87 | * 00cert - Let's Encrypt SSL Certificate private key and chain used by the VMs 88 | * Migrate VM from Node to Node 89 | * Take a Snapshot of the VM 90 | -------------------------------------------------------------------------------- /create-user-daniel.sh: -------------------------------------------------------------------------------- 1 | . ./format/format.sh 2 | __ "Create htpasswd secret" 4 3 | cmd "oc create secret generic htpasswd -n openshift-config --from-literal htpasswd='daniel:$2y$05$GQlPEZ03BPQ4ZzkPD4dhKeM54VjDPN9YtyORDRVT6w.kU1p4Y4ZZ.'" 4 | 5 | __ "Create htpasswd provider" 4 6 | patch='[{"op":"add","path":"/spec/identityProviders/-","value":{"htpasswd":{"fileData":{"name":"htpasswd"}},"mappingMethod":"claim","name":"htpasswd","type":"HTPasswd"}}]' 7 | cmd "oc patch oauth cluster --type='json' -p='$patch'" 8 | -------------------------------------------------------------------------------- /demo.sh: -------------------------------------------------------------------------------- 1 | . ./format/format.sh 2 | NAMESPACE=$1 3 | VMS=$2 4 | CUSTOMER=${3:-Our Valued Customer} 5 | __ "OpenShift Virtualization Demo" 1 6 | __ "Configuration" 2 7 | 8 | __ "Namespace" 3 9 | _? "What namespace should we use?" NAMESPACE ocp-virt $NAMESPACE 10 | 11 | __ "Number VMs" 3 12 | _? "How many Elasticsearch VMs should we use?" vms 3 $VMS 13 | 14 | __ "Base Domain" 3 15 | BASEDOMAIN=$(oc get --namespace openshift-ingress-operator ingresscontrollers/default -o jsonpath='{.status.domain}') 16 | _? "What base domain should we use?" BASEDOMAIN "" $BASEDOMAIN 17 | 18 | __ "Red Hat Subscription" 3 19 | if [ -s subscription.txt ]; then . subscription.txt; fi 20 | _? "Key" SUBSCRIPTION_KEY key $SUBSCRIPTION_KEY 21 | _? "ORG" SUBSCRIPTION_ORG org $SUBSCRIPTION_ORG 22 | 23 | __ "Create resources" 2 24 | if [ ! -s ./ssh.id_rsa ]; then 25 | __ "Create ssh keys" 3 26 | cmd 'ssh-keygen -m PEM -N "" -f ./ssh.id_rsa' 27 | fi 28 | export SSH_PUBLIC_KEY="$(cat ssh.id_rsa.pub)" 29 | export SSH_PRIVATE_KEY="$(cat ssh.id_rsa)" 30 | 31 | if [[ "$(oc get project $NAMESPACE -o=jsonpath='{.metadata.name}' 2>/dev/null)" != $NAMESPACE ]]; then 32 | __ "Create namespace" 3 33 | cmd "oc new-project $NAMESPACE --display-name='$CUSTOMER'" 34 | fi 35 | 36 | __ "Create common resources" 3 37 | if [[ "$(oc get secrets/wildcard-cert -n $NAMESPACE -o name 2>/dev/null)" != 'secret/wildcard-cert' ]]; then 38 | __ "For demo purpose re-use the ingress cert" 4 39 | item="secret/letsencrypt-prod-private-key -n openshift-ingress" 40 | cmd "oc get $item -o yaml | sed 's/namespace: openshift-ingress/namespace: $NAMESPACE/' | sed 's/name: .*/name: wildcard-cert/' | oc apply -f -" 41 | fi 42 | 43 | __ "Import setup template into cluster" 4 44 | cmd oc apply -f setup.template.yaml -n openshift 45 | __ "Process parameters and apply" 4 46 | cmd 'oc process ocp-virt-demo-setup-template -n openshift -p NAMESPACE='$NAMESPACE' -p BASEDOMAIN="'$BASEDOMAIN'" -p SUBSCRIPTION_ORG=$SUBSCRIPTION_ORG -p SUBSCRIPTION_KEY=$SUBSCRIPTION_KEY -p SSH_PRIVATE_KEY="$SSH_PRIVATE_KEY" -p SSH_PUBLIC_KEY="$SSH_PUBLIC_KEY" -p SHARDS=$vms | oc apply -f -' 47 | ___ "We use our imported setup template to instantiate our environment" 10 48 | 49 | __ "Create virtual machines" 3 50 | __ "Import vm template into cluster" 4 51 | cmd oc apply -f vm.template.yaml -n openshift 52 | for i in $(seq 0 $((vms -1))); do 53 | name=$(printf "es-master%02d" "$i") 54 | __ "Process parameters for $name and apply" 4 55 | cmd 'oc process ocp-virt-demo-vms-template -n openshift -p VMNAME='$name' -p NAMESPACE='$NAMESPACE' -p BASEDOMAIN="'$BASEDOMAIN'" -p SSH_PUBLIC_KEY="$SSH_PUBLIC_KEY" | oc apply -f -' 56 | done 57 | ___ "We created $vms VMs using our vm template" 10 58 | 59 | __ "Run demo:" 2 60 | __ "Wait for virtual machines" 3 61 | oo $((vms + 1)) "oc get vmi -n $NAMESPACE --no-headers=true --ignore-not-found=true | wc -l" 62 | 63 | __ "Wait for elasticsearch vms to be ready" 3 64 | jsonpath="{range .items[*]}{@.status.conditions[?(@.type=='Ready')].status}{'\n'}" 65 | oo $vms 'oc get pods -n '$NAMESPACE' -l app=elasticsearch,elasticsearch=master -o jsonpath="'$jsonpath'" | grep True | wc -l' 66 | 67 | __ "Wait for elasticsearch cluster to be ready" 3 68 | oo $vms "./elasticsearch/demo.sh | grep es-master | wc -l" 69 | 70 | __ "Confirm elasticsearch cluster is healthy" 3 71 | cmd ./elasticsearch/demo.sh 72 | ___ "Did $vms VMs and Coordinate Container form an Elasticsearch cluster?" 10 73 | 74 | __ "Check elasticsearch on es-master vms via RHEL container" 3 75 | for i in $(seq 0 $((vms -1))); do 76 | name=$(printf "es-master%02d" "$i") 77 | cmd 'oc rsh -n '$NAMESPACE' pod/ubi9 ssh -o StrictHostKeyChecking=accept-new elasticsearch@'$name' systemctl status elasticsearch | egrep Active -B2 --color=always' 78 | done 79 | ___ "Are all Elasticsearch services healthy?" 10 80 | 81 | __ "Cronjob logs from setting up Elasticseach Index and Kibana View" 3 82 | cmd oc logs -n aardvark -l job-name=elasticsearch-init-job 83 | 84 | __ "What did we create?" 2 85 | kinds=$(grep '\- kind' *.template.yaml -h | sort -n | uniq | sed 's/ //g' | cut -d':' -f 2 | paste -sd "," - ) 86 | kindsMatch=$(echo $kinds | sed 's/,/|/g' ) 87 | names='es-master|elasticsearch|data-generator|coordinate|kibana|windows2019|cockpit' 88 | oc get $kinds -l demo=ocp-virt -n $NAMESPACE | \ 89 | egrep --color=always -i "^($kindsMatch)" -B10 -A10 | \ 90 | GREP_COLOR='01;36' egrep --color=always $names -B10 -A10 91 | 92 | __ "Have fun storming the castle!" 1 93 | _? "Open demo urls in Chrome?" openChrome yes 94 | if [[ "$openChrome" == "yes" ]]; then 95 | /opt/google/chrome/chrome --new-window \ 96 | --profile-directory=Default \ 97 | https://es-master00.apps.virt.ola.purefield.nl/system/services#/elasticsearch.service?name=elastic \ 98 | https://data-generator.apps.virt.ola.purefield.nl \ 99 | https://kibana.apps.virt.ola.purefield.nl/app/discover\#/ \ 100 | https://github.com/purefield/ocp-virt/commit/23a92611a631008ff5fe77a122f63ed34f3a8d79 \ 101 | https://console-openshift-console.apps.virt.ola.purefield.nl/catalog/ns/default?category=other\&catalogType=Template \ 102 | https://grafana-open-cluster-management-observability.apps.acm.ola.purefield.nl/d/WfJLo3rSz/executive-dashboards-single-cluster-view?orgId=1 103 | fi 104 | 105 | exit 0 106 | poc/demo goals 107 | - the speed to market for new applications with a tight inner and outer loop 108 | - scalability of our platform to support fluctuation in demand and states as they come online 109 | - consistency of the platform to support existing and future development 110 | - operational excellence and automation to scale the business without stretching the staff 111 | -------------------------------------------------------------------------------- /elasticsearch/demo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export OK='\033[0;32mOK\033[0m' 3 | export ERROR='\033[0;31mERROR\033[0m' 4 | host=$(oc get route -l app=elasticsearch -l role=api -A -o jsonpath='{.items[].status.ingress[].host}') 5 | cmd="curl -sk https://$host/_cat/nodes" 6 | echo 7 | echo "$cmd" 8 | echo 9 | out=$($cmd 2>&1) 10 | if [[ $? -eq 0 && "x$(echo "$out" | grep es-master)" != "x" ]]; then 11 | if [ "x$(echo "$out" | grep coordinate)" == "x" ]; then 12 | echo -e "[ $ERROR ] VM cluster is up. Waiting on container" 13 | echo "$out" 14 | else 15 | echo -e "[ $OK ] Coordinate container is part of the VM cluster:" 16 | echo "$out" | sort -k9,10 | grep coordinate -A100 --color=always 17 | fi 18 | else 19 | printf "[ $ERROR ] Not ready yet.\n" 20 | fi 21 | echo 22 | -------------------------------------------------------------------------------- /elasticsearch/generate-yaml.sh: -------------------------------------------------------------------------------- 1 | namespace=$1 2 | touch namespace.last 3 | lastNamespace=$(cat namespace.last) 4 | lastNamespace=${lastNamespace:-next-gen-virt} 5 | printf -v sshPubKey "%q" $(<../demo.id_rsa.pub tr -d '\n' | base64 -w0) 6 | if [ "x$namespace" == "x" ] 7 | then 8 | read -p "What namespace name [$lastNamespace]? " namespace 9 | fi 10 | if [ -z "$namespace" ] 11 | then 12 | namespace="$lastNamespace" 13 | fi 14 | echo "$namespace" > namespace.last 15 | source ../subscription.txt 16 | cat namespace.yaml.template | perl -pe "s/\{\{ namespace \}\}/$namespace/g" > $namespace.yaml 17 | echo "---" >> $namespace.yaml 18 | baseDomain=$(oc get --namespace openshift-ingress-operator ingresscontrollers/default -o jsonpath='{.status.domain}') 19 | if [ -n "$GUID" ] 20 | then 21 | altDomain=$baseDomain 22 | baseDomain="apps.$GUID.dynamic.opentlc.com" 23 | fi 24 | if [ -s $HOME/demo/demo.redhat.com/privkey.pem ] 25 | then 26 | oc create secret tls letsencrypt \ 27 | --cert=$HOME/demo/demo.redhat.com/fullchain.pem \ 28 | --key=$HOME/demo/demo.redhat.com/privkey.pem \ 29 | -n $namespace --dry-run=client -o yaml >> $namespace.yaml 30 | else 31 | oc create secret generic letsencrypt \ 32 | -n $namespace --dry-run=client -o yaml >> $namespace.yaml 33 | fi 34 | echo "---" >> $namespace.yaml 35 | oc create secret generic id-rsa --from-file ../demo.id_rsa -n $namespace --dry-run=client -o yaml >> $namespace.yaml 36 | cat elasticsearch.install.yaml.template kibana.yaml.template coordinate.yaml.template ubi9.yaml.template data-generator.yaml.template | \ 37 | perl -pe "s/\{\{ namespace \}\}/$namespace/g" | \ 38 | perl -pe "s/\{\{ baseDomain \}\}/$baseDomain/g" | \ 39 | perl -pe "s/\{\{ subscriptionOrg \}\}/$subscriptionOrg/g" | \ 40 | perl -pe "s/\{\{ subscriptionKey \}\}/$subscriptionKey/g" | \ 41 | perl -MMIME::Base64 -pe "s/\{\{ sshPubKey \}\}/decode_base64('$sshPubKey')/ge" \ 42 | >> $namespace.yaml 43 | for name in es-master00 es-master01 es-master02; do 44 | cat elasticsearch.master.vm.yaml.template | \ 45 | perl -pe "s/\{\{ name \}\}/$name/g" | \ 46 | perl -pe "s/\{\{ namespace \}\}/$namespace/g" | \ 47 | perl -pe "s/\{\{ baseDomain \}\}/$baseDomain/g" | \ 48 | perl -MMIME::Base64 -pe "s/\{\{ sshPubKey \}\}/decode_base64('$sshPubKey')/ge" \ 49 | >> $namespace.yaml 50 | done 51 | cat namespace.yaml.template ../windows/vm.yaml.template | perl -pe "s/\{\{ namespace \}\}/$namespace/g" > ../windows/vm.yaml 52 | echo "# Apply yaml using:" 53 | echo "oc apply -f ../windows/vm.yaml" 54 | echo "oc apply -f $namespace.yaml" 55 | echo "watch --color ./demo.sh" 56 | echo "./kibana.data-view.sh" 57 | -------------------------------------------------------------------------------- /elasticsearch/kibana.data-view.sh: -------------------------------------------------------------------------------- 1 | curl -kX POST "https://$(oc get route -l app=kibana -A -ojsonpath='{.items[0].spec.host}')/api/data_views/data_view" -H "kbn-xsrf: true" -H "Content-Type: application/json" -d ' 2 | { 3 | "data_view": { 4 | "name":"demo", 5 | "title": "generated*", 6 | "timeFieldName": "timestamp" 7 | } 8 | }' 9 | 10 | -------------------------------------------------------------------------------- /generate-data/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | 4 | -------------------------------------------------------------------------------- /generate-data/Containerfile: -------------------------------------------------------------------------------- 1 | FROM registry.access.redhat.com/ubi8/nodejs-18 2 | USER root 3 | WORKDIR /app 4 | COPY package.json . 5 | RUN npm install 6 | USER default 7 | ENV ES_NODE=elasticsearch\ 8 | ES_PORT=443 \ 9 | ES_INDEX=generated \ 10 | DATA_SIZE=5 \ 11 | DATE_RATE=10 \ 12 | DATA_BATCH=100 13 | COPY generate-data.js . 14 | COPY public public 15 | EXPOSE 3000 16 | ENTRYPOINT /usr/bin/node generate-data.js 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /generate-data/demo.js: -------------------------------------------------------------------------------- 1 | const namespace = process.env.NAMESPACE || 'next-gen-virt'; 2 | const express = require('express'); 3 | const port = process.env.PORT || 8080; 4 | const host = process.env.HOST; 5 | 6 | const app = express(); 7 | const server = require('http').Server(app); 8 | // Handle HTTP GET request to the root URL 9 | app.get('/', (req, res) => { 10 | res.send(frames); 11 | }); 12 | // Start the server 13 | server.listen(port, () => { 14 | console.log(`Server is running on port ${port}`); 15 | }); 16 | 17 | let frames = ` 18 | 19 | 20 | 21 | Next Gen Virt Demo 22 | 23 | 24 | 25 | 26 | 27 | `; 28 | -------------------------------------------------------------------------------- /generate-data/generate-data.js: -------------------------------------------------------------------------------- 1 | const elasticsearch = require('@elastic/elasticsearch'); 2 | const express = require('express'); 3 | const socketIO = require('socket.io'); 4 | const LoremIpsum = require('lorem-ipsum').LoremIpsum; 5 | const v8 = require('v8'); 6 | 7 | const es_node = process.env.ES_NODE || 'coordinate'; 8 | const es_port = process.env.ES_PORT || '9200'; 9 | const index = process.env.ES_INDEX || 'generated'; 10 | const port = process.env.UI_PORT || 3000; 11 | const secure = es_port == 443 ? 's' : ''; 12 | 13 | const app = express(); 14 | const server = require('http').Server(app); 15 | const io = socketIO(server); 16 | const lorem = new LoremIpsum({ 17 | sentencesPerParagraph: { max: 8, min: 4 }, 18 | wordsPerSentence: { max: 16, min: 4 } 19 | }); 20 | 21 | var size = process.env.DATA_SIZE || 5; 22 | var rate = process.env.DATA_RATE || 10; 23 | var batch = process.env.DATA_BATCH || 100; // Number of inserts per batch 24 | var logs = true; 25 | var start = new Date().getTime(); 26 | var totalBytes = 0; 27 | var deltaBytes = 0; 28 | var intervalId; 29 | var emitted = start; 30 | var randomDoc = {}; 31 | 32 | // Serve static files 33 | app.use(express.static('public')); 34 | 35 | // Handle HTTP GET request to the root URL 36 | app.get('/', (req, res) => { 37 | res.sendFile(__dirname + '/public/index.html'); 38 | }); 39 | 40 | // Socket.IO connection 41 | io.on('connection', (socket) => { 42 | console.log('A user connected.'); 43 | 44 | socket.on('updateValues', (data) => { 45 | var rateChange = data.rate != rate; 46 | size = data.size ? data.size * 1 : size; 47 | rate = data.rate ? data.rate : rate; 48 | batch = data.batch ? data.batch : batch; 49 | logs = data.logs && true; 50 | if (rateChange) updateInterval(); 51 | }); 52 | 53 | // Disconnect event 54 | socket.on('disconnect', () => { 55 | console.log('A user disconnected.'); 56 | }); 57 | }); 58 | 59 | // Start the server 60 | server.listen(port, () => { 61 | console.log(`Server is running on port ${port}`); 62 | }); 63 | 64 | // Elasticsearch configuration 65 | const client = new elasticsearch.Client({ 66 | node: `http${secure}://${es_node}:${es_port}`, 67 | tls: { 68 | rejectUnauthorized: false 69 | } 70 | }); 71 | 72 | // Update interval 73 | function updateInterval(){ 74 | if (intervalId) 75 | clearInterval(intervalId); 76 | if (rate > 0) { 77 | var intervalMs = Math.round(1000 / rate); 78 | intervalId = setInterval(insertBatch, intervalMs); 79 | } 80 | } 81 | 82 | // Generate a random document 83 | function generateRandomDocument() { 84 | if (!randomDoc.data) { 85 | var data = { 86 | timestamp: new Date(), 87 | message: lorem.generateSentences(size), 88 | data: lorem.generateParagraphs(size), 89 | bytes: 0 90 | }; 91 | var bytes = v8.serialize(data).length; 92 | data.bytes= bytes % 10 + bytes; 93 | randomDoc = data; 94 | } 95 | randomDoc.timestamp = new Date(); 96 | return randomDoc; 97 | } 98 | 99 | // Insert batch of documents into Elasticsearch 100 | async function insertBatch() { 101 | const documents = Array.from({ length: batch }, generateRandomDocument); 102 | const body = documents.flatMap((doc) => [{ index: { _index: index } }, doc]); 103 | 104 | try { 105 | const response = await client.bulk({ body }); 106 | if (response.errors) { 107 | const errorItems = response.items.filter((item) => item.index && item.index.error); 108 | console.error('Error inserting documents:', errorItems); 109 | } else { 110 | var bytes = v8.serialize(body).length; 111 | var duration = new Date().getTime() - start; 112 | totalBytes += bytes; 113 | deltaBytes += bytes; 114 | bytes = humanBytes(bytes); 115 | var msg = `Inserted ${batch} documents ${bytes} into ${index} on ${es_node}:${es_port} (s:${size} r:${rate})` 116 | var now = new Date().getTime(); 117 | var data = { 118 | bytes: humanBytes(totalBytes), 119 | avg: humanBytes(parseInt(totalBytes/(duration/1000/60))), 120 | rate: humanBytes(parseInt(deltaBytes/((now-emitted)/1000))) 121 | }; 122 | if (logs) { 123 | console.log(msg); 124 | data.log = msg; 125 | } 126 | if (now - emitted > 300){ 127 | io.emit('data', data); 128 | emitted = now; 129 | deltaBytes = 0; 130 | } 131 | } 132 | } catch (error) { 133 | console.error('Error inserting documents:', error); 134 | } 135 | } 136 | 137 | function humanBytes(size) { 138 | var i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024)); 139 | return (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i]; 140 | } 141 | 142 | updateInterval(); 143 | -------------------------------------------------------------------------------- /generate-data/mappings.curl: -------------------------------------------------------------------------------- 1 | curl -kXPUT -H "Content-Type: application/json" https://$host/_template/demo -d' 2 | { 3 | "index_patterns": [ 4 | "generated*" 5 | ], 6 | "settings": { 7 | "number_of_shards": 2, 8 | "number_of_replicas": 1 9 | }, 10 | "mappings": { 11 | "properties": { 12 | "@timestamp": { 13 | "type": "date" 14 | }, 15 | "@version": { 16 | "type": "text", 17 | "fields": { 18 | "keyword": { 19 | "type": "keyword" 20 | } 21 | } 22 | }, 23 | "bytes": { 24 | "type": "integer" 25 | }, 26 | "message": { 27 | "type": "text", 28 | "fields": { 29 | "keyword": { 30 | "type": "keyword" 31 | } 32 | } 33 | }, 34 | "data": { 35 | "type": "text", 36 | "fields": { 37 | "keyword": { 38 | "type": "keyword" 39 | } 40 | } 41 | } 42 | } 43 | } 44 | }' 45 | -------------------------------------------------------------------------------- /generate-data/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generate-data", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "generate-data.js", 6 | "dependencies": { 7 | "@elastic/elasticsearch": "^8.13.1", 8 | "express": "^4.18.2", 9 | "lorem-ipsum": "^2.0.8", 10 | "morgan": "^1.10.0", 11 | "socket.io": "^4.6.1" 12 | }, 13 | "devDependencies": {}, 14 | "scripts": { 15 | "test": "echo \"Error: no test specified\" && exit 1", 16 | "start": "node generate-data.js" 17 | }, 18 | "author": "", 19 | "license": "ISC" 20 | } 21 | -------------------------------------------------------------------------------- /generate-data/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Log Streamer 6 | 11 | 12 | 41 | 42 | 43 |

44 | Data Generator 45 | total 46 | /min/avg 47 | /sec 48 |

49 |
50 |
51 | 52 | 53 | paragraphs/document 54 |
55 |
56 | 57 | 58 | documents/batch 59 |
60 |
61 | 62 | 63 | batch/sec 64 |
65 |
66 | 67 | 68 | 69 |
70 |
71 |
72 | 73 | 74 | -------------------------------------------------------------------------------- /generate-data/rebuild.sh: -------------------------------------------------------------------------------- 1 | host=$(oc get route -l app=elasticsearch -l role=coordinate -A -o jsonpath='{.items[].status.ingress[].host}') 2 | podman kill generate-data 3 | podman build -t generate-data . 4 | ./mappings.curl $host 5 | podman run -it --rm -p 3000:3000 -e ES_NODE=$host -e ES_PORT=443 -e DATA_RATE=100 -e DATA_SIZE=5 -e DATA_BATCH=100 --name generate-data generate-data:latest 6 | -------------------------------------------------------------------------------- /hybrid-virt-elasticsearch.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/purefield/ocp-virt/56710f72b38e93299c396df5efe1ce0c8477ef98/hybrid-virt-elasticsearch.webp -------------------------------------------------------------------------------- /net.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: nmstate.io/v1 3 | kind: NodeNetworkConfigurationPolicy 4 | metadata: 5 | name: bond0.777 6 | spec: 7 | desiredState: 8 | interfaces: 9 | - name: bond0.777 10 | type: vlan 11 | state: up 12 | vlan: 13 | base-iface: bond0 14 | id: 777 15 | ipv4: 16 | enabled: false 17 | dhcp: false 18 | ipv6: 19 | enabled: false 20 | dhcp: false 21 | --- 22 | apiVersion: "k8s.cni.cncf.io/v1" 23 | kind: NetworkAttachmentDefinition 24 | metadata: 25 | name: vlan-777-bond0 26 | namespace: engineering-vms 27 | spec: 28 | config: '{ 29 | "cniVersion": "0.3.1", 30 | "name": "vlan-777-bond0", 31 | "type": "cnv-bridge", 32 | "master": "bond0.777", 33 | "macspoofchk": false, 34 | "preserveDefaultVlan": false, 35 | "ipam": { 36 | "type": "whereabouts", 37 | "range": "192.168.99.0/24" 38 | } 39 | }' 40 | -------------------------------------------------------------------------------- /rbac.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: user.openshift.io/v1 3 | kind: Group 4 | metadata: 5 | name: engineering 6 | users: 7 | - therevoman 8 | - daniel 9 | - thomasphall 10 | --- 11 | kind: Project 12 | apiVersion: project.openshift.io/v1 13 | metadata: 14 | name: engineering-vms 15 | --- 16 | kind: RoleBinding 17 | apiVersion: rbac.authorization.k8s.io/v1 18 | metadata: 19 | name: engineering-vms-user 20 | namespace: engineering-vms 21 | subjects: 22 | - kind: Group 23 | apiGroup: rbac.authorization.k8s.io 24 | name: engineering 25 | - kind: Group 26 | apiGroup: rbac.authorization.k8s.io 27 | name: engineering-admin 28 | roleRef: 29 | apiGroup: rbac.authorization.k8s.io 30 | kind: ClusterRole 31 | name: edit 32 | --- 33 | kind: RoleBinding 34 | apiVersion: rbac.authorization.k8s.io/v1 35 | metadata: 36 | name: engineering-vms-admin 37 | namespace: engineering-vms 38 | subjects: 39 | - kind: Group 40 | apiGroup: rbac.authorization.k8s.io 41 | name: engineering-admin 42 | roleRef: 43 | apiGroup: rbac.authorization.k8s.io 44 | kind: ClusterRole 45 | name: admin 46 | --- 47 | kind: Role 48 | apiVersion: rbac.authorization.k8s.io/v1 49 | metadata: 50 | name: vm-templates-engineering-users 51 | namespace: engineering-vms 52 | rules: 53 | - verbs: 54 | - get 55 | - watch 56 | - list 57 | - create 58 | apiGroups: 59 | - processedtemplates.template.openshift.io 60 | - template.openshift.io 61 | resources: 62 | - processedtemplates 63 | - verbs: 64 | - get 65 | - watch 66 | - list 67 | apiGroups: 68 | - k8s.cni.cncf.io 69 | resources: 70 | - network-attachment-definitions 71 | --- 72 | kind: RoleBinding 73 | apiVersion: rbac.authorization.k8s.io/v1 74 | metadata: 75 | name: role-binding-engineering-group-vm-templates 76 | namespace: engineering-vms 77 | subjects: 78 | - kind: Group 79 | apiGroup: rbac.authorization.k8s.io 80 | name: engineering 81 | - kind: Group 82 | apiGroup: rbac.authorization.k8s.io 83 | name: engineering-admin 84 | roleRef: 85 | apiGroup: rbac.authorization.k8s.io 86 | kind: Role 87 | name: vm-templates-engineering-users 88 | -------------------------------------------------------------------------------- /setup.template.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: template.openshift.io/v1 3 | kind: Template 4 | metadata: 5 | name: ocp-virt-demo-setup-template 6 | annotations: 7 | iconClass: "icon-elastic" 8 | tags: "elasticsearch,virtualization" 9 | template.openshift.io/bindable: "true" 10 | openshift.io/display-name: OpenShift Virtualization Demo - 1/2 Environment Setup 11 | openshift.io/documentation-url: https://github.com/purefield/ocp-virt 12 | description: "OpenShift Virtualization and Container Application Stack Demo. 13 | This template provides a demo application stack around Elasticsearch. 14 | 15 | Bringing VMs and Containers together. 16 | 17 | It includes Elasticsearch VMs, Kibana, data generation application and coordinator containers." 18 | openshift.io/provider-display-name: Daniel Schimpfoessl 19 | objects: 20 | - kind: Namespace 21 | apiVersion: v1 22 | metadata: 23 | name: ${NAMESPACE} 24 | - kind: Secret 25 | apiVersion: v1 26 | metadata: 27 | name: letsencrypt 28 | namespace: ${NAMESPACE} 29 | - kind: Secret 30 | apiVersion: v1 31 | metadata: 32 | name: id-rsa 33 | namespace: ${NAMESPACE} 34 | stringData: 35 | demo.id_rsa: ${SSH_PRIVATE_KEY} 36 | - kind: ConfigMap 37 | apiVersion: v1 38 | metadata: 39 | name: install-scripts 40 | namespace: ${NAMESPACE} 41 | data: 42 | elasticsearch.sh: |- 43 | subscription-manager register --activationkey ${SUBSCRIPTION_KEY} --org ${SUBSCRIPTION_ORG} 44 | subscription-manager attach --auto 45 | cat << EOF > /etc/yum.repos.d/elastic.repo 46 | [elasticsearch] 47 | name=Elasticsearch repository for 8.x packages 48 | baseurl=https://artifacts.elastic.co/packages/8.x/yum 49 | gpgcheck=1 50 | gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch 51 | enabled=0 52 | autorefresh=1 53 | type=rpm-md 54 | EOF 55 | update-crypto-policies --set DEFAULT:SHA1 56 | rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch 57 | dnf install -y --enablerepo=elasticsearch elasticsearch-8.7.0 58 | update-crypto-policies --set DEFAULT 59 | systemctl daemon-reload 60 | mkdir -p /srv/elasticsearch/config 61 | mkdir -p /srv/elasticsearch/data 62 | chown elasticsearch:elasticsearch /srv/elasticsearch -R 63 | mkfs.xfs /dev/disk/by-id/virtio-datadisk 64 | config=$(ls -1 /dev/disk/by-id/virtio-00yaml) 65 | mount $config /srv/elasticsearch/config 66 | mount /dev/disk/by-id/virtio-datadisk /srv/elasticsearch/data 67 | echo "$config /srv/elasticsearch/config iso9660 defaults 0 0" >> /etc/fstab 68 | echo '/dev/disk/by-id/virtio-datadisk /srv/elasticsearch/data xfs defaults 0 0' >> /etc/fstab 69 | mv /etc/elasticsearch/elasticsearch.yml /etc/elasticsearch/elasticsearch.yml.org 70 | mv /etc/elasticsearch/jvm.options /etc/elasticsearch/jvm.options.org 71 | ln -sf /srv/elasticsearch/config/elasticsearch.conf /etc/elasticsearch/elasticsearch.yml 72 | ln -sf /srv/elasticsearch/config/jvm.options /etc/elasticsearch/jvm.options 73 | systemctl enable elasticsearch --now 74 | system.sh: |- 75 | cert=$(ls -1 /dev/disk/by-id/virtio-00cert) 76 | mount $cert /etc/cockpit/ws-certs.d/ 77 | if [ -f "/etc/cockpit/ws-certs.d/tls.crt" ]; then echo "$cert /etc/cockpit/ws-certs.d/ xfs defaults 0 0" >> /etc/fstab; 78 | else umount $cert; fi 79 | systemctl enable cockpit.socket --now 80 | immutable: false 81 | - kind: Service 82 | apiVersion: v1 83 | metadata: 84 | name: es-master-svc 85 | namespace: ${NAMESPACE} 86 | spec: 87 | ports: 88 | - name: elasticsearch 89 | protocol: TCP 90 | port: 9200 91 | targetPort: 9200 92 | selector: 93 | app: elasticsearch 94 | elasticsearch: master 95 | - kind: Route 96 | apiVersion: route.openshift.io/v1 97 | metadata: 98 | name: elasticsearch 99 | namespace: ${NAMESPACE} 100 | annotations: 101 | cert-utils-operator.redhat-cop.io/certs-from-secret: letsencrypt 102 | labels: 103 | app: elasticsearch 104 | role: api 105 | spec: 106 | host: elasticsearch.${BASEDOMAIN} 107 | to: 108 | kind: Service 109 | name: es-master-svc 110 | port: 111 | targetPort: elasticsearch 112 | tls: 113 | termination: edge 114 | insecureEdgeTerminationPolicy: Redirect 115 | wildcardPolicy: None 116 | - kind: Deployment 117 | apiVersion: apps/v1 118 | metadata: 119 | name: kibana 120 | namespace: ${NAMESPACE} 121 | annotations: 122 | app.openshift.io/connects-to: '[{"apiVersion":"apps/v1","kind":"Deployment","name":"coordinate"}]' 123 | labels: 124 | app.kubernetes.io/part-of: kibana 125 | app.openshift.io/runtime: elastic 126 | app.openshift.io/name: kibana 127 | spec: 128 | replicas: 1 129 | selector: 130 | matchLabels: 131 | app: kibana 132 | template: 133 | metadata: 134 | labels: 135 | app: kibana 136 | spec: 137 | # securityContext: 138 | # seccompProfile: 139 | # type: RuntimeDefault 140 | containers: 141 | - name: kibana 142 | image: docker.elastic.co/kibana/kibana:8.7.0 143 | ports: 144 | - containerPort: 5601 145 | # securityContext: 146 | # allowPrivilegeEscalation: false 147 | # runAsNonRoot: true 148 | # capabilities: 149 | # drop: 150 | # - ALL 151 | env: 152 | - name: ELASTICSEARCH_HOSTS 153 | value: http://coordinate:9200 154 | - name: SERVER_PUBLICBASEURL 155 | value: https://kibana-demo.${BASEDOMAIN} 156 | - name: SERVER_NAME 157 | value: kibana-demo.${BASEDOMAIN} 158 | resources: {} 159 | restartPolicy: Always 160 | - kind: Service 161 | apiVersion: v1 162 | metadata: 163 | name: kibana 164 | namespace: ${NAMESPACE} 165 | spec: 166 | ports: 167 | - name: ui 168 | port: 5601 169 | targetPort: 5601 170 | selector: 171 | app: kibana 172 | - kind: Route 173 | apiVersion: route.openshift.io/v1 174 | metadata: 175 | name: kibana 176 | namespace: ${NAMESPACE} 177 | labels: 178 | app: kibana 179 | annotations: 180 | cert-utils-operator.redhat-cop.io/certs-from-secret: letsencrypt 181 | spec: 182 | host: kibana.${BASEDOMAIN} 183 | to: 184 | kind: Service 185 | name: kibana 186 | port: 187 | targetPort: ui 188 | tls: 189 | termination: edge 190 | insecureEdgeTerminationPolicy: Redirect 191 | wildcardPolicy: None 192 | - kind: Service 193 | apiVersion: v1 194 | metadata: 195 | name: coordinate 196 | namespace: ${NAMESPACE} 197 | spec: 198 | ports: 199 | - name: elasticsearch 200 | port: 9200 201 | targetPort: 9200 202 | - name: elasticsearch-discovery 203 | port: 9300 204 | targetPort: 9300 205 | selector: 206 | app: elasticsearch 207 | elasticsearch: coordinate 208 | - kind: Deployment 209 | apiVersion: apps/v1 210 | metadata: 211 | name: coordinate 212 | namespace: ${NAMESPACE} 213 | annotations: 214 | app.openshift.io/connects-to: '[{"apiVersion":"kubevirt.io/v1","kind":"VirtualMachine","name":"es-master00"},{"apiVersion":"kubevirt.io/v1","kind":"VirtualMachine","name":"es-master01"},{"apiVersion":"kubevirt.io/v1","kind":"VirtualMachine","name":"es-master02"}]' 215 | labels: 216 | app.kubernetes.io/part-of: elasticsearch 217 | app.openshift.io/runtime: elastic 218 | app.openshift.io/name: coordinate 219 | spec: 220 | replicas: 1 221 | selector: 222 | matchLabels: 223 | app: elasticsearch 224 | elasticsearch: coordinate 225 | template: 226 | metadata: 227 | labels: 228 | app: elasticsearch 229 | elasticsearch: coordinate 230 | spec: 231 | # securityContext: 232 | # seccompProfile: 233 | # type: RuntimeDefault 234 | containers: 235 | - name: coordinate 236 | image: docker.elastic.co/elasticsearch/elasticsearch:8.7.0-amd64 237 | ports: 238 | - containerPort: 9200 239 | - containerPort: 9300 240 | # securityContext: 241 | # allowPrivilegeEscalation: false 242 | # runAsNonRoot: true 243 | # capabilities: 244 | # drop: 245 | # - ALL 246 | env: 247 | - name: ES_JAVA_OPTS 248 | value: -Xms8g -Xmx8g 249 | - name: node.name 250 | value: coordinate 251 | - name: cluster.name 252 | value: demo 253 | - name: xpack.security.enabled 254 | value: "false" 255 | - name: xpack.security.transport.ssl.enabled 256 | value: "false" 257 | - name: xpack.security.http.ssl.enabled 258 | value: "false" 259 | - name: discovery.seed_hosts 260 | value: es-master00,es-master01,es-master02 261 | - name: node.roles 262 | value: "[]" 263 | - name: network.publish_host 264 | value: coordinate.${NAMESPACE}.svc.cluster.local 265 | - name: network.host 266 | value: _site_ 267 | resources: {} 268 | restartPolicy: Always 269 | - kind: Route 270 | apiVersion: route.openshift.io/v1 271 | metadata: 272 | name: coordinate 273 | namespace: ${NAMESPACE} 274 | annotations: 275 | cert-utils-operator.redhat-cop.io/certs-from-secret: letsencrypt 276 | labels: 277 | app: elasticsearch 278 | role: coordinate 279 | spec: 280 | host: coordinate.${BASEDOMAIN} 281 | to: 282 | kind: Service 283 | name: coordinate 284 | port: 285 | targetPort: elasticsearch 286 | tls: 287 | termination: edge 288 | insecureEdgeTerminationPolicy: Redirect 289 | wildcardPolicy: None 290 | - kind: ConfigMap 291 | apiVersion: v1 292 | metadata: 293 | name: ubi9-install 294 | namespace: ${NAMESPACE} 295 | data: 296 | install.sh: |- 297 | mkdir /root/.ssh/ 298 | cp /mnt/demo.id_rsa /root/.ssh/id_rsa 299 | chmod 700 /root/.ssh/ 300 | chmod 0400 /root/.ssh/id_rsa 301 | yum install -y openssh-clients rsync 302 | /bin/sh 303 | immutable: false 304 | - kind: Pod 305 | apiVersion: v1 306 | metadata: 307 | name: ubi9 308 | annotations: 309 | app.openshift.io/connects-to: '[{"apiVersion":"apps/v1","kind":"Deployment","name":"kibana"}]' 310 | labels: 311 | app: ubi9 312 | app.kubernetes.io/part-of: utility 313 | app.openshift.io/runtime: redhat 314 | app.openshift.io/name: utility 315 | namespace: ${NAMESPACE} 316 | spec: 317 | volumes: 318 | - configMap: 319 | name: ubi9-install 320 | name: ubi9-install 321 | - secret: 322 | secretName: id-rsa 323 | name: id-rsa 324 | securityContext: 325 | seLinuxOptions: 326 | level: 's0:c31,c10' 327 | seccompProfile: 328 | type: "RuntimeDefault" 329 | containers: 330 | - name: ubi9 331 | image: 'registry.access.redhat.com/ubi9/ubi' 332 | command: ["/bin/sh"] 333 | args: ["/usr/local/bin/install.sh"] 334 | stdin: true 335 | tty: true 336 | volumeMounts: 337 | - name: ubi9-install 338 | readOnly: true 339 | mountPath: /usr/local/bin/install.sh 340 | subPath: install.sh 341 | - name: id-rsa 342 | readOnly: true 343 | mountPath: /mnt/ 344 | - kind: ImageStream 345 | apiVersion: image.openshift.io/v1 346 | metadata: 347 | name: data-generator-imagestream 348 | namespace: ${NAMESPACE} 349 | - kind: BuildConfig 350 | apiVersion: build.openshift.io/v1 351 | metadata: 352 | annotations: 353 | app.openshift.io/vcs-ref: '' 354 | app.openshift.io/vcs-uri: 'https://github.com/purefield/ocp-virt' 355 | openshift.io/generated-by: OpenShiftWebConsole 356 | name: data-generator-buildconfig 357 | namespace: ${NAMESPACE} 358 | labels: 359 | app: data-generator 360 | app.kubernetes.io/component: data-generator 361 | app.kubernetes.io/instance: data-generator 362 | app.kubernetes.io/name: data-generator 363 | app.kubernetes.io/part-of: application 364 | app.openshift.io/runtime: nodejs 365 | app.openshift.io/runtime-version: 18-ubi9-minimal 366 | spec: 367 | output: 368 | to: 369 | kind: ImageStreamTag 370 | name: 'data-generator-imagestream:latest' 371 | resources: {} 372 | successfulBuildsHistoryLimit: 2 373 | failedBuildsHistoryLimit: 2 374 | strategy: 375 | type: Source 376 | sourceStrategy: 377 | from: 378 | kind: ImageStreamTag 379 | namespace: openshift 380 | name: 'nodejs:18-ubi9-minimal' 381 | postCommit: {} 382 | source: 383 | type: Git 384 | git: 385 | uri: 'https://github.com/purefield/ocp-virt' 386 | contextDir: /generate-data 387 | triggers: 388 | - type: Generic 389 | generic: 390 | secretReference: 391 | name: data-generator-generic-webhook-secret 392 | - type: ConfigChange 393 | runPolicy: Serial 394 | - kind: Deployment 395 | apiVersion: apps/v1 396 | metadata: 397 | name: data-generator 398 | namespace: ${NAMESPACE} 399 | labels: 400 | app: data-generator 401 | app.kubernetes.io/component: data-generator 402 | app.kubernetes.io/instance: data-generator 403 | app.kubernetes.io/name: data-generator 404 | app.kubernetes.io/part-of: application 405 | app.openshift.io/runtime: nodejs 406 | app.openshift.io/runtime-version: 18-ubi9-minimal 407 | annotations: 408 | app.openshift.io/connects-to: '[{"apiVersion":"apps/v1","kind":"Deployment","name":"coordinate"}]' 409 | spec: 410 | replicas: 1 411 | selector: 412 | matchLabels: 413 | app: data-generator 414 | template: 415 | metadata: 416 | labels: 417 | app: data-generator 418 | deployment: data-generator 419 | spec: 420 | containers: 421 | - name: data-generator 422 | stdin: true 423 | tty: true 424 | image: >- 425 | image-registry.openshift-image-registry.svc:5000/${NAMESPACE}/data-generator-imagestream 426 | ports: 427 | - containerPort: 3000 428 | protocol: TCP 429 | env: 430 | - name: ES_NODE 431 | value: coordinate 432 | - name: ES_PORT 433 | value: '9200' 434 | - name: ES_INDEX 435 | value: generated 436 | - name: DATA_SIZE 437 | value: '5' 438 | - name: DATE_RATE 439 | value: '10' 440 | - name: DATA_BATCH 441 | value: '100' 442 | resources: {} 443 | imagePullPolicy: Always 444 | restartPolicy: Always 445 | terminationGracePeriodSeconds: 30 446 | dnsPolicy: ClusterFirst 447 | securityContext: {} 448 | schedulerName: default-scheduler 449 | strategy: 450 | type: RollingUpdate 451 | rollingUpdate: 452 | maxUnavailable: 50% 453 | maxSurge: 25% 454 | revisionHistoryLimit: 2 455 | progressDeadlineSeconds: 60 456 | - kind: Service 457 | apiVersion: v1 458 | metadata: 459 | name: data-generator 460 | namespace: ${NAMESPACE} 461 | spec: 462 | selector: 463 | app: data-generator 464 | ports: 465 | - protocol: TCP 466 | port: 3000 467 | targetPort: 3000 468 | - kind: Route 469 | apiVersion: route.openshift.io/v1 470 | metadata: 471 | name: data-generator 472 | namespace: ${NAMESPACE} 473 | labels: {} 474 | spec: 475 | host: data-generator.${BASEDOMAIN} 476 | to: 477 | kind: Service 478 | name: data-generator 479 | tls: 480 | termination: edge 481 | insecureEdgeTerminationPolicy: Redirect 482 | destinationCACertificate: '' 483 | port: 484 | targetPort: 3000 485 | - kind: ConfigMap 486 | apiVersion: v1 487 | metadata: 488 | name: windows-vm-sysprep 489 | namespace: ${NAMESPACE} 490 | data: 491 | Autounattend.xml: > 492 | 493 | 494 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 1 505 | true 506 | Primary 507 | 508 | 509 | 510 | 511 | true 512 | NTFS 513 | 514 | 1 515 | 1 516 | 517 | 518 | 0 519 | true 520 | 521 | 522 | 523 | 524 | 525 | 526 | /IMAGE/NAME 527 | Windows Server 2019 SERVERSTANDARD 528 | 529 | 530 | 531 | 0 532 | 1 533 | 534 | 535 | 536 | 537 | true 538 | Administrator 539 | My Organization 540 | 541 | false 542 | 543 | 544 | 545 | en-US 546 | 547 | en-US 548 | en-US 549 | en-US 550 | en-US 551 | 552 | 553 | 554 | 555 | false 556 | 557 | 558 | 559 | 560 | 561 | 562 | R3dh4t1! 563 | true</PlainText> 564 | </Password> 565 | <Enabled>true</Enabled> 566 | <LogonCount>999</LogonCount> 567 | <Username>Administrator</Username> 568 | </AutoLogon> 569 | <OOBE> 570 | <HideEULAPage>true</HideEULAPage> 571 | <HideLocalAccountScreen>true</HideLocalAccountScreen> 572 | <HideOnlineAccountScreens>true</HideOnlineAccountScreens> 573 | <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> 574 | <NetworkLocation>Work</NetworkLocation> 575 | <ProtectYourPC>3</ProtectYourPC> 576 | <SkipMachineOOBE>true</SkipMachineOOBE> 577 | </OOBE> 578 | <UserAccounts> 579 | <LocalAccounts> 580 | <LocalAccount wcm:action="add"> 581 | <Description>Local Administrator Account</Description> 582 | <DisplayName>Administrator</DisplayName> 583 | <Group>Administrators</Group> 584 | <Name>Administrator</Name> 585 | </LocalAccount> 586 | </LocalAccounts> 587 | </UserAccounts> 588 | <TimeZone>Eastern Standard Time</TimeZone> 589 | </component> 590 | </settings> 591 | <settings pass="oobeSystem"> 592 | <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> 593 | <InputLocale>en-US</InputLocale> 594 | <SystemLocale>en-US</SystemLocale> 595 | <UILanguage>en-US</UILanguage> 596 | <UserLocale>en-US</UserLocale> 597 | </component> 598 | <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> 599 | <AutoLogon> 600 | <Password> 601 | <Value>R3dh4t1!</Value> 602 | <PlainText>true</PlainText> 603 | </Password> 604 | <Enabled>true</Enabled> 605 | <LogonCount>999</LogonCount> 606 | <Username>Administrator</Username> 607 | </AutoLogon> 608 | <OOBE> 609 | <HideEULAPage>true</HideEULAPage> 610 | <HideLocalAccountScreen>true</HideLocalAccountScreen> 611 | <HideOnlineAccountScreens>true</HideOnlineAccountScreens> 612 | <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> 613 | <NetworkLocation>Work</NetworkLocation> 614 | <ProtectYourPC>3</ProtectYourPC> 615 | <SkipMachineOOBE>true</SkipMachineOOBE> 616 | </OOBE> 617 | <UserAccounts> 618 | <LocalAccounts> 619 | <LocalAccount wcm:action="add"> 620 | <Description>Local Administrator Account</Description> 621 | <DisplayName>Administrator</DisplayName> 622 | <Group>Administrators</Group> 623 | <Name>Administrator</Name> 624 | </LocalAccount> 625 | </LocalAccounts> 626 | </UserAccounts> 627 | <TimeZone>Eastern Standard Time</TimeZone> 628 | </component> 629 | </settings> 630 | </unattend> 631 | - kind: VirtualMachine 632 | apiVersion: kubevirt.io/v1 633 | metadata: 634 | name: windows2019 635 | namespace: ${NAMESPACE} 636 | labels: 637 | app: windows 638 | vm.kubevirt.io/template: windows2k19-server-medium 639 | vm.kubevirt.io/template.namespace: openshift 640 | app.kubernetes.io/part-of: application 641 | annotations: 642 | app.openshift.io/connects-to: '[{"apiVersion":"apps/v1","kind":"Deployment","name":"coordinate"}]' 643 | spec: 644 | dataVolumeTemplates: 645 | - apiVersion: cdi.kubevirt.io/v1beta1 646 | kind: DataVolume 647 | metadata: 648 | annotations: 649 | cdi.kubevirt.io/storage.bind.immediate.requested: 'true' 650 | creationTimestamp: null 651 | name: windows 652 | spec: 653 | source: 654 | blank: {} 655 | storage: 656 | resources: 657 | requests: 658 | storage: 60Gi 659 | - metadata: 660 | creationTimestamp: null 661 | name: windows-installation-cdrom 662 | spec: 663 | source: 664 | pvc: 665 | name: windows-2019 666 | namespace: openshift-virtualization-os-images 667 | storage: 668 | resources: 669 | requests: 670 | storage: 5Gi 671 | running: true 672 | template: 673 | metadata: 674 | annotations: 675 | vm.kubevirt.io/flavor: medium 676 | vm.kubevirt.io/os: windows2k19 677 | vm.kubevirt.io/workload: server 678 | creationTimestamp: null 679 | labels: 680 | kubevirt.io/domain: windows 681 | kubevirt.io/size: medium 682 | spec: 683 | architecture: amd64 684 | domain: 685 | clock: 686 | timer: 687 | hpet: 688 | present: false 689 | hyperv: {} 690 | pit: 691 | tickPolicy: delay 692 | rtc: 693 | tickPolicy: catchup 694 | utc: {} 695 | cpu: 696 | cores: 1 697 | sockets: 1 698 | threads: 1 699 | devices: 700 | disks: 701 | - bootOrder: 2 702 | disk: 703 | bus: sata 704 | name: rootdisk 705 | - bootOrder: 1 706 | cdrom: 707 | bus: sata 708 | name: installation-cdrom 709 | - cdrom: 710 | bus: sata 711 | name: windows-drivers-disk 712 | - cdrom: 713 | bus: sata 714 | name: sysprep 715 | inputs: 716 | - bus: usb 717 | name: tablet 718 | type: tablet 719 | interfaces: 720 | - masquerade: {} 721 | model: e1000e 722 | name: default 723 | memory: 724 | guest: 4Gi 725 | networks: 726 | - name: default 727 | pod: {} 728 | terminationGracePeriodSeconds: 3600 729 | volumes: 730 | - dataVolume: 731 | name: windows 732 | name: rootdisk 733 | - dataVolume: 734 | name: windows-installation-cdrom 735 | name: installation-cdrom 736 | - containerDisk: 737 | image: >- 738 | registry.redhat.io/container-native-virtualization/virtio-win-rhel9 739 | name: windows-drivers-disk 740 | - name: sysprep 741 | sysprep: 742 | configMap: 743 | name: windows-vm-sysprep 744 | - kind: Job 745 | apiVersion: batch/v1 746 | metadata: 747 | name: elasticsearch-init-job 748 | namespace: ${NAMESPACE} 749 | spec: 750 | ttlSecondsAfterFinished: 30 751 | template: 752 | metadata: 753 | name: elasticsearch-init 754 | spec: 755 | restartPolicy: Never 756 | containers: 757 | - name: es-seed 758 | image: curlimages/curl:latest # Using a lightweight Curl image 759 | command: 760 | - "/bin/sh" 761 | - "-c" 762 | - | 763 | echo "Waiting for Elasticsearch to be ready..." 764 | until curl -LfX GET -so /dev/null http://coordinate:9200/_cluster/health; do 765 | echo "Not ready yet, sleeping for 5 sec" 766 | sleep 5; 767 | done 768 | 769 | if curl -Lfso /dev/null http://coordinate:9200/_index_template/default_template; then 770 | echo "Default Template already setup, exiting" 771 | exit 0 772 | fi 773 | 774 | echo "Creating the default index template..." 775 | curl -sX PUT "http://coordinate:9200/_index_template/default_template" -H "Content-Type: application/json" -d ' 776 | { 777 | "index_patterns": ["generated"], 778 | "template": { 779 | "settings": { 780 | "number_of_shards": '${SHARDS}', 781 | "number_of_replicas": 1 782 | }, 783 | "mappings": { 784 | "properties": { 785 | "timestamp": { "type": "date" }, 786 | "message": { "type": "text" } 787 | } 788 | } 789 | } 790 | }' 791 | echo; echo; 792 | 793 | echo "Deleting the 'generated' index..." 794 | curl -sX DELETE "http://coordinate:9200/generated" 795 | echo; echo; 796 | 797 | echo "Elasticsearch index template setup complete." 798 | echo 799 | 800 | echo "Waiting for Kibana to be ready..." 801 | until curl -LfX GET -so /dev/null http://kibana:5601/api/status; do 802 | echo "Not ready yet, sleeping for 5 sec" 803 | sleep 5; 804 | done 805 | 806 | echo "Create Kibana data view" 807 | curl -sX POST "http://kibana:5601/api/data_views/data_view" -H "kbn-xsrf: true" -H "Content-Type: application/json" -d ' 808 | { 809 | "data_view": { 810 | "name":"demo", 811 | "title": "generated*", 812 | "timeFieldName": "timestamp" 813 | } 814 | }' 815 | echo; echo; 816 | 817 | echo "Kibana view setup complete." 818 | 819 | parameters: 820 | - name: NAMESPACE 821 | description: Namespace to use 822 | required: true 823 | - name: BASEDOMAIN 824 | description: Base Domain for externally exposed services 825 | required: true 826 | - name: SHARDS 827 | description: Default number of shards per index 828 | value: "3" 829 | - name: SUBSCRIPTION_ORG 830 | description: Red Hat Subscription ORG string 831 | required: true 832 | - name: SUBSCRIPTION_KEY 833 | description: Red Hat Subscription KEY string 834 | required: true 835 | - name: SSH_PUBLIC_KEY 836 | description: SSH Public Key 837 | required: true 838 | - name: SSH_PRIVATE_KEY 839 | description: SSH Private Key 840 | required: true 841 | labels: 842 | demo: ocp-virt 843 | -------------------------------------------------------------------------------- /vm.template.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: template.openshift.io/v1 3 | kind: Template 4 | metadata: 5 | name: ocp-virt-demo-vms-template 6 | annotations: 7 | iconClass: "icon-elastic" 8 | tags: "elasticsearch,virtualization" 9 | template.openshift.io/bindable: "true" 10 | openshift.io/display-name: OpenShift Virtualization Demo - 2/2 ElasticSearch VM 11 | openshift.io/documentation-url: https://github.com/purefield/ocp-virt 12 | description: "OpenShift Virtualization and Container Application Stack Demo. 13 | This template provides virtual machines running Elasticsearch. 14 | 15 | Bringing VMs and Containers together. 16 | 17 | It includes Elasticsearch VMs, Kibana, data generation application and coordinator containers." 18 | openshift.io/provider-display-name: Daniel Schimpfoessl 19 | objects: 20 | - kind: ConfigMap 21 | apiVersion: v1 22 | metadata: 23 | name: ${VMNAME}-conf 24 | namespace: ${NAMESPACE} 25 | data: 26 | elasticsearch.conf: |- 27 | cluster.name: demo 28 | node.name: ${VMNAME} 29 | node.roles: [ master, data ] 30 | network.host: _site_ 31 | network.publish_host: ${VMNAME}.${NAMESPACE}.svc.cluster.local 32 | discovery.seed_hosts: ["es-master00","es-master01","es-master02"] 33 | cluster.initial_master_nodes: ["es-master00","es-master01","es-master02"] 34 | path.data: /srv/elasticsearch 35 | path.logs: /var/log/elasticsearch 36 | xpack.security.enabled: false 37 | xpack.security.transport.ssl.enabled: false 38 | xpack.security.http.ssl.enabled: false 39 | jvm.options: |- 40 | -Xms4096m 41 | -Xmx4096m 42 | -XX:+DisableExplicitGC 43 | -XX:+AlwaysPreTouch 44 | -server 45 | -Djava.awt.headless=true 46 | -Dfile.encoding=UTF-8 47 | -Djna.nosys=true 48 | -Djdk.io.permissionsUseCanonicalPath=true 49 | -Dio.netty.noUnsafe=true 50 | -Dio.netty.noKeySetOptimization=true 51 | -Dlog4j.shutdownHookEnabled=false 52 | -Dlog4j2.disable.jmx=true 53 | -Dlog4j.skipJansi=true 54 | -XX:+HeapDumpOnOutOfMemoryError 55 | immutable: false 56 | - kind: Service 57 | apiVersion: v1 58 | metadata: 59 | name: ${VMNAME} 60 | namespace: ${NAMESPACE} 61 | annotations: 62 | service.beta.openshift.io/serving-cert-secret-name: ssl-cert-cockpit-${VMNAME} 63 | spec: 64 | selector: 65 | node: ${VMNAME} 66 | ports: 67 | - name: elasticsearch 68 | protocol: TCP 69 | port: 9200 70 | targetPort: 9200 71 | - name: elasticsearch-discovery 72 | protocol: TCP 73 | port: 9300 74 | targetPort: 9300 75 | - name: ssh 76 | protocol: TCP 77 | port: 22 78 | targetPort: 22 79 | - name: cockpit 80 | protocol: TCP 81 | port: 9090 82 | targetPort: 9090 83 | - kind: VirtualMachine 84 | apiVersion: kubevirt.io/v1 85 | metadata: 86 | name: ${VMNAME} 87 | namespace: ${NAMESPACE} 88 | labels: 89 | app.kubernetes.io/part-of: elasticsearch 90 | spec: 91 | dataVolumeTemplates: 92 | - metadata: 93 | creationTimestamp: null 94 | name: ${VMNAME}-data 95 | namespace: ${NAMESPACE} 96 | spec: 97 | preallocation: false 98 | source: 99 | blank: {} 100 | storage: 101 | resources: 102 | requests: 103 | storage: 80Gi 104 | - metadata: 105 | creationTimestamp: null 106 | name: ${VMNAME} 107 | namespace: ${NAMESPACE} 108 | spec: 109 | preallocation: false 110 | sourceRef: 111 | kind: DataSource 112 | name: rhel9 113 | namespace: openshift-virtualization-os-images 114 | storage: 115 | resources: 116 | requests: 117 | storage: 30Gi 118 | volumeMode: Block 119 | running: true 120 | template: 121 | metadata: 122 | annotations: 123 | vm.kubevirt.io/os: rhel9 124 | vm.kubevirt.io/workload: server 125 | labels: 126 | app: elasticsearch 127 | node: ${VMNAME} 128 | elasticsearch: master 129 | spec: 130 | readinessProbe: 131 | httpGet: 132 | port: 9200 133 | path: / 134 | initialDelaySeconds: 90 135 | timeoutSeconds: 3 136 | affinity: 137 | podAntiAffinity: 138 | preferredDuringSchedulingIgnoredDuringExecution: 139 | - weight: 100 140 | podAffinityTerm: 141 | labelSelector: 142 | matchExpressions: 143 | - key: app 144 | operator: In 145 | values: 146 | - elasticsearch 147 | topologyKey: "kubernetes.io/hostname" 148 | domain: 149 | cpu: 150 | cores: 4 151 | sockets: 1 152 | threads: 2 153 | devices: 154 | disks: 155 | - bootOrder: 1 156 | disk: 157 | bus: virtio 158 | name: rootdisk 159 | - disk: 160 | bus: virtio 161 | name: cloudinitdisk 162 | - disk: 163 | bus: virtio 164 | name: elasticsearch-yaml 165 | serial: 00yaml 166 | - disk: 167 | bus: virtio 168 | name: install-scripts 169 | serial: 0000sh 170 | - disk: 171 | bus: virtio 172 | name: ssl-cert-cockpit 173 | serial: 00cert 174 | - disk: 175 | bus: virtio 176 | name: data 177 | serial: datadisk 178 | interfaces: 179 | - masquerade: {} 180 | model: virtio 181 | name: default 182 | resources: 183 | overcommitGuestOverhead: true 184 | requests: 185 | memory: 4Gi 186 | memory: 187 | guest: 8Gi 188 | evictionStrategy: LiveMigrate 189 | networks: 190 | - name: default 191 | pod: {} 192 | terminationGracePeriodSeconds: 180 193 | volumes: 194 | - name: rootdisk 195 | dataVolume: 196 | name: ${VMNAME} 197 | - name: data 198 | dataVolume: 199 | name: ${VMNAME}-data 200 | - name: cloudinitdisk 201 | cloudInitNoCloud: 202 | userData: |- 203 | #cloud-config 204 | hostname: ${VMNAME} 205 | runcmd: 206 | - mount /dev/disk/by-id/virtio-0000sh /mnt/ 207 | - bash /mnt/system.sh 208 | - bash /mnt/elasticsearch.sh 209 | - umount /mnt 210 | user: elasticsearch 211 | password: redhat 212 | chpasswd: { expire: False } 213 | ssh_authorized_keys: 214 | - ${SSH_PUBLIC_KEY} 215 | - name: elasticsearch-yaml 216 | configMap: 217 | name: ${VMNAME}-conf 218 | - name: install-scripts 219 | configMap: 220 | name: install-scripts 221 | - name: ssl-cert-cockpit 222 | secret: 223 | # secretName: ssl-cert-cockpit-${VMNAME} 224 | secretName: wildcard-cert 225 | - kind: Route 226 | apiVersion: route.openshift.io/v1 227 | metadata: 228 | name: ${VMNAME} 229 | namespace: ${NAMESPACE} 230 | spec: 231 | host: ${VMNAME}.${BASEDOMAIN} 232 | to: 233 | kind: Service 234 | name: ${VMNAME} 235 | port: 236 | targetPort: cockpit 237 | tls: 238 | termination: passthrough 239 | insecureEdgeTerminationPolicy: Redirect 240 | wildcardPolicy: None 241 | parameters: 242 | - name: VMNAME 243 | description: Virtual Machine Name 244 | required: true 245 | - name: NAMESPACE 246 | description: Namespace to use 247 | required: true 248 | - name: BASEDOMAIN 249 | description: Base Domain for externally exposed services 250 | required: true 251 | - name: SSH_PUBLIC_KEY 252 | description: SSH Public Key 253 | required: true 254 | labels: 255 | demo: ocp-virt 256 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | vm.yaml 2 | -------------------------------------------------------------------------------- /windows/autounattended.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?> 2 | <unattend xmlns="urn:schemas-microsoft-com:unattend" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:schemas-microsoft-com:unattend"> 3 | <settings pass="windowsPE"> 4 | <component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> 5 | <DiskConfiguration> 6 | <Disk wcm:action="add"> 7 | <CreatePartitions> 8 | <CreatePartition wcm:action="add"> 9 | <Order>1</Order> 10 | <Extend>true</Extend> 11 | <Type>Primary</Type> 12 | </CreatePartition> 13 | </CreatePartitions> 14 | <ModifyPartitions> 15 | <ModifyPartition wcm:action="add"> 16 | <Active>true</Active> 17 | <Format>NTFS</Format> 18 | <Label>System</Label> 19 | <Order>1</Order> 20 | <PartitionID>1</PartitionID> 21 | </ModifyPartition> 22 | </ModifyPartitions> 23 | <DiskID>0</DiskID> 24 | <WillWipeDisk>true</WillWipeDisk> 25 | </Disk> 26 | </DiskConfiguration> 27 | <ImageInstall> 28 | <OSImage> 29 | <InstallFrom> 30 | <MetaData wcm:action="add"> 31 | <Key>/IMAGE/NAME</Key> 32 | <Value>Windows Server 2019 SERVERSTANDARD</Value> 33 | </MetaData> 34 | </InstallFrom> 35 | <InstallTo> 36 | <DiskID>0</DiskID> 37 | <PartitionID>1</PartitionID> 38 | </InstallTo> 39 | </OSImage> 40 | </ImageInstall> 41 | <UserData> 42 | <AcceptEula>true</AcceptEula> 43 | <FullName>Administrator</FullName> 44 | <Organization>My Organization</Organization> 45 | </UserData> 46 | <EnableFirewall>false</EnableFirewall> 47 | </component> 48 | <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> 49 | <SetupUILanguage> 50 | <UILanguage>en-US</UILanguage> 51 | </SetupUILanguage> 52 | <InputLocale>en-US</InputLocale> 53 | <SystemLocale>en-US</SystemLocale> 54 | <UILanguage>en-US</UILanguage> 55 | <UserLocale>en-US</UserLocale> 56 | </component> 57 | </settings> 58 | <settings pass="offlineServicing"> 59 | <component name="Microsoft-Windows-LUA-Settings" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> 60 | <EnableLUA>false</EnableLUA> 61 | </component> 62 | </settings> 63 | <settings pass="specialize"> 64 | <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> 65 | <AutoLogon> 66 | <Password> 67 | <Value>R3dh4t1!</Value> 68 | <PlainText>true</PlainText> 69 | </Password> 70 | <Enabled>true</Enabled> 71 | <LogonCount>999</LogonCount> 72 | <Username>Administrator</Username> 73 | </AutoLogon> 74 | <OOBE> 75 | <HideEULAPage>true</HideEULAPage> 76 | <HideLocalAccountScreen>true</HideLocalAccountScreen> 77 | <HideOnlineAccountScreens>true</HideOnlineAccountScreens> 78 | <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> 79 | <NetworkLocation>Work</NetworkLocation> 80 | <ProtectYourPC>3</ProtectYourPC> 81 | <SkipMachineOOBE>true</SkipMachineOOBE> 82 | </OOBE> 83 | <UserAccounts> 84 | <LocalAccounts> 85 | <LocalAccount wcm:action="add"> 86 | <Description>Local Administrator Account</Description> 87 | <DisplayName>Administrator</DisplayName> 88 | <Group>Administrators</Group> 89 | <Name>Administrator</Name> 90 | </LocalAccount> 91 | </LocalAccounts> 92 | </UserAccounts> 93 | <TimeZone>Eastern Standard Time</TimeZone> 94 | </component> 95 | </settings> 96 | <settings pass="oobeSystem"> 97 | <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> 98 | <InputLocale>en-US</InputLocale> 99 | <SystemLocale>en-US</SystemLocale> 100 | <UILanguage>en-US</UILanguage> 101 | <UserLocale>en-US</UserLocale> 102 | </component> 103 | <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> 104 | <AutoLogon> 105 | <Password> 106 | <Value>R3dh4t1!</Value> 107 | <PlainText>true</PlainText> 108 | </Password> 109 | <Enabled>true</Enabled> 110 | <LogonCount>999</LogonCount> 111 | <Username>Administrator</Username> 112 | </AutoLogon> 113 | <OOBE> 114 | <HideEULAPage>true</HideEULAPage> 115 | <HideLocalAccountScreen>true</HideLocalAccountScreen> 116 | <HideOnlineAccountScreens>true</HideOnlineAccountScreens> 117 | <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> 118 | <NetworkLocation>Work</NetworkLocation> 119 | <ProtectYourPC>3</ProtectYourPC> 120 | <SkipMachineOOBE>true</SkipMachineOOBE> 121 | </OOBE> 122 | <UserAccounts> 123 | <LocalAccounts> 124 | <LocalAccount wcm:action="add"> 125 | <Description>Local Administrator Account</Description> 126 | <DisplayName>Administrator</DisplayName> 127 | <Group>Administrators</Group> 128 | <Name>Administrator</Name> 129 | </LocalAccount> 130 | </LocalAccounts> 131 | </UserAccounts> 132 | <TimeZone>Eastern Standard Time</TimeZone> 133 | </component> 134 | </settings> 135 | </unattend> 136 | -------------------------------------------------------------------------------- /windows/vm.yaml.template: -------------------------------------------------------------------------------- 1 | --- 2 | kind: ConfigMap 3 | apiVersion: v1 4 | metadata: 5 | name: windows-vm-sysprep 6 | namespace: {{ namespace }} 7 | data: 8 | Autounattend.xml: > 9 | <?xml version="1.0" encoding="utf-8"?> 10 | 11 | <unattend xmlns="urn:schemas-microsoft-com:unattend" 12 | xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" 13 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 14 | xsi:schemaLocation="urn:schemas-microsoft-com:unattend"> 15 | <settings pass="windowsPE"> 16 | <component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> 17 | <DiskConfiguration> 18 | <Disk wcm:action="add"> 19 | <CreatePartitions> 20 | <CreatePartition wcm:action="add"> 21 | <Order>1</Order> 22 | <Extend>true</Extend> 23 | <Type>Primary</Type> 24 | </CreatePartition> 25 | </CreatePartitions> 26 | <ModifyPartitions> 27 | <ModifyPartition wcm:action="add"> 28 | <Active>true</Active> 29 | <Format>NTFS</Format> 30 | <Label>System</Label> 31 | <Order>1</Order> 32 | <PartitionID>1</PartitionID> 33 | </ModifyPartition> 34 | </ModifyPartitions> 35 | <DiskID>0</DiskID> 36 | <WillWipeDisk>true</WillWipeDisk> 37 | </Disk> 38 | </DiskConfiguration> 39 | <ImageInstall> 40 | <OSImage> 41 | <InstallFrom> 42 | <MetaData wcm:action="add"> 43 | <Key>/IMAGE/NAME</Key> 44 | <Value>Windows Server 2019 SERVERSTANDARD</Value> 45 | </MetaData> 46 | </InstallFrom> 47 | <InstallTo> 48 | <DiskID>0</DiskID> 49 | <PartitionID>1</PartitionID> 50 | </InstallTo> 51 | </OSImage> 52 | </ImageInstall> 53 | <UserData> 54 | <AcceptEula>true</AcceptEula> 55 | <FullName>Administrator</FullName> 56 | <Organization>My Organization</Organization> 57 | </UserData> 58 | <EnableFirewall>false</EnableFirewall> 59 | </component> 60 | <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> 61 | <SetupUILanguage> 62 | <UILanguage>en-US</UILanguage> 63 | </SetupUILanguage> 64 | <InputLocale>en-US</InputLocale> 65 | <SystemLocale>en-US</SystemLocale> 66 | <UILanguage>en-US</UILanguage> 67 | <UserLocale>en-US</UserLocale> 68 | </component> 69 | </settings> 70 | <settings pass="offlineServicing"> 71 | <component name="Microsoft-Windows-LUA-Settings" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> 72 | <EnableLUA>false</EnableLUA> 73 | </component> 74 | </settings> 75 | <settings pass="specialize"> 76 | <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> 77 | <AutoLogon> 78 | <Password> 79 | <Value>R3dh4t1!</Value> 80 | <PlainText>true</PlainText> 81 | </Password> 82 | <Enabled>true</Enabled> 83 | <LogonCount>999</LogonCount> 84 | <Username>Administrator</Username> 85 | </AutoLogon> 86 | <OOBE> 87 | <HideEULAPage>true</HideEULAPage> 88 | <HideLocalAccountScreen>true</HideLocalAccountScreen> 89 | <HideOnlineAccountScreens>true</HideOnlineAccountScreens> 90 | <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> 91 | <NetworkLocation>Work</NetworkLocation> 92 | <ProtectYourPC>3</ProtectYourPC> 93 | <SkipMachineOOBE>true</SkipMachineOOBE> 94 | </OOBE> 95 | <UserAccounts> 96 | <LocalAccounts> 97 | <LocalAccount wcm:action="add"> 98 | <Description>Local Administrator Account</Description> 99 | <DisplayName>Administrator</DisplayName> 100 | <Group>Administrators</Group> 101 | <Name>Administrator</Name> 102 | </LocalAccount> 103 | </LocalAccounts> 104 | </UserAccounts> 105 | <TimeZone>Eastern Standard Time</TimeZone> 106 | </component> 107 | </settings> 108 | <settings pass="oobeSystem"> 109 | <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> 110 | <InputLocale>en-US</InputLocale> 111 | <SystemLocale>en-US</SystemLocale> 112 | <UILanguage>en-US</UILanguage> 113 | <UserLocale>en-US</UserLocale> 114 | </component> 115 | <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> 116 | <AutoLogon> 117 | <Password> 118 | <Value>R3dh4t1!</Value> 119 | <PlainText>true</PlainText> 120 | </Password> 121 | <Enabled>true</Enabled> 122 | <LogonCount>999</LogonCount> 123 | <Username>Administrator</Username> 124 | </AutoLogon> 125 | <OOBE> 126 | <HideEULAPage>true</HideEULAPage> 127 | <HideLocalAccountScreen>true</HideLocalAccountScreen> 128 | <HideOnlineAccountScreens>true</HideOnlineAccountScreens> 129 | <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> 130 | <NetworkLocation>Work</NetworkLocation> 131 | <ProtectYourPC>3</ProtectYourPC> 132 | <SkipMachineOOBE>true</SkipMachineOOBE> 133 | </OOBE> 134 | <UserAccounts> 135 | <LocalAccounts> 136 | <LocalAccount wcm:action="add"> 137 | <Description>Local Administrator Account</Description> 138 | <DisplayName>Administrator</DisplayName> 139 | <Group>Administrators</Group> 140 | <Name>Administrator</Name> 141 | </LocalAccount> 142 | </LocalAccounts> 143 | </UserAccounts> 144 | <TimeZone>Eastern Standard Time</TimeZone> 145 | </component> 146 | </settings> 147 | </unattend> 148 | --- 149 | apiVersion: kubevirt.io/v1 150 | kind: VirtualMachine 151 | metadata: 152 | name: windows2019 153 | namespace: {{ namespace }} 154 | labels: 155 | app: windows 156 | vm.kubevirt.io/template: windows2k19-server-medium 157 | vm.kubevirt.io/template.namespace: openshift 158 | app.kubernetes.io/part-of: application 159 | annotations: 160 | app.openshift.io/connects-to: '[{"apiVersion":"apps/v1","kind":"Deployment","name":"coordinate"}]' 161 | spec: 162 | dataVolumeTemplates: 163 | - apiVersion: cdi.kubevirt.io/v1beta1 164 | kind: DataVolume 165 | metadata: 166 | annotations: 167 | cdi.kubevirt.io/storage.bind.immediate.requested: 'true' 168 | creationTimestamp: null 169 | name: windows 170 | spec: 171 | source: 172 | blank: {} 173 | storage: 174 | resources: 175 | requests: 176 | storage: 60Gi 177 | - metadata: 178 | creationTimestamp: null 179 | name: windows-installation-cdrom 180 | spec: 181 | source: 182 | pvc: 183 | name: windows-2019 184 | namespace: openshift-virtualization-os-images 185 | storage: 186 | resources: 187 | requests: 188 | storage: 5Gi 189 | running: true 190 | template: 191 | metadata: 192 | annotations: 193 | vm.kubevirt.io/flavor: medium 194 | vm.kubevirt.io/os: windows2k19 195 | vm.kubevirt.io/workload: server 196 | creationTimestamp: null 197 | labels: 198 | kubevirt.io/domain: windows 199 | kubevirt.io/size: medium 200 | spec: 201 | architecture: amd64 202 | domain: 203 | clock: 204 | timer: 205 | hpet: 206 | present: false 207 | hyperv: {} 208 | pit: 209 | tickPolicy: delay 210 | rtc: 211 | tickPolicy: catchup 212 | utc: {} 213 | cpu: 214 | cores: 1 215 | sockets: 1 216 | threads: 1 217 | devices: 218 | disks: 219 | - bootOrder: 2 220 | disk: 221 | bus: sata 222 | name: rootdisk 223 | - bootOrder: 1 224 | cdrom: 225 | bus: sata 226 | name: installation-cdrom 227 | - cdrom: 228 | bus: sata 229 | name: windows-drivers-disk 230 | - cdrom: 231 | bus: sata 232 | name: sysprep 233 | inputs: 234 | - bus: usb 235 | name: tablet 236 | type: tablet 237 | interfaces: 238 | - masquerade: {} 239 | model: e1000e 240 | name: default 241 | memory: 242 | guest: 4Gi 243 | networks: 244 | - name: default 245 | pod: {} 246 | terminationGracePeriodSeconds: 3600 247 | volumes: 248 | - dataVolume: 249 | name: windows 250 | name: rootdisk 251 | - dataVolume: 252 | name: windows-installation-cdrom 253 | name: installation-cdrom 254 | - containerDisk: 255 | image: >- 256 | registry.redhat.io/container-native-virtualization/virtio-win-rhel9 257 | name: windows-drivers-disk 258 | - name: sysprep 259 | sysprep: 260 | configMap: 261 | name: windows-vm-sysprep 262 | -------------------------------------------------------------------------------- /windows/windows2019.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: cdi.kubevirt.io/v1beta1 3 | kind: DataVolume 4 | metadata: 5 | name: windows-2019 6 | labels: 7 | instancetype.kubevirt.io/default-instancetype: u1.medium 8 | instancetype.kubevirt.io/default-preference: windows 9 | namespace: openshift-virtualization-os-images 10 | spec: 11 | source: 12 | http: 13 | url: 'https://www.opentlc.com/download/ocp4_virt_foundations/Windows2019.iso' 14 | storage: 15 | resources: 16 | requests: 17 | storage: 5Gi 18 | 19 | --------------------------------------------------------------------------------