Commit 6d803ade by Karsa Zoltán István

network config for cloud-init

parent b2ed9564
......@@ -60,7 +60,7 @@ from vm.models import (
InstanceTemplate, Lease, InterfaceTemplate, Node, Trait, Instance
)
from .models import Profile, GroupProfile, Message
from .validators import domain_validator, meta_data_validator, user_data_validator
from .validators import domain_validator, meta_data_validator, user_data_validator, network_data_validator
LANGUAGES_WITH_CODE = ((l[0], format_lazy("{} ({})",l[1], l[0]))
for l in LANGUAGES)
......@@ -1540,16 +1540,20 @@ class CIDataForm(forms.ModelForm):
ci_user_data = forms.CharField(
widget=forms.Textarea(attrs={'rows': 12, 'style' : 'display:none;'}),
required=False)
ci_network_config = forms.CharField(
widget=forms.Textarea(attrs={'rows': 5, 'style' : 'display:none;'}),
required=False)
def clean(self):
data = self.cleaned_data
meta_data_validator(self.instance, data['ci_meta_data'])
user_data_validator(self.instance, data['ci_user_data'])
network_data_validator(self.instance, data['ci_network_config'])
return super().clean()
class Meta:
model = Instance
fields = ('ci_meta_data', 'ci_user_data',)
fields = ('ci_meta_data', 'ci_user_data', 'ci_network_config',)
class GroupPermissionForm(forms.ModelForm):
......
......@@ -1374,3 +1374,8 @@ textarea[name="new_members"] {
width: 95%;
height: 350px;
}
#ace-network-data {
position: absolute;
width: 95%;
height: 150px;
}
\ No newline at end of file
......@@ -1608,3 +1608,9 @@ textarea[name="new_members"] {
width: 95%;
height: 350px;
}
#ace-network-data {
position: absolute;
width: 95%;
height: 150px;
}
\ No newline at end of file
var Websock_native; // not sure
$(function() {
if ($("#ace-meta-data").length && $("#ace-user-data").length) {
if ($("#ace-meta-data").length && $("#ace-user-data").length && $("#ace-network-data").length) {
var meta_data = ace.edit('ace-meta-data', {
mode: "ace/mode/yaml", useWorker: false,
selectionStyle: "text" });
......@@ -18,12 +18,23 @@ $(function() {
user_data.getSession().on("change", function () {
textarea_user.val(user_data.getSession().getValue());
});
var network_data = ace.edit('ace-network-data', {
mode: "ace/mode/yaml", useWorker: false,
selectionStyle: "text" });
var textarea_network = $('textarea[name="ci_network_config"]');
network_data.getSession().setValue(textarea_network.val())
network_data.getSession().on("change", function () {
textarea_network.val(network_data.getSession().getValue());
});
meta_data.session.setTabSize(4);
meta_data.session.setUseSoftTabs(true);
user_data.session.setTabSize(4);
user_data.session.setUseSoftTabs(true);
network_data.session.setTabSize(4);
network_data.session.setUseSoftTabs(true);
document.getElementById('ace-meta-data').style.fontSize='14px';
document.getElementById('ace-user-data').style.fontSize='14px';
document.getElementById('ace-network-data').style.fontSize='14px';
/* */
$('#vm-details-cidata-save').click(function(e) {
$.ajax({
......
......@@ -34,6 +34,7 @@
</div>
{{ form.cloud_init|as_crispy_field }}
{{ form.ci_meta_data|as_crispy_field }}
{{ form.ci_network_config|as_crispy_field }}
{{ form.ci_user_data|as_crispy_field }}
</fieldset>
......
......@@ -16,6 +16,9 @@
{{ ci_data.ci_meta_data|as_crispy_field }}
<div id="ace-meta-data" style="border: 1px solid #ccc;"></div>
<div style="height: 160px;"></div>
{{ ci_data.ci_network_config|as_crispy_field }}
<div id="ace-network-data" style="border: 1px solid #ccc;"></div>
<div style="height: 160px;"></div>
{{ ci_data.ci_user_data|as_crispy_field }}
<div id="ace-user-data" style="border: 1px solid #ccc;"></div>
<div style="height: 360px;"></div>
......
......@@ -90,6 +90,23 @@ def user_data_validator(instance, value):
except jinja2.exceptions.TemplateError as exc:
raise ValidationError(exc.message)
def network_data_validator(instance, value):
try:
instance.validate_ci_data(value)
except yaml.YAMLError as exc:
if hasattr(exc, 'problem_mark'):
if exc.context != None:
raise ValidationError(' parser says\n' + str(exc.problem_mark) + '\n ' +
str(exc.problem) + ' ' + str(exc.context) +
'\nPlease correct data and retry.')
else:
raise ValidationError(' parser says\n' + str(exc.problem_mark) + '\n ' +
str(exc.problem) + '\nPlease correct data and retry.')
else:
raise ValidationError("Something went wrong while parsing yaml file")
except jinja2.exceptions.TemplateError as exc:
raise ValidationError(exc.message)
def connect_command_template_validator(value):
"""Validate value as a connect command template.
......
......@@ -448,14 +448,14 @@ class Disk(TimeStampedModel):
return result
@classmethod
def create_ci_disk(cls, meta_data, user_data, user = None, **params):
def create_ci_disk(cls, meta_data, user_data, network_data, user = None, **params):
params.setdefault('name', 'ci-disk')
params.setdefault('type', 'raw-ro')
params.setdefault('size', None)
disk = cls.__create(params=params, user=user)
queue_name = disk.get_remote_queue_name('storage', priority="fast")
disk_desc = disk.get_disk_desc()
result = storage_tasks.create_ci_disk.apply_async(args=[disk_desc, meta_data, user_data],
result = storage_tasks.create_ci_disk.apply_async(args=[disk_desc, meta_data, user_data, network_data],
queue=queue_name
).get(timeout=15)
disk.size = result['size']
......
......@@ -34,7 +34,7 @@ def create(disk_desc):
@celery.task(name='storagedriver.create_ci_disk')
def create_ci_disk(disk_desc, meta_data, user_data):
def create_ci_disk(disk_desc, meta_data, user_data, network_data):
pass
......
# Generated by Django 3.2.3 on 2022-10-06 14:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('vm', '0011_instance_hookurl'),
]
operations = [
migrations.AddField(
model_name='instance',
name='ci_network_config',
field=models.TextField(blank=True, default='#cloud-config\n\nusers:\n - name: {{ sysuser }} \n sudo: [\'ALL=(ALL) NOPASSWD:ALL\']\n groups: sudo\n shell: /bin/bash\n ssh_pwauth: True\n chpasswd: { expire: False }\n lock-passwd: false\n passwd: "{{ password | hash }}"', help_text='When cloud-init is active, set network-config(YAML format)', verbose_name='CI Network Data'),
),
migrations.AddField(
model_name='instancetemplate',
name='ci_network_config',
field=models.TextField(blank=True, default='#cloud-config\n\nusers:\n - name: {{ sysuser }} \n sudo: [\'ALL=(ALL) NOPASSWD:ALL\']\n groups: sudo\n shell: /bin/bash\n ssh_pwauth: True\n chpasswd: { expire: False }\n lock-passwd: false\n passwd: "{{ password | hash }}"', help_text='When cloud-init is active, set network-config(YAML format)', verbose_name='CI Network Data'),
),
]
# Generated by Django 3.2.3 on 2022-10-06 15:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('vm', '0012_auto_20221006_1433'),
]
operations = [
migrations.AlterField(
model_name='instance',
name='ci_network_config',
field=models.TextField(blank=True, default="network:\n version: 2\n ethernets:\n ens3:\n match:\n macaddress: 'mac'\n addresses: \n - ipv4/24\n gateway4: ipv4\n nameservers:\n addresses:\n - 8.8.8.8", help_text='When cloud-init is active, set network-config(YAML format)', verbose_name='CI Network Data'),
),
migrations.AlterField(
model_name='instancetemplate',
name='ci_network_config',
field=models.TextField(blank=True, default="network:\n version: 2\n ethernets:\n ens3:\n match:\n macaddress: 'mac'\n addresses: \n - ipv4/24\n gateway4: ipv4\n nameservers:\n addresses:\n - 8.8.8.8", help_text='When cloud-init is active, set network-config(YAML format)', verbose_name='CI Network Data'),
),
]
......@@ -88,15 +88,34 @@ platform: circle3
CI_USER_DATA_DEF = """
#cloud-config
ssh_pwauth: 1
users:
- name: {{ sysuser }}
sudo: ['ALL=(ALL) NOPASSWD:ALL']
groups: sudo
shell: /bin/bash
ssh_pwauth: True
chpasswd: { expire: False }
lock-passwd: false
passwd: "{{ password | hash }}"
chpasswd:
list: |
{{ sysuser }}:{{ password }}
expire: False
""".strip()
CI_NETWORK_CONFIG_DEF = """
network:
version: 2
ethernets:
ens3:
match:
macaddress: 'mac'
addresses:
- ipv4/24
gateway4: ipv4
nameservers:
addresses:
- 8.8.8.8
""".strip()
try:
......@@ -151,6 +170,8 @@ class VirtualMachineDescModel(BaseResourceConfigModel):
'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=_(
'When cloud-init is active, set user-data (YAML format)'), default=CI_USER_DATA_DEF)
ci_network_config = TextField(verbose_name=_('CI Network Data'), blank=True, help_text=_(
'When cloud-init is active, set network-config(YAML format)'), default=CI_NETWORK_CONFIG_DEF)
req_traits = ManyToManyField(Trait, blank=True,
help_text=_("A set of traits required for a "
"node to declare to be suitable "
......@@ -450,6 +471,13 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
template = env.from_string(data)
return template.render(ci_datas)
@property
def get_network_config(self):
data = str(self.ci_network_config)
ci_datas = self.get_ci_data_dict()
template = env.from_string(data)
return template.render(ci_datas)
def validate_ci_data(self, data):
ci_datas = self.get_ci_data_dict()
template = env.from_string(data)
......@@ -460,8 +488,8 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
def callhookurl(self):
import requests as req
if self.hookurl:
logger.info("notify remote host (callhookurl)")
req.post(url=self.hookurl)
logger.info("notify remote host (callhookurl): " + str(self.hookurl))
req.post(url=str(self.hookurl))
def _update_status(self):
......
......@@ -539,14 +539,16 @@ class DeployOperation(InstanceOperation):
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)
user_data=self.instance.get_user_data,
network_data=self.instance.get_network_config)
disk.full_clean()
disk.save()
self.instance.disks.add(disk)
return create_readable(ugettext_noop(
" ---- META-DATA ----\n%(meta_data)s\n\n ---- USER-DATA ----\n%(user_data)s"),
" ---- META-DATA ----\n%(meta_data)s\n\n ---- NETWORK-CONFIG ----\n%(network_data)s\n\n ---- USER-DATA ----\n%(user_data)s"),
meta_data=self.instance.get_meta_data,
user_data=self.instance.get_user_data)
user_data=self.instance.get_user_data,
network_data=self.instance.get_network_config)
@register_operation
class DeployDisksOperation(SubOperationMixin, InstanceOperation):
......@@ -616,6 +618,7 @@ class DestroyOperation(InstanceOperation):
self.instance.destroyed_at = timezone.now()
self.instance.save()
logger.info('destroy vm operation ' + str(system))
if system:
self.instance.callhookurl()
......@@ -1029,6 +1032,7 @@ class SleepOperation(InstanceOperation):
self.instance.shutdown_net()
self.instance._suspend_vm(parent_activity=activity)
self.instance.yield_node()
logger.info('sleep vm operation ' + str(system))
if system:
self.instance.callhookurl()
......@@ -1502,7 +1506,7 @@ class CIDataChangeOperation(InstanceOperation):
accept_states = ('STOPPED', 'PENDING')
def _operation(self, user, activity,
ci_meta_data, ci_user_data,
ci_meta_data, ci_user_data, ci_network_config,
with_shutdown=False, task=None):
logger.debug('save cloud-init: %s \n %s', ci_meta_data, ci_user_data)
if self.instance.status == 'RUNNING' and not with_shutdown:
......@@ -1517,6 +1521,7 @@ class CIDataChangeOperation(InstanceOperation):
self.instance.ci_meta_data = ci_meta_data
self.instance.ci_user_data = ci_user_data
self.instance.ci_network_config = ci_network_config
self.instance.full_clean()
self.instance.save()
......
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