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)
# 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

View File

@ -3,6 +3,7 @@ from io import StringIO
import os,sys,subprocess,time,re,datetime,threading,random
from model import db, Image
from deploy import *
import json
from log import logger
import env
@ -10,67 +11,54 @@ import requests
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():
def __init__(self):
self.AcsClient = __import__('aliyunsdkcore.client')
self.Request = __import__('aliyunsdkecs.request.v20140526')
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):
try:
accountfile = open(fspath+"/global/sys/cloudaccount", 'r')
account = json.loads(accountfile.read())
accountfile.close()
self.clt = self.AcsClient.AcsClient(account['accesskey'],account['accesssecret'],'cn-shanghai')
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:
except Exception as e:
logger.error(e)
logger.error("account file not existed, can not load CLT")
return False
def createInstance(self,password):
def createInstance(self):
request = self.Request.CreateInstanceRequest.CreateInstanceRequest()
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('InternetMaxBandwidthOut', 1)
request.add_query_param('InstanceName', 'docklet_tmp_worker')
request.add_query_param('HostName', 'worker-tmp')
request.add_query_param('SystemDisk.Size', 500)
request.add_query_param('InstanceType', 'ecs.xn4.small')
request.add_query_param('Password', password)
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)
# 获取实例ID
instanceid=json.loads(bytes.decode(response))['InstanceId']
return instanceid
# 启动ECS
def startInstance(self, instanceid):
request = self.Request.StartInstanceRequest.StartInstanceRequest()
request.set_accept_format('json')
@ -79,11 +67,10 @@ class AliyunMgr():
logger.info(response)
# 创建EIP
def createEIP(self):
request = self.Request.AllocateEipAddressRequest.AllocateEipAddressRequest()
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)
logger.info(response)
@ -94,7 +81,6 @@ class AliyunMgr():
return [eipid, eipaddr]
# 绑定EIP
def associateEIP(self, instanceid, eipid):
request = self.Request.AssociateEipAddressRequest.AssociateEipAddressRequest()
request.set_accept_format('json')
@ -125,12 +111,12 @@ class AliyunMgr():
return False
return True
def rentServers(self):
def rentServers(self,number):
instanceids=[]
eipids=[]
eipaddrs=[]
for i in range(int(number)):
instanceids.append(self.createInstance(password))
instanceids.append(self.createInstance())
time.sleep(2)
time.sleep(10)
for i in range(int(number)):
@ -152,16 +138,25 @@ class AliyunMgr():
time.sleep(5)
return [masterip, eipaddrs]
def addNodes(self,number=1,password="Unias1616"):
if not loadClient():
return False
[masterip, eipaddrs] = self.rentServers(number,password)
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',password))
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 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
#实例ip调用describe api获取
import paramiko, time
from log import logger
import env
import env,os
def myexec(ssh,command):
stdin,stdout,stderr = ssh.exec_command(command)
endtime = time.time() + 300
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)
#上传deploy脚本
def deploy(ipaddr,masterip,account,password):
def deploy(ipaddr,masterip,account,password,volumename):
while True:
try:
transport = paramiko.Transport((ipaddr,22))
@ -31,12 +30,12 @@ def deploy(ipaddr,masterip,account,password):
pass
sftp = paramiko.SFTPClient.from_transport(transport)
fspath = env.getenv('FS_PREFIX')
sftp.put('/home/zhong/docklet-deploy.sh','/root/docklet-deploy.sh')
sftp.put('/home/zhong/docklet-deploy.sh','/root/docklet-deploy.sh')
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()
#执行deploy脚本
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
while True:
@ -46,11 +45,9 @@ def deploy(ipaddr,masterip,account,password):
except Exception as e:
time.sleep(2)
pass
#这行日后可以删掉
myexec(ssh,'chmod +x /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,'mount -t glusterfs ' + masterip + ':docklet /opt/docklet/global/')
myexec(ssh,'/home/docklet/bin/docklet-worker start')
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

@ -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
@ -470,6 +470,14 @@ def modify_account_cloud(cur_user, user, form):
result = G_usermgr.cloud_account_modify(cur_user = cur_user, form = form)
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
@ -793,6 +801,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")
@ -884,6 +893,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

@ -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
#下载git包
apt-get install -y git
#下载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
#挂载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/web/templates/home.template /home/docklet/web/templates/home.html
#获得网卡名称
NETWORK_DEVICE=`route | grep default | awk {'print $8'};`
#更改配置文件
echo "DISKPOOL_SIZE=20000
echo "DISKPOOL_SIZE=200000
ETCD=%MASTERIP%:2379
NETWORK_DEVICE=$NETWORK_DEVICE
PROXY_PORT=8000
NGINX_PORT=80" >> /home/docklet/conf/docklet.conf
#启动worker
#/home/docklet/bin/docklet-supermaster init
#/home/docklet/bin/docklet-worker start
#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
/home/docklet/bin/docklet-worker start
exit 0

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="#"><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

@ -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

@ -534,6 +534,12 @@ def cloud_account_del(cloudname):
def cloud_account_modify(cloudname):
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'])
@administration_required
@ -606,6 +612,7 @@ def adminpage():
def updatesettings():
return updatesettingsView.as_view()
@app.route('/index/', methods=['GET'])
def jupyter_control():
return redirect('/dashboard/')

View File

@ -40,3 +40,14 @@ class cloudAccountModifyView(normalView):
def post(self):
dockletRequest.post('/cloud/account/modify/', request.form)
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()