Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
chain33-pai
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
JIRA
JIRA
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
szh
chain33-pai
Commits
4b45bdc0
Commit
4b45bdc0
authored
Jan 03, 2020
by
szh
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
引用chain33log模块 去除自带go log,日志自动清理
parent
b1f3d5b1
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
79 additions
and
46 deletions
+79
-46
app.ini
conf/app.ini
+21
-0
go.sum
go.sum
+1
-0
main.go
main.go
+12
-7
setting.go
pkg/setting/setting.go
+5
-0
util.go
pkg/util/util.go
+3
-2
node.go
routers/api/v1/node.go
+19
-22
pai.go
routers/api/v1/pai.go
+11
-9
pai.go
service/pai_service/pai.go
+7
-6
No files found.
conf/app.ini
View file @
4b45bdc0
...
...
@@ -19,6 +19,27 @@ LogSaveName = log
LogFileExt
=
log
TimeFormat
=
20060102
[log]
# 日志级别,支持debug(dbug)/info/warn/error(eror)/crit
loglevel
=
"debug"
logConsoleLevel
=
"info"
# 日志文件名,可带目录,所有生成的日志文件都放到此目录下
logFile
=
"logs/chain33-pai.log"
# 单个日志文件的最大值(单位:兆)
maxFileSize
=
100
# 最多保存的历史日志文件个数
maxBackups
=
10
# 最多保存的历史日志消息(单位:天)
maxAge
=
10
# 日志文件名是否使用本地事件(否则使用UTC时间)l
localTime
=
true
# 历史日志文件是否压缩(压缩格式为gz)
compress
=
true
# 是否打印调用源文件和行号
callerFile
=
false
# 是否打印调用方法
callerFunction
=
false
[server]
#debug or release
RunMode
=
debug
...
...
go.sum
View file @
4b45bdc0
...
...
@@ -188,6 +188,7 @@ gopkg.in/go-playground/validator.v8 v8.0.0-20170730050235-5f1438d3fca6/go.mod h1
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/go-playground/webhooks.v5 v5.2.0/go.mod h1:LZbya/qLVdbqDR1aKrGuWV6qbia2zCYSR5dpom2SInQ=
gopkg.in/natefinch/lumberjack.v2 v2.0.0-20170531160350-a96e63847dc3 h1:AFxeG48hTWHhDTQDk/m2gorfVHUEa9vo3tp3D7TzwjI=
gopkg.in/natefinch/lumberjack.v2 v2.0.0-20170531160350-a96e63847dc3/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk=
...
...
main.go
View file @
4b45bdc0
...
...
@@ -2,27 +2,31 @@ package main
import
(
"chain33-pai/pkg/app"
"chain33-pai/pkg/logging"
"chain33-pai/pkg/setting"
"chain33-pai/pkg/util"
"chain33-pai/routers"
"chain33-pai/service/pai_service"
"fmt"
"github.com/gin-gonic/gin"
"log"
//
"log"
"net"
"net/http"
"time"
clog
"github.com/33cn/chain33/common/log"
log
"github.com/33cn/chain33/common/log/log15"
)
func
init
()
{
setting
.
Setup
()
//models.Setup()
logging
.
Setup
()
//
logging.Setup()
//gredis.Setup()
clog
.
SetFileLog
(
setting
.
LogSetting
)
util
.
Setup
()
}
var
tlog
=
log
.
New
(
"main"
,
"main.go"
)
// @title Golang Gin API
// @version 1.0
// @description An example of gin
...
...
@@ -47,7 +51,7 @@ func main() {
MaxHeaderBytes
:
maxHeaderBytes
,
}
log
.
Printf
(
"[info] start http server listening %s"
,
endPoint
)
tlog
.
Info
(
"[info] start http server listening %s"
,
endPoint
)
server
.
ListenAndServe
()
select
{}
...
...
@@ -77,7 +81,7 @@ func broadcast() {
//ip 会变化
ip
,
err
:=
util
.
GetLocalIP
()
if
err
!=
nil
{
log
.
Fatal
(
err
)
tlog
.
Error
(
"broadcast"
,
err
)
}
// 这里设置发送者的IP地址,自己查看一下自己的IP自行设定
laddr
:=
net
.
UDPAddr
{
...
...
@@ -92,7 +96,7 @@ func broadcast() {
}
conn
,
err
:=
net
.
DialUDP
(
"udp"
,
&
laddr
,
&
raddr
)
if
err
!=
nil
{
log
.
Println
(
err
.
Error
()
)
tlog
.
Error
(
"broadcast"
,
err
)
}
for
{
...
...
@@ -100,7 +104,7 @@ func broadcast() {
if
err
!=
nil
{
panic
(
err
)
}
log
.
Println
(
send
)
tlog
.
Info
(
"broadcast"
,
send
)
time
.
Sleep
(
time
.
Second
*
5
)
}
}
\ No newline at end of file
pkg/setting/setting.go
View file @
4b45bdc0
...
...
@@ -6,6 +6,7 @@ import (
"os/exec"
"chain33-pai/pkg/chain33"
"github.com/go-ini/ini"
"github.com/33cn/chain33/types"
)
...
...
@@ -83,6 +84,9 @@ type Chain33_pai struct {
}
var
Chain33Pai
=&
Chain33_pai
{}
var
LogSetting
=&
types
.
Log
{}
var
cfg
*
ini
.
File
// Setup initialize the configuration instance
...
...
@@ -98,6 +102,7 @@ func Setup() {
mapTo
(
"chain33-pai"
,
Chain33Pai
)
mapTo
(
"bityuan"
,
BityuanSetting
)
mapTo
(
"database"
,
DatabaseSetting
)
mapTo
(
"log"
,
LogSetting
)
AppSetting
.
ImageMaxSize
=
AppSetting
.
ImageMaxSize
*
1024
*
1024
ServerSetting
.
ReadTimeout
=
ServerSetting
.
ReadTimeout
*
time
.
Second
ServerSetting
.
WriteTimeout
=
ServerSetting
.
WriteTimeout
*
time
.
Second
...
...
pkg/util/util.go
View file @
4b45bdc0
...
...
@@ -23,6 +23,7 @@ func GetLocalIP() (ipv4 *net.IPNet, err error) {
ipNet
*
net
.
IPNet
// IP地址
isIpNet
bool
)
// 获取所有网卡
if
addrs
,
err
=
net
.
InterfaceAddrs
();
err
!=
nil
{
return
...
...
@@ -30,9 +31,9 @@ func GetLocalIP() (ipv4 *net.IPNet, err error) {
// 取第一个非lo的网卡IP
for
_
,
addr
=
range
addrs
{
// 这个网络地址是IP地址: ipv4, ipv6
if
ipNet
,
isIpNet
=
addr
.
(
*
net
.
IPNet
);
isIpNet
&&
!
ipNet
.
IP
.
IsLoopback
()
{
if
ipNet
,
isIpNet
=
addr
.
(
*
net
.
IPNet
);
isIpNet
&&
!
ipNet
.
IP
.
IsLoopback
(){
// 跳过IPV6
if
ipNet
.
IP
.
To4
()
!=
nil
{
if
ipNet
.
IP
.
To4
()
!=
nil
{
ipv4
=
ipNet
// 192.168.1.1
return
}
...
...
routers/api/v1/node.go
View file @
4b45bdc0
...
...
@@ -2,7 +2,6 @@ package v1
import
(
"fmt"
"log"
"os"
"path"
"os/exec"
...
...
@@ -11,11 +10,9 @@ import (
"chain33-pai/pkg/app"
"chain33-pai/pkg/setting"
"github.com/gin-gonic/gin"
"chain33-pai/service/pai_service"
"bytes"
)
var
flag
bool
//判断是否正在更新
func
ResetWallet
(
c
*
gin
.
Context
)
{
...
...
@@ -26,7 +23,7 @@ func ResetWallet(c *gin.Context) {
}
app
.
BityuanFlag
=
true
if
app
.
NodeError
!=
nil
{
log
.
Println
(
app
.
NodeError
.
Error
())
tlog
.
Error
(
app
.
NodeError
.
Error
())
appG
.
Response
(
http
.
StatusOK
,
e
.
NODE_ERROR
,
"node internal error,could not start node"
)
app
.
BityuanFlag
=
false
return
...
...
@@ -35,14 +32,14 @@ func ResetWallet(c *gin.Context) {
err
:=
cmd
.
Start
()
if
err
!=
nil
{
appG
.
Response
(
http
.
StatusOK
,
e
.
ENV_ERROR
,
"fail to reset wallet"
)
log
.
Fatalln
(
err
)
tlog
.
Error
(
"ResetWallet"
,
err
)
app
.
BityuanFlag
=
false
return
}
err
=
os
.
RemoveAll
(
fmt
.
Sprintf
(
"%s/wallet"
,
path
.
Dir
(
app
.
Bityuan
.
Path
)))
if
err
!=
nil
{
appG
.
Response
(
http
.
StatusOK
,
e
.
NODE_ERROR
,
"fail to re
set
wallet"
)
log
.
Fatalln
(
err
)
appG
.
Response
(
http
.
StatusOK
,
e
.
NODE_ERROR
,
"fail to re
move
wallet"
)
tlog
.
Error
(
"ResetWallet"
,
err
)
app
.
BityuanFlag
=
false
return
}
...
...
@@ -50,7 +47,7 @@ func ResetWallet(c *gin.Context) {
err
=
app
.
StartProcess
(
setting
.
Chain33Pai
.
Auto
)
if
err
!=
nil
{
appG
.
Response
(
http
.
StatusOK
,
e
.
NODE_ERROR
,
"fail to restart node"
)
log
.
Fatalln
(
err
)
tlog
.
Error
(
"ResetWallet"
,
err
)
app
.
BityuanFlag
=
false
return
}
...
...
@@ -115,7 +112,7 @@ func UpdateNode(c *gin.Context){
tar
:=
exec
.
Command
(
"tar"
,
"-xvf"
,
name
)
err
=
tar
.
Start
()
if
err
!=
nil
{
log
.
Println
(
"tar"
,
err
)
tlog
.
Error
(
"tar"
,
err
)
appG
.
Response
(
http
.
StatusOK
,
e
.
TAR_XVF_ERROR
,
err
)
app
.
UpdateInfo
.
Flag
=
true
app
.
BityuanFlag
=
false
...
...
@@ -123,7 +120,7 @@ func UpdateNode(c *gin.Context){
}
err
=
tar
.
Wait
()
if
err
!=
nil
{
log
.
Println
(
"tar"
,
err
)
tlog
.
Error
(
"tar"
,
err
)
appG
.
Response
(
http
.
StatusOK
,
e
.
TAR_XVF_ERROR
,
err
)
app
.
UpdateInfo
.
Flag
=
true
app
.
BityuanFlag
=
false
...
...
@@ -133,7 +130,7 @@ func UpdateNode(c *gin.Context){
bak
:=
exec
.
Command
(
"cp"
,
absPath
+
setting
.
BityuanSetting
.
Name
,
absPath
+
setting
.
BityuanSetting
.
Name
+
".bak"
)
err
=
bak
.
Start
()
if
err
!=
nil
{
log
.
Println
(
"bak"
,
err
)
tlog
.
Error
(
"bak"
,
err
)
appG
.
Response
(
http
.
StatusOK
,
e
.
CP_ERROR
,
err
)
app
.
UpdateInfo
.
Flag
=
true
app
.
BityuanFlag
=
false
...
...
@@ -141,7 +138,7 @@ func UpdateNode(c *gin.Context){
}
err
=
bak
.
Wait
()
if
err
!=
nil
{
log
.
Println
(
"bak"
,
err
)
tlog
.
Error
(
"bak"
,
err
)
appG
.
Response
(
http
.
StatusOK
,
e
.
CP_ERROR
,
err
)
app
.
UpdateInfo
.
Flag
=
true
app
.
BityuanFlag
=
false
...
...
@@ -151,7 +148,7 @@ func UpdateNode(c *gin.Context){
bakconf
:=
exec
.
Command
(
"cp"
,
absPath
+
"bityuan.toml"
,
absPath
+
"bityuan.toml.bak"
)
err
=
bakconf
.
Start
()
if
err
!=
nil
{
log
.
Println
(
"bakconf"
,
err
)
tlog
.
Error
(
"bakconf"
,
err
)
appG
.
Response
(
http
.
StatusOK
,
e
.
CP_ERROR
,
err
)
app
.
UpdateInfo
.
Flag
=
true
app
.
BityuanFlag
=
false
...
...
@@ -159,7 +156,7 @@ func UpdateNode(c *gin.Context){
}
err
=
bakconf
.
Wait
()
if
err
!=
nil
{
log
.
Println
(
"bakconf"
,
err
)
tlog
.
Error
(
"bakconf"
,
err
)
appG
.
Response
(
http
.
StatusOK
,
e
.
CP_ERROR
,
err
)
app
.
UpdateInfo
.
Flag
=
true
app
.
BityuanFlag
=
false
...
...
@@ -173,8 +170,8 @@ func UpdateNode(c *gin.Context){
return
}
//替换原执行文件
log
.
Println
(
setting
.
BityuanSetting
.
Name
+
"_"
+
latestVersion
)
log
.
Println
(
absPath
)
tlog
.
Error
(
setting
.
BityuanSetting
.
Name
+
"_"
+
latestVersion
)
tlog
.
Error
(
absPath
)
err
=
os
.
Chmod
(
pwd
+
setting
.
BityuanSetting
.
Name
+
"_"
+
latestVersion
+
"/"
+
setting
.
BityuanSetting
.
Name
,
0755
)
if
err
!=
nil
{
appG
.
Response
(
http
.
StatusOK
,
e
.
ERROR
,
err
)
...
...
@@ -194,7 +191,7 @@ func UpdateNode(c *gin.Context){
mv
.
Stderr
=
mverr
err
=
mv
.
Start
()
if
err
!=
nil
{
log
.
Println
(
"mv"
,
err
)
tlog
.
Error
(
"mv"
,
err
)
appG
.
Response
(
http
.
StatusOK
,
e
.
MV_ERROR
,
err
)
app
.
UpdateInfo
.
Flag
=
true
app
.
BityuanFlag
=
false
...
...
@@ -202,7 +199,7 @@ func UpdateNode(c *gin.Context){
}
err
=
mv
.
Wait
()
if
err
!=
nil
{
log
.
Println
(
"stderr:"
,
mverr
.
String
())
tlog
.
Error
(
"stderr:"
,
mverr
.
String
())
appG
.
Response
(
http
.
StatusOK
,
e
.
MV_ERROR
,
mverr
.
String
())
app
.
UpdateInfo
.
Flag
=
true
app
.
BityuanFlag
=
false
...
...
@@ -213,7 +210,7 @@ func UpdateNode(c *gin.Context){
mv2
.
Stderr
=
mverr2
err
=
mv2
.
Start
()
if
err
!=
nil
{
log
.
Println
(
"mv2"
,
err
)
tlog
.
Error
(
"mv2"
,
err
)
appG
.
Response
(
http
.
StatusOK
,
e
.
MV_ERROR
,
err
)
app
.
UpdateInfo
.
Flag
=
true
app
.
BityuanFlag
=
false
...
...
@@ -221,7 +218,7 @@ func UpdateNode(c *gin.Context){
}
err
=
mv2
.
Wait
()
if
err
!=
nil
{
log
.
Println
(
"stderr2:"
,
mverr2
.
String
())
tlog
.
Error
(
"stderr2:"
,
mverr2
.
String
())
appG
.
Response
(
http
.
StatusOK
,
e
.
MV_ERROR
,
mverr2
.
String
())
app
.
UpdateInfo
.
Flag
=
true
app
.
BityuanFlag
=
false
...
...
@@ -230,7 +227,7 @@ func UpdateNode(c *gin.Context){
//mv2 := exec.Command("mv",setting.BityuanSetting.Name+"_"+latestVersion+"/"+"bityuan.toml",".")
//err = mv2.Start()
//if err != nil {
//
log.Println
("mv2",err)
//
tlog.Error
("mv2",err)
// appG.Response(http.StatusOK, e.MV_ERROR, err)
// app.UpdateInfo.Flag = true
// app.BityuanFlag = false
...
...
@@ -238,7 +235,7 @@ func UpdateNode(c *gin.Context){
//}
//err = mv2.Wait()
//if err != nil {
//
log.Println
("mv2",err)
//
tlog.Error
("mv2",err)
// appG.Response(http.StatusOK, e.MV_ERROR, err)
// app.UpdateInfo.Flag = true
// app.BityuanFlag = false
...
...
routers/api/v1/pai.go
View file @
4b45bdc0
...
...
@@ -8,10 +8,12 @@ import (
"chain33-pai/service/pai_service"
"chain33-pai/pkg/setting"
"os/exec"
"log
"
log
"github.com/33cn/chain33/common/log/log15
"
"chain33-pai/pkg/util"
)
var
tlog
=
log
.
New
(
"api"
,
"v1"
)
func
GetDevdetail
(
c
*
gin
.
Context
)
{
appG
:=
app
.
Gin
{
C
:
c
}
var
pai
pai_service
.
Pai
...
...
@@ -104,14 +106,14 @@ func UpdatePai(c *gin.Context) {
tar
:=
exec
.
Command
(
"tar"
,
"-xvf"
,
name
)
err
=
tar
.
Start
()
if
err
!=
nil
{
log
.
Println
(
"tar"
,
err
)
tlog
.
Error
(
"tar"
,
err
)
appG
.
Response
(
http
.
StatusOK
,
e
.
TAR_XVF_ERROR
,
err
)
app
.
DPercent
.
Flag
=
false
return
}
err
=
tar
.
Wait
()
if
err
!=
nil
{
log
.
Println
(
"tar"
,
err
)
tlog
.
Error
(
"tar"
,
err
)
appG
.
Response
(
http
.
StatusOK
,
e
.
TAR_XVF_ERROR
,
err
)
app
.
DPercent
.
Flag
=
false
return
...
...
@@ -120,14 +122,14 @@ func UpdatePai(c *gin.Context) {
bak
:=
exec
.
Command
(
"cp"
,
setting
.
Chain33Pai
.
Name
,
setting
.
Chain33Pai
.
Name
+
".bak"
)
err
=
bak
.
Start
()
if
err
!=
nil
{
log
.
Println
(
"bak"
,
err
)
tlog
.
Error
(
"bak"
,
err
)
appG
.
Response
(
http
.
StatusOK
,
e
.
CP_ERROR
,
err
)
app
.
DPercent
.
Flag
=
false
return
}
err
=
bak
.
Wait
()
if
err
!=
nil
{
log
.
Println
(
"bak"
,
err
)
tlog
.
Error
(
"bak"
,
err
)
appG
.
Response
(
http
.
StatusOK
,
e
.
CP_ERROR
,
err
)
app
.
DPercent
.
Flag
=
false
return
...
...
@@ -136,14 +138,14 @@ func UpdatePai(c *gin.Context) {
bakconf
:=
exec
.
Command
(
"cp"
,
"conf/app.ini"
,
"conf/app.ini.bak"
)
err
=
bakconf
.
Start
()
if
err
!=
nil
{
log
.
Println
(
"bakconf"
,
err
)
tlog
.
Error
(
"bakconf"
,
err
)
appG
.
Response
(
http
.
StatusOK
,
e
.
CP_ERROR
,
err
)
app
.
DPercent
.
Flag
=
false
return
}
err
=
bakconf
.
Wait
()
if
err
!=
nil
{
log
.
Println
(
"bakconf"
,
err
)
tlog
.
Error
(
"bakconf"
,
err
)
appG
.
Response
(
http
.
StatusOK
,
e
.
CP_ERROR
,
err
)
app
.
DPercent
.
Flag
=
false
return
...
...
@@ -152,14 +154,14 @@ func UpdatePai(c *gin.Context) {
mv
:=
exec
.
Command
(
"mv"
,
setting
.
Chain33Pai
.
Name
+
"_"
+
req
.
Version
+
"/"
+
setting
.
Chain33Pai
.
Name
,
"."
)
err
=
mv
.
Start
()
if
err
!=
nil
{
log
.
Println
(
"mv"
,
err
)
tlog
.
Error
(
"mv"
,
err
)
appG
.
Response
(
http
.
StatusOK
,
e
.
MV_ERROR
,
err
)
app
.
DPercent
.
Flag
=
false
return
}
err
=
mv
.
Wait
()
if
err
!=
nil
{
log
.
Println
(
"mv"
,
err
)
tlog
.
Error
(
"mv"
,
err
)
appG
.
Response
(
http
.
StatusOK
,
e
.
MV_ERROR
,
err
)
app
.
DPercent
.
Flag
=
false
return
...
...
service/pai_service/pai.go
View file @
4b45bdc0
...
...
@@ -2,13 +2,13 @@ package pai_service
import
(
"os/exec"
"fmt"
"io"
"strings"
"bufio"
"chain33-pai/pkg/chain33"
"chain33-pai/pkg/logging"
"github.com/33cn/chain33/types"
log
"github.com/33cn/chain33/common/log/log15"
"net/http"
"bytes"
"io/ioutil"
...
...
@@ -18,6 +18,7 @@ import (
var
(
pai_serial
string
tlog
=
log
.
New
(
"service"
,
"pai_Service"
)
)
type
Pai
struct
{
...
...
@@ -100,14 +101,14 @@ func getPaiConfig(command string ,arg ...string) (config map[string]string,err e
list
:=
make
(
map
[
string
]
string
,
0
)
cmd
:=
exec
.
Command
(
"cat"
,
arg
...
)
stdout
,
err
:=
cmd
.
StdoutPipe
()
//
fmt.Println
(stdout)
//
tlog.Info
(stdout)
if
err
!=
nil
{
fmt
.
Println
(
err
)
tlog
.
Info
(
"getPaiConfig"
,
err
)
return
nil
,
err
}
defer
stdout
.
Close
()
if
err
:=
cmd
.
Start
();
err
!=
nil
{
fmt
.
Println
(
err
)
tlog
.
Info
(
"getPaiConfig"
,
err
)
return
nil
,
err
}
rd
:=
bufio
.
NewReader
(
stdout
)
...
...
@@ -116,13 +117,13 @@ func getPaiConfig(command string ,arg ...string) (config map[string]string,err e
if
err
!=
nil
||
io
.
EOF
==
err
{
break
}
else
{
//
fmt.Println
("line", line)
//
tlog.Info
("line", line)
l
:=
strings
.
Split
(
line
,
":"
)
if
len
(
l
)
==
2
{
k
:=
strings
.
ToLower
(
strings
.
TrimRight
(
strings
.
Trim
(
strings
.
Replace
(
l
[
0
],
" "
,
" "
,
-
1
),
" "
),
" "
))
v
:=
strings
.
Trim
(
strings
.
Replace
(
l
[
1
],
"
\n
"
,
""
,
-
1
),
" "
)
//
fmt.Println
("KEY:",k, "VAL:", v)
//
tlog.Info
("KEY:",k, "VAL:", v)
if
_
,
ok
:=
list
[
k
];
!
ok
{
list
[
k
]
=
v
if
k
==
"serial"
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment