use cloud-init for templates, add ssh key for cloud-init, delete expire before vm creation

This commit is contained in:
Jordan Rodgers 2018-05-07 01:09:36 -04:00
parent dd876a8b2c
commit a3f15134f2
12 changed files with 55 additions and 88 deletions

View file

@ -410,6 +410,7 @@ def create():
template = request.form['template']
disk = request.form['disk']
iso = request.form['iso']
ssh_key = request.form['ssh_key']
if iso != 'none':
iso = "{}:iso/{}".format(app.config['PROXMOX_ISO_STORAGE'],
iso)
@ -438,17 +439,16 @@ def create():
iso,
timeout=300)
else:
password = gen_password(16)
q.enqueue(
setup_template_task,
template,
name,
username,
password,
ssh_key,
cores,
memory,
timeout=600)
return password, 200
return '', 200
return '', 200
else:
return '', 403
@ -557,10 +557,8 @@ def template_disk(template_id):
def template_edit(template_id):
if 'rtp' in session['userinfo']['groups']:
name = request.form['name']
username = request.form['username']
password = request.form['password']
disk = request.form['disk']
set_template_info(db, template_id, name, username, password, disk)
set_template_info(db, template_id, name, disk)
return '', 200
else:
return '', 403
@ -584,4 +582,4 @@ def exit_handler():
atexit.register(exit_handler)
if __name__ == "__main__":
app.run()
app.run(threaded=False)

View file

@ -145,8 +145,6 @@ def get_templates(db):
template_dict = dict()
template_dict['id'] = template.id
template_dict['name'] = template.name
template_dict['username'] = template.username
template_dict['password'] = template.password
template_dict['disk'] = template.disk
templates.append(template_dict)
return templates
@ -158,8 +156,6 @@ def get_template(db, template_id):
template = db.query(Template).filter(Template.id == template_id).one()
template_dict['id'] = template.id
template_dict['name'] = template.name
template_dict['username'] = template.username
template_dict['password'] = template.password
template_dict['disk'] = template.disk
return template_dict
@ -194,13 +190,10 @@ def delete_allowed_user(db, user):
db.commit()
def set_template_info(db, template_id, name, username, password, disk):
def set_template_info(db, template_id, name, disk):
if db.query(exists().where(Template.id == template_id, )).scalar():
template = db.query(Template).filter(Template.id == template_id,
).one()
template.name = name
template.username = username
if password:
template.password = password
template.disk = disk
db.commit()

View file

@ -34,8 +34,6 @@ class Template(Base):
__tablename__ = 'template'
id = Column(Integer, primary_key=True)
name = Column(String(32), nullable=False)
username = Column(Text, nullable=False)
password = Column(Text, nullable=False)
disk = Column(Integer, nullable=False)

View file

@ -10,8 +10,9 @@ def connect_proxmox():
proxmox = ProxmoxAPI(
host,
user=app.config['PROXMOX_USER'],
password=app.config['PROXMOX_PASS'],
verify_ssl=False)
private_key_file='proxmox_ssh_key',
password=app.config['PROXMOX_SSH_KEY_PASS'],
backend='ssh_paramiko')
version = proxmox.version.get()
return proxmox
except:

View file

@ -129,3 +129,7 @@ table, th, td {
.swal-button--ok {
background-color: #4caf50;
}
#show-for-template {
display: none
}

View file

