Move pages.
4
webpages/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
_site
|
||||
Gemfile.lock
|
||||
.bundle
|
||||
.jekyll-cache
|
||||
5
webpages/Gemfile
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
source 'https://rubygems.org'
|
||||
# gem 'github-pages', group: :jekyll_plugins
|
||||
gem "jekyll", "~> 4.0"
|
||||
gem "jekyll-seo-tag"
|
||||
gem 'webrick', '~> 1.3', '>= 1.3.1'
|
||||
10
webpages/_config.yml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
plugins:
|
||||
- jekyll-seo-tag
|
||||
|
||||
author: Michael N. Lipp
|
||||
|
||||
logo: VM-Operator.svg
|
||||
|
||||
tagline: VM-Operator by mnlipp
|
||||
|
||||
description: A Kubernetes operator for running virtual machines (notably Qemu VMs) as pods.
|
||||
23
webpages/_includes/matomo.html
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<!-- Matomo anonymous, no cookies (https://matomo.org/blog/2018/04/how-to-not-process-any-personal-data-with-matomo-and-what-it-means-for-you/) -->
|
||||
<script type="text/javascript">
|
||||
var _paq = _paq || [];
|
||||
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
||||
_paq.push(["setDocumentTitle", document.domain + "/" + document.title]);
|
||||
_paq.push(["setCookieDomain", "*.mnlipp.github.io"]);
|
||||
_paq.push(["setDomains", ["*.mnlipp.github.io"]]);
|
||||
_paq.push(['disableCookies']);
|
||||
_paq.push(['trackPageView']);
|
||||
_paq.push(['enableLinkTracking']);
|
||||
(function() {
|
||||
var u="//piwik.mnl.de/";
|
||||
_paq.push(['setTrackerUrl', u+'piwik.php']);
|
||||
_paq.push(['setSiteId', '14']);
|
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
|
||||
})();
|
||||
</script>
|
||||
<noscript>
|
||||
<img src="https://piwik.mnl.de/piwik.php?idsite=14&rec=1&action_name=JGrapes" style="border:0" alt="" />
|
||||
</noscript>
|
||||
<!-- End Matomo Code -->
|
||||
|
||||
96
webpages/_includes/toc.html
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
{% capture tocWorkspace %}
|
||||
{% comment %}
|
||||
Version 1.0.10
|
||||
https://github.com/allejo/jekyll-toc
|
||||
|
||||
"...like all things liquid - where there's a will, and ~36 hours to spare, there's usually a/some way" ~jaybe
|
||||
|
||||
Usage:
|
||||
{% include toc.html html=content sanitize=true class="inline_toc" id="my_toc" h_min=2 h_max=3 %}
|
||||
|
||||
Parameters:
|
||||
* html (string) - the HTML of compiled markdown generated by kramdown in Jekyll
|
||||
|
||||
Optional Parameters:
|
||||
* sanitize (bool) : false - when set to true, the headers will be stripped of any HTML in the TOC
|
||||
* class (string) : '' - a CSS class assigned to the TOC
|
||||
* id (string) : '' - an ID to assigned to the TOC
|
||||
* h_min (int) : 1 - the minimum TOC header level to use; any header lower than this value will be ignored
|
||||
* h_max (int) : 6 - the maximum TOC header level to use; any header greater than this value will be ignored
|
||||
* ordered (bool) : false - when set to true, an ordered list will be outputted instead of an unordered list
|
||||
* item_class (string) : '' - add custom class(es) for each list item; has support for '%level%' placeholder, which is the current heading level
|
||||
* baseurl (string) : '' - add a base url to the TOC links for when your TOC is on another page than the actual content
|
||||
* anchor_class (string) : '' - add custom class(es) for each anchor element
|
||||
|
||||
Output:
|
||||
An ordered or unordered list representing the table of contents of a markdown block. This snippet will only
|
||||
generate the table of contents and will NOT output the markdown given to it
|
||||
{% endcomment %}
|
||||
|
||||
{% capture my_toc %}{% endcapture %}
|
||||
{% assign orderedList = include.ordered | default: false %}
|
||||
{% assign minHeader = include.h_min | default: 1 %}
|
||||
{% assign maxHeader = include.h_max | default: 6 %}
|
||||
{% assign nodes = include.html | split: '<h' %}
|
||||
{% assign firstHeader = true %}
|
||||
|
||||
{% capture listModifier %}{% if orderedList %}1.{% else %}-{% endif %}{% endcapture %}
|
||||
|
||||
{% for node in nodes %}
|
||||
{% if node == "" %}
|
||||
{% continue %}
|
||||
{% endif %}
|
||||
|
||||
{% assign headerLevel = node | replace: '"', '' | slice: 0, 1 | times: 1 %}
|
||||
|
||||
{% if headerLevel < minHeader or headerLevel > maxHeader %}
|
||||
{% continue %}
|
||||
{% endif %}
|
||||
|
||||
{% if firstHeader %}
|
||||
{% assign firstHeader = false %}
|
||||
{% assign minHeader = headerLevel %}
|
||||
{% endif %}
|
||||
|
||||
{% assign indentAmount = headerLevel | minus: minHeader %}
|
||||
{% assign _workspace = node | split: '</h' %}
|
||||
|
||||
{% assign _idWorkspace = _workspace[0] | split: 'id="' %}
|
||||
{% assign _idWorkspace = _idWorkspace[1] | split: '"' %}
|
||||
{% assign html_id = _idWorkspace[0] %}
|
||||
|
||||
{% assign _classWorkspace = _workspace[0] | split: 'class="' %}
|
||||
{% assign _classWorkspace = _classWorkspace[1] | split: '"' %}
|
||||
{% assign html_class = _classWorkspace[0] %}
|
||||
|
||||
{% if html_class contains "no_toc" %}
|
||||
{% continue %}
|
||||
{% endif %}
|
||||
|
||||
{% capture _hAttrToStrip %}{{ _workspace[0] | split: '>' | first }}>{% endcapture %}
|
||||
{% assign header = _workspace[0] | replace: _hAttrToStrip, '' %}
|
||||
|
||||
{% assign space = '' %}
|
||||
{% for i in (1..indentAmount) %}
|
||||
{% assign space = space | prepend: ' ' %}
|
||||
{% endfor %}
|
||||
|
||||
{% if include.item_class and include.item_class != blank %}
|
||||
{% capture listItemClass %}{:.{{ include.item_class | replace: '%level%', headerLevel }}}{% endcapture %}
|
||||
{% endif %}
|
||||
|
||||
{% capture heading_body %}{% if include.sanitize %}{{ header | strip_html }}{% else %}{{ header }}{% endif %}{% endcapture %}
|
||||
{% capture my_toc %}{{ my_toc }}
|
||||
{{ space }}{{ listModifier }} {{ listItemClass }} [{{ heading_body | replace: "|", "\|" }}]({% if include.baseurl %}{{ include.baseurl }}{% endif %}#{{ html_id }}){% if include.anchor_class %}{:.{{ include.anchor_class }}}{% endif %}{% endcapture %}
|
||||
{% endfor %}
|
||||
|
||||
{% if include.class and include.class != blank %}
|
||||
{% capture my_toc %}{:.{{ include.class }}}
|
||||
{{ my_toc | lstrip }}{% endcapture %}
|
||||
{% endif %}
|
||||
|
||||
{% if include.id %}
|
||||
{% capture my_toc %}{: #{{ include.id }}}
|
||||
{{ my_toc | lstrip }}{% endcapture %}
|
||||
{% endif %}
|
||||
{% endcapture %}{% assign tocWorkspace = '' %}{{ my_toc | markdownify | strip }}
|
||||
71
webpages/_layouts/vm-operator.html
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
||||
<meta name="referrer" content="no-referrer">
|
||||
<link rel="icon" type="image/svg+xml" href="favicon.svg" sizes="any">
|
||||
<link rel="stylesheet" href="../stylesheets/styles.css">
|
||||
<link rel="stylesheet" href="../stylesheets/pygment_trac.css">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
{% seo %}
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<header>
|
||||
<div>
|
||||
<div style="float: left;">
|
||||
<h1><a style="color: #222;" href="http://vm-operator.jdrupes.org">VM-Operator</a></h1>
|
||||
<h3>By <a href="https://github.com/mnlipp">Michael N. Lipp</a></h3>
|
||||
<p><a rel="me" href="https://fosstodon.org/@mnl"><img alt="Mastodon Follow"
|
||||
src="https://img.shields.io/mastodon/follow/108843609567976408?domain=https%3A%2F%2Ffosstodon.org&style=social"></a></p>
|
||||
</div>
|
||||
<div style="float: right; width: 7em;">
|
||||
<img alt="VM-Operator Logo" src="VM-Operator.svg">
|
||||
</div>
|
||||
<div style="clear:both;"></div>
|
||||
</div>
|
||||
<p></p>
|
||||
|
||||
<p class="view"><a href="https://github.com/mnlipp/VM-Operator">View GitHub Project</a></p>
|
||||
|
||||
<p></p>
|
||||
|
||||
<p class="part-entry"><a href="index.html">Overview</a></p>
|
||||
<p class="part-entry"><a href="runner.html">The Runner</a></p>
|
||||
<p class="part-list-title"><a href="manager.html">The Manager</a></p>
|
||||
<ul style="margin-bottom: 0;" class="no-bullets">
|
||||
<li><p class="part-entry"><a href="controller.html">The Controller</a></p></li>
|
||||
</ul>
|
||||
<p class="part-list-title"><a href="webgui.html">The Web-GUI</a></p>
|
||||
<ul style="margin-bottom: 0;" class="no-bullets">
|
||||
<li><p class="part-entry"><a href="admin-gui.html">For Admins</a></p></li>
|
||||
<li><p class="part-entry"><a href="user-gui.html">For Users</a></p></li>
|
||||
</ulstyle="margin-bottom: 0;">
|
||||
<p class="part-list-title"><a href="upgrading.html">Upgrading</a></p>
|
||||
<p class="part-list-title"><a href="latest-release/javadoc/index.html">Javadoc</a></p>
|
||||
|
||||
</header>
|
||||
<section>
|
||||
<div class="post-date"><span class="post-meta">{{ page.date | date: "%b %-d, %Y" }}</span></div>
|
||||
{% if page.tocTitle %}
|
||||
<h1>{{ page.tocTitle }}</h1>
|
||||
{% include toc.html html=content %}
|
||||
{% endif %}
|
||||
|
||||
{{ content }}
|
||||
</section>
|
||||
<footer>
|
||||
<p><small>Hosted on GitHub Pages — <a href="https://github.com/site/terms">Terms</a>
|
||||
— <a href="https://github.com/site/privacy">Privacy</a>
|
||||
— Theme derived from <a href="https://github.com/orderedlist/minimal">minimal</a></small></p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
{% include matomo.html %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
69
webpages/stylesheets/pygment_trac.css
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
.highlight { background: #ffffff; }
|
||||
.highlight .c { color: #999988; font-style: italic } /* Comment */
|
||||
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
|
||||
.highlight .k { font-weight: bold } /* Keyword */
|
||||
.highlight .o { font-weight: bold } /* Operator */
|
||||
.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */
|
||||
.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */
|
||||
.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */
|
||||
.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
|
||||
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
|
||||
.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */
|
||||
.highlight .ge { font-style: italic } /* Generic.Emph */
|
||||
.highlight .gr { color: #aa0000 } /* Generic.Error */
|
||||
.highlight .gh { color: #999999 } /* Generic.Heading */
|
||||
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
|
||||
.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */
|
||||
.highlight .go { color: #888888 } /* Generic.Output */
|
||||
.highlight .gp { color: #555555 } /* Generic.Prompt */
|
||||
.highlight .gs { font-weight: bold } /* Generic.Strong */
|
||||
.highlight .gu { color: #800080; font-weight: bold; } /* Generic.Subheading */
|
||||
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
|
||||
.highlight .kc { font-weight: bold } /* Keyword.Constant */
|
||||
.highlight .kd { font-weight: bold } /* Keyword.Declaration */
|
||||
.highlight .kn { font-weight: bold } /* Keyword.Namespace */
|
||||
.highlight .kp { font-weight: bold } /* Keyword.Pseudo */
|
||||
.highlight .kr { font-weight: bold } /* Keyword.Reserved */
|
||||
.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */
|
||||
.highlight .m { color: #009999 } /* Literal.Number */
|
||||
.highlight .s { color: #d14 } /* Literal.String */
|
||||
.highlight .na { color: #008080 } /* Name.Attribute */
|
||||
.highlight .nb { color: #0086B3 } /* Name.Builtin */
|
||||
.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */
|
||||
.highlight .no { color: #008080 } /* Name.Constant */
|
||||
.highlight .ni { color: #800080 } /* Name.Entity */
|
||||
.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */
|
||||
.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */
|
||||
.highlight .nn { color: #555555 } /* Name.Namespace */
|
||||
.highlight .nt { color: #000080 } /* Name.Tag */
|
||||
.highlight .nv { color: #008080 } /* Name.Variable */
|
||||
.highlight .ow { font-weight: bold } /* Operator.Word */
|
||||
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
|
||||
.highlight .mf { color: #009999 } /* Literal.Number.Float */
|
||||
.highlight .mh { color: #009999 } /* Literal.Number.Hex */
|
||||
.highlight .mi { color: #009999 } /* Literal.Number.Integer */
|
||||
.highlight .mo { color: #009999 } /* Literal.Number.Oct */
|
||||
.highlight .sb { color: #d14 } /* Literal.String.Backtick */
|
||||
.highlight .sc { color: #d14 } /* Literal.String.Char */
|
||||
.highlight .sd { color: #d14 } /* Literal.String.Doc */
|
||||
.highlight .s2 { color: #d14 } /* Literal.String.Double */
|
||||
.highlight .se { color: #d14 } /* Literal.String.Escape */
|
||||
.highlight .sh { color: #d14 } /* Literal.String.Heredoc */
|
||||
.highlight .si { color: #d14 } /* Literal.String.Interpol */
|
||||
.highlight .sx { color: #d14 } /* Literal.String.Other */
|
||||
.highlight .sr { color: #009926 } /* Literal.String.Regex */
|
||||
.highlight .s1 { color: #d14 } /* Literal.String.Single */
|
||||
.highlight .ss { color: #990073 } /* Literal.String.Symbol */
|
||||
.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */
|
||||
.highlight .vc { color: #008080 } /* Name.Variable.Class */
|
||||
.highlight .vg { color: #008080 } /* Name.Variable.Global */
|
||||
.highlight .vi { color: #008080 } /* Name.Variable.Instance */
|
||||
.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */
|
||||
|
||||
.type-csharp .highlight .k { color: #0000FF }
|
||||
.type-csharp .highlight .kt { color: #0000FF }
|
||||
.type-csharp .highlight .nf { color: #000000; font-weight: normal }
|
||||
.type-csharp .highlight .nc { color: #2B91AF }
|
||||
.type-csharp .highlight .nn { color: #000000 }
|
||||
.type-csharp .highlight .s { color: #A31515 }
|
||||
.type-csharp .highlight .sc { color: #A31515 }
|
||||
244
webpages/stylesheets/styles.css
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
body {
|
||||
background-color: #fff;
|
||||
padding:50px;
|
||||
font: normal 16px/1.5 Verdana, Arial, Helvetica, sans-serif;
|
||||
color:#595959;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color:#222;
|
||||
margin:0 0 20px;
|
||||
}
|
||||
|
||||
p, ul, ol, table, pre, dl {
|
||||
margin:0 0 20px;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
line-height:1.1;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size:28px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color:#393939;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
h3, h4, h5, h6 {
|
||||
color:#494949;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
a {
|
||||
color:#39c;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color:#069;
|
||||
}
|
||||
|
||||
a small {
|
||||
font-size:11px;
|
||||
color:#777;
|
||||
margin-top:-0.3em;
|
||||
display:block;
|
||||
}
|
||||
|
||||
a:hover small {
|
||||
color:#777;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
/* width:860px; */
|
||||
width: 100%;
|
||||
margin:0 auto;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left:1px solid #e5e5e5;
|
||||
margin:0;
|
||||
padding:0 0 0 20px;
|
||||
font-style:italic;
|
||||
}
|
||||
|
||||
code, pre {
|
||||
font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal, Consolas, Liberation Mono, DejaVu Sans Mono, Courier New, monospace;
|
||||
color:#333;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-size: 15px;
|
||||
padding:8px 15px;
|
||||
background: #f8f8f8;
|
||||
border-radius:5px;
|
||||
border:1px solid #e5e5e5;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
a code {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
table {
|
||||
width:100%;
|
||||
border-collapse:collapse;
|
||||
}
|
||||
|
||||
th, td {
|
||||
text-align:left;
|
||||
padding:5px 10px;
|
||||
border-bottom:1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
dt {
|
||||
color:#444;
|
||||
font-weight:500;
|
||||
}
|
||||
|
||||
th {
|
||||
color:#444;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width:100%;
|
||||
}
|
||||
|
||||
header {
|
||||
/* width:270px; */
|
||||
width:calc(29% - 50px);
|
||||
height:calc(100% - 160px);
|
||||
overflow: auto;
|
||||
float:left;
|
||||
position:fixed;
|
||||
-webkit-font-smoothing:subpixel-antialiased;
|
||||
}
|
||||
|
||||
header li {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
header ul {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
header ul > li {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
ul.no-bullets {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
ul.no-bullets > li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
strong {
|
||||
color:#222;
|
||||
font-weight:500;
|
||||
}
|
||||
|
||||
section {
|
||||
width:70%;
|
||||
max-width:54em;
|
||||
float:right;
|
||||
padding-bottom:50px;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size:11px;
|
||||
}
|
||||
|
||||
hr {
|
||||
border:0;
|
||||
background:#e5e5e5;
|
||||
height:1px;
|
||||
margin:0 0 20px;
|
||||
}
|
||||
|
||||
footer {
|
||||
/* width:270px; */
|
||||
width:calc(24% - 50px);
|
||||
height:40px;
|
||||
float:left;
|
||||
position:fixed;
|
||||
padding:30px 0;
|
||||
bottom:0px;
|
||||
background-color:white;
|
||||
-webkit-font-smoothing:subpixel-antialiased;
|
||||
}
|
||||
|
||||
.post-date {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.part-list-title {
|
||||
margin-bottom:5px;
|
||||
}
|
||||
|
||||
.part-entry {
|
||||
margin-bottom:5px;
|
||||
}
|
||||
|
||||
@media print, screen and (max-width: 960px) {
|
||||
|
||||
div.wrapper {
|
||||
width:auto;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
header, section, footer {
|
||||
float:none;
|
||||
position:static;
|
||||
width:auto;
|
||||
}
|
||||
|
||||
header {
|
||||
padding-right:320px;
|
||||
}
|
||||
|
||||
section {
|
||||
border:1px solid #e5e5e5;
|
||||
border-width:1px 0;
|
||||
padding:20px 0;
|
||||
margin:0 0 20px;
|
||||
}
|
||||
|
||||
header a small {
|
||||
display:inline;
|
||||
}
|
||||
}
|
||||
|
||||
@media print, screen and (max-width: 720px) {
|
||||
body {
|
||||
word-wrap:break-word;
|
||||
}
|
||||
|
||||
header {
|
||||
padding:0;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
word-wrap:normal;
|
||||
}
|
||||
}
|
||||
|
||||
@media print, screen and (max-width: 480px) {
|
||||
body {
|
||||
padding:15px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
padding:0.4in;
|
||||
font-size:12pt;
|
||||
color:#444;
|
||||
}
|
||||
}
|
||||
BIN
webpages/vm-operator/02_2_operator.png
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
webpages/vm-operator/VM-Operator-GUI-preview.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
webpages/vm-operator/VM-Operator-GUI-view.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
173
webpages/vm-operator/VM-Operator-with-font.svg
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="93.557968mm"
|
||||
height="91.220795mm"
|
||||
viewBox="0 0 331.50461 323.22329"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
sodipodi:docname="VM-Operator.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.7"
|
||||
inkscape:cx="293.57143"
|
||||
inkscape:cy="145.71429"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="32"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(799.83239,410.74206)">
|
||||
<g
|
||||
id="path300"
|
||||
inkscape:transform-center-x="-0.49891951"
|
||||
inkscape:transform-center-y="-10.814906"
|
||||
transform="matrix(0.93749998,0,0,0.93749998,-364.15225,128.12438)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#326de6;fill-opacity:1;stroke:#ffffff;stroke-linecap:square;stroke-miterlimit:0;paint-order:fill markers stroke"
|
||||
id="path1033"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="7"
|
||||
sodipodi:cx="-790.008"
|
||||
sodipodi:cy="-357.15076"
|
||||
sodipodi:r1="221.23064"
|
||||
sodipodi:r2="199.10757"
|
||||
sodipodi:arg1="1.1215879"
|
||||
sodipodi:arg2="1.5605315"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -693.93801,-157.86816 -94.02622,-0.18551 -97.95052,0.26412 -58.47935,-73.62832 -61.2776,-76.41613 21.10362,-91.6275 21.53854,-95.55347 84.79518,-40.6293 88.13578,-42.7371 84.6342,40.96358 88.36497,42.26117 20.74194,91.71007 22.05354,95.43592 -58.76943,73.39699 z"
|
||||
transform="matrix(0.81788201,0,0,0.81788201,358.19384,-101.37507)"
|
||||
inkscape:transform-center-x="1.2804791"
|
||||
inkscape:transform-center-y="-8.9686433" />
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16.6665px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';letter-spacing:0px;word-spacing:0px;fill:none;stroke:#ffffff;stroke-width:1.04165;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
x="-753.07837"
|
||||
y="-183.35805"
|
||||
id="text300"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan298"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:199.997px;font-family:'Nimbus Sans Narrow';-inkscape-font-specification:'Nimbus Sans Narrow, Bold Semi-Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff6600;fill-opacity:1;stroke:#ffffff;stroke-width:1.04165;stroke-dasharray:none;stroke-opacity:1"
|
||||
x="-753.07837"
|
||||
y="-183.35805">VM</tspan></text>
|
||||
<g
|
||||
id="g1773"
|
||||
transform="matrix(1.5551014,0,0,1.5551014,-923.85519,-409.37793)"
|
||||
style="stroke:#ffffff;stroke-opacity:1">
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:0.692134;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136"
|
||||
cx="149.36122"
|
||||
cy="159.37894"
|
||||
r="5.9195638" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:0.692134;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-8"
|
||||
cx="162.51581"
|
||||
cy="159.37894"
|
||||
r="5.9195638" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:0.692134;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-1"
|
||||
cx="175.67039"
|
||||
cy="159.37894"
|
||||
r="5.9195638" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:0.692134;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-2"
|
||||
cx="188.82498"
|
||||
cy="159.37894"
|
||||
r="5.9195638" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:0.692134;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-8-8"
|
||||
cx="155.7412"
|
||||
cy="170.6261"
|
||||
r="5.9195638" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:0.692134;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-1-9"
|
||||
cx="168.89577"
|
||||
cy="170.6261"
|
||||
r="5.9195638" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:0.692134;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-2-3"
|
||||
cx="182.05035"
|
||||
cy="170.6261"
|
||||
r="5.9195638" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:0.692134;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-8-6"
|
||||
cx="162.25272"
|
||||
cy="182.0706"
|
||||
r="5.9195638" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:0.692134;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-1-8"
|
||||
cx="175.4073"
|
||||
cy="182.0706"
|
||||
r="5.9195638" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:0.692134;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-2-0"
|
||||
cx="168.63269"
|
||||
cy="193.12045"
|
||||
r="5.9195638" />
|
||||
<g
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:180px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial Bold';letter-spacing:0px;word-spacing:0px;fill:#238220;fill-opacity:1;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="flowRoot4814"
|
||||
transform="matrix(0.16182744,0,0,0.16070376,137.53832,122.72639)">
|
||||
<path
|
||||
d="m 210.10352,57.161102 h 25.92773 V 138.7236 q 0,15.9961 -2.8125,24.60938 -3.7793,11.25 -13.71094,18.10547 -9.93164,6.76757 -26.1914,6.76757 -19.07227,0 -29.35547,-10.63476 -10.28321,-10.72266 -10.3711,-31.37696 l 24.52149,-2.8125 q 0.43945,11.07422 3.25195,15.64454 4.21875,6.94336 12.83203,6.94336 8.70117,0 12.30469,-4.92188 3.60352,-5.00977 3.60352,-20.6543 z"
|
||||
style="fill:#238220;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"
|
||||
id="path4823"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.5 KiB |
184
webpages/vm-operator/VM-Operator.svg
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="93.557968mm"
|
||||
height="91.220795mm"
|
||||
viewBox="0 0 331.50461 323.22329"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
sodipodi:docname="VM-Operator.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.7"
|
||||
inkscape:cx="245"
|
||||
inkscape:cy="145.71429"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="32"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(799.83239,410.74206)">
|
||||
<g
|
||||
id="path300"
|
||||
inkscape:transform-center-x="-0.49891951"
|
||||
inkscape:transform-center-y="-10.814906"
|
||||
transform="matrix(0.93749998,0,0,0.93749998,-364.15225,128.12438)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#326de6;fill-opacity:1;stroke:#ffffff;stroke-linecap:square;stroke-miterlimit:0;paint-order:fill markers stroke"
|
||||
id="path1033"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="7"
|
||||
sodipodi:cx="-790.008"
|
||||
sodipodi:cy="-357.15076"
|
||||
sodipodi:r1="221.23064"
|
||||
sodipodi:r2="199.10757"
|
||||
sodipodi:arg1="1.1215879"
|
||||
sodipodi:arg2="1.5605315"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -693.93801,-157.86816 -94.02622,-0.18551 -97.95052,0.26412 -58.47935,-73.62832 -61.2776,-76.41613 21.10362,-91.6275 21.53854,-95.55347 84.79518,-40.6293 88.13578,-42.7371 84.6342,40.96358 88.36497,42.26117 20.74194,91.71007 22.05354,95.43592 -58.76943,73.39699 z"
|
||||
transform="matrix(0.81788201,0,0,0.81788201,358.19384,-101.37507)"
|
||||
inkscape:transform-center-x="1.2804791"
|
||||
inkscape:transform-center-y="-8.9686433" />
|
||||
</g>
|
||||
<g
|
||||
aria-label="VM"
|
||||
id="text300"
|
||||
style="font-size:16.6665px;-inkscape-font-specification:'sans-serif, Normal';letter-spacing:0px;word-spacing:0px;fill:none;stroke:#ffffff;stroke-width:1.04165">
|
||||
<g
|
||||
id="path305">
|
||||
<path
|
||||
style="color:#000000;-inkscape-font-specification:'Nimbus Sans Narrow, Bold Semi-Condensed';fill:#ff6600;stroke:none;-inkscape-stroke:none"
|
||||
d="m -698.0792,-217.35754 -25.39961,-109.59835 h -26.39961 l 39.59941,143.59784 h 23.39965 l 39.99939,-143.59784 h -25.59961 z"
|
||||
id="path312" />
|
||||
<path
|
||||
style="color:#000000;-inkscape-font-specification:'Nimbus Sans Narrow, Bold Semi-Condensed';fill:#ffffff;stroke:none;-inkscape-stroke:none"
|
||||
d="m -750.5625,-327.47656 39.88672,144.63867 h 24.19141 l 40.29101,-144.63867 h -26.69922 l -25.18554,107.82226 -24.98633,-107.82226 z m 1.36719,1.04101 h 25.30273 l 25.30664,109.19532 1.01367,0.002 25.50586,-109.19727 h 24.50196 l -39.71094,142.55664 h -22.60742 z"
|
||||
id="path325" />
|
||||
</g>
|
||||
<g
|
||||
id="path307">
|
||||
<path
|
||||
style="color:#000000;-inkscape-font-specification:'Nimbus Sans Narrow, Bold Semi-Condensed';fill:#ff6600;stroke:none;-inkscape-stroke:none"
|
||||
d="m -518.28172,-326.95589 h -35.39947 l -21.19968,113.99829 -21.59968,-113.99829 h -35.79946 v 143.59784 h 22.79966 v -121.79817 l 21.79967,121.79817 h 24.19963 l 22.39967,-121.79817 v 121.79817 h 22.79966 z"
|
||||
id="path318" />
|
||||
<path
|
||||
style="color:#000000;-inkscape-font-specification:'Nimbus Sans Narrow, Bold Semi-Condensed';fill:#ffffff;stroke:none;-inkscape-stroke:none"
|
||||
d="m -632.80078,-327.47656 v 144.63867 h 23.8418 v -116.45313 l 20.84179,116.45313 h 25.07032 l 21.44531,-116.60742 v 116.60742 h 23.83984 v -144.63867 h -0.51953 -35.83203 l -20.77149,111.69726 -21.16406,-111.69726 z m 1.04101,1.04101 h 34.84766 l 21.51953,113.57422 1.02344,-0.002 21.12109,-113.57227 h 34.44532 v 142.55664 h -21.75782 v -121.27734 l -1.0332,-0.0937 -22.32031,121.37109 h -23.33008 l -21.72266,-121.36914 -1.03515,0.0918 v 121.27734 h -21.75782 z"
|
||||
id="path320" />
|
||||
</g>
|
||||
</g>
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136"
|
||||
cx="-691.58337"
|
||||
cy="-161.52753"
|
||||
r="9.2055216" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-8"
|
||||
cx="-671.12665"
|
||||
cy="-161.52753"
|
||||
r="9.2055216" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-1"
|
||||
cx="-650.66992"
|
||||
cy="-161.52753"
|
||||
r="9.2055216" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-2"
|
||||
cx="-630.2132"
|
||||
cy="-161.52753"
|
||||
r="9.2055216" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-8-8"
|
||||
cx="-681.66187"
|
||||
cy="-144.03705"
|
||||
r="9.2055216" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-1-9"
|
||||
cx="-661.20514"
|
||||
cy="-144.03705"
|
||||
r="9.2055216" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-2-3"
|
||||
cx="-640.74841"
|
||||
cy="-144.03705"
|
||||
r="9.2055216" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-8-6"
|
||||
cx="-671.53577"
|
||||
cy="-126.23969"
|
||||
r="9.2055216" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-1-8"
|
||||
cx="-651.07904"
|
||||
cy="-126.23969"
|
||||
r="9.2055216" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-2-0"
|
||||
cx="-661.61426"
|
||||
cy="-109.05605"
|
||||
r="9.2055216" />
|
||||
<g
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:180px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial Bold';letter-spacing:0px;word-spacing:0px;fill:#238220;fill-opacity:1;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="flowRoot4814"
|
||||
transform="matrix(0.25165808,0,0,0.24991064,-709.96916,-218.52595)">
|
||||
<path
|
||||
d="m 210.10352,57.161102 h 25.92773 V 138.7236 q 0,15.9961 -2.8125,24.60938 -3.7793,11.25 -13.71094,18.10547 -9.93164,6.76757 -26.1914,6.76757 -19.07227,0 -29.35547,-10.63476 -10.28321,-10.72266 -10.3711,-31.37696 l 24.52149,-2.8125 q 0.43945,11.07422 3.25195,15.64454 4.21875,6.94336 12.83203,6.94336 8.70117,0 12.30469,-4.92188 3.60352,-5.00977 3.60352,-20.6543 z"
|
||||
style="fill:#238220;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"
|
||||
id="path4823"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.5 KiB |
BIN
webpages/vm-operator/VmViewer-preview.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
18
webpages/vm-operator/admin-gui.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
title: VM-Operator Web-GUI for Admins
|
||||
layout: vm-operator
|
||||
---
|
||||
|
||||
# Administrator view
|
||||
|
||||
An overview display shows the current CPU and RAM usage and a graph
|
||||
with recent changes.
|
||||
|
||||

|
||||
|
||||
The detail display lists all VMs. From here you can start and stop
|
||||
the VMs and adjust the CPU and RAM usages (modifies the definition
|
||||
in kubernetes).
|
||||
|
||||

|
||||
|
||||
226
webpages/vm-operator/controller.md
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
---
|
||||
title: VM-Operator Controller
|
||||
layout: vm-operator
|
||||
---
|
||||
|
||||
# The Controller
|
||||
|
||||
The controller component (which is part of the manager) monitors
|
||||
custom resources of kind `VirtualMachine`. It creates or modifies
|
||||
other resources in the cluster as required to get the VM defined
|
||||
by the CR up and running.
|
||||
|
||||
Here is the sample definition of a VM from the
|
||||
["local-path" example](https://github.com/mnlipp/VM-Operator/tree/main/example/local-path):
|
||||
|
||||
```yaml
|
||||
apiVersion: "vmoperator.jdrupes.org/v1"
|
||||
kind: VirtualMachine
|
||||
metadata:
|
||||
namespace: vmop-demo
|
||||
name: test-vm
|
||||
spec:
|
||||
guestShutdownStops: false
|
||||
|
||||
vm:
|
||||
state: Running
|
||||
maximumCpus: 4
|
||||
currentCpus: 2
|
||||
maximumRam: 8Gi
|
||||
currentRam: 4Gi
|
||||
|
||||
networks:
|
||||
- user: {}
|
||||
|
||||
disks:
|
||||
- volumeClaimTemplate:
|
||||
metadata:
|
||||
name: system
|
||||
spec:
|
||||
storageClassName: ""
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: vmrunner
|
||||
app.kubernetes.io/instance: test-vm
|
||||
vmrunner.jdrupes.org/disk: system
|
||||
resources:
|
||||
requests:
|
||||
storage: 40Gi
|
||||
- cdrom:
|
||||
image: ""
|
||||
# image: https://download.fedoraproject.org/pub/fedora/linux/releases/38/Workstation/x86_64/iso/Fedora-Workstation-Live-x86_64-38-1.6.iso
|
||||
# image: "Fedora-Workstation-Live-x86_64-38-1.6.iso"
|
||||
|
||||
display:
|
||||
spice:
|
||||
port: 5910
|
||||
# Since 3.0.0:
|
||||
# generateSecret: false
|
||||
```
|
||||
|
||||
## Pod management
|
||||
|
||||
The central resource created by the controller is a
|
||||
[stateful set](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/)
|
||||
with the same name as the VM (metadata.name). Its number of replicas is
|
||||
set to 1 if `spec.vm.state` is "Running" (default is "Stopped" which sets
|
||||
replicas to 0).
|
||||
|
||||
Property `spec.guestShutdownStops` (since 2.2.0) controls the effect of a
|
||||
shutdown initiated by the guest. If set to `false` (default) a new pod
|
||||
is automatically created by the stateful set controller and the VM thus
|
||||
restarted. If set to `true`, the runner sets `spec.vm.state` to "Stopped"
|
||||
before terminating and by this prevents the creation of a new pod.
|
||||
|
||||
## Defining the basics
|
||||
|
||||
How to define the number of CPUs and the size of the RAM of the VM
|
||||
should be obvious from the example. Note that changes of the current
|
||||
number of CPUs and the current RAM size will be propagated to
|
||||
running VMs.
|
||||
|
||||
## Defining disks
|
||||
|
||||
Maybe the most interesting part is the definition of the VM's disks.
|
||||
This is done by adding one or more `volumeClaimTemplate`s to the
|
||||
list of disks. As its name suggests, such a template is used by the
|
||||
controller to generate a PVC.
|
||||
|
||||
The example template does not define any storage. Rather it references
|
||||
some PV that you must have created first. This may be your first approach
|
||||
if you have existing storage from running the VM outside Kubernetes
|
||||
(e.g. with libvirtd).
|
||||
|
||||
If you have ceph or some other full fledged storage provider installed
|
||||
and create a new VM, provisioning a disk can happen automatically
|
||||
as shown in this example:
|
||||
|
||||
```yaml
|
||||
disks:
|
||||
- volumeClaimTemplate:
|
||||
metadata:
|
||||
name: system
|
||||
spec:
|
||||
storageClassName: rook-ceph-block
|
||||
resources:
|
||||
requests:
|
||||
storage: 40Gi
|
||||
```
|
||||
|
||||
The disk will be available as "/dev/*name*-disk" in the VM,
|
||||
using the string from `.volumeClaimTemplate.metadata.name` as *name*.
|
||||
If no name is defined in the metadata, then "/dev/disk-*n*"
|
||||
is used instead, with *n* being the index of the disk
|
||||
definition in the list of disks.
|
||||
|
||||
Apart from appending "-disk" to the name (or generating the name) the
|
||||
`volumeClaimTemplate` is simply copied into the stateful set definition
|
||||
for the VM (with some additional labels, see below). The controller
|
||||
for stateful sets appends the started pod's name to the name of the
|
||||
volume claim templates when it creates the PVCs. Therefore you'll
|
||||
eventually find the PVCs as "*name*-disk-*vmName*-0"
|
||||
(or "disk-*n*-*vmName*-0").
|
||||
|
||||
PVCs generated from stateful set definitions are considered "precious"
|
||||
and never removed automatically. This behavior fits perfectly for VMs.
|
||||
Usually, you do not want the disks to be removed automatically when
|
||||
you (maybe accidentally) remove the CR for the VM. To simplify the lookup
|
||||
for an eventual (manual) removal, all PVCs are labeled with
|
||||
"app.kubernetes.io/name: vm-runner", "app.kubernetes.io/instance: *vmName*",
|
||||
and "app.kubernetes.io/managed-by: vm-operator".
|
||||
|
||||
## Choosing an image for the runner
|
||||
|
||||
The image used for the runner can be configured with
|
||||
[`spec.image`](https://github.com/mnlipp/VM-Operator/blob/7e094e720b7b59a5e50f4a9a4ad29a6000ec76e6/deploy/crds/vms-crd.yaml#L19).
|
||||
This is a mapping with either a single key `source` or a detailed
|
||||
configuration using the keys `repository`, `path` etc.
|
||||
|
||||
Currently two runner images are maintained. One that is based on
|
||||
Arch Linux (`ghcr.io/mnlipp/org.jdrupes.vmoperator.runner.qemu-arch`) and a
|
||||
second one based on Alpine (`ghcr.io/mnlipp/org.jdrupes.vmoperator.runner.qemu-alpine`).
|
||||
|
||||
Starting with release 1.0, all versions of runner images and managers
|
||||
that have the same major release number are guaranteed to be compatible.
|
||||
|
||||
## Generating cloud-init data
|
||||
|
||||
*Since: 2.2.0*
|
||||
|
||||
The optional object `.spec.cloudInit` with sub-objects `.cloudInit.metaData`,
|
||||
`.cloudInit.userData` and `.cloudInit.networkConfig` can be used to provide
|
||||
data for
|
||||
[cloud-init](https://cloudinit.readthedocs.io/en/latest/index.html).
|
||||
The data from the CRD will be made available to the VM by the runner
|
||||
as a vfat formatted disk (see the description of
|
||||
[NoCloud](https://cloudinit.readthedocs.io/en/latest/reference/datasources/nocloud.html)).
|
||||
|
||||
If `.metaData.instance-id` is not defined, the controller automatically
|
||||
generates it from the CRD's `resourceVersion`. If `.metaData.local-hostname`
|
||||
is not defined, the controller adds this property using the value from
|
||||
`metadata.name`.
|
||||
|
||||
Note that there are no schema definitions available for `.userData`
|
||||
and `.networkConfig`. Whatever is defined in the CRD is copied to
|
||||
the corresponding cloud-init file without any checks. (The introductory
|
||||
comment `#cloud-config` required at the beginning of `.userData` is
|
||||
generated automatically by the runner.)
|
||||
|
||||
## Display secret/password
|
||||
|
||||
*Since: 2.3.0*
|
||||
|
||||
You can define a display password using a Kubernetes secret.
|
||||
When you start a VM, the controller checks if there is a secret
|
||||
with labels "app.kubernetes.io/name: vm-runner,
|
||||
app.kubernetes.io/component: display-secret,
|
||||
app.kubernetes.io/instance: *vmname*" in the namespace of the
|
||||
VM definition. The name of the secret can be chosen freely.
|
||||
|
||||
```yaml
|
||||
kind: Secret
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: test-vm-display-secret
|
||||
namespace: vmop-demo
|
||||
labels:
|
||||
app.kubernetes.io/name: vm-runner
|
||||
app.kubernetes.io/instance: test-vm
|
||||
app.kubernetes.io/component: display-secret
|
||||
type: Opaque
|
||||
data:
|
||||
display-password: dGVzdC12bQ==
|
||||
# Since 3.0.0:
|
||||
# password-expiry: bmV2ZXI=
|
||||
```
|
||||
|
||||
If such a secret for the VM is found, the VM is configured to use
|
||||
the display password specified. The display password in the secret
|
||||
can be updated while the VM runs[^delay]. Activating/deactivating
|
||||
the display password while a VM runs is not supported by Qemu and
|
||||
therefore requires stopping the VM, adding/removing the secret and
|
||||
restarting the VM.
|
||||
|
||||
[^delay]: Be aware of the possible delay, see e.g.
|
||||
[here](https://web.archive.org/web/20240223073838/https://ahmet.im/blog/kubernetes-secret-volumes-delay/).
|
||||
|
||||
*Since: 3.0.0*
|
||||
|
||||
The secret's `data` can have an additional property `data.password-expiry` which
|
||||
specifies a (base64 encoded) expiry date for the password. Supported
|
||||
values are those defined by qemu (`+n` seconds from now, `n` Unix
|
||||
timestamp, `never` and `now`).
|
||||
|
||||
Unless `spec.vm.display.spice.generateSecret` is set to `false` in the VM
|
||||
definition (CRD), the controller creates a secret for the display
|
||||
password automatically if none is found. The secret is created
|
||||
with a random password that expires immediately, which makes the
|
||||
display effectively inaccessible until the secret is modified.
|
||||
Note that a password set manually may be overwritten by components
|
||||
of the manager unless the password-expiry is set to "never" or
|
||||
some time in the future.
|
||||
|
||||
## Further reading
|
||||
|
||||
For a detailed description of the available configuration options see the
|
||||
[CRD](https://github.com/mnlipp/VM-Operator/blob/main/deploy/crds/vms-crd.yaml).
|
||||
88
webpages/vm-operator/favicon.svg
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="10.64148mm"
|
||||
height="12.555316mm"
|
||||
viewBox="0 0 37.706033 44.487341"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="ML-Logo1.svg"
|
||||
inkscape:export-filename="/home/mnl/Dokumente/mnl/ML-Logo1.png"
|
||||
inkscape:export-xdpi="299.41104"
|
||||
inkscape:export-ydpi="299.41104">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.7"
|
||||
inkscape:cx="132.46074"
|
||||
inkscape:cy="-297.07411"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
fit-margin-top="1"
|
||||
fit-margin-left="1"
|
||||
fit-margin-right="1"
|
||||
fit-margin-bottom="1"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1016"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-175.34341,-117.71255)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="187.14285"
|
||||
y="150.93362"
|
||||
id="text3370"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3372"
|
||||
x="187.14285"
|
||||
y="150.93362"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono'">M</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:51.30387497px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="173.50081"
|
||||
y="158.65659"
|
||||
id="text3370-6"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3372-5"
|
||||
x="173.50081"
|
||||
y="158.65659"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono'">L</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
60
webpages/vm-operator/index.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
---
|
||||
title: VM-Operator by mnlipp
|
||||
description: A Kubernetes operator for running virtual machines (notably Qemu VMs) in pods on Kubernetes
|
||||
layout: vm-operator
|
||||
---
|
||||
|
||||
# Welcome to VM-Operator
|
||||
|
||||
The goal of this project is to provide the means for running Qemu
|
||||
based VMs in Kubernetes pods.
|
||||
|
||||
The image used for the VM pods combines Qemu and a control program
|
||||
for starting and managing the Qemu process. This application is called
|
||||
"[the runner](runner.md)".
|
||||
|
||||
While you can deploy a runner manually (or with the help of some
|
||||
helm templates), the preferred way is to deploy "[the manager](manager.md)"
|
||||
application which acts as a Kubernetes operator for runners
|
||||
and thus the VMs.
|
||||
|
||||
If you just want to try out things, you can skip the remainder of this
|
||||
page and proceed to "[the manager](manager.md)".
|
||||
|
||||
## Motivation
|
||||
The project was triggered by a remark in the discussion about RedHat
|
||||
[dropping SPICE support](https://bugzilla.redhat.com/show_bug.cgi?id=2030592)
|
||||
from the RHEL packages. Which means that you have to run Qemu in a
|
||||
container on RHEL and derivatives if you want to continue using Spice.
|
||||
So KubeVirt comes to mind. But
|
||||
[one comment](https://bugzilla.redhat.com/show_bug.cgi?id=2030592#c4)
|
||||
mentioned that the [KubeVirt](https://kubevirt.io/) project isn't
|
||||
interested in supporting SPICE either.
|
||||
|
||||
Time to have a look at alternatives. Libvirt has become a common
|
||||
tool to configure and run Qemu. But some of its functionality, notably
|
||||
the management of storage for the VMs and networking is already provided
|
||||
by Kubernetes. Therefore this project takes a fresh approach of
|
||||
running Qemu in a pod using a simple, lightweight manager called "runner".
|
||||
Providing resources to the VM is left to Kubernetes mechanisms as
|
||||
much as possible.
|
||||
|
||||
## VMs and Pods
|
||||
|
||||
VMs are not the typical workload managed by Kubernetes. You can neither
|
||||
have replicas nor can the containers simply be restarted without a major
|
||||
impact on the "application". So there are many features for managing
|
||||
pods that we cannot make use of. Qemu in its container can only be
|
||||
deployed as a pod or using a stateful set with replica 1, which is rather
|
||||
close to simply deploying the pod (you get the restart and some PVC
|
||||
management "for free").
|
||||
|
||||
A second look, however, reveals that Kubernetes has more to offer.
|
||||
* It has a well defined API for managing resources.
|
||||
* It provides access to different kinds of managed storage for the VMs.
|
||||
* Its managing features *are* useful for running the component that
|
||||
manages the pods with the VMs.
|
||||
|
||||
And if you use Kubernetes anyway, well then the VMs within Kubernetes
|
||||
provide you with a unified view of all (or most of) your workloads,
|
||||
which simplifies the maintenance of your platform.
|
||||
150
webpages/vm-operator/manager.md
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
---
|
||||
title: VM-Operator Manager
|
||||
layout: vm-operator
|
||||
---
|
||||
|
||||
# The Manager
|
||||
|
||||
The Manager is the program that provides the controller from the
|
||||
[operator pattern](https://github.com/cncf/tag-app-delivery/blob/eece8f7307f2970f46f100f51932db106db46968/operator-wg/whitepaper/Operator-WhitePaper_v1-0.md#operator-components-in-kubernetes)
|
||||
together with a Web-GUI. It should be run in a container in the cluster.
|
||||
|
||||
## Installation
|
||||
|
||||
A manager instance manages the VMs in its own namespace. The only
|
||||
common (and therefore cluster scoped) resource used by all instances
|
||||
is the CRD. It is available
|
||||
[here](https://github.com/mnlipp/VM-Operator/raw/main/deploy/crds/vms-crd.yaml)
|
||||
and must be created first.
|
||||
|
||||
```sh
|
||||
kubectl apply -f https://github.com/mnlipp/VM-Operator/raw/main/deploy/crds/vms-crd.yaml
|
||||
```
|
||||
|
||||
The example above uses the CRD from the main branch. This is okay if
|
||||
you apply it once. If you want to preserve the link for automatic
|
||||
upgrades, you should use a link that points to one of the release branches.
|
||||
|
||||
The next step is to create a namespace for the manager and the VMs, e.g.
|
||||
`vmop-demo`.
|
||||
|
||||
```sh
|
||||
kubectl create namespace vmop-demo
|
||||
```
|
||||
|
||||
Finally you have to create an account, the role, the binding etc. The
|
||||
default files for creating these resources using the default namespace
|
||||
can be found in the
|
||||
[deploy](https://github.com/mnlipp/VM-Operator/tree/main/deploy)
|
||||
directory. I recommend to use
|
||||
[kustomize](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/) to create your own configuration.
|
||||
|
||||
## Initial Configuration
|
||||
|
||||
Use one of the `kustomize.yaml` files from the
|
||||
[example](https://github.com/mnlipp/VM-Operator/tree/main/example) directory
|
||||
as a starting point. The directory contains two examples. Here's the file
|
||||
from subdirectory `local-path`:
|
||||
|
||||
```yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
# Again, I recommend to use the deploy directory from a
|
||||
# release branch for anything but test environments.
|
||||
- https://github.com/mnlipp/VM-Operator/deploy
|
||||
|
||||
namespace: vmop-demo
|
||||
|
||||
patches:
|
||||
- patch: |-
|
||||
kind: PersistentVolumeClaim
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: vmop-image-repository
|
||||
spec:
|
||||
# Default is ReadOnlyMany
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
# Default is 100Gi
|
||||
storage: 10Gi
|
||||
# Default is to use the default storage class
|
||||
storageClassName: local-path
|
||||
|
||||
- patch: |-
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: vm-operator
|
||||
data:
|
||||
config.yaml: |
|
||||
"/Manager":
|
||||
# "/GuiHttpServer":
|
||||
# See section about the GUI
|
||||
"/Controller":
|
||||
"/Reconciler":
|
||||
runnerDataPvc:
|
||||
# Default is to use the default storage class
|
||||
storageClassName: local-path
|
||||
```
|
||||
|
||||
The sample file adds a namespace (`vmop-demo`) to all resource
|
||||
definitions and patches the PVC `vmop-image-repository`. This is a volume
|
||||
that is mounted into all pods that run a VM. The volume is intended
|
||||
to be used as a common repository for CDROM images. The PVC must exist
|
||||
and it must be bound before any pods can run.
|
||||
|
||||
The second patch affects the small volume that is created for each
|
||||
runner and contains the VM's configuration data such as the EFI vars.
|
||||
The manager's default configuration causes the PVC for this volume
|
||||
to be created with no storage class (which causes the default storage
|
||||
class to be used). The patch provides a new configuration file for
|
||||
the manager that makes the reconciler use local-path as storage
|
||||
class for this PVC. Details about the manager configuration can be
|
||||
found in the next section.
|
||||
|
||||
Note that you need none of the patches if you are fine with using your
|
||||
cluster's default storage class and this class supports ReadOnlyMany as
|
||||
access mode.
|
||||
|
||||
Check that the pod with the manager is running:
|
||||
|
||||
```sh
|
||||
kubectl -n vmop-demo get pods -l app.kubernetes.io/name=vm-operator
|
||||
```
|
||||
|
||||
Proceed to the description of [the controller](controller.html)
|
||||
for creating your first VM.
|
||||
|
||||
## Configuration Details
|
||||
|
||||
The [config map](https://github.com/mnlipp/VM-Operator/blob/main/deploy/vmop-config-map.yaml)
|
||||
for the manager may provide a configuration file (`config.yaml`) and
|
||||
a file with logging properties (`logging.properties`). Both files are mounted
|
||||
into the container that runs the manager and are evaluated by the manager
|
||||
on startup. If no files are provided, the manager uses built-in defaults.
|
||||
|
||||
The configuration file for the Manager follows the conventions of
|
||||
the [JGrapes](https://jgrapes.org/) component framework.
|
||||
The keys that start with a slash select the component within the
|
||||
application's component hierarchy. The mapping associated with the
|
||||
selected component configures this component's properties.
|
||||
|
||||
The available configuration options for the components can be found
|
||||
in their respective JavaDocs (e.g.
|
||||
[here](latest-release/javadoc/org/jdrupes/vmoperator/manager/Reconciler.html)
|
||||
for the Reconciler).
|
||||
|
||||
## Development Configuration
|
||||
|
||||
The [dev-example](https://github.com/mnlipp/VM-Operator/tree/main/dev-example)
|
||||
directory contains a `kustomize.yaml` that uses the development namespace
|
||||
`vmop-dev` and creates a deployment for the manager with 0 replicas.
|
||||
|
||||
This environment can be used for running the manager in the IDE. As the
|
||||
namespace to manage cannot be detected from the environment, you must use
|
||||
`-c ../dev-example/config.yaml` as argument when starting the manager. This
|
||||
configures it to use the namespace `vmop-dev`.
|
||||
108
webpages/vm-operator/runner.md
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
---
|
||||
title: VM-Operator Runner
|
||||
layout: vm-operator
|
||||
---
|
||||
|
||||
# The Runner
|
||||
|
||||
For most use cases, Qemu needs to be started and controlled by another
|
||||
program that manages the Qemu process. This program is called the
|
||||
runner in this context.
|
||||
|
||||
The most prominent reason for this second program is that it allows
|
||||
a VM to be shutdown cleanly in response to a TERM signal. Qemu handles
|
||||
the TERM signal by flushing all buffers and stopping, leaving the disks in
|
||||
a [crash consistent state](https://gitlab.com/qemu-project/qemu/-/issues/148).
|
||||
For a graceful shutdown, a parent process must handle the TERM signal, send
|
||||
the `system_powerdown` command to the qemu process and wait for its completion.
|
||||
|
||||
Another reason for having the runner is that another process needs to be started
|
||||
before qemu if the VM is supposed to include a TPM (software TPM).
|
||||
|
||||
Finally, we want some kind of higher level interface for applying runtime
|
||||
changes to the VM such as changing the CD or configuring the number of
|
||||
CPUs and the memory.
|
||||
|
||||
The runner takes care of all these issues. Although it is intended to
|
||||
run in a container (which runs in a Kubernetes pod) it does not require
|
||||
a container. You can start and use it as an ordinary program on any
|
||||
system, provided that you have the required commands (qemu, swtpm)
|
||||
installed.
|
||||
|
||||
## Stand-alone Configuration
|
||||
|
||||
Upon startup, the runner reads its main configuration file
|
||||
which defaults to `/etc/opt/vmrunner/config.yaml` and may be changed
|
||||
using the `-c` (or `--config`) command line option.
|
||||
|
||||
A sample configuration file with annotated options can be found
|
||||
[here](https://github.com/mnlipp/VM-Operator/blob/main/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml).
|
||||
As the runner implementation uses the
|
||||
[JGrapes](https://jgrapes.org/) framework, the file
|
||||
follows the framework's
|
||||
[conventions](https://jgrapes.org/latest-release/javadoc/org/jgrapes/util/YamlConfigurationStore.html). The top level "`/Runner`" selects
|
||||
the component to be configured. Nested within is the information
|
||||
to be applied to the component.
|
||||
|
||||
The main entries in the configuration file are the "template" and
|
||||
the "vm" information. The runner processes the
|
||||
[freemarker template](https://freemarker.apache.org/), using the
|
||||
"vm" information to derive the qemu command. The idea is that
|
||||
the "vm" section provides high level information such as the boot
|
||||
mode, the number of CPUs, the RAM size and the disks. The template
|
||||
defines a particular VM type, i.e. it contains the "nasty details"
|
||||
that do not need to be modified for some given set of VM instances.
|
||||
|
||||
The templates provided with the runner can be found
|
||||
[here](https://github.com/mnlipp/VM-Operator/tree/main/org.jdrupes.vmoperator.runner.qemu/templates). When details
|
||||
of the VM configuration need modification, a new VM type
|
||||
(i.e. a new template) has to be defined. Authoring a new
|
||||
template requires some knowledge about the
|
||||
[qemu invocation](https://www.qemu.org/docs/master/system/invocation.html).
|
||||
Despite many "warnings" that you find in the web, configuring the
|
||||
invocation arguments of qemu is only a bit (but not much) more
|
||||
challenging than editing libvirt's XML.
|
||||
|
||||
## Running in a Pod
|
||||
|
||||
The real purpose of the runner is to run a VM on Kubernetes in a pod.
|
||||
When running in a Kubernetes pod, `/etc/opt/vmrunner/config.yaml` should be
|
||||
provided by a
|
||||
[ConfigMap](https://kubernetes.io/docs/concepts/configuration/configmap/).
|
||||
|
||||
If additional templates are required, some ReadOnlyMany PV should
|
||||
be mounted in `/opt/vmrunner/templates`. The PV should contain copies
|
||||
of the standard templates as well as the additional templates. Of course,
|
||||
a ConfigMap can be used for this purpose again.
|
||||
|
||||
Networking options are rather limited. The assumption is that in general
|
||||
the VM wants full network connectivity. To achieve this, the pod must
|
||||
run with host networking and the host's networking must provide a
|
||||
bridge that the VM can attach to. The only currently supported
|
||||
alternative is the less performant
|
||||
"[user networking](https://wiki.qemu.org/Documentation/Networking#User_Networking_(SLIRP))",
|
||||
which may be used in a stand-alone development configuration.
|
||||
|
||||
## Runtime changes
|
||||
|
||||
The runner supports adaption to changes of the RAM size (using the
|
||||
balloon device) and to changes of the number of CPUs. Note that
|
||||
in order to get new CPUs online on Linux guests, you need a
|
||||
[udev rule](https://docs.kernel.org/core-api/cpu_hotplug.html#user-space-notification) which is not installed by default[^simplest].
|
||||
|
||||
The runner also changes the images loaded in CDROM drives. If the
|
||||
drive is locked, i.e. if it doesn't respond to the "open tray" command
|
||||
the change will be suspended until the VM opens the tray.
|
||||
|
||||
Finally, `powerdownTimeout` can be changed while the qemu process runs.
|
||||
|
||||
[^simplest]: The simplest form of the rule is probably:
|
||||
```
|
||||
ACTION=="add", SUBSYSTEM=="cpu", ATTR{online}="1"
|
||||
```
|
||||
|
||||
## Testing with Helm
|
||||
|
||||
There is a
|
||||
[Helm Chart](https://github.com/mnlipp/VM-Operator/tree/main/org.jdrupes.vmoperator.runner.qemu/helm-test)
|
||||
for testing the runner.
|
||||
29
webpages/vm-operator/upgrading.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
title: Upgrading
|
||||
layout: vm-operator
|
||||
---
|
||||
|
||||
# Upgrading
|
||||
|
||||
## To version 3.0.0
|
||||
|
||||
All configuration files are backward compatible to version 2.3.0.
|
||||
Note that in order to make use of the new viewer component,
|
||||
[permissions](https://mnlipp.github.io/VM-Operator/user-gui.html#control-access-to-vms)
|
||||
must be configured in the CR definition. Also note that
|
||||
[display secrets](https://mnlipp.github.io/VM-Operator/user-gui.html#securing-access)
|
||||
are automatically created unless explicitly disabled.
|
||||
|
||||
## To version 2.3.0
|
||||
|
||||
Starting with version 2.3.0, the web GUI uses a login conlet that
|
||||
supports OIDC providers. This effects the configuration of the
|
||||
web GUI components.
|
||||
|
||||
## To version 2.2.0
|
||||
|
||||
Version 2.2.0 sets the stateful set's `.spec.updateStrategy.type` to
|
||||
"OnDelete". This fails for no apparent reason if a definition of
|
||||
the stateful set with the default value "RollingUpdate" already exists.
|
||||
In order to fix this, either the stateful set or the complete VM definition
|
||||
must be deleted and the manager must be restarted.
|
||||
143
webpages/vm-operator/user-gui.md
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
---
|
||||
title: VM-Operator Web-GUI for Users
|
||||
layout: vm-operator
|
||||
---
|
||||
|
||||
# User view
|
||||
|
||||
*Since 3.0.0*
|
||||
|
||||
The idea of the user view is to provide an intuitive widget that
|
||||
allows the users to access their own VMs and to optionally start
|
||||
and stop them.
|
||||
|
||||

|
||||
|
||||
The configuration options resulting from this seemingly simple
|
||||
requirement are unexpectedly complex.
|
||||
|
||||
## Control access to VMs
|
||||
|
||||
First of all, we have to define which VMs a user can access. This
|
||||
is done using the optional property `spec.permissions` of the
|
||||
VM definition (CRD).
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
permissions:
|
||||
- role: admin
|
||||
may:
|
||||
- "*"
|
||||
- user: test
|
||||
may:
|
||||
- start
|
||||
- stop
|
||||
- accessConsole
|
||||
```
|
||||
|
||||
Permissions can be granted to individual users or to roles. There
|
||||
is a permission for each possible action. "*" grants them all.
|
||||
|
||||
## Simple usage vs. expert usage
|
||||
|
||||
Next, there are two ways to create the VM widgets (preview conlets
|
||||
in the framework's terms). They can be created on demand or
|
||||
automatically for each VM that a logged in user has permission to
|
||||
access. The former is the preferred way for an administrator who
|
||||
has access to all VMs and needs to open a particular VM's console
|
||||
for trouble shooting only. The latter is the preferred way
|
||||
for a regular user who has access to a limited number of VMs.
|
||||
In this case, creating the widgets automatically has the additional
|
||||
benefit that regular users don't need to know how to create and
|
||||
configure the widgets using the menu and the properties dialog.
|
||||
|
||||
Automatic synchronization of widgets and accessible VMs is controlled
|
||||
by the property `syncPreviewsFor` of the VM viewer. It's an array with
|
||||
objects that either specify a role or a user.
|
||||
|
||||
```yaml
|
||||
"/Manager":
|
||||
# This configures the GUI
|
||||
"/GuiHttpServer":
|
||||
"/ConsoleWeblet":
|
||||
"/WebConsole":
|
||||
"/ComponentCollector":
|
||||
"/VmViewer":
|
||||
syncPreviewsFor:
|
||||
- role: user
|
||||
- user: test
|
||||
displayResource:
|
||||
preferredIpVersion: ipv4
|
||||
```
|
||||
|
||||
## Console access
|
||||
|
||||
Access to the VM's console is implemented by generating a
|
||||
[connection file](https://manpages.debian.org/testing/virt-viewer/remote-viewer.1.en.html#CONNECTION_FILE) for virt-viewer when the user clicks on
|
||||
the console icon. If automatic open is enabled for this kind of
|
||||
files in the browser, the console opens without further user action.
|
||||
|
||||
The file contains all required and optional information to start the
|
||||
remote viewer.
|
||||
|
||||
* The "host" is by default the IP address of the node that the
|
||||
VM's pod is running on (remember that the runner uses host
|
||||
networking).
|
||||
* The "port" is simply taken from the VM definition.
|
||||
|
||||
In more complex scenarios, an administrator may have set up a load
|
||||
balancer that hides the worker node's IP addresses or the worker
|
||||
nodes use an internal network and can only be accessed through a
|
||||
proxy. For both cases, the values to include in the connection file
|
||||
can be specified as properties of `spec.vm.display.spice` in the
|
||||
VM definition.
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
vm:
|
||||
display:
|
||||
spice:
|
||||
port: 5930
|
||||
server: 192.168.19.32
|
||||
proxyUrl: http://lgpe-spice.some.host:1234
|
||||
generateSecret: true
|
||||
```
|
||||
|
||||
The value of `server` is used as value for key "host" in the
|
||||
connection file, thus overriding the default value. The
|
||||
value of `proxyUrl` is used as value for key "proxy".
|
||||
|
||||
## Securing access
|
||||
|
||||
As described [previously](./controller.html#display-secretpassword),
|
||||
access to a VM's display can be secured with a password. If a secret
|
||||
with a password exists for a VM, the password is
|
||||
included in the connection file.
|
||||
|
||||
While this approach is very convenient for the user, it is not
|
||||
secure, because this leaves the password as plain text in a file on
|
||||
the user's computer (the downloaded connection file). To work around
|
||||
this, the display secret is updated with a random password with
|
||||
limited validity, unless the display secret defines a `password-expiry`
|
||||
in the future or with value "never" or doesn't define a
|
||||
`password-expiry` at all.
|
||||
|
||||
The automatically generated password is the base64 encoded value
|
||||
of 16 (strong) random bytes (128 random bits). It is valid for
|
||||
10 seconds only. This may be challenging on a slower computer
|
||||
or if users may not enable automatic open for connection files
|
||||
in the browser. The validity can therefore be adjusted in the
|
||||
configuration.
|
||||
|
||||
```yaml
|
||||
"/Manager":
|
||||
"/Controller":
|
||||
"/DisplaySecretMonitor":
|
||||
# Validity of generated password in seconds
|
||||
passwordValidity: 10
|
||||
```
|
||||
|
||||
Taking into account that the controller generates a display
|
||||
secret automatically by default, this approach to securing
|
||||
console access should be sufficient in all cases. (Any feedback
|
||||
if something has been missed is appreciated.)
|
||||
117
webpages/vm-operator/webgui.md
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
---
|
||||
title: VM-Operator Web-GUI
|
||||
layout: vm-operator
|
||||
---
|
||||
|
||||
# The Web-GUI
|
||||
|
||||
The manager component provides a GUI via a web server. The web GUI is
|
||||
implemented using components from the
|
||||
[JGrapes WebConsole](https://jgrapes.org/WebConsole.html)
|
||||
project. Configuration of the GUI therefore follows the conventions
|
||||
of that framework.
|
||||
|
||||
The structure of the configuration information should be easy to
|
||||
understand from the examples provided. In general, configuration values
|
||||
are applied to the individual components that make up an application.
|
||||
The hierarchy of the components is reflected in the configuration
|
||||
information because components are "addressed" by their position in
|
||||
that hierarchy. (See
|
||||
[the package description](latest-release/javadoc/org/jdrupes/vmoperator/manager/package-summary.html)
|
||||
for information about the complete component structure.)
|
||||
|
||||
## Network access
|
||||
|
||||
By default, the service is made available at port 8080 of the manager
|
||||
pod. Of course, a kubernetes service and an ingress configuration must
|
||||
be added as required by the environment. (See the
|
||||
[definition](https://github.com/mnlipp/VM-Operator/blob/main/deploy/vmop-service.yaml)
|
||||
from the
|
||||
[sample deployment](https://github.com/mnlipp/VM-Operator/tree/main/deploy)).
|
||||
|
||||
## User Access
|
||||
|
||||
Access to the web GUI is controlled by the login conlet. The framework
|
||||
does not include sophisticated components for user management. Rather,
|
||||
it assumes that an OIDC provider is responsible for user authentication
|
||||
and role management.
|
||||
|
||||
```yaml
|
||||
"/Manager":
|
||||
# "/GuiSocketServer":
|
||||
# port: 8080
|
||||
"/GuiHttpServer":
|
||||
# This configures the GUI
|
||||
"/ConsoleWeblet":
|
||||
"/WebConsole":
|
||||
"/LoginConlet":
|
||||
# Starting with version 2.3.0 the preferred approach is to
|
||||
# configure an OIDC provider for user management and
|
||||
# authorization. See the text for details.
|
||||
oidcProviders: {}
|
||||
|
||||
# Support for "local" users is provided as a fallback mechanism.
|
||||
# Note that up to Version 2.2.x "users" was an object with user names
|
||||
# as its properties. Starting with 2.3.0 it is a list as shown.
|
||||
users:
|
||||
- name: admin
|
||||
fullName: Administrator
|
||||
password: "Generate hash with bcrypt"
|
||||
- name: test
|
||||
fullName: Test Account
|
||||
password: "Generate hash with bcrypt"
|
||||
|
||||
# Required for using OIDC, see the text for details.
|
||||
"/OidcClient":
|
||||
redirectUri: https://my.server.here/oauth/callback"
|
||||
|
||||
# May be used for assigning roles to both local users and users from
|
||||
# the OIDC provider. Not needed if roles are managed by the OIDC provider.
|
||||
"/RoleConfigurator":
|
||||
rolesByUser:
|
||||
# User admin has role admin
|
||||
admin:
|
||||
- admin
|
||||
# Non-privileged users are users
|
||||
test:
|
||||
- user
|
||||
# All users have role other
|
||||
"*":
|
||||
- other
|
||||
replace: false
|
||||
|
||||
# Manages the permissions for the roles.
|
||||
"/RoleConletFilter":
|
||||
conletTypesByRole:
|
||||
# Admins can use all conlets
|
||||
admin:
|
||||
- "*"
|
||||
# Users can use the viewer conlet
|
||||
user:
|
||||
- org.jdrupes.vmoperator.vmviewer.VmViewer
|
||||
# Others cannot use any conlet (except login conlet to log out)
|
||||
other:
|
||||
# Up to version 2.2.x
|
||||
# - org.jgrapes.webconlet.locallogin.LoginConlet
|
||||
# Starting with version 2.3.0
|
||||
- org.jgrapes.webconlet.oidclogin.LoginConlet
|
||||
```
|
||||
|
||||
How local users can be configured should be obvious from the example.
|
||||
The configuration of OIDC providers for user authentication (and
|
||||
optionally for role assignment) is explained in the documentation of the
|
||||
[login conlet](https://jgrapes.org/javadoc-webconsole/org/jgrapes/webconlet/oidclogin/LoginConlet.html).
|
||||
Details about the `RoleConfigurator` and `RoleConletFilter` can also be found
|
||||
in the documentation of the
|
||||
[JGrapes WebConsole](https://jgrapes.org/WebConsole.html)
|
||||
project.
|
||||
|
||||
The configuration above allows all users with role "admin" to use all
|
||||
GUI components and users with role "user" to only use the viewer conlet,
|
||||
i.e. the [User view](user-gui.html). The fallback role "other" allows
|
||||
all users to use the login conlet to log out.
|
||||
|
||||
## Views
|
||||
|
||||
The configuration of the components that provide the manager and
|
||||
users views is explained in the respective sections.
|
||||