Implement addnode function on aliyun cloud

This commit is contained in:
zhongyehong 2018-05-20 16:05:11 +08:00
parent 2ca69b8e47
commit d3750eb7f8
11 changed files with 122 additions and 101 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

@ -15,13 +15,13 @@ fi
# install packages that docklet needs (in ubuntu) # install packages that docklet needs (in ubuntu)
# some packages' name maybe different in debian # 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 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-psutil python3-flask-migrate
apt-get install -y python3-lxc apt-get install -y python3-lxc
apt-get install -y python3-requests python3-suds apt-get install -y python3-requests python3-suds
apt-get install -y nodejs nodejs-legacy npm apt-get install -y nodejs nodejs-legacy npm
apt-get install -y etcd apt-get install -y etcd
apt-get install -y glusterfs-client apt-get install -y glusterfs-client attr
apt-get install -y nginx apt-get install -y nginx
#add ip forward #add ip forward
@ -55,7 +55,7 @@ mkdir -p /opt/docklet/local/
echo "directory /opt/docklet have been created" 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 mkdir -p /opt/docklet/local/basefs
echo "Generating 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 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

View File

@ -3,6 +3,7 @@ from io import StringIO
import os,sys,subprocess,time,re,datetime,threading,random import os,sys,subprocess,time,re,datetime,threading,random
from model import db, Image from model import db, Image
from deploy import * from deploy import *
import json
from log import logger from log import logger
import env import env
@ -10,67 +11,54 @@ import requests
fspath = env.getenv('FS_PREFIX') fspath = env.getenv('FS_PREFIX')
class CludAccountMgr():
def cloud_account_query(*args, **kwargs):
try:
accountfile = open(fspath+"/global/sys/cloudaccount", 'r')
account = json.loads(accountfile.read())
accountfile.close()
except:
account = {}
account['cloud'] = env.getenv('CLOUD')
account['accesskey'] = ""
account['accesssecret'] = ""
return {"success": 'true', 'accounts':account}
def cloud_account_modify(*args, **kwargs):
form = kwargs.get('form')
account = {}
account['cloud'] = form['cloud']
account['accesskey'] = form['accesskey']
account['accesssecret'] = form['accesssecret']
accountfile = open(fspath+"/global/sys/cloudaccount", 'w')
accountfile.write(json.dumps(account))
accountfile.close()
return {"success": "true"}
class AliyunMgr(): class AliyunMgr():
def __init__(self): def __init__(self):
self.AcsClient = __import__('aliyunsdkcore.client') self.AcsClient = __import__('aliyunsdkcore.client', fromlist=["AcsClient"])
self.Request = __import__('aliyunsdkecs.request.v20140526') self.Request = __import__('aliyunsdkecs.request.v20140526', fromlist=[
"CreateInstanceRequest",
"StopInstanceRequest",
"DescribeInstancesRequest",
"DeleteInstanceRequest",
"StartInstanceRequest",
"DescribeInstancesRequest",
"AllocateEipAddressRequest",
"AssociateEipAddressRequest"])
def loadClient(self): def loadClient(self):
try: try:
accountfile = open(fspath+"/global/sys/cloudaccount", 'r') settingfile = open(fspath+"/global/sys/cloudsetting.json", 'r')
account = json.loads(accountfile.read()) self.setting = json.loads(settingfile.read())
accountfile.close() settingfile.close()
self.clt = self.AcsClient.AcsClient(account['accesskey'],account['accesssecret'],'cn-shanghai') self.clt = self.AcsClient.AcsClient(self.setting['AccessKeyId'],self.setting['AccessKeySecret'], self.setting['RegionId'])
logger.info("load CLT of Aliyun success") logger.info("load CLT of Aliyun success")
return True return True
except: except Exception as e:
logger.error(e)
logger.error("account file not existed, can not load CLT") logger.error("account file not existed, can not load CLT")
return False return False
def createInstance(self,password): def createInstance(self):
request = self.Request.CreateInstanceRequest.CreateInstanceRequest() request = self.Request.CreateInstanceRequest.CreateInstanceRequest()
request.set_accept_format('json') request.set_accept_format('json')
request.add_query_param('RegionId', 'cn-shanghai') 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('ImageId', 'ubuntu_16_0402_64_20G_alibase_20170818.vhd')
request.add_query_param('InternetMaxBandwidthOut', 1) request.add_query_param('InternetMaxBandwidthOut', 1)
request.add_query_param('InstanceName', 'docklet_tmp_worker') request.add_query_param('InstanceName', 'docklet_tmp_worker')
request.add_query_param('HostName', 'worker-tmp') request.add_query_param('HostName', 'worker-tmp')
request.add_query_param('SystemDisk.Size', 500) request.add_query_param('SystemDisk.Size', int(self.setting['SystemDisk.Size']))
request.add_query_param('InstanceType', 'ecs.xn4.small') request.add_query_param('InstanceType', self.setting['InstanceType'])
request.add_query_param('Password', password) request.add_query_param('Password', self.setting['Password'])
response = self.clt.do_action_with_exception(request) response = self.clt.do_action_with_exception(request)
logger.info(response) logger.info(response)
# 获取实例ID
instanceid=json.loads(bytes.decode(response))['InstanceId'] instanceid=json.loads(bytes.decode(response))['InstanceId']
return instanceid return instanceid
# 启动ECS
def startInstance(self, instanceid): def startInstance(self, instanceid):
request = self.Request.StartInstanceRequest.StartInstanceRequest() request = self.Request.StartInstanceRequest.StartInstanceRequest()
request.set_accept_format('json') request.set_accept_format('json')
@ -79,11 +67,10 @@ class AliyunMgr():
logger.info(response) logger.info(response)
# 创建EIP
def createEIP(self): def createEIP(self):
request = self.Request.AllocateEipAddressRequest.AllocateEipAddressRequest() request = self.Request.AllocateEipAddressRequest.AllocateEipAddressRequest()
request.set_accept_format('json') request.set_accept_format('json')
request.add_query_param('RegionId', 'cn-shanghai') request.add_query_param('RegionId', self.setting['RegionId'])
response = self.clt.do_action_with_exception(request) response = self.clt.do_action_with_exception(request)
logger.info(response) logger.info(response)
@ -94,7 +81,6 @@ class AliyunMgr():
return [eipid, eipaddr] return [eipid, eipaddr]
# 绑定EIP
def associateEIP(self, instanceid, eipid): def associateEIP(self, instanceid, eipid):
request = self.Request.AssociateEipAddressRequest.AssociateEipAddressRequest() request = self.Request.AssociateEipAddressRequest.AssociateEipAddressRequest()
request.set_accept_format('json') request.set_accept_format('json')
@ -125,12 +111,12 @@ class AliyunMgr():
return False return False
return True return True
def rentServers(self): def rentServers(self,number):
instanceids=[] instanceids=[]
eipids=[] eipids=[]
eipaddrs=[] eipaddrs=[]
for i in range(int(number)): for i in range(int(number)):
instanceids.append(self.createInstance(password)) instanceids.append(self.createInstance())
time.sleep(2) time.sleep(2)
time.sleep(10) time.sleep(10)
for i in range(int(number)): for i in range(int(number)):
@ -152,16 +138,25 @@ class AliyunMgr():
time.sleep(5) time.sleep(5)
return [masterip, eipaddrs] return [masterip, eipaddrs]
def addNodes(self,number=1,password="Unias1616"): def addNode(self):
if not loadClient(): if not self.loadClient():
return False return {'success':'false'}
[masterip, eipaddrs] = self.rentServers(number,password) [masterip, eipaddrs] = self.rentServers(1)
threads = [] threads = []
for eip in eipaddrs: for eip in eipaddrs:
thread = threading.Thread(target = deploy, args=(eip,masterip,'root',password)) thread = threading.Thread(target = deploy, args=(eip,masterip,'root',self.setting['Password'],self.setting['VolumeName']))
thread.setDaemon(True) thread.setDaemon(True)
thread.start() thread.start()
threads.append(thread) threads.append(thread)
for thread in threads: for thread in threads:
thread.join() thread.join()
return True return {'success':'true'}
def addNodeAsync(self):
thread = threading.Thread(target = self.addNode)
thread.setDaemon(True)
thread.start()
class CloudMgr():
def __init__(self):
self.engine = AliyunMgr()

View File

@ -1,26 +1,25 @@
#!/usr/bin/python3 #!/usr/bin/python3
#实例ip调用describe api获取
import paramiko, time import paramiko, time
from log import logger from log import logger
import env import env,os
def myexec(ssh,command): def myexec(ssh,command):
stdin,stdout,stderr = ssh.exec_command(command) stdin,stdout,stderr = ssh.exec_command(command)
endtime = time.time() + 300 endtime = time.time() + 3600
while not stdout.channel.eof_received: while not stdout.channel.eof_received:
time.sleep(2) time.sleep(2)
if time.time() > endtime: if time.time() > endtime:
stdout.channel.close() stdout.channel.close()
logger.error(command + ": fail") logger.error(command + ": fail")
return
# for line in stdout.readlines(): # for line in stdout.readlines():
# if line is None: # if line is None:
# time.sleep(5) # time.sleep(5)
# else: # else:
# print(line) # print(line)
#上传deploy脚本 def deploy(ipaddr,masterip,account,password,volumename):
def deploy(ipaddr,masterip,account,password):
while True: while True:
try: try:
transport = paramiko.Transport((ipaddr,22)) transport = paramiko.Transport((ipaddr,22))
@ -31,12 +30,12 @@ def deploy(ipaddr,masterip,account,password):
pass pass
sftp = paramiko.SFTPClient.from_transport(transport) sftp = paramiko.SFTPClient.from_transport(transport)
fspath = env.getenv('FS_PREFIX') currentfilepath = os.path.dirname(os.path.abspath(__file__))
sftp.put('/home/zhong/docklet-deploy.sh','/root/docklet-deploy.sh') deployscriptpath = currentfilepath + "/../tools/docklet-deploy.sh"
sftp.put('/home/zhong/docklet-deploy.sh','/root/docklet-deploy.sh') sftp.put(deployscriptpath,'/root/docklet-deploy.sh')
sftp.put('/etc/hosts', '/etc/hosts')
transport.close() transport.close()
#执行deploy脚本
ssh = paramiko.SSHClient() ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
while True: while True:
@ -46,11 +45,9 @@ def deploy(ipaddr,masterip,account,password):
except Exception as e: except Exception as e:
time.sleep(2) time.sleep(2)
pass pass
#这行日后可以删掉
myexec(ssh,'chmod +x /root/docklet-deploy.sh')
myexec(ssh,"sed -i 's/%MASTERIP%/" + masterip + "/g' /root/docklet-deploy.sh") myexec(ssh,"sed -i 's/%MASTERIP%/" + masterip + "/g' /root/docklet-deploy.sh")
myexec(ssh,'/root/docklet-deploy.sh ' + ipaddr) myexec(ssh,"sed -i 's/%VOLUMENAME%/" + volumename + "/g' /root/docklet-deploy.sh")
myexec(ssh,'mount -t glusterfs ' + masterip + ':docklet /opt/docklet/global/') myexec(ssh,'chmod +x /root/docklet-deploy.sh')
myexec(ssh,'/home/docklet/bin/docklet-worker start') myexec(ssh,'/root/docklet-deploy.sh')
ssh.close() ssh.close()
return return

View File

@ -23,7 +23,7 @@ import os
import http.server, cgi, json, sys, shutil import http.server, cgi, json, sys, shutil
import xmlrpc.client import xmlrpc.client
from socketserver import ThreadingMixIn 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 from logs import logs
import userManager,beansapplicationmgr import userManager,beansapplicationmgr
import monitor,traceback import monitor,traceback
@ -470,6 +470,14 @@ def modify_account_cloud(cur_user, user, form):
result = G_usermgr.cloud_account_modify(cur_user = cur_user, form = form) result = G_usermgr.cloud_account_modify(cur_user = cur_user, form = form)
return json.dumps(result) 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']) @app.route("/addproxy/", methods=['POST'])
@login_required @login_required
@ -793,6 +801,7 @@ if __name__ == '__main__':
global G_historymgr global G_historymgr
global G_applicationmgr global G_applicationmgr
global G_ulockmgr global G_ulockmgr
global G_cloudmgr
# move 'tools.loadenv' to the beginning of this file # move 'tools.loadenv' to the beginning of this file
fs_path = env.getenv("FS_PREFIX") fs_path = env.getenv("FS_PREFIX")
@ -884,6 +893,8 @@ if __name__ == '__main__':
G_networkmgr = network.NetworkMgr(clusternet, etcdclient, mode, ipaddr) G_networkmgr = network.NetworkMgr(clusternet, etcdclient, mode, ipaddr)
G_networkmgr.printpools() G_networkmgr.printpools()
G_cloudmgr = cloudmgr.CloudMgr()
# start NodeMgr and NodeMgr will wait for all nodes to start ... # start NodeMgr and NodeMgr will wait for all nodes to start ...
G_nodemgr = nodemgr.NodeMgr(G_networkmgr, etcdclient, addr = ipaddr, mode=mode) G_nodemgr = nodemgr.NodeMgr(G_networkmgr, etcdclient, addr = ipaddr, mode=mode)
logger.info("nodemgr started") logger.info("nodemgr started")

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,51 +1,28 @@
#配置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 apt-get update
#下载git包
apt-get install -y git apt-get install -y git
#下载docklet源码
git clone http://github.com/unias/docklet.git /home/docklet git clone http://github.com/unias/docklet.git /home/docklet
#运行prepare.sh
sed -i '61s/^/#&/g' /home/docklet/prepare.sh
sed -i '62s/^/#&/g' /home/docklet/prepare.sh
/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
#解压镜像
#mkdir -p /opt/docklet/local
#mkdir -p /opt/docklet/global
#tar -jxvf /root/basefs-0.11.tar.bz2 -C /opt/docklet/local/
#获得docklet.conf
cp /home/docklet/conf/docklet.conf.template /home/docklet/conf/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 cp /home/docklet/web/templates/home.template /home/docklet/web/templates/home.html
#获得网卡名称
NETWORK_DEVICE=`route | grep default | awk {'print $8'};` NETWORK_DEVICE=`route | grep default | awk {'print $8'};`
#更改配置文件 echo "DISKPOOL_SIZE=200000
echo "DISKPOOL_SIZE=20000
ETCD=%MASTERIP%:2379 ETCD=%MASTERIP%:2379
NETWORK_DEVICE=$NETWORK_DEVICE NETWORK_DEVICE=$NETWORK_DEVICE
PROXY_PORT=8000 PROXY_PORT=8000
NGINX_PORT=80" >> /home/docklet/conf/docklet.conf NGINX_PORT=80" >> /home/docklet/conf/docklet.conf
#启动worker #please modify the mount command for your corresponding distributed file system if you are not using glusterfs
#/home/docklet/bin/docklet-supermaster init mount -t glusterfs %VOLUMENAME% /opt/docklet/global/
#/home/docklet/bin/docklet-worker start
if [ -f /opt/docklet/global/packagefs.tgz ]; then
tar zxvf /opt/docklet/global/packagefs.tgz -C /opt/docklet/local/ > /dev/null
fi
/home/docklet/bin/docklet-worker start
exit 0 exit 0