@ -390,6 +390,7 @@ $("#create-vm").click(function(){
const cores = document.getElementById('cores').value;
const mem = document.getElementById('mem').value;
const template = document.getElementById('template').value;
const ssh_key = document.getElementById('ssh-key').value;
const iso = document.getElementById('iso').value;
const user = document.getElementById('user');
const max_cpu = $(this).data('max_cpu');
@ -451,6 +452,7 @@ $("#create-vm").click(function(){
data.append('template', template);
data.append('disk', disk);
data.append('iso', iso);
data.append('ssh_key', ssh_key);
if (user) {
data.append('user', user.value);
}
@ -459,12 +461,10 @@ $("#create-vm").click(function(){
method: 'post',
body: data
}).then((response) => {
return response.text()
}).then((password) => {
if (template == 'none') {
var swal_text = `${name} is now being created. Check back soon and it should be good to go.`
} else {
var swal_text = `${name} is now being created. Check back soon and it should be good to go. The SSH credentials are your CSH username for the user and ${password} for the password. Save this password because you will not be able to retrieve it again!`
var swal_text = `${name} is now being created. Check back soon and it should be good to go. The SSH credentials are your CSH username for the user and the SSH key you provided.`
}
return swal(`${swal_text}`, {
icon: "success",
@ -753,16 +753,19 @@ $(".add-ignored-pool").click(function(){
});
});
function hide_for_template(obj) {
function change_for_template(obj) {
var template_element = obj;
var selected = template_element.options[template_element.selectedIndex].value;
var hide_area = document.getElementById('hide-for-template');
var show_area = document.getElementById('show-for-template');
if(selected === 'none'){
if (selected === 'none') {
hide_area.style.display = 'block';
show_area.style.display = 'none';
}
else{
else {
hide_area.style.display = 'none';
show_area.style.display = 'block';
}
}
@ -869,7 +872,6 @@ $(".resize-disk").click(function(){
$(".edit-template").click(function(){
const template_id = $(this).data('template_id');
const template_name = $(this).data('template_name');
const template_username = $(this).data('template_username');
const template_disk = $(this).data('template_disk');
var options = document.createElement('div');
name_text = document.createElement('p');
@ -878,18 +880,6 @@ $(".edit-template").click(function(){
var name = document.createElement('input');
name.defaultValue = template_name;
options.append(name);
username_text = document.createElement('p');
username_text.innerHTML = 'Username';
options.append(username_text);
var username = document.createElement('input');
username.defaultValue = template_username;
options.append(username);
password_text = document.createElement('p');
password_text.innerHTML = 'Password';
options.append(password_text);
var password = document.createElement('input');
password.type = 'password';
options.append(password);
disk_text = document.createElement('p');
disk_text.innerHTML = 'Disk Size (GB)';
options.append(disk_text);
@ -917,8 +907,6 @@ $(".edit-template").click(function(){
if (willChange) {
var data = new FormData();
data.append('name', $(name).val());
data.append('username', $(username).val());
data.append('password', $(password).val());
data.append('disk', $(disk).val());
fetch(`/template/${template_id}/edit`, {
credentials: 'same-origin',

View file

@ -58,6 +58,7 @@ def create_vm_task(user, name, cores, memory, disk, iso):
get_next_ip(starrs, app.config['STARRS_IP_RANGE']))
job.meta['status'] = 'setting VM expiration'
job.save_meta()
delete_vm_expire(db, vmid)
get_vm_expire(db, vmid, app.config['VM_EXPIRE_MONTHS'])
job.meta['status'] = 'complete'
job.save_meta()
@ -114,7 +115,7 @@ def generate_pool_cache_task():
store_pool_cache(db, pools)
def setup_template_task(template_id, name, user, password, cores, memory):
def setup_template_task(template_id, name, user, ssh_key, cores, memory):
with app.app_context():
job = get_current_job()
proxmox = connect_proxmox()
@ -139,6 +140,11 @@ def setup_template_task(template_id, name, user, password, cores, memory):
vm = VM(vmid)
vm.set_cpu(cores)
vm.set_mem(memory)
print("[{}] Applying cloud-init config.".format(name))
job.meta['status'] = 'applying cloud-init'
vm.set_ci_user(user)
vm.set_ci_ssh_key(ssh_key)
vm.set_ci_network()
print(
"[{}] Waiting for STARRS to propogate before starting VM.".format(
name))
@ -149,46 +155,6 @@ def setup_template_task(template_id, name, user, password, cores, memory):
job.meta['status'] = 'starting VM'
job.save_meta()
vm.start()
print("[{}] Waiting for VM to start before SSHing.".format(name))
job.meta['status'] = 'waiting for VM to start'
job.save_meta()
time.sleep(20)
print("[{}] Creating SSH session.".format(name))
job.meta['status'] = 'creating SSH session'
job.save_meta()
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
retry = 0
while retry < 30:
try:
client.connect(
ip,
username=template['username'],
password=template['password'])
break
except:
retry += 1
time.sleep(3)
print("[{}] Running user creation commands.".format(name))
job.meta['status'] = 'running user creation commands'
job.save_meta()
stdin, stdout, stderr = client.exec_command("useradd {}".format(user))
exit_status = stdout.channel.recv_exit_status()
root_password = gen_password(32)
stdin, stdout, stderr = client.exec_command(
"echo '{}' | passwd root --stdin".format(root_password))
exit_status = stdout.channel.recv_exit_status()
stdin, stdout, stderr = client.exec_command(
"echo '{}' | passwd '{}' --stdin".format(password, user))
exit_status = stdout.channel.recv_exit_status()
stdin, stdout, stderr = client.exec_command(
"passwd -e '{}'".format(user))
exit_status = stdout.channel.recv_exit_status()
stdin, stdout, stderr = client.exec_command(
"echo '{} ALL=(ALL:ALL) ALL' | sudo EDITOR='tee -a' visudo".format(
user))
exit_status = stdout.channel.recv_exit_status()
client.close()
print("[{}] Template successfully provisioned.".format(name))
job.meta['status'] = 'completed'
job.save_meta()

View file

@ -39,8 +39,8 @@
</div>
<div class="form-group">
<label for="template" class="pull-left">Template</label>
<select name="template" id="template" class="form-control" onchange="hide_for_template(this)">
<option value="none"></option>
<select name="template" id="template" class="form-control" onchange="change_for_template(this)">
<option value="none">None</option>
{% for template in templates %}
<option value="{{ template['id'] }}">{{ template['name'] }} ({{ template['disk'] }}GB)</option>
{% endfor %}
@ -61,6 +61,12 @@
</select>
</div>
</div>
<div id="show-for-template">
<div class="form-group">
<label for="ssh-key" class="pull-left">Public SSH Key</label>
<input type="text" name="ssh-key" id="ssh-key" class="form-control">
</div>
</div>
{% if user['rtp'] %}
<div class="form-group">
<label for="user" class="pull-left">User</label>

View file

@ -14,7 +14,6 @@
<tr role="row">
<th>Proxmox ID</th>
<th>Name</th>
<th>Username</th>
<th>Disk Size (GB)</th>
<th>Delete</th>
</tr>
@ -24,10 +23,9 @@
<tr role="row">
<td>{{ template['id'] }}</td>
<td>{{ template['name'] }}</td>
<td>{{ template['username'] }}</td>
<td>{{ template['disk'] }}</td>
<td>
<button class="btn btn-sm btn-info edit-template" data-template_id="{{ template['id'] }}" data-template_name="{{ template['name'] }}" data-template_username="{{ template['username'] }}" data-template_disk="{{ template['disk'] }}">
<button class="btn btn-sm btn-info edit-template" data-template_id="{{ template['id'] }}" data-template_name="{{ template['name'] }}" data-template_disk="{{ template['disk'] }}">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-sm btn-danger delete-template" data-template_id="{{ template['id'] }}">

View file

@ -1,5 +1,6 @@
import time
import json
import urllib
from proxstar import db, starrs
from proxstar.db import get_vm_expire
from proxstar.util import lazy_property
@ -202,6 +203,19 @@ class VM(object):
def expire(self):
return get_vm_expire(db, self.id, app.config['VM_EXPIRE_MONTHS'])
def set_ci_user(self, user):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).config.put(ciuser=user)
def set_ci_ssh_key(self, ssh_key):
proxmox = connect_proxmox()
escaped_key = urllib.parse.quote(ssh_key, safe='')
proxmox.nodes(self.node).qemu(self.id).config.put(sshkey=escaped_key)
def set_ci_network(self):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).config.put(ipconfig0='ip=dhcp')
def create_vm(proxmox, user, name, cores, memory, disk, iso):
node = proxmox.nodes(get_node_least_mem(proxmox))
@ -237,7 +251,7 @@ def clone_vm(proxmox, template_id, name, pool):
name=name,
pool=pool,
full=1,
description='Managed by Proxstar',
description='Managed\ by\ Proxstar',
target=target)
retry = 0
while retry < 60:

View file

@ -80,6 +80,7 @@ def start_ssh_tunnel(node, port):
node,
ssh_username='root',
ssh_pkey='proxmox_ssh_key',
ssh_private_key_password=app.config['PROXMOX_SSH_KEY_PASS'],
remote_bind_address=('127.0.0.1', port),
local_bind_address=('127.0.0.1', port))
server.start()

View file

@ -1,6 +1,6 @@
flask
flask_pyoidc
proxmoxer
git+https://github.com/merinos/proxmoxer.git@python3#egg=bad01313d9f32a239882195325b21767a25f72e0
psycopg2-binary
sqlalchemy
python-dateutil