Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ecccb8bdf6 | |||
| f232a9316a | |||
| 689ae7255b | |||
| 8a52a10fb6 | |||
| e17ec5a485 | |||
| 03a68da029 | |||
| f2733eef4e | |||
| b89a492b6a | |||
| 6cb42d6b8d | |||
| 98fae3427e | |||
| 4e37223538 |
31
bin/completions-sunhpc.py
Executable file
31
bin/completions-sunhpc.py
Executable file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/opt/sunpy3/bin/python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
cword = int(os.environ['COMP_CWORD'])
|
||||||
|
cwords = os.environ['COMP_WORDS'].split()
|
||||||
|
cline = os.environ['COMP_LINE']
|
||||||
|
#curr = os.environ['COMP_CURR']
|
||||||
|
|
||||||
|
if 'sunhpc' in cwords:
|
||||||
|
cwords.remove('sunhpc')
|
||||||
|
|
||||||
|
|
||||||
|
commands = '/opt/sunhpc/lib/sunhpc/commands/'
|
||||||
|
modules = commands + '/'.join(cwords)
|
||||||
|
if os.path.exists(modules):
|
||||||
|
cmdpath = os.listdir(modules)
|
||||||
|
else:
|
||||||
|
cmdpath = os.listdir(commands)
|
||||||
|
|
||||||
|
print (modules)
|
||||||
|
print ('-------------------------------')
|
||||||
|
print(' '.join(cmdpath))
|
||||||
|
#print("candaidate1 candbidate2")
|
||||||
|
|
||||||
|
#print ('\n-cword---%s---' % cword)
|
||||||
|
#print ('\n-cwords--%s---' % cwords)
|
||||||
|
#print ('\n-cline---%s---' % cline)
|
||||||
|
#print ('\n-curr----%s---' % sys.argv)
|
||||||
10
bin/sunhpc
10
bin/sunhpc
@@ -6,20 +6,14 @@ import sys
|
|||||||
import sunhpc
|
import sunhpc
|
||||||
import syslog
|
import syslog
|
||||||
import shutil
|
import shutil
|
||||||
|
import readline
|
||||||
import sunhpc.invoke
|
import sunhpc.invoke
|
||||||
|
|
||||||
if sys.getdefaultencoding() != 'utf-8':
|
if sys.getdefaultencoding() != 'utf-8':
|
||||||
reload(sys)
|
reload(sys)
|
||||||
sys.setdefaultencoding('utf-8')
|
sys.setdefaultencoding('utf-8')
|
||||||
|
|
||||||
def ttySize():
|
os.environ['COLUMNS'] = str(sunhpc.commands.get_help_width())
|
||||||
try:
|
|
||||||
(width, heigh) = shutil.get_terminal_size()
|
|
||||||
except:
|
|
||||||
width = 80
|
|
||||||
return width
|
|
||||||
|
|
||||||
os.environ['COLUMNS'] = str(ttySize())
|
|
||||||
syslog.openlog('SunhpcCMD', syslog.LOG_PID, syslog.LOG_LOCAL0)
|
syslog.openlog('SunhpcCMD', syslog.LOG_PID, syslog.LOG_LOCAL0)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2023-04-16 08:28:49
|
2023-10-12 18:44:23
|
||||||
|
|||||||
BIN
data/sunhpc.db
BIN
data/sunhpc.db
Binary file not shown.
55
etc/bash-complete-sunhpc
Normal file
55
etc/bash-complete-sunhpc
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# sunhpc(1) completion
|
||||||
|
#
|
||||||
|
# /etc/bash_completion.d/sunhpc
|
||||||
|
#
|
||||||
|
|
||||||
|
_sunhpc_array_delete_at()
|
||||||
|
{
|
||||||
|
eval "local ARRAY=(\"\${$1[@]}\")"
|
||||||
|
local i
|
||||||
|
local tmp=()
|
||||||
|
local lower=$2
|
||||||
|
local upper=${3:-$lower}
|
||||||
|
|
||||||
|
for i in "${!ARRAY[@]}"; do
|
||||||
|
if [[ "$i" -lt "$2" || "$i" -gt "${3-$2}" ]]; then
|
||||||
|
tmp=("${tmp[@]}" "${ARRAY[$i]}")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
eval "$1=(\"\${tmp[@]}\")"
|
||||||
|
}
|
||||||
|
|
||||||
|
_sunhpc()
|
||||||
|
{
|
||||||
|
local cur prev words cword i output
|
||||||
|
_init_completion || return
|
||||||
|
|
||||||
|
_sunhpc_array_delete_at words $((cword+1)) ${#words[@]}
|
||||||
|
_sunhpc_array_delete_at words 0
|
||||||
|
|
||||||
|
for i in ${!words[@]}; do
|
||||||
|
words[i]="$(printf '%s' "${words[i]}" | xargs printf '%s\n' 2>/dev/null || true)"
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$cur" =~ ^[[:space:]]+ ]]; then
|
||||||
|
cur=''
|
||||||
|
fi
|
||||||
|
|
||||||
|
output="$(sunhpc report completion ${words[@]} 2>/dev/null)"
|
||||||
|
|
||||||
|
if [ $? = 65 ]; then
|
||||||
|
compopt -o default
|
||||||
|
COMPREPLY=()
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
#local IFS=$'\n'
|
||||||
|
COMPREPLY=( $( compgen -W '$output' -- ${cur} ) )
|
||||||
|
|
||||||
|
# do not append a space to words that end with =
|
||||||
|
[[ $COMPREPLY == *= ]] && compopt -o nospace
|
||||||
|
|
||||||
|
} &&
|
||||||
|
complete -F _sunhpc sunhpc
|
||||||
|
|
||||||
|
# ex: ts=4 sw=4 et filetype=sh
|
||||||
46
etc/sunhpc.conf
Normal file
46
etc/sunhpc.conf
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#
|
||||||
|
# sunhpc cluster config file.
|
||||||
|
#
|
||||||
|
country CN
|
||||||
|
state LiaoNing
|
||||||
|
city DaLian
|
||||||
|
url https://www.sunhpc.com
|
||||||
|
name Sunhpc-cluster
|
||||||
|
contact info@sunhpc.com
|
||||||
|
worksdir /export
|
||||||
|
distrodir /export/sunhpc
|
||||||
|
partition default
|
||||||
|
safeport 372
|
||||||
|
safedirs safe.d
|
||||||
|
safesecurity safe-security
|
||||||
|
publichostname cluster.sunhpc.com
|
||||||
|
publicinterface eth0
|
||||||
|
publicaddress 172.16.1.1
|
||||||
|
publicgateway 172.16.1.1
|
||||||
|
publicnetmask 255.255.255.0
|
||||||
|
publicdnsserver 223.5.5.5
|
||||||
|
publicmacaddress B8:CA:3A:A8:C5:D3
|
||||||
|
publiccidr 24
|
||||||
|
publicnetwork 172.16.1.0
|
||||||
|
publicdomain sunhpc.com
|
||||||
|
publicmtu 1500
|
||||||
|
publicntphost pool.ntp.org
|
||||||
|
privatehostname cluster
|
||||||
|
privateinterface wlan0
|
||||||
|
privateaddress 192.168.199.146
|
||||||
|
privatenetmask 255.255.255.0
|
||||||
|
privatemacaddress 00:21:6A:A7:D5:62
|
||||||
|
privatecidr 24
|
||||||
|
privatenetwork 192.168.199.0
|
||||||
|
privatedomain local
|
||||||
|
privatemtu 1500
|
||||||
|
privatentphost cluster.local
|
||||||
|
timezone Asia/Shanghai
|
||||||
|
bootargs net.ifnames=0 biosdevname=0 ksdevice=bootif
|
||||||
|
basedir install
|
||||||
|
ganglia 224.0.0.3
|
||||||
|
nextserver 192.168.199.146
|
||||||
|
plugin_port 12345
|
||||||
|
pxefilename pxelinux.0
|
||||||
|
pxelinuxdir /tftpboot/pxelinux
|
||||||
|
distribution sunhpc-dist
|
||||||
@@ -14,7 +14,6 @@ import socket
|
|||||||
import syslog
|
import syslog
|
||||||
import struct
|
import struct
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import termios
|
|
||||||
import argparse
|
import argparse
|
||||||
import textwrap
|
import textwrap
|
||||||
import datetime
|
import datetime
|
||||||
@@ -38,10 +37,8 @@ DEFAULT_HELP_WIDTH = 8
|
|||||||
|
|
||||||
def get_help_width():
|
def get_help_width():
|
||||||
try:
|
try:
|
||||||
data = fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, '1234')
|
(columns, heigh) = shutil.get_terminal_size()
|
||||||
columns = int(struct.unpack('hh', data)[1])
|
|
||||||
except (IOError, ValueError) as e:
|
except (IOError, ValueError) as e:
|
||||||
print ("terminal size detection failed, using default width.")
|
|
||||||
return DEFAULT_HELP_WIDTH
|
return DEFAULT_HELP_WIDTH
|
||||||
|
|
||||||
columns = columns - RIGHT_PADDING
|
columns = columns - RIGHT_PADDING
|
||||||
@@ -653,12 +650,12 @@ class Command(object):
|
|||||||
#print("\033[92m[+]\033[0m \033[96m%s\033[0m" % (msg))
|
#print("\033[92m[+]\033[0m \033[96m%s\033[0m" % (msg))
|
||||||
print("\033[92m[+]\033[0m %s" % (msg))
|
print("\033[92m[+]\033[0m %s" % (msg))
|
||||||
|
|
||||||
def message(self, msg, tag='[+]', end=''):
|
def message(self, tag='[+]', msg=' ', end=' '):
|
||||||
header = '\033[92m%s\033[0m' % tag
|
header = '\033[92m%s\033[0m' % tag
|
||||||
ender = '\033[95m%s\033[0m' % end
|
ender = '\033[95m%s\033[0m' % end
|
||||||
msger = '\033[31m%s\033[0m' % msg
|
msger = '\033[31m%s\033[0m' % msg
|
||||||
result = '%s %s %s' % (header, msger, ender)
|
result = '%s %s %s' % (header, msger, ender)
|
||||||
return result
|
print (result)
|
||||||
|
|
||||||
|
|
||||||
def abort(self, msg):
|
def abort(self, msg):
|
||||||
@@ -773,6 +770,18 @@ class Command(object):
|
|||||||
|
|
||||||
return newline
|
return newline
|
||||||
|
|
||||||
|
def formatByte(self, number):
|
||||||
|
"""格式化文件大小函数, number: 要格式化的字节数"""
|
||||||
|
symbols = ('KB', 'MB', 'GB', 'TB', 'PB')
|
||||||
|
prefix = {}
|
||||||
|
for i, s in enumerate(symbols):
|
||||||
|
prefix[s] = 1 << (i + 1) * 10
|
||||||
|
for s in reversed(symbols):
|
||||||
|
if number >= prefix[s]:
|
||||||
|
value = float(number) / prefix[s]
|
||||||
|
return '%6.2f %s' % (value, s)
|
||||||
|
return '%s KB' % number
|
||||||
|
|
||||||
def shrun(self, cmd):
|
def shrun(self, cmd):
|
||||||
return subprocess.run(cmd, shell=True, check=True)
|
return subprocess.run(cmd, shell=True, check=True)
|
||||||
|
|
||||||
@@ -785,8 +794,11 @@ class Command(object):
|
|||||||
stderr=subprocess.PIPE)
|
stderr=subprocess.PIPE)
|
||||||
|
|
||||||
if ret == 'str':
|
if ret == 'str':
|
||||||
info['o'] = p.stdout.read().decode('UTF-8')
|
out, err = p.communicate()
|
||||||
info['e'] = p.stderr.read().decode('UTF-8')
|
info['o'] = out.decode('UTF-8')
|
||||||
|
info['e'] = err.decode('UTF-8')
|
||||||
|
#info['o'] = p.stdout.read().decode('UTF-8')
|
||||||
|
#info['e'] = p.stderr.read().decode('UTF-8')
|
||||||
if ret == 'list':
|
if ret == 'list':
|
||||||
info['o'] = [ l.decode('UTF-8') for l in p.stdout.readlines() ]
|
info['o'] = [ l.decode('UTF-8') for l in p.stdout.readlines() ]
|
||||||
info['e'] = [ l.decode('UTF-8') for l in p.stderr.readlines() ]
|
info['e'] = [ l.decode('UTF-8') for l in p.stderr.readlines() ]
|
||||||
@@ -856,7 +868,7 @@ class Command(object):
|
|||||||
w, r ,e = (p.stdin, p.stdout, p.stderr)
|
w, r ,e = (p.stdin, p.stdout, p.stderr)
|
||||||
currLength = 0
|
currLength = 0
|
||||||
prevLength = 0
|
prevLength = 0
|
||||||
spinChars = '-\|/'
|
spinChars = r'-\|/'
|
||||||
spinIndex = 0
|
spinIndex = 0
|
||||||
while 1:
|
while 1:
|
||||||
line = e.readline().decode()
|
line = e.readline().decode()
|
||||||
@@ -977,17 +989,17 @@ class Command(object):
|
|||||||
def getText(self):
|
def getText(self):
|
||||||
return self.text
|
return self.text
|
||||||
|
|
||||||
def dictOutput(self, strDict, sep='\t'):
|
def dictOutput(self, strDict, sep=' '):
|
||||||
maxlen = max(map(len, strDict.keys()))
|
maxlen = max(map(len, strDict.keys()))
|
||||||
for k in strDict:
|
for k in strDict:
|
||||||
strings = str(strDict[k])
|
strings = str(strDict[k])
|
||||||
if strings.strip().lower() in ['ok', 'on', 'yes']:
|
if strings.strip().lower() in ['ok', 'on', 'yes']:
|
||||||
value = '|\033[1;32m %s \033[0m' % strings.upper()
|
value = '\033[1;32m %s \033[0m' % strings.upper()
|
||||||
elif strings.strip().lower() in ['fail', 'off', 'no', 'error', 'failed']:
|
elif strings.strip().lower() in ['fail', 'off', 'no', 'error', 'failed']:
|
||||||
value = '|\033[1;31m %s \033[0m' % strings.upper()
|
value = '\033[1;31m %s \033[0m' % strings.upper()
|
||||||
else:
|
else:
|
||||||
value = '| %s' % strings
|
value = '%s' % strings
|
||||||
print ('\t\033[1;95m%s\033[0m %s \033[1;96m%s\033[0m' % (k.ljust(maxlen), sep, value))
|
print ('\t\033[1;95m%s\033[0m %s = \033[1;96m%s\033[0m' % (k.ljust(maxlen), sep, value))
|
||||||
|
|
||||||
def beginFmtOutput(self, header=[]):
|
def beginFmtOutput(self, header=[]):
|
||||||
self.fmtOutput = prettytable.PrettyTable(header)
|
self.fmtOutput = prettytable.PrettyTable(header)
|
||||||
@@ -1255,8 +1267,22 @@ class Command(object):
|
|||||||
o.runWrapper(n, args)
|
o.runWrapper(n, args)
|
||||||
return o.getText()
|
return o.getText()
|
||||||
|
|
||||||
|
def remove_empty_values(self, input_list):
|
||||||
|
"""
|
||||||
|
从列表中删除空值
|
||||||
|
|
||||||
|
:param input_list: 输入的列表
|
||||||
|
:return: 不包含空值的新列表
|
||||||
|
"""
|
||||||
|
# 使用列表推导式过滤掉空值
|
||||||
|
result_list = [value for value in input_list if value is not None and value != ""]
|
||||||
|
return result_list
|
||||||
|
|
||||||
def runWrapper(self, name, args):
|
def runWrapper(self, name, args):
|
||||||
|
|
||||||
|
# 删除所有空值和None.
|
||||||
|
args = self.remove_empty_values(args)
|
||||||
|
|
||||||
username = pwd.getpwuid(os.geteuid())[0]
|
username = pwd.getpwuid(os.geteuid())[0]
|
||||||
if args:
|
if args:
|
||||||
command = '%s %s' % (name, ' '.join(args))
|
command = '%s %s' % (name, ' '.join(args))
|
||||||
@@ -1269,7 +1295,7 @@ class Command(object):
|
|||||||
pdict = {}
|
pdict = {}
|
||||||
plist = []
|
plist = []
|
||||||
nparams = 0
|
nparams = 0
|
||||||
flagpattern=re.compile("^[a-zA-z0-9\-_+]+=")
|
flagpattern=re.compile(r"^[a-zA-z0-9\-_+]+=")
|
||||||
for arg in args:
|
for arg in args:
|
||||||
tokens = arg.split()
|
tokens = arg.split()
|
||||||
if tokens[0] == 'select':
|
if tokens[0] == 'select':
|
||||||
@@ -1543,7 +1569,7 @@ class DocStringHandler(
|
|||||||
s += '*[%s]*' % name
|
s += '*[%s]*' % name
|
||||||
else:
|
else:
|
||||||
s += '*{%s}*' % name
|
s += '*{%s}*' % name
|
||||||
txt = txt.replace('*', '\*')
|
txt = txt.replace('*', '\\*')
|
||||||
s += '\n%s\n' % txt.replace('\t', ' ')
|
s += '\n%s\n' % txt.replace('\t', ' ')
|
||||||
if self.section['param']:
|
if self.section['param']:
|
||||||
s += '\n**Parameters:**\n\n'
|
s += '\n**Parameters:**\n\n'
|
||||||
@@ -1553,12 +1579,12 @@ class DocStringHandler(
|
|||||||
s += '*[%s=%s]*' % (name, type)
|
s += '*[%s=%s]*' % (name, type)
|
||||||
else:
|
else:
|
||||||
s += '*{%s=%s}*' % (name, type)
|
s += '*{%s=%s}*' % (name, type)
|
||||||
txt = txt.replace('*', '\*')
|
txt = txt.replace('*', '\\*')
|
||||||
s += '\n%s\n' % txt.replace('\t', ' ')
|
s += '\n%s\n' % txt.replace('\t', ' ')
|
||||||
if self.section['example']:
|
if self.section['example']:
|
||||||
s += '\n**Examples:**\n'
|
s += '\n**Examples:**\n'
|
||||||
for (cmd, txt) in self.section['example']:
|
for (cmd, txt) in self.section['example']:
|
||||||
txt = txt.replace('*', '\*')
|
txt = txt.replace("*", "\\*")
|
||||||
s += '%s::\n\n' % txt.replace('\t',' ')
|
s += '%s::\n\n' % txt.replace('\t',' ')
|
||||||
s += ' %s sunhpc %s\n' % (prompt, cmd)
|
s += ' %s sunhpc %s\n' % (prompt, cmd)
|
||||||
if self.section['related']:
|
if self.section['related']:
|
||||||
|
|||||||
84
lib/sunhpc/commands/build/init/__init__.py
Normal file
84
lib/sunhpc/commands/build/init/__init__.py
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#
|
||||||
|
#coding:utf-8
|
||||||
|
#
|
||||||
|
#Author : QCSun
|
||||||
|
#Email : qcsun@sunhpc.com
|
||||||
|
#Times : 2023-04-14 05:21:02
|
||||||
|
#WebSite : https://www.sunhpc.com
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import sunhpc
|
||||||
|
import readline
|
||||||
|
from sunhpc.core.interpreter import SunhpcInterpreter
|
||||||
|
|
||||||
|
class Struct(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class command(sunhpc.commands.build.command):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Command(command, SunhpcInterpreter):
|
||||||
|
#class Command(command):
|
||||||
|
"""
|
||||||
|
Initialzed the sunhpc database.
|
||||||
|
"""
|
||||||
|
def run(self, params, args):
|
||||||
|
SunhpcInterpreter.__init__(self)
|
||||||
|
|
||||||
|
self.results = {}
|
||||||
|
self.results['country'] = 'CN'
|
||||||
|
self.results['state'] = 'LiaoNing'
|
||||||
|
self.results['city'] = 'Dalian'
|
||||||
|
self.results['url'] = 'https://www.sunhpc.com'
|
||||||
|
|
||||||
|
self.current_keys = list(self.results.keys())
|
||||||
|
self.current_values = self.results
|
||||||
|
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
def preceding(self):
|
||||||
|
print(' 下列是数据库的初始化配置的基础选项.')
|
||||||
|
print(' 可以使用命令 set key=value 进行修改:\n')
|
||||||
|
self.dictOutput(self.results)
|
||||||
|
print(' ---------------------------------------------\n')
|
||||||
|
|
||||||
|
def _SunhpcInterpreter__show_all(self, root=''):
|
||||||
|
|
||||||
|
self.dictOutput(self.current_values)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -466,12 +466,12 @@ class Command(command):
|
|||||||
Networks = '''
|
Networks = '''
|
||||||
CREATE TABLE Networks (
|
CREATE TABLE Networks (
|
||||||
ID integer NOT NULL primary key autoincrement,
|
ID integer NOT NULL primary key autoincrement,
|
||||||
Node integer(11) default NULL,
|
Node integer(11) default NULL,
|
||||||
MAC varchar(64) default NULL,
|
MAC varchar(64) default NULL,
|
||||||
IP varchar(32) default NULL,
|
IP varchar(32) default NULL,
|
||||||
Name varchar(128) default NULL,
|
Name varchar(128) default NULL,
|
||||||
Device varchar(32) default NULL,
|
Device varchar(32) default NULL,
|
||||||
Subnet integer(11) default NULL,
|
Subnet integer(11) default NULL,
|
||||||
Foreign key(subnet) references subnets(id) on delete cascade on update restrict,
|
Foreign key(subnet) references subnets(id) on delete cascade on update restrict,
|
||||||
Foreign key(node) references nodes(id) on delete cascade on update restrict
|
Foreign key(node) references nodes(id) on delete cascade on update restrict
|
||||||
)'''
|
)'''
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ class Command(sunhpc.commands.create.command):
|
|||||||
mirrors = distro.getMirrors()
|
mirrors = distro.getMirrors()
|
||||||
fullmirror = mirrors[0].getRollsPath()
|
fullmirror = mirrors[0].getRollsPath()
|
||||||
# modify all dirs mode 755
|
# modify all dirs mode 755
|
||||||
os.system('find %s -type d ' % (fullmirror) + '-exec chmod -R 0755 {} \;')
|
os.system(r'find %s -type d ' % (fullmirror) + r'-exec chmod -R 0755 {} \;')
|
||||||
|
|
||||||
if self.arch != arch and os.path.exists(dist):
|
if self.arch != arch and os.path.exists(dist):
|
||||||
shutil.move(os.path.join(tempdist, arch), os.path.join(dist, arch))
|
shutil.move(os.path.join(tempdist, arch), os.path.join(dist, arch))
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class Command(sunhpc.commands.list.command):
|
|||||||
def run(self, params, args):
|
def run(self, params, args):
|
||||||
|
|
||||||
(subdir, cols) = self.fillParams([('subdir', ), ('cols', 80) ], params)
|
(subdir, cols) = self.fillParams([('subdir', ), ('cols', 80) ], params)
|
||||||
|
|
||||||
if subdir:
|
if subdir:
|
||||||
filepath = os.path.join(sunhpc.commands.__path__[0], subdir)
|
filepath = os.path.join(sunhpc.commands.__path__[0], subdir)
|
||||||
modpath = 'sunhpc.commands.%s' % '.'.join(subdir.split(os.sep))
|
modpath = 'sunhpc.commands.%s' % '.'.join(subdir.split(os.sep))
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ class RollHandler(object):
|
|||||||
self.cmd = cmd
|
self.cmd = cmd
|
||||||
self.clean = clean
|
self.clean = clean
|
||||||
self.mntdir = mnt
|
self.mntdir = mnt
|
||||||
|
self.repodirs = ''
|
||||||
self.rinfo = None
|
self.rinfo = None
|
||||||
|
self.isoname = 'unknown'
|
||||||
|
self.foreign = False
|
||||||
|
|
||||||
def is_mounted(self):
|
def is_mounted(self):
|
||||||
cmd = 'mount |grep %s' % self.mntdir
|
cmd = 'mount |grep %s' % self.mntdir
|
||||||
@@ -22,12 +25,13 @@ class RollHandler(object):
|
|||||||
|
|
||||||
def mount_iso(self, iso):
|
def mount_iso(self, iso):
|
||||||
subprocess.run('mount -r %s %s' % (iso, self.mntdir), shell=True, check=True)
|
subprocess.run('mount -r %s %s' % (iso, self.mntdir), shell=True, check=True)
|
||||||
|
self.isoname = iso
|
||||||
|
|
||||||
def umount_iso(self):
|
def umount_iso(self):
|
||||||
subprocess.run('umount %s' % self.mntdir, shell=True)
|
subprocess.run('umount %s' % self.mntdir, shell=True)
|
||||||
|
|
||||||
def read_iso(self):
|
def read_iso(self):
|
||||||
cmd = 'find %s -type f -name roll-\*.xml' % self.mntdir
|
cmd = r'find %s -type f -name roll-\*.xml' % self.mntdir
|
||||||
ret = self.cmd.shcmd(cmd, code=True)
|
ret = self.cmd.shcmd(cmd, code=True)
|
||||||
try:
|
try:
|
||||||
roll = sunhpc.core.files.RollInfoFile(ret['o'].strip())
|
roll = sunhpc.core.files.RollInfoFile(ret['o'].strip())
|
||||||
@@ -36,7 +40,25 @@ class RollHandler(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def foreign_roll(self):
|
def foreign_roll(self):
|
||||||
self.cmd.msg('This ISO file cannot be recognized.This ISO was not produced by sunhpc created.', 'w')
|
self.cmd.msg('The "%s" was not produced by sunhpc created.' % self.isoname, 'w')
|
||||||
|
# check iso .discinfo file
|
||||||
|
discinfo = os.path.join(self.mntdir, '.discinfo')
|
||||||
|
if not os.path.exists(discinfo):
|
||||||
|
self.cmd.msg('The %s is not exists.' % discinfo, 'a')
|
||||||
|
|
||||||
|
info = []
|
||||||
|
with open(discinfo, 'r') as fn:
|
||||||
|
info = fn.readlines()
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.foreign = True
|
||||||
|
self.rinfo = sunhpc.core.utils.Struct()
|
||||||
|
self.rinfo.name = info[1].split()[0].strip()
|
||||||
|
self.rinfo.version = info[1].split()[-1].strip()
|
||||||
|
self.rinfo.arch = info[2].strip()
|
||||||
|
except IndexError:
|
||||||
|
self.foreign = False
|
||||||
|
|
||||||
|
|
||||||
def copy_iso(self):
|
def copy_iso(self):
|
||||||
self.read_iso()
|
self.read_iso()
|
||||||
@@ -47,11 +69,20 @@ class RollHandler(object):
|
|||||||
|
|
||||||
def copy_roll(self):
|
def copy_roll(self):
|
||||||
# self.rinfo是经过file.RollInfoFile处理过的,返回相应的类对象.
|
# self.rinfo是经过file.RollInfoFile处理过的,返回相应的类对象.
|
||||||
roll_name = self.rinfo.getRollName()
|
if self.foreign:
|
||||||
roll_vers = self.rinfo.getRollVersion()
|
roll_name = self.rinfo.name
|
||||||
roll_arch = self.rinfo.getRollArch()
|
roll_vers = self.rinfo.version
|
||||||
roll_os = self.rinfo.getRollOS()
|
roll_arch = self.rinfo.arch
|
||||||
roll_boot = self.rinfo.isBootable()
|
roll_os = self.cmd.os
|
||||||
|
roll_boot = None
|
||||||
|
roll_path = self.mntdir
|
||||||
|
else:
|
||||||
|
roll_name = self.rinfo.getRollName()
|
||||||
|
roll_vers = self.rinfo.getRollVersion()
|
||||||
|
roll_arch = self.rinfo.getRollArch()
|
||||||
|
roll_os = self.rinfo.getRollOS()
|
||||||
|
roll_boot = self.rinfo.isBootable()
|
||||||
|
roll_path = os.path.join(self.mntdir, roll_name)
|
||||||
|
|
||||||
# 获取rolls存放位置
|
# 获取rolls存放位置
|
||||||
cmd = '/opt/sunhpc/bin/sunhpc report distro'
|
cmd = '/opt/sunhpc/bin/sunhpc report distro'
|
||||||
@@ -59,10 +90,16 @@ class RollHandler(object):
|
|||||||
distro = line[:-1]
|
distro = line[:-1]
|
||||||
|
|
||||||
# /export/sunhpc/install/rolls
|
# /export/sunhpc/install/rolls
|
||||||
rolls_dir = '%s/rolls' % (distro)
|
if self.foreign:
|
||||||
|
rolls_dir = '%s/repos' % (distro)
|
||||||
|
else:
|
||||||
|
rolls_dir = '%s/rolls' % (distro)
|
||||||
|
|
||||||
# /export/sunhpc/install/rolls/kernel......
|
# /export/sunhpc/install/rolls/kernel......
|
||||||
roll_dir = os.path.join(rolls_dir, roll_name)
|
if self.foreign:
|
||||||
|
roll_dir = os.path.join(rolls_dir, roll_name, roll_vers, roll_arch)
|
||||||
|
else:
|
||||||
|
roll_dir = os.path.join(rolls_dir, roll_name)
|
||||||
|
|
||||||
if self.clean:
|
if self.clean:
|
||||||
# /export/sunhpc/install/rolls/kernel/version/x86_64
|
# /export/sunhpc/install/rolls/kernel/version/x86_64
|
||||||
@@ -75,7 +112,7 @@ class RollHandler(object):
|
|||||||
os.makedirs(specific_roll_dir)
|
os.makedirs(specific_roll_dir)
|
||||||
|
|
||||||
cwd = os.getcwd()
|
cwd = os.getcwd()
|
||||||
os.chdir(os.path.join(self.mntdir, roll_name))
|
os.chdir(roll_path)
|
||||||
|
|
||||||
# 计算一下大小
|
# 计算一下大小
|
||||||
cmd = 'du -sh %s' % self.mntdir
|
cmd = 'du -sh %s' % self.mntdir
|
||||||
@@ -104,7 +141,7 @@ class RollHandler(object):
|
|||||||
os.chdir(os.path.join(self.mntdir, roll_name))
|
os.chdir(os.path.join(self.mntdir, roll_name))
|
||||||
|
|
||||||
# 修改所有目录权限,确保所有人能够访问,例如Apache等.
|
# 修改所有目录权限,确保所有人能够访问,例如Apache等.
|
||||||
self.cmd.shcmd("find %s -type d -exec chmod a+rx {} \;" % roll_dir, code=True)
|
self.cmd.shcmd(r"find %s -type d -exec chmod a+rx {} \;" % roll_dir, code=True)
|
||||||
|
|
||||||
# 插入roll信息到数据库,如果存在则放弃插入.
|
# 插入roll信息到数据库,如果存在则放弃插入.
|
||||||
rows = self.cmd.db.search('select * from rolls where' \
|
rows = self.cmd.db.search('select * from rolls where' \
|
||||||
@@ -122,6 +159,7 @@ class RollHandler(object):
|
|||||||
# 如果是引导roll,需要使用软件Kylins create distro重新部署.
|
# 如果是引导roll,需要使用软件Kylins create distro重新部署.
|
||||||
# 重新创建images,stage2,和相关连接相关任务.
|
# 重新创建images,stage2,和相关连接相关任务.
|
||||||
self.cmd.msg('Copy the %s finished.' % roll_name)
|
self.cmd.msg('Copy the %s finished.' % roll_name)
|
||||||
|
self.repodirs = roll_dir
|
||||||
|
|
||||||
def clean_dirs(self, dirs):
|
def clean_dirs(self, dirs):
|
||||||
for root, dirs, files in os.walk(dirs, topdown=False):
|
for root, dirs, files in os.walk(dirs, topdown=False):
|
||||||
@@ -202,6 +240,9 @@ class Command(command):
|
|||||||
roll_handler.copy_iso()
|
roll_handler.copy_iso()
|
||||||
roll_handler.umount_iso()
|
roll_handler.umount_iso()
|
||||||
|
|
||||||
|
if roll_handler.foreign:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
if pxesrv:
|
if pxesrv:
|
||||||
self.configSRV(q)
|
self.configSRV(q)
|
||||||
else:
|
else:
|
||||||
|
|||||||
382
lib/sunhpc/commands/pxelinux/build/cdpxe/__init__.py
Normal file
382
lib/sunhpc/commands/pxelinux/build/cdpxe/__init__.py
Normal file
@@ -0,0 +1,382 @@
|
|||||||
|
#coding:utf-8
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import sunhpc
|
||||||
|
import shutil
|
||||||
|
import textwrap
|
||||||
|
import subprocess
|
||||||
|
class Command(sunhpc.commands.pxelinux.build.command):
|
||||||
|
"""
|
||||||
|
Build the iso pxe (dhcpd, tftpd, httpd service for the sunhpc cluster.
|
||||||
|
|
||||||
|
1) config interface(e,g.. eth0/eth1) ip address
|
||||||
|
2) source /opt/sunhpc/etc/env.sunhpc
|
||||||
|
3) git clone https://gitee.com/qcsun/sunhpc.git
|
||||||
|
4) git clone https://gitee.com/qcsun/tools.git
|
||||||
|
5) rpm -ivh tools/sunhpc-python-3.12.0-1.el8.x86_64.rpm
|
||||||
|
6) sunhpc pxelinux build Rocky-8.7-x86_64-dvd1.iso
|
||||||
|
7) sunhpc pxelinux build cdpxe dev=eth1 repo=/export/sunhpc/install/repos/Rocky/8/x86_64
|
||||||
|
|
||||||
|
<param type='Bool' name='Quiet'>
|
||||||
|
Whether to output detailed information, default: no
|
||||||
|
</param>
|
||||||
|
|
||||||
|
<param type='name' name='dev'>
|
||||||
|
supply an network device name,e.g,, eth0,eth1...
|
||||||
|
</param>
|
||||||
|
|
||||||
|
<param type='path' name='repo'>
|
||||||
|
supply an pxe repo path,e.g,, /export/sunhpc/install/repos/Rocky/8/x86_64/
|
||||||
|
</param>
|
||||||
|
|
||||||
|
<param type='path' name='pxedir'>
|
||||||
|
supply an pxelinux dirs. Default: /tftpboot/pxelinux
|
||||||
|
</param>
|
||||||
|
|
||||||
|
<param type='string' name='boot'>
|
||||||
|
supply an boot mode, mbr or uefi. Default: MBR
|
||||||
|
</param>
|
||||||
|
|
||||||
|
<example cmd='pxelinux build cdpxe'>
|
||||||
|
In local build the iso pxe dhcpd, tftpd, httpd services.
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<example cmd='pxelinux build cdpxe dev=eth1 repo=/export/sunhpc/install/repos/Rocky/8/x86_64'>
|
||||||
|
Build the iso pxe dhcpd, tftpd, httpd service.
|
||||||
|
</example>
|
||||||
|
"""
|
||||||
|
def writeRepos(self, repo_dirs):
|
||||||
|
|
||||||
|
base_repo = os.path.join(repo_dirs, 'BaseOS')
|
||||||
|
apps_repo = os.path.join(repo_dirs, 'AppStream')
|
||||||
|
|
||||||
|
local_repos = '/etc/yum.repos.d/sunhpc-local.repo'
|
||||||
|
with open(local_repos, 'w') as fn:
|
||||||
|
fn.write('#\n# sunhpc-local.repo\n# Generate by sunhpc\n#\n\n')
|
||||||
|
fn.write('[sunhpc-baseos]\n')
|
||||||
|
fn.write('name=Sunhpc Linux $releasever - Local - BaseOS\n')
|
||||||
|
fn.write('baseurl=file://%s\n' % base_repo)
|
||||||
|
fn.write('gpgcheck=1\n')
|
||||||
|
fn.write('enabled=1\n')
|
||||||
|
fn.write('gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial\n\n')
|
||||||
|
|
||||||
|
fn.write('[sunhpc-appstream]\n')
|
||||||
|
fn.write('name=Sunhpc Linux $releasever - Local - AppStream\n')
|
||||||
|
fn.write('baseurl=file://%s\n' % apps_repo)
|
||||||
|
fn.write('gpgcheck=1\n')
|
||||||
|
fn.write('enabled=1\n')
|
||||||
|
fn.write('gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial\n\n')
|
||||||
|
|
||||||
|
def copyit(self, src, dst, perms=0o644, owner=None):
|
||||||
|
if not os.path.exists(src):
|
||||||
|
self.msg('The %s not exists.' % src, 'a')
|
||||||
|
|
||||||
|
if os.path.exists(dst): os.remove(dst)
|
||||||
|
|
||||||
|
shutil.copyfile(src, dst)
|
||||||
|
os.chmod(dst, perms)
|
||||||
|
if owner:
|
||||||
|
os.system('chown %s %s' % (owner, dst))
|
||||||
|
|
||||||
|
def run(self, params, args):
|
||||||
|
|
||||||
|
(quiet, pxedir, dev, rpath, boot) = self.fillParams([
|
||||||
|
('quiet', 'no'),
|
||||||
|
('pxedir', '/tftpboot/pxelinux'),
|
||||||
|
('dev', None),
|
||||||
|
('repo', ''),
|
||||||
|
('boot', 'mbr')
|
||||||
|
])
|
||||||
|
q = self.str2bool(quiet)
|
||||||
|
|
||||||
|
if dev is None:
|
||||||
|
self.msg('Must supply an network dev name,e.g,, eth0/eth1...', 'a')
|
||||||
|
|
||||||
|
address = os.popen("ip -4 addr show %s |grep inet|awk '{print $2}'" % dev).read().strip().split('/')[0]
|
||||||
|
|
||||||
|
if not os.path.exists(rpath):
|
||||||
|
self.msg('supply an pxe repo path,e.g,, repo=/export/sunhpc/install/repos/Rocky/8/x86_64', 'a')
|
||||||
|
|
||||||
|
# write new repos
|
||||||
|
self.writeRepos(rpath)
|
||||||
|
|
||||||
|
# install dhcp server
|
||||||
|
self.installDhcpd(dev, boot, address, q)
|
||||||
|
|
||||||
|
# install tftp server
|
||||||
|
self.installTftpd(pxedir, rpath, boot, address, q)
|
||||||
|
|
||||||
|
# install httpd server
|
||||||
|
self.installHttpd(dev, rpath, address, q)
|
||||||
|
|
||||||
|
# off selinux
|
||||||
|
os.system('sed -i "s/^SELINUX=.*/SELINUX=disabled/g" /etc/selinux/config')
|
||||||
|
|
||||||
|
def installHttpd(self, devices, rpath, address, q):
|
||||||
|
self.msg('Starting build the httpd service...', q=q)
|
||||||
|
|
||||||
|
webdir = '/var/www/html'
|
||||||
|
ksdirs = os.path.join(webdir, 'ksfile')
|
||||||
|
redhat = os.path.join(webdir, 'redhat')
|
||||||
|
repos = os.path.join(redhat, 'r8')
|
||||||
|
|
||||||
|
if not os.path.exists(ksdirs):
|
||||||
|
os.makedirs(ksdirs)
|
||||||
|
|
||||||
|
if not os.path.exists(redhat):
|
||||||
|
os.makedirs(redhat)
|
||||||
|
|
||||||
|
if not os.path.islink(repos):
|
||||||
|
os.symlink(rpath, repos)
|
||||||
|
|
||||||
|
shared = '/opt/sunhpc/share/pxeboot/kickstart'
|
||||||
|
ksfile = os.listdir(shared)
|
||||||
|
for i in ksfile:
|
||||||
|
if i.startswith('r8_min.conf'):
|
||||||
|
self.msg('Copying to %s - %s' % (ksdirs, i), q=q)
|
||||||
|
self.copyit(os.path.join(shared, i), os.path.join(ksdirs, i), 0o755, '0.0')
|
||||||
|
if i.startswith('r8_gui.conf'):
|
||||||
|
self.msg('Copying to %s - %s' % (ksdirs, i), q=q)
|
||||||
|
self.copyit(os.path.join(shared, i), os.path.join(ksdirs, i), 0o755, '0.0')
|
||||||
|
|
||||||
|
if not os.path.exists(os.path.join(ksdirs, i)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# replace ip address for kickstart config file.
|
||||||
|
os.system('sed -i "s/ipaddress/%s/g" %s' % (address, os.path.join(ksdirs, i)))
|
||||||
|
|
||||||
|
sunhpc_httpd_config = '/etc/httpd/conf.d/ks-sunhpc.conf'
|
||||||
|
if not os.path.exists(sunhpc_httpd_config):
|
||||||
|
cmds = 'yum install -y httpd'
|
||||||
|
cmds += '--disablerepo=* --enablerepo=sunhpc-appstream,sunhpc-baseos'
|
||||||
|
self.shcmd(cmds)
|
||||||
|
|
||||||
|
cmds = 'sed -i "s/#ServerName www.example.com:80/ServerName localhost:80/" /etc/httpd/conf/httpd.conf'
|
||||||
|
self.shcmd(cmds)
|
||||||
|
|
||||||
|
with open(sunhpc_httpd_config, 'w') as f:
|
||||||
|
f.write(textwrap.dedent("""\
|
||||||
|
<VirtualHost %s:80>
|
||||||
|
ServerAdmin root@cluster.hpc.com
|
||||||
|
DocumentRoot %s
|
||||||
|
ServerName cluster.hpc.com
|
||||||
|
ErrorLog logs/ks-sunhpc-error_log
|
||||||
|
CustomLog logs/ks-sunhpc-access_log common
|
||||||
|
<Directory %s>
|
||||||
|
AllowOverride All
|
||||||
|
Require all granted
|
||||||
|
</Directory>
|
||||||
|
<Directory %s>
|
||||||
|
Options Indexes MultiViews
|
||||||
|
AllowOverride All
|
||||||
|
Require all granted
|
||||||
|
</Directory>
|
||||||
|
</VirtualHost>
|
||||||
|
""" % (address, webdir, ksdirs, repos)))
|
||||||
|
|
||||||
|
self.fw.addports(['80', '443'], ['tcp'])
|
||||||
|
self.fw.addservice('http')
|
||||||
|
self.fw.addservice('https')
|
||||||
|
|
||||||
|
os.system('systemctl daemon-reload')
|
||||||
|
os.system('systemctl restart httpd.service')
|
||||||
|
os.system('systemctl status httpd.service')
|
||||||
|
|
||||||
|
|
||||||
|
def installTftpd(self, pxedir, rpath, boot, address, q):
|
||||||
|
self.msg('Starting build the tftpd service...', q=q)
|
||||||
|
|
||||||
|
sunhpc_tftpd_config = '/etc/xinetd.d/tftp'
|
||||||
|
# The dhcpd is not installed.
|
||||||
|
if not os.path.exists(sunhpc_tftpd_config):
|
||||||
|
cmds = 'yum install -y tftp tftp-server syslinux '
|
||||||
|
cmds += '--disablerepo=* --enablerepo=sunhpc-appstream,sunhpc-baseos'
|
||||||
|
self.shcmd(cmds)
|
||||||
|
|
||||||
|
# pxedir: /tftpboot/pxelinux
|
||||||
|
c7 = os.path.join(pxedir, 'c7')
|
||||||
|
r8 = os.path.join(pxedir, 'r8')
|
||||||
|
r9 = os.path.join(pxedir, 'r9')
|
||||||
|
pd = os.path.join(pxedir, 'pxelinux.cfg')
|
||||||
|
if not os.path.exists(pxedir):
|
||||||
|
os.makedirs(c7)
|
||||||
|
os.makedirs(r8)
|
||||||
|
os.makedirs(r9)
|
||||||
|
os.makedirs(pd)
|
||||||
|
|
||||||
|
self.fw.addports(['69'], ['udp'])
|
||||||
|
self.fw.addservice('tftp')
|
||||||
|
|
||||||
|
sunhpc_tftpd_config = '/etc/xinetd.d/tftp'
|
||||||
|
with open(sunhpc_tftpd_config, 'w') as f:
|
||||||
|
f.write(textwrap.dedent("""\
|
||||||
|
# default: off
|
||||||
|
# protocol. The tftp protocol is often used to boot diskless
|
||||||
|
# workstations, download configuration files to network-aware printers,
|
||||||
|
# and to start the installation process for some operating systems.
|
||||||
|
service tftp
|
||||||
|
{
|
||||||
|
socket_type = dgram
|
||||||
|
protocol = udp
|
||||||
|
wait = yes
|
||||||
|
user = root
|
||||||
|
server = /usr/sbin/in.tftpd
|
||||||
|
server_args = -s %s -B 1468
|
||||||
|
disable = no
|
||||||
|
per_source = 11
|
||||||
|
cps = 100 2
|
||||||
|
flags = IPv4
|
||||||
|
}
|
||||||
|
""" % pxedir))
|
||||||
|
|
||||||
|
tftpd_srvs_config = '/usr/lib/systemd/system/tftp.service'
|
||||||
|
with open(tftpd_srvs_config, 'w') as f:
|
||||||
|
f.write(textwrap.dedent(f"""\
|
||||||
|
[Unit]
|
||||||
|
Description=Tftp Server
|
||||||
|
Requires=tftp.socket
|
||||||
|
Documentation=man:in.tftpd
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/usr/sbin/in.tftpd -s %s
|
||||||
|
StandardInput=socket
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
Also=tftp.socket
|
||||||
|
""" % pxedir))
|
||||||
|
|
||||||
|
os.system('systemctl daemon-reload')
|
||||||
|
os.system('systemctl restart tftp.service')
|
||||||
|
#
|
||||||
|
# copy uefi boot file
|
||||||
|
# BaseOS/Packages/s/shim-x64-15.6-1.el8.x86_64.rpm
|
||||||
|
# BaseOS/Packages/g/grub2-efi-x64-2.02-142.el8.rocky.0.2.x86_64.rpm
|
||||||
|
# /usr/share/syslinux/{pxelinux.0, menu.c32}
|
||||||
|
# isolinux/{ldlinux.c32, libcom32.c32, libutil.c32}
|
||||||
|
# decompress: rpm2cpio xxx-xxx-xxx.rpm |cpio -dim
|
||||||
|
# boot/efi/EFI/BOOT/shimx64.efi
|
||||||
|
# boot/efi/EFI/rocky/grubx64.efi
|
||||||
|
#
|
||||||
|
share = '/opt/sunhpc/share/pxeboot'
|
||||||
|
repos = os.path.join(rpath, 'isolinux')
|
||||||
|
|
||||||
|
pxefile = os.listdir(share) + os.listdir(repos)
|
||||||
|
for i in pxefile:
|
||||||
|
if i.startswith('initrd.img'):
|
||||||
|
self.msg('Copying to %s - %s' % (r8, i), q=q)
|
||||||
|
self.copyit(os.path.join(repos, i), os.path.join(r8, i), 0o755, '0.0')
|
||||||
|
if i.startswith('vmlinuz'):
|
||||||
|
self.msg('Copying to %s - %s' % (r8, i), q=q)
|
||||||
|
self.copyit(os.path.join(repos, i), os.path.join(r8, i), 0o755, '0.0')
|
||||||
|
|
||||||
|
if i.startswith('grubx64.efi'):
|
||||||
|
self.msg('Copying to %s - %s' % (pxedir, i), q=q)
|
||||||
|
self.copyit(os.path.join(share, i), os.path.join(pxedir, i), 0o644, '0.0')
|
||||||
|
if i.startswith('ldlinux.c32'):
|
||||||
|
self.msg('Copying to %s - %s' % (pxedir, i), q=q)
|
||||||
|
self.copyit(os.path.join(share, i), os.path.join(pxedir, i), 0o644, '0.0')
|
||||||
|
if i.startswith('libcom32.c32'):
|
||||||
|
self.msg('Copying to %s - %s' % (pxedir, i), q=q)
|
||||||
|
self.copyit(os.path.join(share, i), os.path.join(pxedir, i), 0o644, '0.0')
|
||||||
|
if i.startswith('libutil.c32'):
|
||||||
|
self.msg('Copying to %s - %s' % (pxedir, i), q=q)
|
||||||
|
self.copyit(os.path.join(share, i), os.path.join(pxedir, i), 0o644, '0.0')
|
||||||
|
if i.startswith('menu.c32'):
|
||||||
|
self.msg('Copying to %s - %s' % (pxedir, i), q=q)
|
||||||
|
self.copyit(os.path.join(share, i), os.path.join(pxedir, i), 0o644, '0.0')
|
||||||
|
if i.startswith('pxelinux.0'):
|
||||||
|
self.msg('Copying to %s - %s' % (pxedir, i), q=q)
|
||||||
|
self.copyit(os.path.join(share, i), os.path.join(pxedir, i), 0o644, '0.0')
|
||||||
|
if i.startswith('shimx64.efi'):
|
||||||
|
self.msg('Copying to %s - %s' % (pxedir, i), q=q)
|
||||||
|
self.copyit(os.path.join(share, i), os.path.join(pxedir, i), 0o644, '0.0')
|
||||||
|
if i.startswith('vesamenu.c32'):
|
||||||
|
self.msg('Copying to %s - %s' % (pxedir, i), q=q)
|
||||||
|
self.copyit(os.path.join(share, i), os.path.join(pxedir, i), 0o644, '0.0')
|
||||||
|
|
||||||
|
if i.startswith('default'):
|
||||||
|
self.msg('Copying to %s - %s' % (pd, i), q=q)
|
||||||
|
self.copyit(os.path.join(share, i), os.path.join(pd, i), 0o644, '0.0')
|
||||||
|
|
||||||
|
# replace ip address for pxelinux.cfg/default
|
||||||
|
os.system('sed -i "s/ipaddress/%s/g" %s' % (address, os.path.join(pd, 'default')))
|
||||||
|
|
||||||
|
|
||||||
|
def installDhcpd(self, devices, boot, address, q):
|
||||||
|
self.msg('Starting build the dhcpd service...', q=q)
|
||||||
|
|
||||||
|
config = '/etc/dhcp/dhcpd.conf'
|
||||||
|
# The dhcpd is not installed.
|
||||||
|
if not os.path.exists(config):
|
||||||
|
cmds = 'yum install -y dhcp-server '
|
||||||
|
cmds += '--disablerepo=* --enablerepo=sunhpc-appstream,sunhpc-baseos'
|
||||||
|
self.shcmd(cmds)
|
||||||
|
|
||||||
|
# 172.16.2.1 -> ['172','16','0'] -> 172.16.2
|
||||||
|
addr_pre = '.'.join(address.split('.')[:3])
|
||||||
|
|
||||||
|
with open(config, 'w') as f:
|
||||||
|
f.write(textwrap.dedent("""\
|
||||||
|
#
|
||||||
|
# dhcpd.conf
|
||||||
|
#
|
||||||
|
|
||||||
|
ddns-update-style none;
|
||||||
|
allow bootp;
|
||||||
|
allow booting;
|
||||||
|
option space pxelinux;
|
||||||
|
option pxelinux.magic code 208 = string;
|
||||||
|
option pxelinux.configfile code 209 = text;
|
||||||
|
option pxelinux.pathprefix code 210 = text;
|
||||||
|
option pxelinux.reboottime code 211 = unsigned integer 32;
|
||||||
|
option arch-type code 93 = unsigned integer 16;
|
||||||
|
|
||||||
|
subnet %s.0 netmask 255.255.255.0 {
|
||||||
|
default-lease-time 600;
|
||||||
|
max-lease-time 7200;
|
||||||
|
|
||||||
|
range %s.10 %s.200;
|
||||||
|
option routers %s;
|
||||||
|
option domain-name "local";
|
||||||
|
option domain-name-servers %s;
|
||||||
|
option interface-mtu 1500;
|
||||||
|
|
||||||
|
class "pxeclients" {
|
||||||
|
match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
|
||||||
|
next-server %s;
|
||||||
|
if option arch-type = 00:07 {
|
||||||
|
filename "shimx64.efi";
|
||||||
|
} else {
|
||||||
|
filename "pxelinux.0";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""" % (addr_pre, addr_pre, addr_pre, address, address, address)))
|
||||||
|
|
||||||
|
dhcpd_srv_conf = '/usr/lib/systemd/system/dhcpd.service'
|
||||||
|
with open(dhcpd_srv_conf, 'w') as f:
|
||||||
|
f.write(textwrap.dedent("""\
|
||||||
|
[Unit]
|
||||||
|
Description=DHCPv4 Server Daemon
|
||||||
|
Documentation=man:dhcpd(8) man:dhcpd.conf(5)
|
||||||
|
Wants=network-online.target
|
||||||
|
After=network-online.target
|
||||||
|
After=time-sync.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=notify
|
||||||
|
ExecStart=/usr/sbin/dhcpd -f -cf /etc/dhcp/dhcpd.conf -user dhcpd -group dhcpd --no-pid %s
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
""" % devices))
|
||||||
|
|
||||||
|
self.fw.addports(['67'], ['udp'])
|
||||||
|
self.fw.addports(['68'], ['udp'])
|
||||||
|
self.fw.addservice('dhcp')
|
||||||
|
os.system('systemctl daemon-reload')
|
||||||
|
os.system('systemctl restart dhcpd')
|
||||||
|
os.system('systemctl status dhcpd')
|
||||||
|
|
||||||
|
RollName = "base"
|
||||||
110
lib/sunhpc/commands/report/completion/__init__.py
Normal file
110
lib/sunhpc/commands/report/completion/__init__.py
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
#coding:utf-8
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import sunhpc
|
||||||
|
class Command(sunhpc.commands.report.command):
|
||||||
|
"""
|
||||||
|
Output the path prefix for the location of the Rocks distribution.
|
||||||
|
|
||||||
|
<example cmd='report distro'>
|
||||||
|
Output the current path prefix to the distribution.
|
||||||
|
</example>
|
||||||
|
"""
|
||||||
|
def run(self, params, args):
|
||||||
|
|
||||||
|
basepath = '/opt/sunhpc/lib'
|
||||||
|
|
||||||
|
cmd = args
|
||||||
|
dotscmd = ''
|
||||||
|
listcmd = []
|
||||||
|
module = None
|
||||||
|
if len(cmd):
|
||||||
|
s = 'sunhpc.commands.%s' % '.'.join(cmd)
|
||||||
|
try:
|
||||||
|
__import__(s)
|
||||||
|
module, listcmd, dotscmd = eval(s), s.split('.'), '.'.join(cmd)
|
||||||
|
i = 1
|
||||||
|
except:
|
||||||
|
module = None
|
||||||
|
else:
|
||||||
|
listcmd = ['sunhpc', 'commands']
|
||||||
|
|
||||||
|
if not module:
|
||||||
|
for i in range(len(args), 0, -1):
|
||||||
|
s = 'sunhpc.commands.%s' % '.'.join(args[:i])
|
||||||
|
try:
|
||||||
|
__import__(s)
|
||||||
|
module, listcmd, dotscmd = eval(s), s.split('.'), '.'.join(args[:i])
|
||||||
|
if module:
|
||||||
|
break
|
||||||
|
except ImportError:
|
||||||
|
listcmd = s.split('.')[:-1]
|
||||||
|
continue
|
||||||
|
|
||||||
|
cmdpath = os.path.join(basepath, '/'.join(listcmd))
|
||||||
|
|
||||||
|
cmddirs = []
|
||||||
|
for d in os.listdir(cmdpath):
|
||||||
|
tmpdirs = os.path.join(cmdpath, d)
|
||||||
|
|
||||||
|
if d.startswith('__'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not os.path.isdir(tmpdirs):
|
||||||
|
continue
|
||||||
|
|
||||||
|
cmddirs.append(d)
|
||||||
|
|
||||||
|
print (' '.join(cmddirs))
|
||||||
|
|
||||||
|
try:
|
||||||
|
o = getattr(module, 'Command')(None)
|
||||||
|
except AttributeError:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if o.MustBeRoot and not self.isRootUser():
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
results = []
|
||||||
|
for arg in o.usage().split():
|
||||||
|
tmp = arg.split('=', 1)
|
||||||
|
if len(tmp) != 2:
|
||||||
|
continue
|
||||||
|
|
||||||
|
tmpstr = arg.replace('[', '')
|
||||||
|
tmpstr = tmpstr.replace(']', '')
|
||||||
|
tmpstr = tmpstr.split('=')[0] + '='
|
||||||
|
results.append(tmpstr)
|
||||||
|
|
||||||
|
print (' '.join(results))
|
||||||
|
|
||||||
|
RollName = "base"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ class Command(sunhpc.commands.report.command):
|
|||||||
#
|
#
|
||||||
# Generated by sunhpc report yumrepos commands.
|
# Generated by sunhpc report yumrepos commands.
|
||||||
#
|
#
|
||||||
[appstream]
|
[baseos]
|
||||||
name=Rocky Linux $releasever - BaseOS
|
name=Rocky Linux $releasever - BaseOS
|
||||||
%s
|
%s
|
||||||
gpgcheck=1
|
gpgcheck=1
|
||||||
@@ -131,7 +131,7 @@ class Command(sunhpc.commands.report.command):
|
|||||||
#
|
#
|
||||||
# Generated by sunhpc report yumrepos commands.
|
# Generated by sunhpc report yumrepos commands.
|
||||||
#
|
#
|
||||||
[appstream]
|
[rockyextras]
|
||||||
name=Rocky Linux $releasever - Extras
|
name=Rocky Linux $releasever - Extras
|
||||||
%s
|
%s
|
||||||
gpgcheck=1
|
gpgcheck=1
|
||||||
|
|||||||
57
lib/sunhpc/commands/set/grub/__init__.py
Normal file
57
lib/sunhpc/commands/set/grub/__init__.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#coding:utf-8
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import sunhpc
|
||||||
|
class command(sunhpc.commands.HostArgumentProcessor,
|
||||||
|
sunhpc.commands.set.command):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Command(command):
|
||||||
|
"""
|
||||||
|
Grub2 boot configure
|
||||||
|
<param type='strings' name='default'>
|
||||||
|
Set the default boot options
|
||||||
|
</param>
|
||||||
|
|
||||||
|
<example cmd='grub default=1'>
|
||||||
|
Set the default boot options index 1
|
||||||
|
</example>
|
||||||
|
"""
|
||||||
|
|
||||||
|
def run(self, params, args):
|
||||||
|
(default, ) = self.fillParams([
|
||||||
|
('default', None),
|
||||||
|
])
|
||||||
|
|
||||||
|
if default:
|
||||||
|
try:
|
||||||
|
default_number = int(default)
|
||||||
|
#self.msg('Please modify it manually by using the command: grubby --set-defautl-index=%s' % default)
|
||||||
|
info = self.message('[*]', 'Please modify it manually by using the command:', 'grubby --set-default-index=%s' % default)
|
||||||
|
print (info)
|
||||||
|
sys.exit(0)
|
||||||
|
except ValueError:
|
||||||
|
self.msg('The "default" parameters must be the numbers.', 'a')
|
||||||
|
|
||||||
|
|
||||||
|
current_kernel = os.popen('uname -r').read().strip()
|
||||||
|
self.msg('--> Current kernel: %s' % current_kernel)
|
||||||
|
|
||||||
|
default_kernel = os.popen('grubby --default-kernel').read().strip()
|
||||||
|
self.msg('--> Default kernel: %s' % default_kernel)
|
||||||
|
|
||||||
|
kernel_index = os.popen('grubby --default-index').read().strip()
|
||||||
|
self.msg('--> Default kernel index: %s' % kernel_index)
|
||||||
|
|
||||||
|
kernel_list = os.popen('grubby --info=ALL').readlines()
|
||||||
|
self.msg('')
|
||||||
|
self.msg('-------- All Kernel List begin----------')
|
||||||
|
for i in kernel_list:
|
||||||
|
self.msg(i.strip())
|
||||||
|
self.msg('-------- All Kernel List ender----------')
|
||||||
|
self.msg('')
|
||||||
|
|
||||||
|
self.msg('Using index number, you can change the default boot kernel.e,g.. sunhpc set grub default=1', 'i')
|
||||||
|
|
||||||
|
RollName = "base"
|
||||||
@@ -51,7 +51,10 @@ class Command(command):
|
|||||||
softname = 'autodock'
|
softname = 'autodock'
|
||||||
suffname = 'tar.gz'
|
suffname = 'tar.gz'
|
||||||
dirsname = os.path.join(source, 'hpcsoft', 'AutoDock')
|
dirsname = os.path.join(source, 'hpcsoft', 'AutoDock')
|
||||||
verslist = self.getVersions(key=softname, suffix=suffname, dirs=dirsname)
|
try:
|
||||||
|
verslist = self.getVersions(key=softname, suffix=suffname, dirs=dirsname)
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
self.msg(str(e), 'a')
|
||||||
|
|
||||||
if len(args):
|
if len(args):
|
||||||
version = args[0]
|
version = args[0]
|
||||||
|
|||||||
358
lib/sunhpc/core/app.py
Normal file
358
lib/sunhpc/core/app.py
Normal file
@@ -0,0 +1,358 @@
|
|||||||
|
#
|
||||||
|
# Time for an initial checkin. Datastructure and general layout of the
|
||||||
|
# code is correct. Still need comparison code for File and RPM objects.
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import types
|
||||||
|
import sunhpc
|
||||||
|
import getopt
|
||||||
|
import sunhpc.invoke
|
||||||
|
import xml
|
||||||
|
from xml.sax import saxutils
|
||||||
|
from xml.sax import handler
|
||||||
|
from xml.sax import make_parser
|
||||||
|
from xml.sax._exceptions import SAXParseException
|
||||||
|
|
||||||
|
class Application:
|
||||||
|
|
||||||
|
def __init__(self, argv=None):
|
||||||
|
|
||||||
|
# Applets (code in the kickstart graph) doesn't pass sys.argv
|
||||||
|
# so pick it up anyway to keep everything working.
|
||||||
|
|
||||||
|
if not argv:
|
||||||
|
argv = sys.argv
|
||||||
|
|
||||||
|
self.args = []
|
||||||
|
self.caller_args = argv[1:]
|
||||||
|
self.usage_command = os.path.basename(argv[0])
|
||||||
|
self.usage_name = 'Application'
|
||||||
|
self.usage_version = 0
|
||||||
|
self.rcfileHandler = RCFileHandler
|
||||||
|
self.rcfileList = []
|
||||||
|
self.rcForce = []
|
||||||
|
|
||||||
|
self.projectName = 'sunhpc'
|
||||||
|
self.projectVersionName = 'base'
|
||||||
|
self.projectVersionMajor = sunhpc.version_major
|
||||||
|
self.projectVersionMinor = sunhpc.version_minor
|
||||||
|
self.projectVersionMicro = sunhpc.version_micro
|
||||||
|
|
||||||
|
|
||||||
|
self.getopt = sunhpc.core.utils.Struct()
|
||||||
|
self.getopt.s = [ 'h' ]
|
||||||
|
self.getopt.l = [ 'help',
|
||||||
|
'list-rcfiles',
|
||||||
|
'list-project-info',
|
||||||
|
'rcfile='
|
||||||
|
]
|
||||||
|
|
||||||
|
# Unset our local
|
||||||
|
try:
|
||||||
|
os.environ["LANG"]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
def projectInfo(self):
|
||||||
|
return [ self.projectName,
|
||||||
|
self.projectVersionName,
|
||||||
|
int(self.projectVersionMajor),
|
||||||
|
int(self.projectVersionMinor),
|
||||||
|
int(self.projectVersionMicro) ]
|
||||||
|
|
||||||
|
def getArgs(self):
|
||||||
|
return self.args
|
||||||
|
|
||||||
|
def setArgs(self, argslist):
|
||||||
|
self.args = argslist
|
||||||
|
|
||||||
|
|
||||||
|
def parseArgs(self, rcbase=None):
|
||||||
|
"""Parses the command line arguments and all the relevant
|
||||||
|
resource-control (RC) files for this application. The usage_command
|
||||||
|
(generally argv[0]) will determine which the name of our rcfile,
|
||||||
|
unless overrided with the rcbase argument."""
|
||||||
|
|
||||||
|
# Save any existing options
|
||||||
|
args = self.getArgs()
|
||||||
|
|
||||||
|
# First pass to get rcfiles specified on the cmd line
|
||||||
|
self.setArgs(self.caller_args)
|
||||||
|
self.parseCommandLine(rcfile=1)
|
||||||
|
|
||||||
|
# Parse Resource Control files
|
||||||
|
self.setArgs([])
|
||||||
|
if not rcbase:
|
||||||
|
rcbase = self.usage_command
|
||||||
|
self.parseRC(rcbase)
|
||||||
|
|
||||||
|
for rc in self.rcForce:
|
||||||
|
self.parseRCFile(rc, rcbase)
|
||||||
|
|
||||||
|
# Command line options always win
|
||||||
|
self.setArgs(args + self.args + self.caller_args)
|
||||||
|
self.parseCommandLine()
|
||||||
|
|
||||||
|
|
||||||
|
def parseRC(self, rcbase, section=None):
|
||||||
|
rcfile = rcbase + 'rc'
|
||||||
|
|
||||||
|
if not section:
|
||||||
|
section = rcbase
|
||||||
|
|
||||||
|
# Where we look for resource-control files. First in
|
||||||
|
# the default 'etc' location, then HOME, finally in this dir.
|
||||||
|
# We use data from all three, such that later rc files take
|
||||||
|
# precedence.
|
||||||
|
|
||||||
|
dirList = [ os.path.join(os.sep,'opt', self.projectName, 'etc') ]
|
||||||
|
if 'HOME' in os.environ:
|
||||||
|
dirList.append(os.environ['HOME'])
|
||||||
|
dirList.append('.')
|
||||||
|
|
||||||
|
# Look for both hidden and visible rc files.
|
||||||
|
for dir in dirList:
|
||||||
|
self.parseRCFile(os.path.join(dir, '.' + rcfile), section)
|
||||||
|
self.parseRCFile(os.path.join(dir, rcfile), section)
|
||||||
|
|
||||||
|
def parseRCFile(self, filename, section):
|
||||||
|
if os.path.isfile(filename) and filename not in self.rcfileList:
|
||||||
|
self.rcfileList.append(filename)
|
||||||
|
file = open(filename, 'r')
|
||||||
|
parser = make_parser()
|
||||||
|
handler = self.rcfileHandler(self)
|
||||||
|
if section:
|
||||||
|
handler.setSection(section)
|
||||||
|
parser.setContentHandler(handler)
|
||||||
|
try:
|
||||||
|
parser.parse(file)
|
||||||
|
except SAXParseException as msg:
|
||||||
|
print (filename, "XML parse exception: ", msg)
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
def parseCommandLine(self, rcfile=0):
|
||||||
|
"""Calls getopt to parse the command line flags. In
|
||||||
|
rcfile mode we just get --rcfile options."""
|
||||||
|
|
||||||
|
short = ''
|
||||||
|
for e in self.getopt.s:
|
||||||
|
if isinstance(e, tuple):
|
||||||
|
short = short + e[0]
|
||||||
|
else:
|
||||||
|
short = short + e
|
||||||
|
|
||||||
|
long = []
|
||||||
|
for e in self.getopt.l:
|
||||||
|
if isinstance(e, tuple):
|
||||||
|
long.append(e[0])
|
||||||
|
else:
|
||||||
|
long.append(e)
|
||||||
|
|
||||||
|
try:
|
||||||
|
opts, args = getopt.getopt(self.args, short, long)
|
||||||
|
except getopt.GetoptError as msg:
|
||||||
|
sys.stderr.write("error - %s\n" % msg)
|
||||||
|
self.usage()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
for c in opts:
|
||||||
|
if rcfile:
|
||||||
|
if c[0] != "--rcfile":
|
||||||
|
continue
|
||||||
|
self.parseArg(c)
|
||||||
|
|
||||||
|
if not rcfile:
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def parseArg(self, c):
|
||||||
|
if c[0] in ('-h', '--help'):
|
||||||
|
self.help()
|
||||||
|
sys.exit(0)
|
||||||
|
elif c[0] == '--list-rcfiles':
|
||||||
|
print (self.rcfileList)
|
||||||
|
sys.exit(0)
|
||||||
|
elif c[0] == '--list-project-info':
|
||||||
|
print (self.projectInfo())
|
||||||
|
sys.exit(0)
|
||||||
|
elif c[0] == '--rcfile':
|
||||||
|
self.rcForce.append(c[1])
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def usage(self):
|
||||||
|
|
||||||
|
if 'COLUMNS' in os.environ:
|
||||||
|
cols = os.environ['COLUMNS']
|
||||||
|
else:
|
||||||
|
cols = 80
|
||||||
|
|
||||||
|
usagelist = [ 'Usage: ', self.usage_command, ' ' ]
|
||||||
|
|
||||||
|
# Build string of argument-free short options.
|
||||||
|
s = '[-'
|
||||||
|
for e in self.getopt.s:
|
||||||
|
if len(e) == 1:
|
||||||
|
s = s + e
|
||||||
|
s = s + ']'
|
||||||
|
if len(s) == 3:
|
||||||
|
s = ''
|
||||||
|
usagelist.append(s)
|
||||||
|
|
||||||
|
# Add the argument short options to the above string
|
||||||
|
for e in self.getopt.s:
|
||||||
|
if isinstance(e, tuple):
|
||||||
|
v = e[0]
|
||||||
|
h = e[1]
|
||||||
|
else:
|
||||||
|
v = e
|
||||||
|
h = 'arg'
|
||||||
|
if len(v) != 1:
|
||||||
|
usagelist.append(' [-' + v[:-1] + ' ' + h + ']')
|
||||||
|
|
||||||
|
# Add argument-free long options
|
||||||
|
for e in self.getopt.l:
|
||||||
|
if isinstance(e, tuple):
|
||||||
|
v = e[0]
|
||||||
|
else:
|
||||||
|
v = e
|
||||||
|
if v[len(v)-1] != '=':
|
||||||
|
usagelist.append(' [--' + v + ']')
|
||||||
|
|
||||||
|
# Add argument long options
|
||||||
|
for e in self.getopt.l:
|
||||||
|
if isinstance(e, tuple):
|
||||||
|
v = e[0]
|
||||||
|
h = e[1]
|
||||||
|
else:
|
||||||
|
v = e
|
||||||
|
h = 'arg'
|
||||||
|
if v[len(v)-1] == '=':
|
||||||
|
usagelist.append(' [--' + v[:-1] + ' ' + h + ']')
|
||||||
|
|
||||||
|
usagelist.append(self.usageTail())
|
||||||
|
|
||||||
|
# Print the usage, word wrapped to the correct screen size.
|
||||||
|
print (self.usage_name, '- version', self.usage_version)
|
||||||
|
l = 0
|
||||||
|
s = ''
|
||||||
|
for e in usagelist:
|
||||||
|
if l + len(e) <= cols:
|
||||||
|
s = s + e
|
||||||
|
l = l + len(e)
|
||||||
|
else:
|
||||||
|
print (s)
|
||||||
|
l = len(e)
|
||||||
|
s = e
|
||||||
|
if s:
|
||||||
|
print (s)
|
||||||
|
|
||||||
|
def help(self):
|
||||||
|
self.usage()
|
||||||
|
|
||||||
|
def usageTail(self):
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def getArch(self):
|
||||||
|
return sunhpc.core.utils.getNativeArch()
|
||||||
|
|
||||||
|
class RCFileHandler(sunhpc.core.utils.ParseXML):
|
||||||
|
|
||||||
|
def __init__(self, application):
|
||||||
|
sunhpc.core.utils.ParseXML.__init__(self, application)
|
||||||
|
self.foundSection = 0
|
||||||
|
self.section = self.app.usage_command
|
||||||
|
|
||||||
|
def setSection(self, section):
|
||||||
|
self.section = section
|
||||||
|
|
||||||
|
def startElement(self, name, attrs):
|
||||||
|
#
|
||||||
|
# need to convert all the attributes to ascii strings.
|
||||||
|
# starting in python 2, the xml parser puts the attributes in
|
||||||
|
# unicode which causes problems with sunhpc apps classes, specifically
|
||||||
|
# those that append path names found in the attributes to the sys.path
|
||||||
|
#
|
||||||
|
newattrs = {}
|
||||||
|
for (aname, avalue) in attrs.items():
|
||||||
|
newattrs[aname] = str(attrs[aname])
|
||||||
|
|
||||||
|
if self.foundSection:
|
||||||
|
sunhpc.core.utils.ParseXML.startElement(self, name, newattrs)
|
||||||
|
elif name == self.section:
|
||||||
|
self.startElementSection(name, newattrs)
|
||||||
|
|
||||||
|
def endElement(self, name):
|
||||||
|
if self.foundSection and name == self.foundSection:
|
||||||
|
self.endElementSection(name)
|
||||||
|
if self.foundSection:
|
||||||
|
sunhpc.core.utils.ParseXML.endElement(self, name)
|
||||||
|
|
||||||
|
def getOptions(self):
|
||||||
|
return self.options
|
||||||
|
|
||||||
|
|
||||||
|
# <section parent="base">
|
||||||
|
|
||||||
|
def startElementSection(self, name, attrs):
|
||||||
|
parent = attrs.get('parent')
|
||||||
|
if parent:
|
||||||
|
self.app.parseRC(parent, parent)
|
||||||
|
self.foundSection = 1
|
||||||
|
|
||||||
|
def endElementSection(self, name, attrs):
|
||||||
|
self.foundSection = 0
|
||||||
|
|
||||||
|
# <usage>
|
||||||
|
|
||||||
|
def startElement_usage(self, name, attrs):
|
||||||
|
usageName = attrs.get('name')
|
||||||
|
usageVersion = attrs.get('version')
|
||||||
|
|
||||||
|
if usageName:
|
||||||
|
self.app.usage_name = usageName
|
||||||
|
if usageVersion:
|
||||||
|
self.app.usage_version = usageVersion
|
||||||
|
|
||||||
|
# <option>
|
||||||
|
|
||||||
|
def startElement_option(self, name, attrs):
|
||||||
|
argName = attrs.get('name')
|
||||||
|
# Will return None if value is not present.
|
||||||
|
argValue = attrs.get('value')
|
||||||
|
|
||||||
|
list = self.app.getArgs()
|
||||||
|
|
||||||
|
if len(argName) > 1:
|
||||||
|
flag = '--' + argName
|
||||||
|
|
||||||
|
# We differentiate between empty values and
|
||||||
|
# no value at all.
|
||||||
|
if argValue is not None:
|
||||||
|
list.append('%s=%s' % (flag, argValue))
|
||||||
|
else:
|
||||||
|
list.append(flag)
|
||||||
|
else:
|
||||||
|
flag = '-' + argName
|
||||||
|
if argValue:
|
||||||
|
list.append('%s %s' % (flag, argValue))
|
||||||
|
else:
|
||||||
|
list.append(flag)
|
||||||
|
|
||||||
|
self.app.setArgs(list)
|
||||||
|
|
||||||
|
|
||||||
|
# <project>
|
||||||
|
|
||||||
|
def startElement_project(self, name, attrs):
|
||||||
|
self.app.projectName = attrs.get('name')
|
||||||
|
self.app.projectVersionName = attrs.get('versionName')
|
||||||
|
self.app.projectVersionMajor = attrs.get('versionMajor')
|
||||||
|
self.app.projectVersionMinor = attrs.get('versionMinor')
|
||||||
|
self.app.projectVersionMicro = attrs.get('versionMicro')
|
||||||
|
|
||||||
@@ -400,7 +400,7 @@ class DistributionBuilder(Builder):
|
|||||||
#
|
#
|
||||||
os.chdir(self.dist.getReleasePath())
|
os.chdir(self.dist.getReleasePath())
|
||||||
if self.calcmd5:
|
if self.calcmd5:
|
||||||
cmd = '/usr/bin/md5sum `find -L . -type f | sed "s/^\.\///" | '
|
cmd = r'/usr/bin/md5sum `find -L . -type f | sed "s/^\.\///" | '
|
||||||
cmd += 'egrep -v "^build|^SRPMS|^force" | egrep -v "rpm$"` '
|
cmd += 'egrep -v "^build|^SRPMS|^force" | egrep -v "rpm$"` '
|
||||||
cmd += '> %s/packages.md5' % (productfilesdir)
|
cmd += '> %s/packages.md5' % (productfilesdir)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -162,9 +162,9 @@ class Tree:
|
|||||||
os.path.islink(filepath):
|
os.path.islink(filepath):
|
||||||
self.build(os.path.join(dir, f))
|
self.build(os.path.join(dir, f))
|
||||||
else:
|
else:
|
||||||
if re.match('.*\.rpm$', f) != None:
|
if re.match(r'.*\.rpm$', f) != None:
|
||||||
v.append(RPMFile(filepath))
|
v.append(RPMFile(filepath))
|
||||||
elif re.match('roll-.*\.iso$', f) != None:
|
elif re.match(r'roll-.*\.iso$', f) != None:
|
||||||
v.append(RollFile(filepath))
|
v.append(RollFile(filepath))
|
||||||
else:
|
else:
|
||||||
v.append(File(filepath))
|
v.append(File(filepath))
|
||||||
@@ -204,7 +204,7 @@ class RPMBaseFile(File):
|
|||||||
|
|
||||||
def versionList(self, s):
|
def versionList(self, s):
|
||||||
list = []
|
list = []
|
||||||
for e in re.split('\.+|_+', s):
|
for e in re.split(r'\.+|_+', s):
|
||||||
l = []
|
l = []
|
||||||
num = ''
|
num = ''
|
||||||
alpha = ''
|
alpha = ''
|
||||||
|
|||||||
371
lib/sunhpc/core/interpreter.py
Normal file
371
lib/sunhpc/core/interpreter.py
Normal file
@@ -0,0 +1,371 @@
|
|||||||
|
#
|
||||||
|
#coding:utf-8
|
||||||
|
#
|
||||||
|
#Author : QCSun
|
||||||
|
#Email : qcsun@sunhpc.com
|
||||||
|
#Times : 2023-04-14 05:21:02
|
||||||
|
#WebSite : https://www.sunhpc.com
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import queue
|
||||||
|
import atexit
|
||||||
|
import textwrap
|
||||||
|
import readline
|
||||||
|
import itertools
|
||||||
|
|
||||||
|
from sunhpc.core.utils import SunhpcCliException
|
||||||
|
|
||||||
|
GLOBAL_OPTS = {}
|
||||||
|
|
||||||
|
def is_libedit():
|
||||||
|
return "libedit" in readline.__doc__
|
||||||
|
|
||||||
|
class BaseInterpreter(object):
|
||||||
|
history_file = os.path.expanduser("~/.history")
|
||||||
|
history_length = 100
|
||||||
|
global_help = ""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.setup()
|
||||||
|
self.banner = ""
|
||||||
|
|
||||||
|
def preceding(self):
|
||||||
|
"""占位函数.在命令模块中覆盖此函数.可以预处理一些事情"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
""" Initialization of third-party libraries
|
||||||
|
|
||||||
|
Setting interpreter history.
|
||||||
|
Setting appropriate completer function.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if not os.path.exists(self.history_file):
|
||||||
|
with open(self.history_file, "a+") as history:
|
||||||
|
if is_libedit():
|
||||||
|
history.write("_HiStOrY_V2_\n\n")
|
||||||
|
|
||||||
|
readline.read_history_file(self.history_file)
|
||||||
|
readline.set_history_length(self.history_length)
|
||||||
|
atexit.register(readline.write_history_file, self.history_file)
|
||||||
|
|
||||||
|
readline.parse_and_bind("set enable-keypad on")
|
||||||
|
|
||||||
|
readline.set_completer(self.complete)
|
||||||
|
readline.set_completer_delims(" \t\n;")
|
||||||
|
if is_libedit():
|
||||||
|
readline.parse_and_bind("bind ^I rl_complete")
|
||||||
|
else:
|
||||||
|
readline.parse_and_bind("tab: complete")
|
||||||
|
|
||||||
|
def parse_line(self, line):
|
||||||
|
""" Split line into command and argument.
|
||||||
|
|
||||||
|
:param line: line to parse
|
||||||
|
:return: (command, argument, named_arguments)
|
||||||
|
"""
|
||||||
|
kwargs = dict()
|
||||||
|
command, _, arg = line.strip().partition(" ")
|
||||||
|
args = arg.strip().split()
|
||||||
|
for word in args:
|
||||||
|
if '=' in word:
|
||||||
|
(key, value) = word.split('=', 1)
|
||||||
|
kwargs[key.lower()] = value
|
||||||
|
arg = arg.replace(word, '')
|
||||||
|
return command, ' '.join(arg.split()), kwargs
|
||||||
|
|
||||||
|
@property
|
||||||
|
def prompt(self):
|
||||||
|
""" Returns prompt string """
|
||||||
|
return ">>>"
|
||||||
|
|
||||||
|
def get_command_handler(self, command):
|
||||||
|
""" Parsing command and returning appropriate handler.
|
||||||
|
|
||||||
|
:param command: command
|
||||||
|
:return: command_handler
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
command_handler = getattr(self, "command_{}".format(command))
|
||||||
|
except AttributeError:
|
||||||
|
raise SunhpcCliException("Unknown command: '{}'".format(command))
|
||||||
|
|
||||||
|
return command_handler
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
""" Routersploit main entry point. Starting interpreter loop. """
|
||||||
|
|
||||||
|
print(self.banner)
|
||||||
|
self.preceding()
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
command, args, kwargs = self.parse_line(input(self.prompt))
|
||||||
|
if not command:
|
||||||
|
continue
|
||||||
|
command_handler = self.get_command_handler(command)
|
||||||
|
command_handler(args, **kwargs)
|
||||||
|
except (EOFError, KeyboardInterrupt, SystemExit):
|
||||||
|
print("Sunhpc cli stopped")
|
||||||
|
break
|
||||||
|
|
||||||
|
def complete(self, text, state):
|
||||||
|
"""Return the next possible completion for 'text'.
|
||||||
|
|
||||||
|
If a command has not been entered, then complete against command list.
|
||||||
|
Otherwise try to call complete_<command> to get list of completions.
|
||||||
|
"""
|
||||||
|
if state == 0:
|
||||||
|
original_line = readline.get_line_buffer()
|
||||||
|
line = original_line.lstrip()
|
||||||
|
stripped = len(original_line) - len(line)
|
||||||
|
start_index = readline.get_begidx() - stripped
|
||||||
|
end_index = readline.get_endidx() - stripped
|
||||||
|
|
||||||
|
if start_index > 0:
|
||||||
|
cmd, args, _ = self.parse_line(line)
|
||||||
|
if cmd == "":
|
||||||
|
complete_function = self.default_completer
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
complete_function = getattr(self, "complete_" + cmd)
|
||||||
|
except AttributeError:
|
||||||
|
complete_function = self.default_completer
|
||||||
|
else:
|
||||||
|
complete_function = self.raw_command_completer
|
||||||
|
|
||||||
|
self.completion_matches = complete_function(text, line, start_index, end_index)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.completion_matches[state]
|
||||||
|
except IndexError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def commands(self, *ignored):
|
||||||
|
""" Returns full list of interpreter commands.
|
||||||
|
|
||||||
|
:param ignored:
|
||||||
|
:return: full list of interpreter commands
|
||||||
|
"""
|
||||||
|
return [command.rsplit("_").pop() for command in dir(self) if command.startswith("command_")]
|
||||||
|
|
||||||
|
def raw_command_completer(self, text, line, start_index, end_index):
|
||||||
|
""" Complete command w/o any argument """
|
||||||
|
return [command for command in self.suggested_commands() if command.startswith(text)]
|
||||||
|
|
||||||
|
def default_completer(self, *ignored):
|
||||||
|
return []
|
||||||
|
|
||||||
|
def suggested_commands(self):
|
||||||
|
""" Entry point for intelligent tab completion.
|
||||||
|
|
||||||
|
Overwrite this method to suggest suitable commands.
|
||||||
|
|
||||||
|
:return: list of suitable commands
|
||||||
|
"""
|
||||||
|
return self.commands()
|
||||||
|
|
||||||
|
class SunhpcInterpreter(BaseInterpreter):
|
||||||
|
history_file = os.path.expanduser("~/.rsf_history")
|
||||||
|
global_help = """Global commands:
|
||||||
|
help Print this help menu
|
||||||
|
use <module> Select a module for usage
|
||||||
|
exec <shell command> <args> Execute a command in a shell
|
||||||
|
search <search term> Search for appropriate module
|
||||||
|
exit Exit RouterSploit"""
|
||||||
|
|
||||||
|
module_help = """Module commands:
|
||||||
|
run Run the selected module with the given options
|
||||||
|
back De-select the current module
|
||||||
|
set <option name> <option value> Set an option for the selected module
|
||||||
|
setg <option name> <option value> Set an option for all of the modules
|
||||||
|
unsetg <option name> Unset option that was set globally
|
||||||
|
show [info|options|devices] Print information, options, or target devices for a module
|
||||||
|
check Check if a given target is vulnerable to a selected module's exploit"""
|
||||||
|
|
||||||
|
def __init__(self, global_commands=[], sub_commands=[]):
|
||||||
|
super(SunhpcInterpreter, self).__init__()
|
||||||
|
|
||||||
|
self.current_keys = None
|
||||||
|
self.current_values = None
|
||||||
|
|
||||||
|
|
||||||
|
self.current_module = None
|
||||||
|
self.prompt_hostname = "sunhpc"
|
||||||
|
self.raw_prompt_template = None
|
||||||
|
self.module_prompt_template = None
|
||||||
|
|
||||||
|
self.base_global_commands = ['help', 'exit']
|
||||||
|
self.global_commands = ['show ', 'set ', 'check ']
|
||||||
|
if global_commands:
|
||||||
|
self.global_commands.extend(global_commands)
|
||||||
|
self.global_commands.extend(self.base_global_commands)
|
||||||
|
sorted(self.global_commands)
|
||||||
|
|
||||||
|
|
||||||
|
self.base_sub_commands = ['all', 'info', 'options']
|
||||||
|
self.sub_commands = ['wordlist', 'advanced']
|
||||||
|
if sub_commands:
|
||||||
|
self.sub_commands.extend(sub_commands)
|
||||||
|
self.sub_commands.extend(self.base_sub_commands)
|
||||||
|
sorted(self.sub_commands)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self.__parse_prompt()
|
||||||
|
self.banner = r"""
|
||||||
|
_____ _
|
||||||
|
/ ____| | |
|
||||||
|
======= | (___ _ _ _ __ | |__ _ __ ___
|
||||||
|
\ // \___ \| | | | '_ \| '_ \| '_ \ / __|
|
||||||
|
\ // ___ ) | |_| | | | | | | | |_) | (__
|
||||||
|
\// |_____/ \__,_|_| |_|_| |_| .__/ \___|
|
||||||
|
| |
|
||||||
|
Powered by HengPU Technology |_| By kylins
|
||||||
|
|
||||||
|
PlatName : HengPu High Performance Computing Platform
|
||||||
|
WeChat : xiubuzhe
|
||||||
|
Version : 1.0.0
|
||||||
|
Email : xiubuzhe@sina.com
|
||||||
|
GitPage : https://git.sunhpc.com - @kylins
|
||||||
|
BBSPage : https://bbs.sunhpc.com - @kylins
|
||||||
|
DocPage : https://doc.sunhpc.com - @kylins
|
||||||
|
Homepage : https://www.sunhpc.com - @kylins
|
||||||
|
|
||||||
|
Kelvin SunHPC Beta Program - https://www.sunhpc.com
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __parse_prompt(self):
|
||||||
|
raw_prompt_default_template = "\001\033[4m\002{host}\001\033[0m\002 > "
|
||||||
|
raw_prompt_template = os.getenv("RSF_RAW_PROMPT", raw_prompt_default_template).replace('\\033', '\033')
|
||||||
|
self.raw_prompt_template = raw_prompt_template if '{host}' in raw_prompt_template else raw_prompt_default_template
|
||||||
|
|
||||||
|
module_prompt_default_template = "\001\033[4m\002{host}\001\033[0m\002 (\001\033[91m\002{module}\001\033[0m\002) > "
|
||||||
|
module_prompt_template = os.getenv("RSF_MODULE_PROMPT", module_prompt_default_template).replace('\\033', '\033')
|
||||||
|
self.module_prompt_template = module_prompt_template if all(map(lambda x: x in module_prompt_template, \
|
||||||
|
['{host}', "{module}"])) else module_prompt_default_template
|
||||||
|
|
||||||
|
@property
|
||||||
|
def prompt(self):
|
||||||
|
""" Returns prompt string based on current_module attribute.
|
||||||
|
Adding module prefix (module.name) if current_module attribute is set.
|
||||||
|
|
||||||
|
:return: prompt string with appropriate module prefix.
|
||||||
|
"""
|
||||||
|
if self.current_module:
|
||||||
|
try:
|
||||||
|
return self.module_prompt_template.format(host=self.prompt_hostname, module=self.module_metadata['name'])
|
||||||
|
except (AttributeError, KeyError):
|
||||||
|
return self.module_prompt_template.format(host=self.prompt_hostname, module="UnnamedModule")
|
||||||
|
else:
|
||||||
|
return self.raw_prompt_template.format(host=self.prompt_hostname)
|
||||||
|
|
||||||
|
def suggested_commands(self):
|
||||||
|
""" Entry point for intelligent tab completion.
|
||||||
|
|
||||||
|
Based on state of interpreter this method will return intelligent suggestions.
|
||||||
|
|
||||||
|
:return: list of most accurate command suggestions
|
||||||
|
"""
|
||||||
|
if self.current_keys and self.current_values:
|
||||||
|
# 将unsetg和current_keys列表组合后重新生成一个列表.(迭代器)
|
||||||
|
return sorted(itertools.chain(self.global_commands, ('unset ',)))
|
||||||
|
|
||||||
|
return self.global_commands
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# show command functions
|
||||||
|
#
|
||||||
|
def command_show(self, *args, **kwargs):
|
||||||
|
sub_command = args[0]
|
||||||
|
try:
|
||||||
|
getattr(self, "_show_{}".format(sub_command))(*args, **kwargs)
|
||||||
|
except AttributeError:
|
||||||
|
# sunhpc debug
|
||||||
|
#raise
|
||||||
|
#self.msg("Unknown 'show' sub-command choices are:{}".format(self.sub_commands), 'w')
|
||||||
|
self.message('[-]', "Unknown 'show' sub-command choices are:", self.sub_commands)
|
||||||
|
|
||||||
|
def complete_show(self, text, *args, **kwargs):
|
||||||
|
if text:
|
||||||
|
return [command for command in self.sub_commands if command.startswith(text)]
|
||||||
|
else:
|
||||||
|
return self.sub_commands
|
||||||
|
|
||||||
|
def _show_all(self, *args, **kwargs):
|
||||||
|
self.__show_all()
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# set command functions
|
||||||
|
#
|
||||||
|
def command_set(self, *args, **kwargs):
|
||||||
|
key, _, value = args[0].partition(" ")
|
||||||
|
|
||||||
|
if key in self.current_keys:
|
||||||
|
if kwargs.get('glob', False):
|
||||||
|
self.current_values[key] = value
|
||||||
|
self.dictOutput({key:value})
|
||||||
|
|
||||||
|
elif kwargs:
|
||||||
|
for k, v in kwargs.items():
|
||||||
|
self.current_values[k] = v
|
||||||
|
self.dictOutput(kwargs)
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("You can't set option '{}'.Available options: {}".format(key, self.current_keys))
|
||||||
|
|
||||||
|
|
||||||
|
def complete_set(self, text, *args, **kwargs):
|
||||||
|
if text:
|
||||||
|
return ["=".join((keys, "")) for keys in self.current_keys if keys.startswith(text)]
|
||||||
|
else:
|
||||||
|
return self.current_keys
|
||||||
|
|
||||||
|
def command_unset(self, *args, **kwargs):
|
||||||
|
key, _, value = args[0].partition(' ')
|
||||||
|
try:
|
||||||
|
del self.current_values[key]
|
||||||
|
except KeyError:
|
||||||
|
self.msg("You can't unset option '{}'.Available options: {}".format(key, list(self.current_values.keys())), 'w')
|
||||||
|
else:
|
||||||
|
self.dictOutput({key: value})
|
||||||
|
|
||||||
|
def complete_unset(self, text, *args, **kwargs):
|
||||||
|
if text:
|
||||||
|
return [''.join((attr, "")) for attr in self.current_values.keys() if attr.startswith(text)]
|
||||||
|
else:
|
||||||
|
return list(self.current_values.keys())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def command_exit(self, *args, **kwargs):
|
||||||
|
raise EOFError
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -34,10 +34,10 @@ class Security(object):
|
|||||||
self.masters = []
|
self.masters = []
|
||||||
|
|
||||||
# A regex for our header search.
|
# A regex for our header search.
|
||||||
pattern = "\n*(?P<comment>.*?)\$110id\$"
|
pattern = r"\n*(?P<comment>.*?)\$110id\$"
|
||||||
self.header_pattern = re.compile(pattern)
|
self.header_pattern = re.compile(pattern)
|
||||||
|
|
||||||
pattern = "<a href=.+>(?P<filename>.+)</a> +(?P<date>\d+.*) +(?P<size>\d+.*)"
|
pattern = r"<a href=.+>(?P<filename>.+)</a> +(?P<date>\d+.*) +(?P<size>\d+.*)"
|
||||||
# Make the pattern matching engine case-insensitive.
|
# Make the pattern matching engine case-insensitive.
|
||||||
self.dir_pattern = re.compile(pattern, re.I)
|
self.dir_pattern = re.compile(pattern, re.I)
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ class CommandError(SunhpcException):
|
|||||||
something goes awry"""
|
something goes awry"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class SunhpcCliException(Exception):
|
||||||
|
def __init__(self, msg: str = ""):
|
||||||
|
super(SunhpcCliException, self).__init__(msg)
|
||||||
|
class OptionValidationError(SunhpcCliException):
|
||||||
|
pass
|
||||||
|
|
||||||
class KickstartError(SunhpcException):
|
class KickstartError(SunhpcException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -191,7 +197,7 @@ def startSpinner(cmd):
|
|||||||
w, r ,e = (p.stdin, p.stdout, p.stderr)
|
w, r ,e = (p.stdin, p.stdout, p.stderr)
|
||||||
currLength = 0
|
currLength = 0
|
||||||
prevLength = 0
|
prevLength = 0
|
||||||
spinChars = '-\|/'
|
spinChars = r'-\|/'
|
||||||
spinIndex = 0
|
spinIndex = 0
|
||||||
while 1:
|
while 1:
|
||||||
line = e.readline()
|
line = e.readline()
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
import sunhpc
|
import sunhpc
|
||||||
import sunhpc.core
|
import sunhpc.core
|
||||||
import sunhpc.core.ip
|
import sunhpc.core.ip
|
||||||
|
import sunhpc.core.app
|
||||||
import sunhpc.core.sql
|
import sunhpc.core.sql
|
||||||
import sunhpc.core.dist
|
import sunhpc.core.dist
|
||||||
import sunhpc.core.files
|
import sunhpc.core.files
|
||||||
import sunhpc.core.build
|
import sunhpc.core.build
|
||||||
|
import sunhpc.core.utils
|
||||||
import sunhpc.core.security
|
import sunhpc.core.security
|
||||||
import sunhpc.core.partition
|
import sunhpc.core.partition
|
||||||
|
|||||||
46
sbin/check-port
Executable file
46
sbin/check-port
Executable file
@@ -0,0 +1,46 @@
|
|||||||
|
#!/opt/sunpy3/bin/python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import socket
|
||||||
|
import sunhpc.invoke
|
||||||
|
|
||||||
|
class App(sunhpc.core.app.Application):
|
||||||
|
|
||||||
|
def __init__(self, argv):
|
||||||
|
sunhpc.core.app.Application.__init__(self,argv)
|
||||||
|
|
||||||
|
self.usage_name = 'Check Port'
|
||||||
|
self.usage_version = '4.2'
|
||||||
|
self.node_name = 'localhost'
|
||||||
|
self.port_num = 5901
|
||||||
|
|
||||||
|
nodehelp = '(nodename default=%s)' % self.node_name
|
||||||
|
porthelp = '(port number default=%d)' % (self.port_num)
|
||||||
|
self.getopt.l.extend([ ('node=',nodehelp),('port=',porthelp) ])
|
||||||
|
self.getopt.s.extend([('n:',nodehelp),('p:',porthelp)])
|
||||||
|
|
||||||
|
def parseArg(self, c):
|
||||||
|
sunhpc.core.app.Application.parseArg(self,c)
|
||||||
|
|
||||||
|
key, val = c
|
||||||
|
if key in ('--port'):
|
||||||
|
self.port_num = int(val)
|
||||||
|
elif key in ('--node'):
|
||||||
|
self.node_name = val
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||||
|
try:
|
||||||
|
s.connect((self.node_name,self.port_num))
|
||||||
|
except:
|
||||||
|
print ('Port(%s) is closed' % self.port_num)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print ('Port(%s) is open' % self.port_num)
|
||||||
|
s.close()
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
app = App(sys.argv)
|
||||||
|
app.parseArgs()
|
||||||
|
app.run()
|
||||||
100
share/pxeboot/default
Normal file
100
share/pxeboot/default
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
default vesamenu.c32
|
||||||
|
timeout 300
|
||||||
|
|
||||||
|
display boot.msg
|
||||||
|
|
||||||
|
menu clear
|
||||||
|
menu background splash.png
|
||||||
|
menu title CentOS and Rocky Linux Install
|
||||||
|
menu vshift 8
|
||||||
|
menu rows 18
|
||||||
|
menu margin 8
|
||||||
|
menu helpmsgrow 15
|
||||||
|
menu tabmsgrow 13
|
||||||
|
|
||||||
|
menu color border * #00000000 #00000000 none
|
||||||
|
menu color sel 0 #ffffffff #00000000 none
|
||||||
|
menu color title 0 #ff7ba3d0 #00000000 none
|
||||||
|
menu color tabmsg 0 #ff3a6496 #00000000 none
|
||||||
|
menu color unsel 0 #84b8ffff #00000000 none
|
||||||
|
menu color hotsel 0 #84b8ffff #00000000 none
|
||||||
|
menu color hotkey 0 #ffffffff #00000000 none
|
||||||
|
menu color help 0 #ffffffff #00000000 none
|
||||||
|
menu color scrollbar 0 #ffffffff #ff355594 none
|
||||||
|
menu color timeout 0 #ffffffff #00000000 none
|
||||||
|
menu color timeout_msg 0 #ffffffff #00000000 none
|
||||||
|
menu color cmdmark 0 #84b8ffff #00000000 none
|
||||||
|
menu color cmdline 0 #ffffffff #00000000 none
|
||||||
|
|
||||||
|
menu tabmsg Press Tab for full configuration options on menu items.
|
||||||
|
menu separator # insert an empty line
|
||||||
|
menu separator # insert an empty line
|
||||||
|
|
||||||
|
menu begin Install CentOS 7
|
||||||
|
menu title Install CentOS 7
|
||||||
|
|
||||||
|
label linux 7
|
||||||
|
menu label ^Auto Install CentOS 7 (Minimal)
|
||||||
|
kernel c7/vmlinuz
|
||||||
|
append initrd=c7/initrd.img inst.ks=http://ipaddress/ksfile/c7_min.conf quiet net.ifnames=0
|
||||||
|
|
||||||
|
label linux 7
|
||||||
|
menu label ^Auto Install CentOS 7 (^GUI)
|
||||||
|
kernel c7/vmlinuz
|
||||||
|
append initrd=c7/initrd.img inst.ks=http://ipaddress/ksfile/c7_gui.conf quiet net.ifnames=0
|
||||||
|
|
||||||
|
label linux 7
|
||||||
|
menu label ^Manual Install CentOS 7
|
||||||
|
kernel c7/vmlinuz
|
||||||
|
append initrd=c7/initrd.img inst.repo=http://ipaddress/redhat/c7 quiet net.ifnames=0
|
||||||
|
|
||||||
|
label linux 7
|
||||||
|
menu indent count 5
|
||||||
|
menu label ^Rescue CentOS 7 system
|
||||||
|
kernel c7/vmlinuz
|
||||||
|
append initrd=c7/initrd.img inst.repo=http://ipaddress/redhat/c7 rescue
|
||||||
|
|
||||||
|
menu separator # insert an empty line
|
||||||
|
label returntomain
|
||||||
|
menu label Return to ^main menu
|
||||||
|
menu exit
|
||||||
|
menu end
|
||||||
|
|
||||||
|
# Rockys options
|
||||||
|
menu begin Install Rocky 8
|
||||||
|
menu title Install Rocky 8
|
||||||
|
|
||||||
|
label linux 8
|
||||||
|
menu label ^Auto Install Rocky 8 (Minimal)
|
||||||
|
kernel r8/vmlinuz
|
||||||
|
append initrd=r8/initrd.img inst.ks=http://ipaddress/ksfile/r8_min.conf quiet net.ifnames=0
|
||||||
|
|
||||||
|
label linux 8
|
||||||
|
menu label ^Auto Install Rocky 8 (^GUI)
|
||||||
|
kernel r8/vmlinuz
|
||||||
|
append initrd=r8/initrd.img inst.ks=http://ipaddress/ksfile/r8_gui.conf quiet net.ifnames=0
|
||||||
|
|
||||||
|
label linux 8
|
||||||
|
menu label ^Manual Install Rocky 8
|
||||||
|
kernel r8/vmlinuz
|
||||||
|
append initrd=r8/initrd.img inst.repo=http://ipaddress/redhat/r8 quiet net.ifnames=0
|
||||||
|
|
||||||
|
label linux 8
|
||||||
|
menu indent count 5
|
||||||
|
menu label ^Rescue Rocky 8 system
|
||||||
|
kernel r8/vmlinuz
|
||||||
|
append initrd=r8/initrd.img inst.repo=http://ipaddress/redhat/r8 rescue
|
||||||
|
|
||||||
|
menu separator # insert an empty line
|
||||||
|
label returntomain
|
||||||
|
menu label Return to ^main menu
|
||||||
|
menu exit
|
||||||
|
menu end
|
||||||
|
|
||||||
|
menu separator # insert an empty line
|
||||||
|
label local
|
||||||
|
menu default
|
||||||
|
menu label Boot from ^local drive
|
||||||
|
localboot 0xffff
|
||||||
|
|
||||||
|
menu end
|
||||||
BIN
share/pxeboot/grubx64.efi
Normal file
BIN
share/pxeboot/grubx64.efi
Normal file
Binary file not shown.
49
share/pxeboot/kickstart/c7_gui.conf
Executable file
49
share/pxeboot/kickstart/c7_gui.conf
Executable file
@@ -0,0 +1,49 @@
|
|||||||
|
#version=RHEL8
|
||||||
|
# Use graphical install
|
||||||
|
|
||||||
|
install
|
||||||
|
keyboard 'us'
|
||||||
|
lang zh_CN
|
||||||
|
text
|
||||||
|
skipx
|
||||||
|
eula --agreed
|
||||||
|
firstboot --disable
|
||||||
|
selinux --disabled
|
||||||
|
timezone Asia/Shanghai
|
||||||
|
auth --useshadow --passalgo=sha512
|
||||||
|
|
||||||
|
url --url="http://ipaddress/redhat/c7"
|
||||||
|
#autopart --type=lvm
|
||||||
|
zerombr
|
||||||
|
bootloader --location=mbr
|
||||||
|
clearpart --all --initlabel
|
||||||
|
|
||||||
|
part /boot --fstype="ext4" --size=1024
|
||||||
|
part swap --fstype="swap" --size=4096
|
||||||
|
part / --fstype="xfs" --grow
|
||||||
|
|
||||||
|
reboot
|
||||||
|
rootpw --plaintext 123456
|
||||||
|
|
||||||
|
%post --interpreter=/usr/bin/bash
|
||||||
|
useradd admin
|
||||||
|
echo 123456 | passwd --stdin admin
|
||||||
|
sed -i 's/^#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config
|
||||||
|
%end
|
||||||
|
|
||||||
|
%packages
|
||||||
|
@^infrastructure-server-environment
|
||||||
|
@base
|
||||||
|
@core
|
||||||
|
chrony
|
||||||
|
kexec-tools
|
||||||
|
%end
|
||||||
|
|
||||||
|
%addon com_redhat_kdump --disable --reserve-mb='auto'
|
||||||
|
%end
|
||||||
|
|
||||||
|
%anaconda
|
||||||
|
pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty
|
||||||
|
pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok
|
||||||
|
pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty
|
||||||
|
%end
|
||||||
50
share/pxeboot/kickstart/c7_min.conf
Executable file
50
share/pxeboot/kickstart/c7_min.conf
Executable file
@@ -0,0 +1,50 @@
|
|||||||
|
#version=RHEL8
|
||||||
|
# Use graphical install
|
||||||
|
|
||||||
|
install
|
||||||
|
keyboard 'us'
|
||||||
|
lang zh_CN
|
||||||
|
text
|
||||||
|
skipx
|
||||||
|
|
||||||
|
eula --agreed
|
||||||
|
firstboot --disable
|
||||||
|
selinux --disabled
|
||||||
|
timezone Asia/Shanghai
|
||||||
|
auth --useshadow --passalgo=sha512
|
||||||
|
|
||||||
|
url --url="http://ipaddress/redhat/c7"
|
||||||
|
#autopart --type=lvm
|
||||||
|
zerombr
|
||||||
|
bootloader --location=mbr
|
||||||
|
clearpart --all --initlabel
|
||||||
|
|
||||||
|
part /boot --fstype="ext4" --size=1024
|
||||||
|
part swap --fstype="swap" --size=4096
|
||||||
|
part / --fstype="xfs" --grow
|
||||||
|
|
||||||
|
reboot
|
||||||
|
rootpw --plaintext 123456
|
||||||
|
|
||||||
|
%post --interpreter=/usr/bin/bash
|
||||||
|
useradd admin
|
||||||
|
echo 123456 | passwd --stdin admin
|
||||||
|
sed -i 's/^#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config
|
||||||
|
%end
|
||||||
|
|
||||||
|
%packages
|
||||||
|
@^infrastructure-server-environment
|
||||||
|
@base
|
||||||
|
@core
|
||||||
|
chrony
|
||||||
|
kexec-tools
|
||||||
|
%end
|
||||||
|
|
||||||
|
%addon com_redhat_kdump --disable --reserve-mb='auto'
|
||||||
|
%end
|
||||||
|
|
||||||
|
%anaconda
|
||||||
|
pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty
|
||||||
|
pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok
|
||||||
|
pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty
|
||||||
|
%end
|
||||||
51
share/pxeboot/kickstart/ks-example.conf
Executable file
51
share/pxeboot/kickstart/ks-example.conf
Executable file
@@ -0,0 +1,51 @@
|
|||||||
|
#version=RHEL8
|
||||||
|
# Use graphical install
|
||||||
|
graphical
|
||||||
|
|
||||||
|
%packages
|
||||||
|
@^graphical-server-environment
|
||||||
|
@container-management
|
||||||
|
@development
|
||||||
|
@graphical-admin-tools
|
||||||
|
@headless-management
|
||||||
|
@infiniband
|
||||||
|
@legacy-unix
|
||||||
|
@network-file-system-client
|
||||||
|
@performance
|
||||||
|
@remote-desktop-clients
|
||||||
|
@remote-system-management
|
||||||
|
@rpm-development-tools
|
||||||
|
@system-tools
|
||||||
|
@web-server
|
||||||
|
%end
|
||||||
|
|
||||||
|
keyboard --xlayouts='us'
|
||||||
|
lang en_US.UTF-8
|
||||||
|
firstboot --enable
|
||||||
|
selinux --disabled
|
||||||
|
skipx
|
||||||
|
timezone Asia/Shanghai --isUtc --nontp
|
||||||
|
|
||||||
|
network --bootproto=dhcp --onboot=on --ipv6=auto
|
||||||
|
network --hostname=compute.local
|
||||||
|
clearpart --all --initlabel
|
||||||
|
#part /boot --fstype="ext4" --size=1024
|
||||||
|
#part swap --fstype="swap" --size=4096
|
||||||
|
#part / --fstype="xfs" --grow
|
||||||
|
autopart --type=lvm
|
||||||
|
|
||||||
|
url --url="http://172.16.2.1/redhat/r8"
|
||||||
|
#repo --name="AppStream" --baseurl=http://172.16.2.1/redhat/AppStream
|
||||||
|
#repo --name="BaseOS" --baseurl=http://172.16.2.1/redhat/BaseOS
|
||||||
|
|
||||||
|
rootpw --iscrypted $6$eT.sWNwV69wyvhaR$N5tY4rJn6Y2slOrwuejfUBq3MhC5SNe1gUfTSYmOACObwW4ckUK9fjkKwC9TgSPhwwlaGdbtn0EFg11TJmAbC1
|
||||||
|
user --name=admin --password=$6$0WN9CulSREVORA0S$FVWh6tkvdncbmSrsNPEb13GwxnGnEOKZliZLdL2IdY5E/fzLB8QmZAmgxyHh6ElSjObUCTbDEJ0l9.wRCRrsU. --iscrypted --gecos="admin"
|
||||||
|
|
||||||
|
%addon com_redhat_kdump --disable --reserve-mb='auto'
|
||||||
|
%end
|
||||||
|
|
||||||
|
%anaconda
|
||||||
|
pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty
|
||||||
|
pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok
|
||||||
|
pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty
|
||||||
|
%end
|
||||||
55
share/pxeboot/kickstart/r8_gui.conf
Executable file
55
share/pxeboot/kickstart/r8_gui.conf
Executable file
@@ -0,0 +1,55 @@
|
|||||||
|
#version=RHEL8
|
||||||
|
# Use graphical install
|
||||||
|
graphical
|
||||||
|
|
||||||
|
url --url="http://ipaddress/redhat/r8"
|
||||||
|
|
||||||
|
lang en_US.UTF-8
|
||||||
|
eula --agreed
|
||||||
|
keyboard --xlayouts='us'
|
||||||
|
firstboot --disable
|
||||||
|
selinux --disabled
|
||||||
|
timezone Asia/Shanghai --isUtc --nontp
|
||||||
|
skipx
|
||||||
|
|
||||||
|
zerombr
|
||||||
|
bootloader --location=mbr
|
||||||
|
clearpart --all --initlabel
|
||||||
|
part /boot --fstype="ext4" --size=1024
|
||||||
|
part swap --fstype="swap" --size=4096
|
||||||
|
part / --fstype="xfs" --grow
|
||||||
|
|
||||||
|
rootpw --plaintext 123456
|
||||||
|
reboot
|
||||||
|
|
||||||
|
%post --interpreter=/usr/bin/bash
|
||||||
|
useradd admin
|
||||||
|
echo 123456 | passwd --stdin admin
|
||||||
|
sed -i 's/^#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config
|
||||||
|
%end
|
||||||
|
|
||||||
|
%packages
|
||||||
|
@^graphical-server-environment
|
||||||
|
@container-management
|
||||||
|
@development
|
||||||
|
@graphical-admin-tools
|
||||||
|
@headless-management
|
||||||
|
@infiniband
|
||||||
|
@legacy-unix
|
||||||
|
@network-file-system-client
|
||||||
|
@performance
|
||||||
|
@remote-desktop-clients
|
||||||
|
@remote-system-management
|
||||||
|
@rpm-development-tools
|
||||||
|
@system-tools
|
||||||
|
@web-server
|
||||||
|
%end
|
||||||
|
|
||||||
|
%addon com_redhat_kdump --disable --reserve-mb='auto'
|
||||||
|
%end
|
||||||
|
|
||||||
|
%anaconda
|
||||||
|
pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty
|
||||||
|
pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok
|
||||||
|
pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty
|
||||||
|
%end
|
||||||
42
share/pxeboot/kickstart/r8_min.conf
Executable file
42
share/pxeboot/kickstart/r8_min.conf
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
#version=RHEL8
|
||||||
|
# Use graphical install
|
||||||
|
graphical
|
||||||
|
|
||||||
|
url --url="http://ipaddress/redhat/r8"
|
||||||
|
|
||||||
|
lang en_US.UTF-8
|
||||||
|
eula --agreed
|
||||||
|
keyboard --xlayouts='us'
|
||||||
|
firstboot --disable
|
||||||
|
selinux --disabled
|
||||||
|
timezone Asia/Shanghai --isUtc --nontp
|
||||||
|
skipx
|
||||||
|
|
||||||
|
zerombr
|
||||||
|
bootloader --location=mbr
|
||||||
|
clearpart --all --initlabel
|
||||||
|
part /boot --fstype="ext4" --size=1024
|
||||||
|
part swap --fstype="swap" --size=4096
|
||||||
|
part / --fstype="xfs" --grow
|
||||||
|
|
||||||
|
rootpw --plaintext 123456
|
||||||
|
reboot
|
||||||
|
|
||||||
|
%post --interpreter=/usr/bin/bash
|
||||||
|
useradd admin
|
||||||
|
echo 123456 | passwd --stdin admin
|
||||||
|
sed -i 's/^#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config
|
||||||
|
%end
|
||||||
|
|
||||||
|
%packages
|
||||||
|
@^minimal-environment
|
||||||
|
%end
|
||||||
|
|
||||||
|
%addon com_redhat_kdump --disable --reserve-mb='auto'
|
||||||
|
%end
|
||||||
|
|
||||||
|
%anaconda
|
||||||
|
pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty
|
||||||
|
pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok
|
||||||
|
pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty
|
||||||
|
%end
|
||||||
BIN
share/pxeboot/ldlinux.c32
Normal file
BIN
share/pxeboot/ldlinux.c32
Normal file
Binary file not shown.
BIN
share/pxeboot/libcom32.c32
Normal file
BIN
share/pxeboot/libcom32.c32
Normal file
Binary file not shown.
BIN
share/pxeboot/libutil.c32
Normal file
BIN
share/pxeboot/libutil.c32
Normal file
Binary file not shown.
BIN
share/pxeboot/menu.c32
Normal file
BIN
share/pxeboot/menu.c32
Normal file
Binary file not shown.
BIN
share/pxeboot/pxelinux.0
Normal file
BIN
share/pxeboot/pxelinux.0
Normal file
Binary file not shown.
BIN
share/pxeboot/shimx64.efi
Normal file
BIN
share/pxeboot/shimx64.efi
Normal file
Binary file not shown.
BIN
share/pxeboot/vesamenu.c32
Normal file
BIN
share/pxeboot/vesamenu.c32
Normal file
Binary file not shown.
0
var/nodes/database.xml
Normal file
0
var/nodes/database.xml
Normal file
Reference in New Issue
Block a user