View File

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

View File

@ -46,7 +46,7 @@
</div> </div>
<div class="box-body table-responsive"> <div class="box-body table-responsive">
<p> <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> </p>
<table class="table table-bordered"> <table class="table table-bordered">
<thead> <thead>

View File

@ -534,6 +534,12 @@ def cloud_account_del(cloudname):
def cloud_account_modify(cloudname): def cloud_account_modify(cloudname):
return cloudAccountModifyView.as_view() return cloudAccountModifyView.as_view()
@app.route("/cloud/<masterip>/node/add/", methods = ['POST', 'GET'])
@administration_required
def cloud_node_add(masterip):
cloudNodeAddView.masterip = masterip
return cloudNodeAddView.as_view()
@app.route("/notification/", methods=['GET']) @app.route("/notification/", methods=['GET'])
@administration_required @administration_required
@ -606,6 +612,7 @@ def adminpage():
def updatesettings(): def updatesettings():
return updatesettingsView.as_view() return updatesettingsView.as_view()
@app.route('/index/', methods=['GET']) @app.route('/index/', methods=['GET'])
def jupyter_control(): def jupyter_control():
return redirect('/dashboard/') return redirect('/dashboard/')

View File

@ -40,3 +40,14 @@ class cloudAccountModifyView(normalView):
def post(self): def post(self):
dockletRequest.post('/cloud/account/modify/', request.form) dockletRequest.post('/cloud/account/modify/', request.form)
return redirect('/cloud/') return redirect('/cloud/')
class cloudNodeAddView(normalView):
@classmethod
def post(self):
data = {}
dockletRequest.post('/cloud/node/add/', data, self.masterip)
return redirect('/hosts/')
@classmethod
def get(self):
return self.post()