Merge pull request #297 from zhongyehong/master

Implement addnode function on aliyun cloud
This commit is contained in:
zhong yehong 2018-05-20 17:42:35 +08:00 committed by GitHub
commit fbe88b0cbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 368 additions and 268 deletions

11
cloudsdk-installer.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
if [[ "`whoami`" != "root" ]]; then
echo "FAILED: Require root previledge !" > /dev/stderr
exit 1
fi
pip3 install aliyun-python-sdk-core-v3
pip3 install aliyun-python-sdk-ecs
exit 0

View File

@ -178,3 +178,7 @@
# The two ports next to '-' are inclueded. If there are several ranges,
# Please seperate them by ',' , for example: 10000-20000,30000-40000
# ALLOCATED_PORTS=10000-65535
# ALLOW_SCALE_OUT: allow docklet to rent server on the cloud to scale out
# Only when you deploy docklet on the cloud can you set it to True
# ALLOW_SCALE_OUT=False

View File

@ -15,13 +15,13 @@ fi
# install packages that docklet needs (in ubuntu)
# some packages' name maybe different in debian
apt-get install -y cgmanager lxc lxcfs lxc-templates lvm2 bridge-utils curl exim4 openssh-server openvswitch-switch
apt-get install -y python3 python3-netifaces python3-flask python3-flask-sqlalchemy python3-pampy python3-httplib2
apt-get install -y python3 python3-netifaces python3-flask python3-flask-sqlalchemy python3-pampy python3-httplib2 python3-pip
apt-get install -y python3-psutil python3-flask-migrate
apt-get install -y python3-lxc
apt-get install -y python3-requests python3-suds
apt-get install -y nodejs nodejs-legacy npm
apt-get install -y etcd
apt-get install -y glusterfs-client
apt-get install -y glusterfs-client attr
apt-get install -y nginx
#add ip forward
@ -55,7 +55,7 @@ mkdir -p /opt/docklet/local/
echo "directory /opt/docklet have been created"
if [ ! -d /opt/docklet/local/basefs ]; then
if [[ ! -d /opt/docklet/local/basefs && ! $1 = "withoutfs" ]]; then
mkdir -p /opt/docklet/local/basefs
echo "Generating basefs"
wget -P /opt/docklet/local http://iwork.pku.edu.cn:1616/basefs-0.11.tar.bz2 && tar xvf /opt/docklet/local/basefs-0.11.tar.bz2 -C /opt/docklet/local/ > /dev/null

196
src/cloudmgr.py Executable file
View File

@ -0,0 +1,196 @@
#!/usr/bin/python3
from io import StringIO
import os,sys,subprocess,time,re,datetime,threading,random,shutil
from model import db, Image
from deploy import *
import json
from log import logger
import env
import requests
fspath = env.getenv('FS_PREFIX')
class AliyunMgr():
def __init__(self):
self.AcsClient = __import__('aliyunsdkcore.client', fromlist=["AcsClient"])
self.Request = __import__('aliyunsdkecs.request.v20140526', fromlist=[
"CreateInstanceRequest",
"StopInstanceRequest",
"DescribeInstancesRequest",
"DeleteInstanceRequest",
"StartInstanceRequest",
"DescribeInstancesRequest",
"AllocateEipAddressRequest",
"AssociateEipAddressRequest"])
def loadClient(self):
if not os.path.exists(fspath+"/global/sys/cloudsetting.json"):
currentfilepath = os.path.dirname(os.path.abspath(__file__))
templatefilepath = currentfilepath + "/../tools/cloudsetting.aliyun.template.json"
shutil.copyfile(templatefilepath,fspath+"/global/sys/cloudsetting.json")
logger.error("please modify the setting file first")
return False
try:
settingfile = open(fspath+"/global/sys/cloudsetting.json", 'r')
self.setting = json.loads(settingfile.read())
settingfile.close()
self.clt = self.AcsClient.AcsClient(self.setting['AccessKeyId'],self.setting['AccessKeySecret'], self.setting['RegionId'])
logger.info("load CLT of Aliyun success")
return True
except Exception as e:
logger.error(e)
return False
def createInstance(self):
request = self.Request.CreateInstanceRequest.CreateInstanceRequest()
request.set_accept_format('json')
request.add_query_param('RegionId', self.setting['RegionId'])
if 'ZoneId' in self.setting and not self.setting['ZoneId'] == "":
request.add_query_param('ZoneId', self.setting['ZoneId'])
if 'VSwitchId' in self.setting and not self.setting['VSwitchId'] == "":
request.add_query_param('VSwitchId', self.setting['VSwitchId'])
request.add_query_param('ImageId', 'ubuntu_16_0402_64_20G_alibase_20170818.vhd')
request.add_query_param('InternetMaxBandwidthOut', 1)
request.add_query_param('InstanceName', 'docklet_tmp_worker')
request.add_query_param('HostName', 'worker-tmp')
request.add_query_param('SystemDisk.Size', int(self.setting['SystemDisk.Size']))
request.add_query_param('InstanceType', self.setting['InstanceType'])
request.add_query_param('Password', self.setting['Password'])
response = self.clt.do_action_with_exception(request)
logger.info(response)
instanceid=json.loads(bytes.decode(response))['InstanceId']
return instanceid
def startInstance(self, instanceid):
request = self.Request.StartInstanceRequest.StartInstanceRequest()
request.set_accept_format('json')
request.add_query_param('InstanceId', instanceid)
response = self.clt.do_action_with_exception(request)
logger.info(response)
def createEIP(self):
request = self.Request.AllocateEipAddressRequest.AllocateEipAddressRequest()
request.set_accept_format('json')
request.add_query_param('RegionId', self.setting['RegionId'])
response = self.clt.do_action_with_exception(request)
logger.info(response)
response=json.loads(bytes.decode(response))
eipid=response['AllocationId']
eipaddr=response['EipAddress']
return [eipid, eipaddr]
def associateEIP(self, instanceid, eipid):
request = self.Request.AssociateEipAddressRequest.AssociateEipAddressRequest()
request.set_accept_format('json')
request.add_query_param('AllocationId', eipid)
request.add_query_param('InstanceId', instanceid)
response = self.clt.do_action_with_exception(request)
logger.info(response)
def getInnerIP(self, instanceid):
request = self.Request.DescribeInstancesRequest.DescribeInstancesRequest()
request.set_accept_format('json')
response = self.clt.do_action_with_exception(request)
instances = json.loads(bytes.decode(response))['Instances']['Instance']
for instance in instances:
if instance['InstanceId'] == instanceid:
return instance['NetworkInterfaces']['NetworkInterface'][0]['PrimaryIpAddress']
return json.loads(bytes.decode(response))['Instances']['Instance'][0]['VpcAttributes']['PrivateIpAddress']['IpAddress'][0]
def isStarted(self, instanceids):
request = self.Request.DescribeInstancesRequest.DescribeInstancesRequest()
request.set_accept_format('json')
response = self.clt.do_action_with_exception(request)
instances = json.loads(bytes.decode(response))['Instances']['Instance']
for instance in instances:
if instance['InstanceId'] in instanceids:
if not instance['Status'] == "Running":
return False
return True
def rentServers(self,number):
instanceids=[]
eipids=[]
eipaddrs=[]
for i in range(int(number)):
instanceids.append(self.createInstance())
time.sleep(2)
time.sleep(10)
for i in range(int(number)):
[eipid,eipaddr]=self.createEIP()
eipids.append(eipid)
eipaddrs.append(eipaddr)
time.sleep(2)
masterip=env.getenv('ETCD').split(':')[0]
for i in range(int(number)):
self.associateEIP(instanceids[i],eipids[i])
time.sleep(2)
time.sleep(5)
for instanceid in instanceids:
self.startInstance(instanceid)
time.sleep(2)
time.sleep(10)
while not self.isStarted(instanceids):
time.sleep(10)
time.sleep(5)
return [masterip, eipaddrs]
def addNode(self):
if not self.loadClient():
return {'success':'false'}
[masterip, eipaddrs] = self.rentServers(1)
threads = []
for eip in eipaddrs:
thread = threading.Thread(target = deploy, args=(eip,masterip,'root',self.setting['Password'],self.setting['VolumeName']))
thread.setDaemon(True)
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
return {'success':'true'}
def addNodeAsync(self):
thread = threading.Thread(target = self.addNode)
thread.setDaemon(True)
thread.start()
class EmptyMgr():
def addNodeAsync(self):
logger.error("current cluster does not support scale out")
return False
class CloudMgr():
def getSettingFile(self):
if not os.path.exists(fspath+"/global/sys/cloudsetting.json"):
currentfilepath = os.path.dirname(os.path.abspath(__file__))
templatefilepath = currentfilepath + "/../tools/cloudsetting.aliyun.template.json"
shutil.copyfile(templatefilepath,fspath+"/global/sys/cloudsetting.json")
settingfile = open(fspath+"/global/sys/cloudsetting.json", 'r')
setting = settingfile.read()
settingfile.close()
return {'success':'true', 'result':setting}
def modifySettingFile(self, setting):
if setting == None:
logger.error("setting is None")
return {'success':'false'}
settingfile = open(fspath+"/global/sys/cloudsetting.json", 'w')
settingfile.write(setting)
settingfile.close()
return {'success':'true'}
def __init__(self):
if env.getenv("ALLOW_SCALE_OUT") == "True":
self.engine = AliyunMgr()
else:
self.engine = EmptyMgr()

53
src/deploy.py Executable file
View File

@ -0,0 +1,53 @@
#!/usr/bin/python3
import paramiko, time
from log import logger
import env,os
def myexec(ssh,command):
stdin,stdout,stderr = ssh.exec_command(command)
endtime = time.time() + 3600
while not stdout.channel.eof_received:
time.sleep(2)
if time.time() > endtime:
stdout.channel.close()
logger.error(command + ": fail")
return
# for line in stdout.readlines():
# if line is None:
# time.sleep(5)
# else:
# print(line)
def deploy(ipaddr,masterip,account,password,volumename):
while True:
try:
transport = paramiko.Transport((ipaddr,22))
transport.connect(username=account,password=password)
break
except Exception as e:
time.sleep(2)
pass
sftp = paramiko.SFTPClient.from_transport(transport)
currentfilepath = os.path.dirname(os.path.abspath(__file__))
deployscriptpath = currentfilepath + "/../tools/docklet-deploy.sh"
sftp.put(deployscriptpath,'/root/docklet-deploy.sh')
sftp.put('/etc/hosts', '/etc/hosts')
transport.close()
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
while True:
try:
ssh.connect(ipaddr, username = account, password = password, timeout = 300)
break
except Exception as e:
time.sleep(2)
pass
myexec(ssh,"sed -i 's/%MASTERIP%/" + masterip + "/g' /root/docklet-deploy.sh")
myexec(ssh,"sed -i 's/%VOLUMENAME%/" + volumename + "/g' /root/docklet-deploy.sh")
myexec(ssh,'chmod +x /root/docklet-deploy.sh')
myexec(ssh,'/root/docklet-deploy.sh')
ssh.close()
return

View File

@ -77,5 +77,7 @@ def getenv(key):
return os.environ.get("APPROVAL_RBT","ON")
elif key =="ALLOCATED_PORTS":
return os.environ.get("ALLOCATED_PORTS","10000-65535")
elif key =="ALLOW_SCALE_OUT":
return os.environ.get("ALLOW_SCALE_OUT", "False")
else:
return os.environ.get(key,"")

View File

@ -23,7 +23,7 @@ import os
import http.server, cgi, json, sys, shutil
import xmlrpc.client
from socketserver import ThreadingMixIn
import nodemgr, vclustermgr, etcdlib, network, imagemgr, notificationmgr, lockmgr
import nodemgr, vclustermgr, etcdlib, network, imagemgr, notificationmgr, lockmgr, cloudmgr
from logs import logs
import userManager,beansapplicationmgr
import monitor,traceback
@ -438,6 +438,31 @@ def copytarget_image(user, beans, form):
return json.dumps({'success':'false', 'message':str(e)})
return json.dumps({'success':'true', 'action':'copy image to target.'})
@app.route("/cloud/setting/get/", methods=['POST'])
@login_required
def query_account_cloud(cur_user, user, form):
global G_cloudmgr
logger.info("handle request: cloud/setting/get/")
result = G_cloudmgr.getSettingFile()
return json.dumps(result)
@app.route("/cloud/setting/modify/", methods=['POST'])
@login_required
def modify_account_cloud(cur_user, user, form):
global G_cloudmgr
logger.info("handle request: cloud/setting/modify/")
result = G_cloudmgr.modifySettingFile(form.get('setting',None))
return json.dumps(result)
@app.route("/cloud/node/add/", methods=['POST'])
@login_required
def add_node_cloud(user, beans, form):
global G_cloudmgr
logger.info("handle request: cloud/node/add/")
G_cloudmgr.engine.addNodeAsync()
result = {'success':'true'}
return json.dumps(result)
@app.route("/addproxy/", methods=['POST'])
@login_required
def addproxy(user, beans, form):
@ -760,6 +785,7 @@ if __name__ == '__main__':
global G_historymgr
global G_applicationmgr
global G_ulockmgr
global G_cloudmgr
# move 'tools.loadenv' to the beginning of this file
fs_path = env.getenv("FS_PREFIX")
@ -851,6 +877,8 @@ if __name__ == '__main__':
G_networkmgr = network.NetworkMgr(clusternet, etcdclient, mode, ipaddr)
G_networkmgr.printpools()
G_cloudmgr = cloudmgr.CloudMgr()
# start NodeMgr and NodeMgr will wait for all nodes to start ...
G_nodemgr = nodemgr.NodeMgr(G_networkmgr, etcdclient, addr = ipaddr, mode=mode)
logger.info("nodemgr started")

View File

@ -968,62 +968,6 @@ class userManager:
lxcsettingfile.close()
return {"success": 'true'}
@administration_required
def cloud_account_query(*args, **kwargs):
accountfile = open(fspath+"/global/sys/cloudaccount", 'r')
account = json.loads(accountfile.read())
accountfile.close()
return {"success": 'true', 'accounts':account}
@administration_required
def cloud_account_add(*args, **kwargs):
form = kwargs.get('form')
accountfile = open(fspath+"/global/sys/cloudaccount", 'r')
account = json.loads(accountfile.read())
accountfile.close()
account.append(
{ 'cloudname' : form['cloudname'],
'username' : form['username'],
'password' : form['password'],
})
accountfile = open(fspath+"/global/sys/cloudaccount", 'w')
accountfile.write(json.dumps(account))
accountfile.close()
return {"success": 'true'}
@administration_required
def cloud_account_del(*args, **kwargs):
form = kwargs.get('form')
cloudname = form['cloudname']
accountfile = open(fspath+"/global/sys/cloudaccount", 'r')
account = json.loads(accountfile.read())
accountfile.close()
for acc in account:
if acc['cloudname'] == cloudname:
account.remove(acc)
break
accountfile = open(fspath+"/global/sys/cloudaccount", 'w')
accountfile.write(json.dumps(account))
accountfile.close()
return {"success": 'true'}
@administration_required
def cloud_account_modify(*args, **kwargs):
form = kwargs.get('form')
cloudname = form['cloudname']
accountfile = open(fspath+"/global/sys/cloudaccount", 'r')
account = json.loads(accountfile.read())
accountfile.close()
for acc in account:
if acc['cloudname'] == cloudname:
acc['username'] = form['username']
acc['password'] = form['password']
break
accountfile = open(fspath+"/global/sys/cloudaccount", 'w')
accountfile.write(json.dumps(account))
accountfile.close()
return {"success": "true"}
def queryForDisplay(*args, **kwargs):
'''

View File

@ -0,0 +1,12 @@
{
"CloudName": "aliyun",
"AccessKeyId": "your-key",
"AccessKeySecret": "your-secret",
"RegionId": "cn-beijing",
"ZoneId": "cn-beijing-a",
"InstanceType": "ecs.sn1ne.xlarge",
"SystemDisk.Size": 500,
"Password": "Unias1234",
"VSwitchId": "the vswitchid of your vpc",
"VolumeName": "the volume name (with host name) of your glusterfs"
}

View File

@ -1,53 +1,28 @@
if [ $# -lt 1 ]; then
echo "please input master ip";
exit 1;
fi
MASTER_IP=$1
#配置apt源
#echo "deb http://mirrors.ustc.edu.cn/ubuntu/ xenial main restricted universe multiverse
#deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-security main restricted universe multiverse
#deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-updates main restricted universe multiverse
#deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-proposed main restricted universe multiverse
#deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-backports main restricted universe multiverse" > /etc/apt/sources.list
#更新apt
apt-get update
#更新hosts
echo "$MASTER_IP docklet-master" >> /etc/hosts
apt-get install -y git
#下载git包
apt-get -y install git
#下载docklet源码
git clone http://github.com/unias/docklet.git /home/docklet
#运行prepare.sh
/home/docklet/prepare.sh
#挂载global目录,通过gluster方式
mount -t glusterfs docklet-master:/docklet /opt/docklet/global
#下载base镜像
wget http://docklet.unias.org/images/basefs-0.11.tar.bz2 -P /opt/local/temp
#解压镜像
tar -zxvf /opt/local/temp/basefs-0.11.tar.bz2 -C /opt/local/
#获得docklet.conf
cp /home/docklet/conf/docklet.conf.template /home/docklet/conf/docklet.conf
cp /home/docklet/web/templates/home.template /home/docklet/web/templates/home.html
#获得网卡名称
NETWORK_DEVICE=`route | grep default | awk {'print $8'};`
#更改配置文件
echo "DISKPOOL_SIZE=10000
ETCD=$MASTER_IP:2379
echo "DISKPOOL_SIZE=200000
ETCD=%MASTERIP%:2379
NETWORK_DEVICE=$NETWORK_DEVICE
PORTAL_URL=http://iwork.pku.edu.cn
PROXY_PORT=80" >> /home/docklet/conf/docklet.conf
PROXY_PORT=8000
NGINX_PORT=80" >> /home/docklet/conf/docklet.conf
#please modify the mount command for your corresponding distributed file system if you are not using glusterfs
mount -t glusterfs %VOLUMENAME% /opt/docklet/global/
if [ -f /opt/docklet/global/packagefs.tgz ]; then
tar zxvf /opt/docklet/global/packagefs.tgz -C /opt/docklet/local/ > /dev/null
fi
#启动worker
/home/docklet/bin/docklet-worker start
exit 0

View File

@ -164,38 +164,6 @@ def auth_token(cur_user, user, form):
logger.info("auth success")
return req
@app.route("/cloud/account/query/", methods=['POST'])
@login_required
def query_account_cloud(cur_user, user, form):
global G_usermgr
logger.info("handle request: cloud/account/query/")
result = G_usermgr.cloud_account_query(cur_user = cur_user)
return json.dumps(result)
@app.route("/cloud/account/add/", methods=['POST'])
@login_required
def add_account_cloud(cur_user, user, form):
global G_usermgr
logger.info("handle request: cloud/account/add/")
result = G_usermgr.cloud_account_add(cur_user = cur_user, form = form)
return json.dumps(result)
@app.route("/cloud/account/delete/", methods=['POST'])
@login_required
def del_account_cloud(cur_user, user, form):
global G_usermgr
logger.info("handle request: cloud/account/delete/")
result = G_usermgr.cloud_account_del(cur_user = cur_user, form = form)
return json.dumps(result)
@app.route("/cloud/account/modify/", methods=['POST'])
@login_required
def modify_account_cloud(cur_user, user, form):
global G_usermgr
logger.info("handle request: cloud/account/modify/")
result = G_usermgr.cloud_account_modify(cur_user = cur_user, form = form)
return json.dumps(result)
@app.route("/user/modify/", methods=['POST'])
@login_required

View File

@ -174,7 +174,7 @@
</li>
<li id="nav_Cloud">
<a href='/cloud/'><i class="fa fa-cloud"></i> <span class="nav-label">Cloud</span></a>
<a href="/cloud/"><i class="fa fa-cloud"></i> <span class="nav-label">Cloud</span></a>
<li>
<li id="logs">
<a href='/logs/'><i class="fa fa-user"></i> <span class="nav-label">Logs</span></a>

View File

@ -24,11 +24,27 @@
{% block content %}
<ul class="nav nav-tabs" role="tablist" id="myTabs">
{% for master,info in settings.items() %}
{% if loop.index == 1 %}
<li role="presentation" class="active"><a href="#{{master.split("@")[1]}}" data-toggle="tab" aria-controls="{{master.split("@")[1]}}">{{master.split("@")[1]}}</a></li>
{% else %}
<li role="presentation"><a href="#{{master.split("@")[1]}}" data-toggle="tab" aria-controls="{{master.split("@")[1]}}">{{master.split("@")[1]}}</a></li>
{% endif %}
{% endfor %}
</ul>
<div id="myTabContent" class="tab-content">
{% for master,info in settings.items() %}
{% if loop.index == 1 %}
<div role="tabpanel" class="tab-pane active" aria-labelledby="{{master.split("@")[1]}}" id="{{master.split("@")[1]}}">
{% else %}
<div role="tabpanel" class="tab-pane" aria-labelledby="{{master.split("@")[1]}}" id="{{master.split("@")[1]}}">
{% endif %}
<div class="row">
<div class="col-md-12">
<div class="box box-info">
<div class="box-header with-border">
<h3 class="box-title">Cloud</h3>
<h3 class="box-title">Cloud Setting</h3>
<div class="box-tools pull-right">
<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i>
@ -37,117 +53,21 @@
</button>
</div>
</div>
<div class="box-body">
<p>
<button type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#AddAccountModal"><i class="fa fa-plus"></i> Add Account</button>
</p>
<div class="modal inmodal" id="AddAccountModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content animated fadeIn">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
<i class="fa fa-laptop modal-icon"></i>
<h4 class="modal-title">Add Account</h4>
<small class="font-bold">Add a Cloud Account</small>
</div>
<div class="modal-body">
<form action="/cloud/account/add/" method="POST" id="addAccountForm">
<div class="form-group">
<label>Cloud name</label>
<input type="text" class="form-control" name="cloudname"/>
</div>
<div class="form-group">
<label>username</label>
<input type="text" class="form-control" name="username" />
</div>
<div class="form-group">
<label>password</label>
<input type="text" class="form-control" name="password" />
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-white" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" onClick="javascript:sendAddAccount();">Submit</button>
</div>
</div>
</div>
</div>
<div class="table table-responsive">
<table id="accountTable" class="table table-striped table-bordered">
<thead>
<tr>
<th>Cloud Name</th>
<th>username</th>
<th>password</th>
<th>Command</th>
</tr>
</thead>
<tbody>
{% for account in accounts %}
<tr>
<th>{{ account['cloudname'] }}</th>
<th>{{ account['username'] }}</th>
<th>{{ account['password'] }}</th>
<th><a class="btn btn-xs btn-info" data-toggle="modal" data-target="#ModifyAccountModal_{{ account['cloudname'] }}">Edit</a>&nbsp;
<a class="btn btn-xs btn-danger" href="/cloud/account/delete/{{account['cloudname']}}">Delete</a>&nbsp;
</th>
<div class="modal inmodal" id="ModifyAccountModal_{{ account['cloudname'] }}" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content animated fadeIn">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
<i class="fa fa-laptop modal-icon"></i>
<h4 class="modal-title">Modify Account</h4>
<small class="font-bold">Modify a Cloud Account</small>
</div>
<form action="/cloud/account/modify/{{account['cloudname']}}/" method="POST" >
<div class="modal-body">
<div class="form-group">
<label>Cloud Name</label>
<input type="text" placeholder="Enter Name" class="form-control" name="cloudname" readonly="true" value={{ account['cloudname'] }} />
</div>
<div class="form-group">
<label>username</label>
<input type="text" class="form-control" name="username" value={{ account['username']}} />
</div>
<div class="form-group">
<label>password</label>
<input type="text" class="form-control" name="password" value={{ account['password']}} />
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-white" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
</div>
</div>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="box-body table-responsive">
<form action="/cloud/{{master.split("@")[0]}}/setting/modify/" method="POST">
<textarea id="setting" name="setting" class="form-control" rows="20">{{ info['result'] }}</textarea>
<button type="submit" class="btn btn-primary">Save</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
{% endblock %}
{% block script_src %}
<script src="//cdn.bootcss.com/datatables/1.10.11/js/jquery.dataTables.min.js"></script>
<script src="//cdn.bootcss.com/datatables/1.10.11/js/dataTables.bootstrap.min.js"></script>
<script src="http://cdn.bootcss.com/datatables-tabletools/2.1.5/js/TableTools.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#accountTable').DataTable();
})
function sendAddAccount(){
document.getElementById("addAccountForm").submit();
}
</script>
{% endblock %}

View File

@ -46,7 +46,7 @@
</div>
<div class="box-body table-responsive">
<p>
<button type="button" class="btn btn-primary btn-sm" data-toggle="modal"><i class="fa fa-plus"></i>Add Host</button>
<a href="/cloud/{{master.split("@")[0]}}/node/add/"><button type="button" class="btn btn-primary btn-sm"><i class="fa fa-plus"></i>Add Host</button></a>
</p>
<table class="table table-bordered">
<thead>

View File

@ -517,22 +517,17 @@ def userquery():
def cloud():
return cloudView.as_view()
@app.route("/cloud/account/add/", methods = ['POST'])
@app.route("/cloud/<masterip>/setting/modify/", methods = ['POST'])
@administration_required
def cloud_account_add():
return cloudAccountAddView.as_view()
def cloud_setting_modify(masterip):
cloudSettingModifyView.masterip = masterip
return cloudSettingModifyView.as_view()
@app.route("/cloud/account/delete/<cloudname>/", methods = ['POST', 'GET'])
@app.route("/cloud/<masterip>/node/add/", methods = ['POST', 'GET'])
@administration_required
def cloud_account_del(cloudname):
cloudAccountDelView.cloudname = cloudname
return cloudAccountDelView.as_view()
@app.route("/cloud/account/modify/<cloudname>/", methods = ['POST'])
@administration_required
def cloud_account_modify(cloudname):
return cloudAccountModifyView.as_view()
def cloud_node_add(masterip):
cloudNodeAddView.masterip = masterip
return cloudNodeAddView.as_view()
@app.route("/notification/", methods=['GET'])
@ -606,6 +601,7 @@ def adminpage():
def updatesettings():
return updatesettingsView.as_view()
@app.route('/index/', methods=['GET'])
def jupyter_control():
return redirect('/dashboard/')

View File

@ -9,34 +9,26 @@ class cloudView(normalView):
@classmethod
def post(self):
accounts = dockletRequest.post('/cloud/account/query/').get('accounts',[])
return self.render(self.template_path, accounts = accounts)
settings = dockletRequest.post_to_all('/cloud/setting/get/')
return self.render(self.template_path, settings = settings)
@classmethod
def get(self):
return self.post()
class cloudAccountAddView(normalView):
class cloudSettingModifyView(normalView):
@classmethod
def post(self):
dockletRequest.post('/cloud/account/add/', request.form)
dockletRequest.post('/cloud/setting/modify/', request.form, self.masterip)
return redirect('/cloud/')
class cloudAccountDelView(normalView):
class cloudNodeAddView(normalView):
@classmethod
def post(self):
data = {
'cloudname' : self.cloudname,
}
dockletRequest.post('/cloud/account/delete/', data)
return redirect('/cloud/')
data = {}
dockletRequest.post('/cloud/node/add/', data, self.masterip)
return redirect('/hosts/')
@classmethod
def get(self):
return self.post()
class cloudAccountModifyView(normalView):
@classmethod
def post(self):
dockletRequest.post('/cloud/account/modify/', request.form)
return redirect('/cloud/')

View File

@ -38,7 +38,6 @@ class dockletRequest():
'user',
'beans',
'notification',
'cloud',
'settings'
}
if ":" not in endpoint: