mirror of
https://github.com/ComputerScienceHouse/proxstar.git
synced 2025-03-09 15:40:09 +00:00
use cloud-init for templates, add ssh key for cloud-init, delete expire before vm creation
This commit is contained in:
parent
dd876a8b2c
commit
a3f15134f2
12 changed files with 55 additions and 88 deletions
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -129,3 +129,7 @@ table, th, td {
|
|||
.swal-button--ok {
|
||||
background-color: #4caf50;
|
||||
}
|
||||
|
||||
#show-for-template {
|
||||
display: none
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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'] }}">
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
flask
|
||||
flask_pyoidc
|
||||
proxmoxer
|
||||
git+https://github.com/merinos/proxmoxer.git@python3#egg=bad01313d9f32a239882195325b21767a25f72e0
|
||||
psycopg2-binary
|
||||
sqlalchemy
|
||||
python-dateutil
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue