Commit 467a447e by Karsa Zoltán István

add extra parameters to cloud-init

parent a1e745ed
{% load i18n %}
<p class="text-right">
<a class="btn btn-primary" data-toggle="collapse" href="#collapseExample" role="button" aria-expanded="false" aria-controls="collapseExample">
Cloud-init options
</a>
</p>
<div class="collapse" id="collapseExample">
<div class="card card-body">
<b> {% trans "Use keys in ci-data:" %}</b>
<ul>
<li>{% trans "<strong>{{hostname}}</strong> - vm hostname" %}</li>
<li>{% trans "<strong>{{sysuser}}</strong> - vm system default user (cloud)" %}</li>
<li>{% trans "<strong>{{password}}</strong> - default user sha512 password" %}</li>
<li>{% trans "<strong>{{owner}}</strong> - vm owner django username" %}</li>
<li>{% trans "<strong>{{acl.allusers}}</strong> - get all associated user's username to vm" %}</li>
<li>{% trans "<strong>{{acl.operators}}</strong> - get all associated operator level user's username to vm" %}</li>
<li>{% trans "<strong>{{acl.users}}</strong> - get all associated operator user user's username to vm" %}</li>
<li>{% trans "<strong>{{net.ipv4}}</strong> - default host ip address" %}</li>
<li>{% trans "<strong>{{net.ipv6}}</strong> - default host ipv6 address" %}</li>
<li>{% trans "<strong>{{net.[vlan_name].ipv4/ipv6}}</strong> - associated vlan ([vlan_name]) host ip addresses, example: when vm associated to vlan 'public' and vlan 'internal', we can get these ips: {{net.public.ipv4}} and {{net.internal.ipv4}}" %}</li>
<li>{% trans "<strong>{{rndstr%[length]}}</strong> - make random string with [length] charachters lenght" %}</li>
</ul>
</div>
</div>
\ No newline at end of file
......@@ -27,6 +27,7 @@
{{ form.description|as_crispy_field }}
{{ form.system|as_crispy_field }}
<hr/>
{% include "dashboard/_ci-data-help.html" %}
{{ form.cloud_init|as_crispy_field }}
{{ form.ci_meta_data|as_crispy_field }}
{{ form.ci_user_data|as_crispy_field }}
......
......@@ -53,6 +53,7 @@
{{ form.system|as_crispy_field }}
{{ form.has_agent|as_crispy_field }}
<hr/>
{% include "dashboard/_ci-data-help.html" %}
{{ form.cloud_init|as_crispy_field }}
{{ form.ci_meta_data|as_crispy_field }}
{{ form.ci_user_data|as_crispy_field }}
......
......@@ -86,8 +86,11 @@
<h3>
{% trans "Cloud-init" %}
</h3>
{% crispy ci_data %}
<p>Keys (in "{{ ci_key }}") mapping for cloud-init:</p>
<small>
{{ci_data_mapping}}
</small>
</div>
</div>
{% endif %}
......
......@@ -56,7 +56,7 @@ def domain_validator(value):
def meta_data_validator(value):
try:
Instance.validate_meta_data(value)
Instance.validate_ci_data(value)
except yaml.YAMLError as exc:
if hasattr(exc, 'problem_mark'):
if exc.context != None:
......@@ -72,7 +72,7 @@ def meta_data_validator(value):
def user_data_validator(value):
try:
Instance.validate_user_data(value)
Instance.validate_ci_data(value)
except yaml.YAMLError as exc:
if hasattr(exc, 'problem_mark'):
if exc.context != None:
......
......@@ -146,6 +146,8 @@ class VmDetailView(GraphMixin, CheckedDetailView):
'connect_commands': user.profile.get_connect_commands(instance),
'hide_tutorial': hide_tutorial,
'fav': instance.favourite_set.filter(user=user).exists(),
'ci_data_mapping' : json.dumps(instance.get_ci_data_dict()),
'ci_key': '{{key}}'
})
# activity data
......
......@@ -15,11 +15,13 @@
# You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
import random, string
from contextlib import contextmanager
from datetime import timedelta
from dbm import dumb
from functools import partial
from importlib import import_module
from ipaddress import ip_interface
from logging import getLogger
from warnings import warn
from xml.dom.minidom import Text
......@@ -39,6 +41,7 @@ from django.utils.translation import ugettext_lazy as _, ugettext_noop
from passlib.hash import sha512_crypt
import yaml
import re
from model_utils import Choices
from model_utils.managers import QueryManager
......@@ -46,7 +49,7 @@ from model_utils.models import TimeStampedModel, StatusModel
from taggit.managers import TaggableManager
from django.db import models
import json
from acl.models import AclBase
from django import forms
from common.models import (
......@@ -81,7 +84,7 @@ CI_USER_DATA_DEF = """
users:
- default
- name: {{user}}
- name: {{sysuser}}
sudo: ['ALL=(ALL) NOPASSWD:ALL']
groups: sudo
shell: /bin/bash
......@@ -139,9 +142,9 @@ class VirtualMachineDescModel(BaseResourceConfigModel):
cloud_init = BooleanField(verbose_name=_('Use Cloud-init'), default=False,
help_text=_(
'VM use cloud-init, set user- and meta-data below'))
ci_meta_data = TextField(verbose_name=_('ci_meta_data'), blank=True, help_text=_(
ci_meta_data = TextField(verbose_name=_('CI Meta Data'), blank=True, help_text=_(
'When cloud-init is active, set meta-data (YAML format)'), default=CI_META_DATA_DEF)
ci_user_data = TextField(verbose_name=_('ci_user_data'), blank=True, help_text=_(
ci_user_data = TextField(verbose_name=_('CI User Data'), blank=True, help_text=_(
'When cloud-init is active, set user-data (YAML format)'), default=CI_USER_DATA_DEF)
req_traits = ManyToManyField(Trait, blank=True,
help_text=_("A set of traits required for a "
......@@ -364,33 +367,60 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
DeprecationWarning)
return self.status
def get_ci_data_dict(self):
datas = {
"sysuser": "cloud",
"hostname": self.short_hostname,
"password": sha512_crypt.hash(self.pw),
"net.ipv4": str(self.ipv4),
"net.ipv6": str(self.ipv6),
"owner": str(self.owner.username)
}
for net in self.interface_set.all():
if net.host:
datas["net.%s.ipv4" % net.vlan.name] = str(net.host.ipv4)
datas["net.%s.ipv6" % net.vlan.name] = str(net.host.ipv6)
user_levels = list({ 'username': u.username, 'level': l } for u, l in self.get_users_with_level())
datas['acl.allusers'] = yaml.dump(list(k['username'] for k in user_levels), default_flow_style=None)
datas['acl.operators'] = yaml.dump(list(k['username'] for k in user_levels if k['level'] == 'operator'), default_flow_style=None)
datas['acl.users'] = yaml.dump(list(k['username'] for k in user_levels if k['level'] == 'user'), default_flow_style=None)
return datas
@property
def get_user_data(self):
passwd = sha512_crypt.hash(self.pw)
user = 'cloud'
data = str(self.ci_user_data).replace('{{user}}', user)
data = data.replace('{{password}}', passwd)
data = str(self.ci_user_data)
ci_datas = self.get_ci_data_dict()
for k, v in ci_datas.items():
repl = '{{%s}}' % k
data = data.replace(repl, v)
data = Instance.__replace_rndstr(data)
return data
@classmethod
def validate_user_data(cls, user_data):
data = user_data.replace('{{user}}', 'user')
data = data.replace('{{password}}', 'passwd')
yaml.dump(yaml.load(data, Loader=yaml.Loader))
return True
@property
def get_meta_data(self):
data = str(self.ci_meta_data).replace('{{hostname}}', self.short_hostname)
data = str(self.ci_meta_data)
ci_datas = self.get_ci_data_dict()
for k, v in ci_datas.items():
repl = '{{%s}}' % k
data = data.replace(repl, v)
data = Instance.__replace_rndstr(data)
return data
@classmethod
def __replace_rndstr(cls, data):
search = re.search(r'\{\{rndstr%(\d+)\}\}', data)
while search:
randomstr = ''.join(random.choice(string.ascii_letters) for i in range(int(search.group(1))))
data = data.replace(search.group(0), randomstr, 1)
search = re.search(r'\{\{rndstr%(\d+)\}\}', data)
return data
@classmethod
def validate_meta_data(cls, meta_data):
data = meta_data.replace('{{hostname}}', 'hostname')
def validate_ci_data(cls, meta_data):
data = re.sub(r'\{\{([a-zA-Z_.0-9%]+)\}\}', 'ci-data', meta_data)
yaml.dump(yaml.load(data, Loader=yaml.Loader))
return True
def _update_status(self):
"""Set the proper status of the instance to Instance.status.
"""
......
......@@ -498,6 +498,7 @@ class DeployOperation(InstanceOperation):
description = _("create cloud init iso from user and meta-data")
def _operation(self, user, activity, name=None):
import json
logger.info('create ci image')
disk = Disk.create_ci_disk(meta_data=self.instance.get_meta_data,
user_data=self.instance.get_user_data)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment