mirror of
https://github.com/alibaba/canal.git
synced 2026-11-04 00:30:15 +08:00
Compare commits
27 Commits
canal-1.1.
...
canal-1.1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d53bfd7ee7 | ||
|
|
ccc45b672c | ||
|
|
0d42bdafe2 | ||
|
|
95050ff382 | ||
|
|
5addd246ac | ||
|
|
cfc95e2b7a | ||
|
|
44187d9e3a | ||
|
|
c76e8ba0e4 | ||
|
|
1bcae9dc49 | ||
|
|
3a388308d9 | ||
|
|
a60ec6fa42 | ||
|
|
456945fe3a | ||
|
|
ba24c15833 | ||
|
|
4e0b8a0278 | ||
|
|
274e91f8aa | ||
|
|
89055f7405 | ||
|
|
e8dba41216 | ||
|
|
f52b2cccd5 | ||
|
|
7386327af9 | ||
|
|
c48407b449 | ||
|
|
d92badeb45 | ||
|
|
7093179cc8 | ||
|
|
bf89974f7c | ||
|
|
2e1b7a5093 | ||
|
|
3d4dbc9a8c | ||
|
|
e376a129ed | ||
|
|
983ff5fc73 |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -18,7 +18,8 @@ jtester.properties
|
||||
*.rpm
|
||||
client-adapter/example/
|
||||
*.dat
|
||||
|
||||
canal-admin/canal-admin-ui/dist
|
||||
canal-admin/canal-admin-ui/node
|
||||
canal-admin/canal-admin-ui/node_modules
|
||||
conf/
|
||||
canal-admin/canal-admin-ui/dist/
|
||||
canal-admin/canal-admin-ui/node/
|
||||
canal-admin/canal-admin-ui/node_modules/
|
||||
canal-admin/canal-admin-server/src/main/resources/conf/
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>canal-admin</artifactId>
|
||||
<groupId>com.alibaba.otter</groupId>
|
||||
<version>1.1.4-SNAPSHOT</version>
|
||||
<version>1.1.4</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>canal-admin-server</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.otter</groupId>
|
||||
<artifactId>canal.protocol</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
@@ -48,24 +50,17 @@
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
</dependency>
|
||||
<!-- junit -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>canal-admin-${project.version}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>2.0.1.RELEASE</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>io.repaint.maven</groupId>
|
||||
<artifactId>tiles-maven-plugin</artifactId>
|
||||
@@ -77,16 +72,56 @@
|
||||
</tiles>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>assemble</id>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
<attach>false</attach>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addClasspath>true</addClasspath>
|
||||
<classpathPrefix>lib/</classpathPrefix>
|
||||
<mainClass>com.alibaba.otter.canal.admin.CanalAdminApplication</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
<excludes>
|
||||
<exclude>**/*.properties</exclude>
|
||||
<exclude>**/*.xml</exclude>
|
||||
<exclude>**/*.yml</exclude>
|
||||
<exclude>static/**</exclude>
|
||||
<exclude>public/**</exclude>
|
||||
<exclude>templates/**</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<id>npm</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>env</name>
|
||||
<value>release</value>
|
||||
<value>npm</value>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
@@ -133,6 +168,58 @@
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>dev</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
<property>
|
||||
<name>env</name>
|
||||
<value>!release</value>
|
||||
</property>
|
||||
</activation>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>${basedir}/src/main/assembly/dev.xml</descriptor>
|
||||
</descriptors>
|
||||
<finalName>canal-admin</finalName>
|
||||
<outputDirectory>${project.build.directory}</outputDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>env</name>
|
||||
<value>release</value>
|
||||
</property>
|
||||
</activation>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>${basedir}/src/main/assembly/release.xml</descriptor>
|
||||
</descriptors>
|
||||
<finalName>canal.admin-${project.version}</finalName>
|
||||
<outputDirectory>${project.basedir}/../../target</outputDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
||||
52
canal-admin/canal-admin-server/src/main/assembly/dev.xml
Normal file
52
canal-admin/canal-admin-server/src/main/assembly/dev.xml
Normal file
@@ -0,0 +1,52 @@
|
||||
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
|
||||
<id>dist</id>
|
||||
<formats>
|
||||
<format>dir</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>.</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<includes>
|
||||
<include>README*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>./src/main/bin</directory>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
<fileMode>0755</fileMode>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>./src/main/resources</directory>
|
||||
<outputDirectory>/conf</outputDirectory>
|
||||
<includes>
|
||||
<include>application.yml</include>
|
||||
<include>logback.xml</include>
|
||||
<include>public/**</include>
|
||||
<include>canal_manager.sql</include>
|
||||
<include>canal-template.properties</include>
|
||||
<include>instance-template.properties</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>target</directory>
|
||||
<outputDirectory>logs</outputDirectory>
|
||||
<excludes>
|
||||
<exclude>**/*</exclude>
|
||||
</excludes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<outputDirectory>lib</outputDirectory>
|
||||
<excludes>
|
||||
<exclude>junit:junit</exclude>
|
||||
</excludes>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
</assembly>
|
||||
53
canal-admin/canal-admin-server/src/main/assembly/release.xml
Normal file
53
canal-admin/canal-admin-server/src/main/assembly/release.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
|
||||
<id>dist</id>
|
||||
<formats>
|
||||
<format>tar.gz</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>.</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<includes>
|
||||
<include>README*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>./src/main/bin</directory>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
<fileMode>0755</fileMode>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>./src/main/resources</directory>
|
||||
<outputDirectory>/conf</outputDirectory>
|
||||
<includes>
|
||||
<include>application.yml</include>
|
||||
<include>logback.xml</include>
|
||||
<include>public/**</include>
|
||||
<include>canal_manager.sql</include>
|
||||
<include>canal-template.properties</include>
|
||||
<include>instance-template.properties</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>target</directory>
|
||||
<outputDirectory>logs</outputDirectory>
|
||||
<excludes>
|
||||
<exclude>**/*</exclude>
|
||||
</excludes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<outputDirectory>lib</outputDirectory>
|
||||
<excludes>
|
||||
<exclude>junit:junit</exclude>
|
||||
</excludes>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
</assembly>
|
||||
5
canal-admin/canal-admin-server/src/main/bin/restart.sh
Normal file
5
canal-admin/canal-admin-server/src/main/bin/restart.sh
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
sh stop.sh
|
||||
|
||||
sh startup.sh
|
||||
22
canal-admin/canal-admin-server/src/main/bin/startup.bat
Executable file
22
canal-admin/canal-admin-server/src/main/bin/startup.bat
Executable file
@@ -0,0 +1,22 @@
|
||||
@echo off
|
||||
@if not "%ECHO%" == "" echo %ECHO%
|
||||
@if "%OS%" == "Windows_NT" setlocal
|
||||
|
||||
set ENV_PATH=.\
|
||||
if "%OS%" == "Windows_NT" set ENV_PATH=%~dp0%
|
||||
|
||||
set conf_dir=%ENV_PATH%\..\conf
|
||||
|
||||
set CLASSPATH=%conf_dir%
|
||||
set CLASSPATH=%conf_dir%\..\lib\*;%CLASSPATH%
|
||||
|
||||
set JAVA_MEM_OPTS= -Xms128m -Xmx512m
|
||||
set JAVA_OPTS_EXT= -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Dapplication.codeset=UTF-8 -Dfile.encoding=UTF-8
|
||||
set ADAPTER_OPTS= -DappName=canal-admin
|
||||
|
||||
set JAVA_OPTS= %JAVA_MEM_OPTS% %JAVA_OPTS_EXT% %ADAPTER_OPTS%
|
||||
|
||||
set CMD_STR= java %JAVA_OPTS% -classpath "%CLASSPATH%" com.alibaba.otter.canal.admin.CanalAdminApplication
|
||||
echo start cmd : %CMD_STR%
|
||||
|
||||
java %JAVA_OPTS% -classpath "%CLASSPATH%" com.alibaba.otter.canal.admin.CanalAdminApplication
|
||||
84
canal-admin/canal-admin-server/src/main/bin/startup.sh
Normal file
84
canal-admin/canal-admin-server/src/main/bin/startup.sh
Normal file
@@ -0,0 +1,84 @@
|
||||
#!/bin/bash
|
||||
|
||||
current_path=`pwd`
|
||||
case "`uname`" in
|
||||
Linux)
|
||||
bin_abs_path=$(readlink -f $(dirname $0))
|
||||
;;
|
||||
*)
|
||||
bin_abs_path=`cd $(dirname $0); pwd`
|
||||
;;
|
||||
esac
|
||||
base=${bin_abs_path}/..
|
||||
export LANG=en_US.UTF-8
|
||||
export BASE=$base
|
||||
|
||||
if [ -f $base/bin/adapter.pid ] ; then
|
||||
echo "found adapter.pid , Please run stop.sh first ,then startup.sh" 2>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d $base/logs ] ; then
|
||||
mkdir -p $base/logs
|
||||
fi
|
||||
|
||||
## set java path
|
||||
if [ -z "$JAVA" ] ; then
|
||||
JAVA=$(which java)
|
||||
fi
|
||||
|
||||
ALIBABA_JAVA="/usr/alibaba/java/bin/java"
|
||||
TAOBAO_JAVA="/opt/taobao/java/bin/java"
|
||||
if [ -z "$JAVA" ]; then
|
||||
if [ -f $ALIBABA_JAVA ] ; then
|
||||
JAVA=$ALIBABA_JAVA
|
||||
elif [ -f $TAOBAO_JAVA ] ; then
|
||||
JAVA=$TAOBAO_JAVA
|
||||
else
|
||||
echo "Cannot find a Java JDK. Please set either set JAVA or put java (>=1.5) in your PATH." 2>&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
case "$#"
|
||||
in
|
||||
0 )
|
||||
;;
|
||||
2 )
|
||||
if [ "$1" = "debug" ]; then
|
||||
DEBUG_PORT=$2
|
||||
DEBUG_SUSPEND="n"
|
||||
JAVA_DEBUG_OPT="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=$DEBUG_PORT,server=y,suspend=$DEBUG_SUSPEND"
|
||||
fi
|
||||
;;
|
||||
* )
|
||||
echo "THE PARAMETERS MUST BE TWO OR LESS.PLEASE CHECK AGAIN."
|
||||
exit;;
|
||||
esac
|
||||
|
||||
str=`file -L $JAVA | grep 64-bit`
|
||||
if [ -n "$str" ]; then
|
||||
JAVA_OPTS="-server -Xms2048m -Xmx3072m"
|
||||
else
|
||||
JAVA_OPTS="-server -Xms1024m -Xmx1024m"
|
||||
fi
|
||||
|
||||
JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=250 -XX:+UseGCOverheadLimit -XX:+ExplicitGCInvokesConcurrent -XX:+PrintAdaptiveSizePolicy -XX:+PrintTenuringDistribution"
|
||||
JAVA_OPTS=" $JAVA_OPTS -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8"
|
||||
CANAL_OPTS="-DappName=canal-admin"
|
||||
|
||||
for i in $base/lib/*;
|
||||
do CLASSPATH=$i:"$CLASSPATH";
|
||||
done
|
||||
|
||||
CLASSPATH="$base/conf:$CLASSPATH";
|
||||
|
||||
echo "cd to $bin_abs_path for workaround relative path"
|
||||
cd $bin_abs_path
|
||||
|
||||
echo CLASSPATH :$CLASSPATH
|
||||
$JAVA $JAVA_OPTS $JAVA_DEBUG_OPT $CANAL_OPTS -classpath .:$CLASSPATH com.alibaba.otter.canal.admin.CanalAdminApplication 1>>/dev/null 2>&1 &
|
||||
echo $! > $base/bin/admin.pid
|
||||
|
||||
echo "cd to $current_path for continue"
|
||||
cd $current_path
|
||||
65
canal-admin/canal-admin-server/src/main/bin/stop.sh
Normal file
65
canal-admin/canal-admin-server/src/main/bin/stop.sh
Normal file
@@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
|
||||
cygwin=false;
|
||||
linux=false;
|
||||
case "`uname`" in
|
||||
CYGWIN*)
|
||||
cygwin=true
|
||||
;;
|
||||
Linux*)
|
||||
linux=true
|
||||
;;
|
||||
esac
|
||||
|
||||
get_pid() {
|
||||
STR=$1
|
||||
PID=$2
|
||||
if $cygwin; then
|
||||
JAVA_CMD="$JAVA_HOME\bin\java"
|
||||
JAVA_CMD=`cygpath --path --unix $JAVA_CMD`
|
||||
JAVA_PID=`ps |grep $JAVA_CMD |awk '{print $1}'`
|
||||
else
|
||||
if $linux; then
|
||||
if [ ! -z "$PID" ]; then
|
||||
JAVA_PID=`ps -C java -f --width 1000|grep "$STR"|grep "$PID"|grep -v grep|awk '{print $2}'`
|
||||
else
|
||||
JAVA_PID=`ps -C java -f --width 1000|grep "$STR"|grep -v grep|awk '{print $2}'`
|
||||
fi
|
||||
else
|
||||
if [ ! -z "$PID" ]; then
|
||||
JAVA_PID=`ps aux |grep "$STR"|grep "$PID"|grep -v grep|awk '{print $2}'`
|
||||
else
|
||||
JAVA_PID=`ps aux |grep "$STR"|grep -v grep|awk '{print $2}'`
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
echo $JAVA_PID;
|
||||
}
|
||||
|
||||
base=`dirname $0`/..
|
||||
pidfile=$base/bin/admin.pid
|
||||
if [ ! -f "$pidfile" ];then
|
||||
echo "canal-admin is not running. exists"
|
||||
exit
|
||||
fi
|
||||
|
||||
pid=`cat $pidfile`
|
||||
if [ "$pid" == "" ] ; then
|
||||
pid=`get_pid "appName=canal-admin"`
|
||||
fi
|
||||
|
||||
echo -e "`hostname`: stopping canal $pid ... "
|
||||
kill $pid
|
||||
|
||||
LOOPS=0
|
||||
while (true);
|
||||
do
|
||||
gpid=`get_pid "appName=canal-admin" "$pid"`
|
||||
if [ "$gpid" == "" ] ; then
|
||||
echo "Oook! cost:$LOOPS"
|
||||
`rm $pidfile`
|
||||
break;
|
||||
fi
|
||||
let LOOPS=LOOPS+1
|
||||
sleep 1
|
||||
done
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.alibaba.otter.canal.admin.common;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
public class DaemonThreadFactory implements ThreadFactory {
|
||||
|
||||
public static final ThreadFactory daemonThreadFactory = new DaemonThreadFactory();
|
||||
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = Executors.defaultThreadFactory().newThread(r);
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.alibaba.otter.canal.admin.common;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class TemplateConfigLoader {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(TemplateConfigLoader.class);
|
||||
|
||||
public static final String CONF_DIR = "conf";
|
||||
public static final String CANAL_CONFIG_TMP = "canal-template.properties";
|
||||
public static final String INSTANCE_CONFIG_TMP = "instance-template.properties";
|
||||
|
||||
public static String loadCanalConfig() {
|
||||
return loadFile(CANAL_CONFIG_TMP);
|
||||
}
|
||||
|
||||
public static String loadInstanceConfig() {
|
||||
return loadFile(INSTANCE_CONFIG_TMP);
|
||||
}
|
||||
|
||||
private static String loadFile(String fileName) {
|
||||
File configFile = new File(".." + File.separator + CONF_DIR + File.separator + fileName);
|
||||
if (!configFile.exists()) {
|
||||
URL url = TemplateConfigLoader.class.getClassLoader().getResource("");
|
||||
if (url != null) {
|
||||
configFile = new File(url.getPath() + fileName + File.separator);
|
||||
}
|
||||
}
|
||||
if (!configFile.exists()) {
|
||||
return null;
|
||||
}
|
||||
try (InputStream in = new FileInputStream(configFile)) {
|
||||
byte[] bytes = new byte[in.available()];
|
||||
in.read(bytes);
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
logger.error("Read " + fileName + " error", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.alibaba.otter.canal.admin.common;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Threads {
|
||||
|
||||
public static int pool = 60;
|
||||
public static final ExecutorService executorService = new ThreadPoolExecutor(pool,
|
||||
pool,
|
||||
0L,
|
||||
TimeUnit.MILLISECONDS,
|
||||
new ArrayBlockingQueue<Runnable>(pool * 20),
|
||||
DaemonThreadFactory.daemonThreadFactory);
|
||||
}
|
||||
@@ -25,23 +25,25 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
@ResponseStatus(value = HttpStatus.SERVICE_UNAVAILABLE)
|
||||
public class ServiceException extends RuntimeException {
|
||||
|
||||
public ServiceException() {
|
||||
}
|
||||
private static final long serialVersionUID = -663217666968123330L;
|
||||
|
||||
public ServiceException(String message) {
|
||||
super(message);
|
||||
}
|
||||
public ServiceException(){
|
||||
}
|
||||
|
||||
public ServiceException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
public ServiceException(String message){
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ServiceException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
public ServiceException(String message, Throwable cause){
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
public ServiceException(Throwable cause){
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace){
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.alibaba.otter.canal.admin.config;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* spring util配置类
|
||||
*
|
||||
* @author rewerma @ 2018-10-20
|
||||
* @version 1.0.0
|
||||
*/
|
||||
@Component
|
||||
public class SpringContext implements ApplicationContextAware {
|
||||
|
||||
private static ApplicationContext context;
|
||||
|
||||
/*
|
||||
* 注入ApplicationContext
|
||||
*/
|
||||
public void setApplicationContext(final ApplicationContext context) throws BeansException {
|
||||
// 在加载Spring时自动获得context
|
||||
SpringContext.context = context;
|
||||
}
|
||||
|
||||
public static Object getBean(final String beanName) {
|
||||
return SpringContext.context.getBean(beanName);
|
||||
}
|
||||
|
||||
public static Object getBean(final Class<?> clz) {
|
||||
return context.getBean(clz);
|
||||
}
|
||||
}
|
||||
@@ -74,8 +74,8 @@ public class WebConfig implements WebMvcConfigurer {
|
||||
httpServletResponse.setContentType("application/json;charset=UTF-8");
|
||||
PrintWriter out = httpServletResponse.getWriter();
|
||||
out.print(json);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -84,6 +84,7 @@ public class WebConfig implements WebMvcConfigurer {
|
||||
}
|
||||
})
|
||||
.addPathPatterns("/api/**")
|
||||
.excludePathPatterns("/api/**/config/**")
|
||||
.excludePathPatterns("/api/**/user/login")
|
||||
.excludePathPatterns("/api/**/user/logout")
|
||||
.excludePathPatterns("/api/**/user/info");
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.alibaba.otter.canal.admin.connector;
|
||||
|
||||
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
|
||||
import com.alibaba.otter.canal.protocol.exception.CanalClientException;
|
||||
|
||||
/**
|
||||
* canal admin操作客户端
|
||||
*
|
||||
* @author agapple 2019年8月31日 下午12:46:29
|
||||
* @since 1.1.4
|
||||
*/
|
||||
public interface AdminConnector {
|
||||
|
||||
/**
|
||||
* 链接对应的canal server
|
||||
*
|
||||
* @throws CanalClientException
|
||||
*/
|
||||
void connect() throws ServiceException;
|
||||
|
||||
/**
|
||||
* 释放链接
|
||||
*
|
||||
* @throws CanalClientException
|
||||
*/
|
||||
void disconnect() throws ServiceException;
|
||||
|
||||
/**
|
||||
* 获取Canal Server状态
|
||||
*
|
||||
* @return 状态代码
|
||||
*/
|
||||
boolean check();
|
||||
|
||||
/**
|
||||
* 启动Canal Server
|
||||
*
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean start();
|
||||
|
||||
/**
|
||||
* 停止Canal Server
|
||||
*
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean stop();
|
||||
|
||||
/**
|
||||
* 重启Canal Server
|
||||
*
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean restart();
|
||||
|
||||
/**
|
||||
* 获取所有当前节点下运行中的实例
|
||||
*
|
||||
* @return 实例信息
|
||||
*/
|
||||
String getRunningInstances();
|
||||
|
||||
/**
|
||||
* 通过实例名检查
|
||||
*
|
||||
* @param destination
|
||||
* @return
|
||||
*/
|
||||
boolean checkInstance(String destination);
|
||||
|
||||
/**
|
||||
* 通过实例名启动实例
|
||||
*
|
||||
* @param destination 实例名
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean startInstance(String destination);
|
||||
|
||||
/**
|
||||
* 通过实例名关闭实例
|
||||
*
|
||||
* @param destination 实例名
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean stopInstance(String destination);
|
||||
|
||||
/**
|
||||
* 通过实例名释放,主要针对cluster模式有效(通知当前主机释放instance运行交给其他人来抢占)
|
||||
*
|
||||
* @param destination 实例名
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean releaseInstance(String destination);
|
||||
|
||||
/**
|
||||
* 通过实例名重启实例
|
||||
*
|
||||
* @param destination 实例名
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean restartInstance(String destination);
|
||||
|
||||
/**
|
||||
* 获取Canal Server日志列表
|
||||
*
|
||||
* @return 日志信息
|
||||
*/
|
||||
String listCanalLog();
|
||||
|
||||
/**
|
||||
* 获取Canal Server日志
|
||||
*
|
||||
* @return 日志信息
|
||||
*/
|
||||
String canalLog(int lines);
|
||||
|
||||
/**
|
||||
* 获取Instance的机器日志列表
|
||||
*
|
||||
* @param destination
|
||||
*/
|
||||
String listInstanceLog(String destination);
|
||||
|
||||
/**
|
||||
* 通过实例名获取实例日志
|
||||
*
|
||||
* @return 日志信息
|
||||
*/
|
||||
String instanceLog(String destination, String fileName, int lines);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
package com.alibaba.otter.canal.admin.connector;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.Channel;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
|
||||
import com.alibaba.otter.canal.protocol.AdminPacket.Ack;
|
||||
import com.alibaba.otter.canal.protocol.AdminPacket.ClientAuth;
|
||||
import com.alibaba.otter.canal.protocol.AdminPacket.Handshake;
|
||||
import com.alibaba.otter.canal.protocol.AdminPacket.InstanceAdmin;
|
||||
import com.alibaba.otter.canal.protocol.AdminPacket.LogAdmin;
|
||||
import com.alibaba.otter.canal.protocol.AdminPacket.Packet;
|
||||
import com.alibaba.otter.canal.protocol.AdminPacket.PacketType;
|
||||
import com.alibaba.otter.canal.protocol.AdminPacket.ServerAdmin;
|
||||
import com.alibaba.otter.canal.protocol.SecurityUtil;
|
||||
import com.alibaba.otter.canal.protocol.exception.CanalClientException;
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
/**
|
||||
* 基于netty实现的admin控制
|
||||
*
|
||||
* @author agapple 2019年8月26日 上午10:23:44
|
||||
* @since 1.1.4
|
||||
*/
|
||||
public class SimpleAdminConnector implements AdminConnector {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SimpleAdminConnector.class);
|
||||
private String user;
|
||||
private String passwd;
|
||||
private SocketAddress address;
|
||||
private int soTimeout = 60000; // milliseconds
|
||||
private int idleTimeout = 60 * 60 * 1000; // client和server之间的空闲链接超时的时间,默认为1小时
|
||||
private final ByteBuffer readHeader = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN);
|
||||
private final ByteBuffer writeHeader = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN);
|
||||
private SocketChannel channel;
|
||||
private ReadableByteChannel readableChannel;
|
||||
private WritableByteChannel writableChannel;
|
||||
private volatile boolean connected = false;
|
||||
|
||||
public SimpleAdminConnector(String ip, int port, String user, String passwd){
|
||||
this.address = new InetSocketAddress(ip, port);
|
||||
this.user = user;
|
||||
this.passwd = passwd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect() throws ServiceException {
|
||||
try {
|
||||
if (connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
channel = SocketChannel.open();
|
||||
channel.socket().setSoTimeout(soTimeout);
|
||||
channel.connect(address);
|
||||
readableChannel = Channels.newChannel(channel.socket().getInputStream());
|
||||
writableChannel = Channels.newChannel(channel.socket().getOutputStream());
|
||||
Packet p = Packet.parseFrom(readNextPacket());
|
||||
if (p.getVersion() != 1) {
|
||||
throw new CanalClientException("unsupported version at this client.");
|
||||
}
|
||||
|
||||
if (p.getType() != PacketType.HANDSHAKE) {
|
||||
throw new CanalClientException("expect handshake but found other type.");
|
||||
}
|
||||
|
||||
Handshake handshake = Handshake.parseFrom(p.getBody());
|
||||
ByteString seed = handshake.getSeeds(); // seed for auth
|
||||
String newPasswd = passwd;
|
||||
if (passwd != null) {
|
||||
// encode passwd
|
||||
newPasswd = SecurityUtil.byte2HexStr(SecurityUtil.scramble411(passwd.getBytes(), seed.toByteArray()));
|
||||
}
|
||||
|
||||
ClientAuth ca = ClientAuth.newBuilder()
|
||||
.setUsername(user != null ? user : "")
|
||||
.setPassword(ByteString.copyFromUtf8(newPasswd != null ? newPasswd : ""))
|
||||
.setNetReadTimeout(idleTimeout)
|
||||
.setNetWriteTimeout(idleTimeout)
|
||||
.build();
|
||||
writeWithHeader(Packet.newBuilder()
|
||||
.setType(PacketType.CLIENTAUTHENTICATION)
|
||||
.setBody(ca.toByteString())
|
||||
.build()
|
||||
.toByteArray());
|
||||
//
|
||||
Packet ack = Packet.parseFrom(readNextPacket());
|
||||
if (ack.getType() != PacketType.ACK) {
|
||||
throw new CanalClientException("unexpected packet type when ack is expected");
|
||||
}
|
||||
|
||||
Ack ackBody = Ack.parseFrom(ack.getBody());
|
||||
if (ackBody.getCode() > 0) {
|
||||
throw new ServiceException("something goes wrong when doing authentication: " + ackBody.getMessage());
|
||||
}
|
||||
|
||||
connected = true;
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException(e);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new ServiceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() throws ServiceException {
|
||||
if (!connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
connected = false;
|
||||
if (readableChannel != null) {
|
||||
quietlyClose(readableChannel);
|
||||
readableChannel = null;
|
||||
}
|
||||
if (writableChannel != null) {
|
||||
quietlyClose(writableChannel);
|
||||
writableChannel = null;
|
||||
}
|
||||
if (channel != null) {
|
||||
quietlyClose(channel);
|
||||
channel = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean check() {
|
||||
return BooleanUtils.toBoolean(Integer.parseInt(doServerAdmin("check")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean start() {
|
||||
return BooleanUtils.toBoolean(Integer.parseInt(doServerAdmin("start")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stop() {
|
||||
return BooleanUtils.toBoolean(Integer.parseInt(doServerAdmin("stop")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean restart() {
|
||||
return BooleanUtils.toBoolean(Integer.parseInt(doServerAdmin("restart")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRunningInstances() {
|
||||
return doServerAdmin("list");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkInstance(String destination) {
|
||||
return BooleanUtils.toBoolean(Integer.parseInt(doInstanceAdmin(destination, "check")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startInstance(String destination) {
|
||||
return BooleanUtils.toBoolean(Integer.parseInt(doInstanceAdmin(destination, "start")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stopInstance(String destination) {
|
||||
return BooleanUtils.toBoolean(Integer.parseInt(doInstanceAdmin(destination, "stop")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean releaseInstance(String destination) {
|
||||
return BooleanUtils.toBoolean(Integer.parseInt(doInstanceAdmin(destination, "release")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean restartInstance(String destination) {
|
||||
return BooleanUtils.toBoolean(Integer.parseInt(doInstanceAdmin(destination, "restart")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String listCanalLog() {
|
||||
return doLogAdmin("server", "list", null, null, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String canalLog(int lines) {
|
||||
return doLogAdmin("server", "file", null, null, lines);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String listInstanceLog(String destination) {
|
||||
return doLogAdmin("instance", "list", destination, null, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String instanceLog(String destination, String fileName, int lines) {
|
||||
return doLogAdmin("instance", "file", destination, fileName, lines);
|
||||
}
|
||||
|
||||
// ==================== helper method ====================
|
||||
|
||||
private String doServerAdmin(String action) {
|
||||
try {
|
||||
writeWithHeader(Packet.newBuilder()
|
||||
.setType(PacketType.SERVER)
|
||||
.setBody(ServerAdmin.newBuilder().setAction(action).build().toByteString())
|
||||
.build()
|
||||
.toByteArray());
|
||||
|
||||
Packet p = Packet.parseFrom(readNextPacket());
|
||||
Ack ack = Ack.parseFrom(p.getBody());
|
||||
if (ack.getCode() > 0) {
|
||||
throw new ServiceException("failed to subscribe with reason: " + ack.getMessage());
|
||||
}
|
||||
|
||||
return ack.getMessage();
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String doInstanceAdmin(String destination, String action) {
|
||||
try {
|
||||
writeWithHeader(Packet.newBuilder()
|
||||
.setType(PacketType.INSTANCE)
|
||||
.setBody(InstanceAdmin.newBuilder()
|
||||
.setDestination(destination)
|
||||
.setAction(action)
|
||||
.build()
|
||||
.toByteString())
|
||||
.build()
|
||||
.toByteArray());
|
||||
|
||||
Packet p = Packet.parseFrom(readNextPacket());
|
||||
Ack ack = Ack.parseFrom(p.getBody());
|
||||
if (ack.getCode() > 0) {
|
||||
throw new ServiceException("failed to subscribe with reason: " + ack.getMessage());
|
||||
}
|
||||
|
||||
return ack.getMessage();
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String doLogAdmin(String type, String action, String destination, String file, int count) {
|
||||
try {
|
||||
writeWithHeader(Packet.newBuilder()
|
||||
.setType(PacketType.LOG)
|
||||
.setBody(LogAdmin.newBuilder()
|
||||
.setType(type)
|
||||
.setAction(action)
|
||||
.setDestination(destination == null ? "" : destination)
|
||||
.setFile(file == null ? "" : file)
|
||||
.setCount(count)
|
||||
.build()
|
||||
.toByteString())
|
||||
.build()
|
||||
.toByteArray());
|
||||
|
||||
Packet p = Packet.parseFrom(readNextPacket());
|
||||
Ack ack = Ack.parseFrom(p.getBody());
|
||||
if (ack.getCode() > 0) {
|
||||
throw new ServiceException("failed to subscribe with reason: " + ack.getMessage());
|
||||
}
|
||||
|
||||
return ack.getMessage();
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeWithHeader(byte[] body) throws IOException {
|
||||
writeWithHeader(writableChannel, body);
|
||||
}
|
||||
|
||||
private byte[] readNextPacket() throws IOException {
|
||||
return readNextPacket(readableChannel);
|
||||
}
|
||||
|
||||
private void writeWithHeader(WritableByteChannel channel, byte[] body) throws IOException {
|
||||
writeHeader.clear();
|
||||
writeHeader.putInt(body.length);
|
||||
writeHeader.flip();
|
||||
channel.write(writeHeader);
|
||||
channel.write(ByteBuffer.wrap(body));
|
||||
}
|
||||
|
||||
private byte[] readNextPacket(ReadableByteChannel channel) throws IOException {
|
||||
readHeader.clear();
|
||||
read(channel, readHeader);
|
||||
int bodyLen = readHeader.getInt(0);
|
||||
ByteBuffer bodyBuf = ByteBuffer.allocate(bodyLen).order(ByteOrder.BIG_ENDIAN);
|
||||
read(channel, bodyBuf);
|
||||
return bodyBuf.array();
|
||||
}
|
||||
|
||||
private void read(ReadableByteChannel channel, ByteBuffer buffer) throws IOException {
|
||||
while (buffer.hasRemaining()) {
|
||||
int r = channel.read(buffer);
|
||||
if (r == -1) {
|
||||
throw new IOException("end of stream when reading header");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void quietlyClose(Channel channel) {
|
||||
try {
|
||||
channel.close();
|
||||
} catch (IOException e) {
|
||||
logger.warn("exception on closing channel:{} \n {}", channel, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.alibaba.otter.canal.admin.connector;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.alibaba.otter.canal.admin.config.SpringContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
public class SimpleAdminConnectors {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SimpleAdminConnectors.class);
|
||||
|
||||
public static <R> R execute(String ip, int port, Function<AdminConnector, R> function) {
|
||||
Environment env = (Environment) SpringContext.getBean(Environment.class);
|
||||
String defaultUser = env.getProperty("canal.adminUser", "admin");
|
||||
String defaultPasswd = env.getProperty("canal.adminPasswd", "admin");
|
||||
|
||||
return execute(ip, port, defaultUser, defaultPasswd, function);
|
||||
}
|
||||
|
||||
public static <R> R execute(String ip, int port, String user, String passwd, Function<AdminConnector, R> function) {
|
||||
SimpleAdminConnector connector = new SimpleAdminConnector(ip, port, user, passwd);
|
||||
try {
|
||||
connector.connect();
|
||||
return function.apply(connector);
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage());
|
||||
} finally {
|
||||
connector.disconnect();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.alibaba.otter.canal.admin.controller;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.otter.canal.admin.model.BaseModel;
|
||||
import com.alibaba.otter.canal.admin.model.CanalCluster;
|
||||
import com.alibaba.otter.canal.admin.model.NodeServer;
|
||||
import com.alibaba.otter.canal.admin.service.CanalClusterService;
|
||||
import com.alibaba.otter.canal.admin.service.NodeServerService;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/{env}/canal")
|
||||
public class CanalClusterController {
|
||||
|
||||
@Autowired
|
||||
CanalClusterService canalClusterServic;
|
||||
|
||||
@Autowired
|
||||
NodeServerService nodeServerService;
|
||||
|
||||
@GetMapping(value = "/clusters")
|
||||
public BaseModel<List<CanalCluster>> clusters(CanalCluster canalCluster, @PathVariable String env) {
|
||||
return BaseModel.getInstance(canalClusterServic.findList(canalCluster));
|
||||
}
|
||||
|
||||
@PostMapping(value = "/cluster")
|
||||
public BaseModel<String> save(@RequestBody CanalCluster canalCluster, @PathVariable String env) {
|
||||
canalClusterServic.save(canalCluster);
|
||||
return BaseModel.getInstance("success");
|
||||
}
|
||||
|
||||
@GetMapping(value = "/cluster/{id}")
|
||||
public BaseModel<CanalCluster> detail(@PathVariable Long id, @PathVariable String env) {
|
||||
return BaseModel.getInstance(canalClusterServic.detail(id));
|
||||
}
|
||||
|
||||
@PutMapping(value = "/cluster")
|
||||
public BaseModel<String> update(@RequestBody CanalCluster canalCluster, @PathVariable String env) {
|
||||
canalClusterServic.update(canalCluster);
|
||||
return BaseModel.getInstance("success");
|
||||
}
|
||||
|
||||
@DeleteMapping(value = "/cluster/{id}")
|
||||
public BaseModel<String> delete(@PathVariable Long id, @PathVariable String env) {
|
||||
canalClusterServic.delete(id);
|
||||
return BaseModel.getInstance("success");
|
||||
}
|
||||
|
||||
@GetMapping(value = "/clustersAndServers")
|
||||
public BaseModel<List<?>> clustersAndServers(@PathVariable String env) {
|
||||
List<CanalCluster> clusters = canalClusterServic.findList(new CanalCluster());
|
||||
JSONObject group = new JSONObject();
|
||||
group.put("label", "集群");
|
||||
JSONArray jsonArray = new JSONArray();
|
||||
clusters.forEach(cluster -> {
|
||||
JSONObject item = new JSONObject();
|
||||
item.put("label", cluster.getName());
|
||||
item.put("value", "cluster:" + cluster.getId());
|
||||
jsonArray.add(item);
|
||||
});
|
||||
group.put("options", jsonArray);
|
||||
|
||||
NodeServer param = new NodeServer();
|
||||
param.setClusterId(-1L);
|
||||
List<NodeServer> servers = nodeServerService.findAll(param); // 取所有standalone的节点
|
||||
JSONObject group2 = new JSONObject();
|
||||
group2.put("label", "单机主机");
|
||||
JSONArray jsonArray2 = new JSONArray();
|
||||
servers.forEach(server -> {
|
||||
JSONObject item = new JSONObject();
|
||||
item.put("label", server.getName());
|
||||
item.put("value", "server:" + server.getId());
|
||||
jsonArray2.add(item);
|
||||
});
|
||||
group2.put("options", jsonArray2);
|
||||
|
||||
List<JSONObject> result = new ArrayList<>();
|
||||
result.add(group);
|
||||
result.add(group2);
|
||||
return BaseModel.getInstance(result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,15 @@
|
||||
package com.alibaba.otter.canal.admin.controller;
|
||||
|
||||
import com.alibaba.otter.canal.admin.model.BaseModel;
|
||||
import com.alibaba.otter.canal.admin.common.TemplateConfigLoader;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.alibaba.otter.canal.admin.model.BaseModel;
|
||||
import com.alibaba.otter.canal.admin.model.CanalConfig;
|
||||
import com.alibaba.otter.canal.admin.service.CanalConfigService;
|
||||
|
||||
@@ -26,9 +32,10 @@ public class CanalConfigController {
|
||||
* @param env 环境变量
|
||||
* @return 配置信息
|
||||
*/
|
||||
@GetMapping(value = "/config")
|
||||
public BaseModel<CanalConfig> canalConfig(@PathVariable String env) {
|
||||
return BaseModel.getInstance(canalConfigService.getCanalConfig());
|
||||
@GetMapping(value = "/config/{clusterId}/{serverId}")
|
||||
public BaseModel<CanalConfig> canalConfig(@PathVariable Long clusterId, @PathVariable Long serverId,
|
||||
@PathVariable String env) {
|
||||
return BaseModel.getInstance(canalConfigService.getCanalConfig(clusterId, serverId));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,4 +50,9 @@ public class CanalConfigController {
|
||||
canalConfigService.updateContent(canalConfig);
|
||||
return BaseModel.getInstance("success");
|
||||
}
|
||||
|
||||
@GetMapping(value = "/config/template")
|
||||
public BaseModel<String> template(@PathVariable String env) {
|
||||
return BaseModel.getInstance(TemplateConfigLoader.loadCanalConfig());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,21 @@ package com.alibaba.otter.canal.admin.controller;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.otter.canal.admin.common.TemplateConfigLoader;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.alibaba.otter.canal.admin.model.BaseModel;
|
||||
import com.alibaba.otter.canal.admin.model.CanalInstanceConfig;
|
||||
import com.alibaba.otter.canal.admin.model.Pager;
|
||||
import com.alibaba.otter.canal.admin.service.CanalInstanceService;
|
||||
|
||||
/**
|
||||
@@ -31,9 +41,9 @@ public class CanalInstanceController {
|
||||
* @return 实例列表
|
||||
*/
|
||||
@GetMapping(value = "/instances")
|
||||
public BaseModel<List<CanalInstanceConfig>> nodeServers(CanalInstanceConfig canalInstanceConfig,
|
||||
@PathVariable String env) {
|
||||
return BaseModel.getInstance(canalInstanceConfigService.findList(canalInstanceConfig));
|
||||
public BaseModel<Pager<CanalInstanceConfig>> list(CanalInstanceConfig canalInstanceConfig,
|
||||
Pager<CanalInstanceConfig> pager, @PathVariable String env) {
|
||||
return BaseModel.getInstance(canalInstanceConfigService.findList(canalInstanceConfig, pager));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,6 +59,18 @@ public class CanalInstanceController {
|
||||
return BaseModel.getInstance("success");
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例详情信息
|
||||
*
|
||||
* @param id 实例配置id
|
||||
* @param env 环境变量
|
||||
* @return 实例信息
|
||||
*/
|
||||
@GetMapping(value = "/instance")
|
||||
public BaseModel<CanalInstanceConfig> config(@PathVariable Long id, @PathVariable String env) {
|
||||
return BaseModel.getInstance(canalInstanceConfigService.detail(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例详情信息
|
||||
*
|
||||
@@ -112,6 +134,20 @@ public class CanalInstanceController {
|
||||
return BaseModel.getInstance(canalInstanceConfigService.remoteOperation(id, nodeId, "stop"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过操作instance状态启动/停止远程instance
|
||||
*
|
||||
* @param id 实例配置id
|
||||
* @param option 操作类型: start/stop
|
||||
* @param env 环境变量
|
||||
* @return 是否成功
|
||||
*/
|
||||
@PutMapping(value = "/instance/status/{id}")
|
||||
public BaseModel<Boolean> instanceStart(@PathVariable Long id, @RequestParam String option,
|
||||
@PathVariable String env) {
|
||||
return BaseModel.getInstance(canalInstanceConfigService.instanceOperation(id, option));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取远程实例运行日志
|
||||
*
|
||||
@@ -125,4 +161,21 @@ public class CanalInstanceController {
|
||||
@PathVariable String env) {
|
||||
return BaseModel.getInstance(canalInstanceConfigService.remoteInstanceLog(id, nodeId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过Server id获取所有活动的Instance
|
||||
*
|
||||
* @param serverId 节点id
|
||||
* @param env 环境变量
|
||||
* @return 实例列表
|
||||
*/
|
||||
@GetMapping(value = "/active/instances/{serverId}")
|
||||
public BaseModel<List<CanalInstanceConfig>> activeInstances(@PathVariable Long serverId, @PathVariable String env) {
|
||||
return BaseModel.getInstance(canalInstanceConfigService.findActiveInstanceByServerId(serverId));
|
||||
}
|
||||
|
||||
@GetMapping(value = "/instance/template")
|
||||
public BaseModel<String> template(@PathVariable String env) {
|
||||
return BaseModel.getInstance(TemplateConfigLoader.loadInstanceConfig());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
package com.alibaba.otter.canal.admin.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.alibaba.otter.canal.admin.model.BaseModel;
|
||||
import com.alibaba.otter.canal.admin.model.NodeServer;
|
||||
import com.alibaba.otter.canal.admin.model.Pager;
|
||||
import com.alibaba.otter.canal.admin.service.NodeServerService;
|
||||
|
||||
/**
|
||||
@@ -30,8 +37,9 @@ public class NodeServerController {
|
||||
* @return 节点信息列表
|
||||
*/
|
||||
@GetMapping(value = "/nodeServers")
|
||||
public BaseModel<List<NodeServer>> nodeServers(NodeServer nodeServer, @PathVariable String env) {
|
||||
return BaseModel.getInstance(nodeServerService.findList(nodeServer));
|
||||
public BaseModel<Pager<NodeServer>> nodeServers(NodeServer nodeServer, Pager<NodeServer> pager,
|
||||
@PathVariable String env) {
|
||||
return BaseModel.getInstance(nodeServerService.findList(nodeServer, pager));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.alibaba.otter.canal.admin.controller;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.alibaba.otter.canal.admin.model.BaseModel;
|
||||
import com.alibaba.otter.canal.admin.model.CanalConfig;
|
||||
import com.alibaba.otter.canal.admin.model.CanalInstanceConfig;
|
||||
import com.alibaba.otter.canal.admin.service.PollingConfigService;
|
||||
import com.alibaba.otter.canal.protocol.SecurityUtil;
|
||||
|
||||
/**
|
||||
* Canal Instance配置管理控制层
|
||||
*
|
||||
* @author rewerma 2019-07-13 下午05:12:16
|
||||
* @version 1.0.0
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/{env}/config")
|
||||
public class PollingConfigController {
|
||||
|
||||
private static final byte[] seeds = "canal is best!".getBytes();
|
||||
|
||||
@Value(value = "${canal.adminUser}")
|
||||
String user;
|
||||
|
||||
@Value(value = "${canal.adminPasswd}")
|
||||
String passwd;
|
||||
|
||||
@Autowired
|
||||
PollingConfigService pollingConfigService;
|
||||
|
||||
/**
|
||||
* 获取server全局配置
|
||||
*/
|
||||
@GetMapping(value = "/server_polling")
|
||||
public BaseModel<CanalConfig> canalConfigPoll(@RequestHeader String user, @RequestHeader String passwd,
|
||||
@RequestParam String ip, @RequestParam Integer port,
|
||||
@RequestParam String md5, @RequestParam boolean register,
|
||||
@RequestParam String cluster, @PathVariable String env) {
|
||||
if (!auth(user, passwd)) {
|
||||
throw new RuntimeException("auth :" + user + " is failed");
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(md5) && register) {
|
||||
// do something
|
||||
pollingConfigService.autoRegister(ip, port, cluster);
|
||||
}
|
||||
|
||||
CanalConfig canalConfig = pollingConfigService.getChangedConfig(ip, port, md5);
|
||||
return BaseModel.getInstance(canalConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单个instance的配置
|
||||
*/
|
||||
@GetMapping(value = "/instance_polling/{destination}")
|
||||
public BaseModel<CanalInstanceConfig> instanceConfigPoll(@RequestHeader String user, @RequestHeader String passwd,
|
||||
@PathVariable String env,
|
||||
@PathVariable String destination, @RequestParam String md5) {
|
||||
if (!auth(user, passwd)) {
|
||||
throw new RuntimeException("auth :" + user + " is failed");
|
||||
}
|
||||
|
||||
CanalInstanceConfig canalInstanceConfig = pollingConfigService.getInstanceConfig(destination, md5);
|
||||
return BaseModel.getInstance(canalInstanceConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对应server(ip+port)所需要运行的instance列表
|
||||
*/
|
||||
@GetMapping(value = "/instances_polling")
|
||||
public BaseModel<CanalInstanceConfig> instancesPoll(@RequestHeader String user, @RequestHeader String passwd,
|
||||
@RequestParam String ip, @RequestParam Integer port,
|
||||
@RequestParam String md5, @PathVariable String env) {
|
||||
if (!auth(user, passwd)) {
|
||||
throw new RuntimeException("auth :" + user + " is failed");
|
||||
}
|
||||
|
||||
CanalInstanceConfig canalInstanceConfig = pollingConfigService.getInstancesConfig(ip, port, md5);
|
||||
return BaseModel.getInstance(canalInstanceConfig);
|
||||
}
|
||||
|
||||
private boolean auth(String user, String passwd) {
|
||||
// 如果user/passwd密码为空,则任何用户账户都能登录
|
||||
if ((StringUtils.isEmpty(this.user) || StringUtils.equals(this.user, user))) {
|
||||
if (StringUtils.isEmpty(this.passwd)) {
|
||||
return true;
|
||||
} else if (StringUtils.isEmpty(passwd)) {
|
||||
// 如果server密码有配置,客户端密码为空,则拒绝
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// manager这里保存了原始密码,反过来和canal发送过来的进行校验
|
||||
byte[] passForClient = SecurityUtil.scramble411(this.passwd.getBytes(), seeds);
|
||||
return SecurityUtil.scrambleServerAuth(passForClient, SecurityUtil.hexStr2Bytes(passwd), seeds);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ public class UserController {
|
||||
|
||||
public static final LoadingCache<String, User> loginUsers = Caffeine.newBuilder()
|
||||
.maximumSize(10_000)
|
||||
.expireAfterAccess(10, TimeUnit.MINUTES)
|
||||
.expireAfterAccess(30, TimeUnit.MINUTES)
|
||||
.build(key -> null); // 用户登录信息缓存
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
package com.alibaba.otter.canal.admin.jmx;
|
||||
|
||||
/**
|
||||
* Canal Server JMX MBean
|
||||
*
|
||||
* @author rewerma 2019-07-13 下午05:12:16
|
||||
* @version 1.0.0
|
||||
*/
|
||||
public interface CanalServerMXBean {
|
||||
|
||||
/**
|
||||
* 获取Canal Server状态
|
||||
*
|
||||
* @return 状态代码
|
||||
*/
|
||||
int getStatus();
|
||||
|
||||
/**
|
||||
* 启动Canal Server
|
||||
*
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean start();
|
||||
|
||||
/**
|
||||
* 停止Canal Server
|
||||
*
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean stop();
|
||||
|
||||
/**
|
||||
* 重启Canal Server
|
||||
*
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean restart();
|
||||
|
||||
/**
|
||||
* 退出Canal Server(关闭进程)
|
||||
*
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean exit();
|
||||
|
||||
/**
|
||||
* 通过实例名启动实例
|
||||
*
|
||||
* @param destination 实例名
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean startInstance(String destination);
|
||||
|
||||
/**
|
||||
* 通过实例名关闭实例
|
||||
*
|
||||
* @param destination 实例名
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean stopInstance(String destination);
|
||||
|
||||
/**
|
||||
* 通过实例名重启实例
|
||||
*
|
||||
* @param destination 实例名
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean reloadInstance(String destination);
|
||||
|
||||
/**
|
||||
* 获取所有当前节点下运行中的实例
|
||||
*
|
||||
* @return 实例信息
|
||||
*/
|
||||
String getRunningInstances();
|
||||
|
||||
/**
|
||||
* 获取Canal Server日志(末尾100行)
|
||||
*
|
||||
* @return 日志信息
|
||||
*/
|
||||
String canalLog();
|
||||
|
||||
/**
|
||||
* 通过实例名获取实例日志(末尾100行)
|
||||
*
|
||||
* @return 日志信息
|
||||
*/
|
||||
String instanceLog(String destination);
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
package com.alibaba.otter.canal.admin.jmx;
|
||||
|
||||
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.management.*;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Canal Server JMX 控制层
|
||||
*
|
||||
* @author rewerma 2019-07-13 下午05:12:16
|
||||
* @version 1.0.0
|
||||
*/
|
||||
public class JMXConnection {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(JMXConnection.class);
|
||||
|
||||
private String ip;
|
||||
private Integer port;
|
||||
private JMXConnector jmxc;
|
||||
private CanalServerMXBean canalServerMXBean;
|
||||
|
||||
public JMXConnection(String ip, Integer port){
|
||||
this.ip = ip;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行相关操作
|
||||
*
|
||||
* @param ip jmx ip
|
||||
* @param port jmx port
|
||||
* @param function 执行方法
|
||||
* @param <R> 返回泛型
|
||||
* @return 执行结果
|
||||
*/
|
||||
public static <R> R execute(String ip, int port, Function<CanalServerMXBean, R> function) {
|
||||
JMXConnection jmxConnection = new JMXConnection(ip, port);
|
||||
try {
|
||||
CanalServerMXBean canalServerMXBean = jmxConnection.getCanalServerMXBean();
|
||||
return function.apply(canalServerMXBean);
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage());
|
||||
} finally {
|
||||
jmxConnection.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接远程jmx
|
||||
*/
|
||||
public void connect() {
|
||||
try {
|
||||
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + ip + ":" + port + "/jmxrmi");
|
||||
// jmxc = JMXConnectorFactory.connect(url, null);
|
||||
jmxc = connectWithTimeout(url, 3, TimeUnit.SECONDS);
|
||||
MBeanServerConnection mBeanServerConnection = jmxc.getMBeanServerConnection();
|
||||
ObjectName name = new ObjectName("CanalServerAgent:type=CanalServerStatus");
|
||||
mBeanServerConnection.addNotificationListener(name, (notification, handback) -> {
|
||||
}, null, null);
|
||||
canalServerMXBean = MBeanServerInvocationHandler
|
||||
.newProxyInstance(mBeanServerConnection, name, CanalServerMXBean.class, false);
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 带超时的jmx连接器
|
||||
*
|
||||
* @param url jmx url
|
||||
* @param timeout 超时时间
|
||||
* @param unit 超时时间单位
|
||||
* @return JMXConnector
|
||||
* @throws IOException 连接异常
|
||||
*/
|
||||
private static JMXConnector connectWithTimeout(final JMXServiceURL url, long timeout,
|
||||
TimeUnit unit) throws IOException {
|
||||
final BlockingQueue<Object> mailbox = new ArrayBlockingQueue<>(1);
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor(daemonThreadFactory);
|
||||
executor.submit(() -> {
|
||||
try {
|
||||
JMXConnector connector = JMXConnectorFactory.connect(url);
|
||||
if (!mailbox.offer(connector)) connector.close();
|
||||
} catch (Throwable t) {
|
||||
mailbox.offer(t);
|
||||
}
|
||||
});
|
||||
Object result;
|
||||
try {
|
||||
result = mailbox.poll(timeout, unit);
|
||||
if (result == null) {
|
||||
if (!mailbox.offer("")) result = mailbox.take();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw initCause(new InterruptedIOException(e.getMessage()), e);
|
||||
} finally {
|
||||
executor.shutdown();
|
||||
}
|
||||
if (result == null) throw new SocketTimeoutException("Connect timed out: " + url);
|
||||
if (result instanceof JMXConnector) return (JMXConnector) result;
|
||||
try {
|
||||
throw (Throwable) result;
|
||||
} catch (IOException | RuntimeException | Error e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
// In principle this can't happen but we wrap it anyway
|
||||
throw new IOException(e.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T extends Throwable> T initCause(T wrapper, Throwable wrapped) {
|
||||
wrapper.initCause(wrapped);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
private static class DaemonThreadFactory implements ThreadFactory {
|
||||
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = Executors.defaultThreadFactory().newThread(r);
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
private static final ThreadFactory daemonThreadFactory = new DaemonThreadFactory();
|
||||
|
||||
/**
|
||||
* 获取MBean
|
||||
*
|
||||
* @return canalServerMXBean
|
||||
*/
|
||||
public CanalServerMXBean getCanalServerMXBean() {
|
||||
if (jmxc == null) {
|
||||
connect();
|
||||
}
|
||||
return canalServerMXBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭jmx连接
|
||||
*/
|
||||
public void close() {
|
||||
try {
|
||||
if (jmxc != null) {
|
||||
jmxc.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
} finally {
|
||||
jmxc = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.alibaba.otter.canal.admin.model;
|
||||
|
||||
import io.ebean.Finder;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Canal集群信息实体类
|
||||
*
|
||||
* @author rewerma 2019-07-13 下午05:12:16
|
||||
* @version 1.0.0
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "canal_cluster")
|
||||
public class CanalCluster extends Model {
|
||||
|
||||
public static final CanalClusterFinder find = new CanalClusterFinder();
|
||||
|
||||
public static class CanalClusterFinder extends Finder<Long, CanalCluster> {
|
||||
|
||||
/**
|
||||
* Construct using the default EbeanServer.
|
||||
*/
|
||||
public CanalClusterFinder(){
|
||||
super(CanalCluster.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
private String name;
|
||||
private String zkHosts;
|
||||
private Date modifiedTime;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getZkHosts() {
|
||||
return zkHosts;
|
||||
}
|
||||
|
||||
public void setZkHosts(String zkHosts) {
|
||||
this.zkHosts = zkHosts;
|
||||
}
|
||||
|
||||
public Date getModifiedTime() {
|
||||
return modifiedTime;
|
||||
}
|
||||
|
||||
public void setModifiedTime(Date modifiedTime) {
|
||||
this.modifiedTime = modifiedTime;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/**
|
||||
* Canal住配置实体类
|
||||
* Canal主配置实体类
|
||||
*
|
||||
* @author rewerma 2019-07-13 下午05:12:16
|
||||
* @version 1.0.0
|
||||
@@ -31,10 +31,18 @@ public class CanalConfig extends Model {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
private Long clusterId;
|
||||
private Long serverId;
|
||||
private String name;
|
||||
private String content;
|
||||
private String contentMd5;
|
||||
private String status;
|
||||
private Date modifiedTime;
|
||||
|
||||
public void init() {
|
||||
this.name = "canal.properties";
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
@@ -43,6 +51,22 @@ public class CanalConfig extends Model {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getClusterId() {
|
||||
return clusterId;
|
||||
}
|
||||
|
||||
public void setClusterId(Long clusterId) {
|
||||
this.clusterId = clusterId;
|
||||
}
|
||||
|
||||
public Long getServerId() {
|
||||
return serverId;
|
||||
}
|
||||
|
||||
public void setServerId(Long serverId) {
|
||||
this.serverId = serverId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@@ -59,6 +83,14 @@ public class CanalConfig extends Model {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getContentMd5() {
|
||||
return contentMd5;
|
||||
}
|
||||
|
||||
public void setContentMd5(String contentMd5) {
|
||||
this.contentMd5 = contentMd5;
|
||||
}
|
||||
|
||||
public Date getModifiedTime() {
|
||||
return modifiedTime;
|
||||
}
|
||||
@@ -66,4 +98,13 @@ public class CanalConfig extends Model {
|
||||
public void setModifiedTime(Date modifiedTime) {
|
||||
this.modifiedTime = modifiedTime;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package com.alibaba.otter.canal.admin.model;
|
||||
|
||||
import io.ebean.Finder;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Transient;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
import io.ebean.Finder;
|
||||
|
||||
/**
|
||||
* Canal实例配置信息实体类
|
||||
*
|
||||
@@ -30,15 +29,31 @@ public class CanalInstanceConfig extends Model {
|
||||
}
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
private String name;
|
||||
private String content;
|
||||
private Date modifiedTime;
|
||||
private Long id;
|
||||
@Column(name = "cluster_id")
|
||||
private Long clusterId;
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "cluster_id", updatable = false, insertable = false)
|
||||
private CanalCluster canalCluster;
|
||||
@Column(name = "server_id")
|
||||
private Long serverId;
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "server_id", updatable = false, insertable = false)
|
||||
private NodeServer nodeServer;
|
||||
private String name;
|
||||
private String content;
|
||||
private String contentMd5;
|
||||
private String status; // 1: 正常 0: 停止
|
||||
private Date modifiedTime;
|
||||
|
||||
@Transient
|
||||
private Long nodeId;
|
||||
private String clusterServerId;
|
||||
@Transient
|
||||
private String nodeIp;
|
||||
private String runningStatus = "0"; // 1: 运行中 0: 停止
|
||||
|
||||
public void init() {
|
||||
status = "1";
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
@@ -48,6 +63,38 @@ public class CanalInstanceConfig extends Model {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getClusterId() {
|
||||
return clusterId;
|
||||
}
|
||||
|
||||
public void setClusterId(Long clusterId) {
|
||||
this.clusterId = clusterId;
|
||||
}
|
||||
|
||||
public CanalCluster getCanalCluster() {
|
||||
return canalCluster;
|
||||
}
|
||||
|
||||
public void setCanalCluster(CanalCluster canalCluster) {
|
||||
this.canalCluster = canalCluster;
|
||||
}
|
||||
|
||||
public Long getServerId() {
|
||||
return serverId;
|
||||
}
|
||||
|
||||
public void setServerId(Long serverId) {
|
||||
this.serverId = serverId;
|
||||
}
|
||||
|
||||
public NodeServer getNodeServer() {
|
||||
return nodeServer;
|
||||
}
|
||||
|
||||
public void setNodeServer(NodeServer nodeServer) {
|
||||
this.nodeServer = nodeServer;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@@ -64,6 +111,22 @@ public class CanalInstanceConfig extends Model {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getContentMd5() {
|
||||
return contentMd5;
|
||||
}
|
||||
|
||||
public void setContentMd5(String contentMd5) {
|
||||
this.contentMd5 = contentMd5;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Date getModifiedTime() {
|
||||
return modifiedTime;
|
||||
}
|
||||
@@ -72,19 +135,19 @@ public class CanalInstanceConfig extends Model {
|
||||
this.modifiedTime = modifiedTime;
|
||||
}
|
||||
|
||||
public Long getNodeId() {
|
||||
return nodeId;
|
||||
public String getClusterServerId() {
|
||||
return clusterServerId;
|
||||
}
|
||||
|
||||
public void setNodeId(Long nodeId) {
|
||||
this.nodeId = nodeId;
|
||||
public void setClusterServerId(String clusterServerId) {
|
||||
this.clusterServerId = clusterServerId;
|
||||
}
|
||||
|
||||
public String getNodeIp() {
|
||||
return nodeIp;
|
||||
public String getRunningStatus() {
|
||||
return runningStatus;
|
||||
}
|
||||
|
||||
public void setNodeIp(String nodeIp) {
|
||||
this.nodeIp = nodeIp;
|
||||
public void setRunningStatus(String runningStatus) {
|
||||
this.runningStatus = runningStatus;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,14 @@ package com.alibaba.otter.canal.admin.model;
|
||||
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.EbeanServer;
|
||||
import org.apache.commons.beanutils.PropertyUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.apache.commons.beanutils.PropertyUtils;
|
||||
|
||||
/**
|
||||
* EBean Model扩展类
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package com.alibaba.otter.canal.admin.model;
|
||||
|
||||
import io.ebean.Finder;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import io.ebean.Finder;
|
||||
import javax.persistence.*;
|
||||
|
||||
/**
|
||||
* 节点信息实体类
|
||||
@@ -32,16 +30,22 @@ public class NodeServer extends Model {
|
||||
}
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
private String name;
|
||||
private String ip;
|
||||
private Integer port;
|
||||
private Integer port2;
|
||||
private Integer status;
|
||||
private Date modifiedTime;
|
||||
private Long id;
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "cluster_id", updatable = false, insertable = false)
|
||||
private CanalCluster canalCluster;
|
||||
@Column(name = "cluster_id")
|
||||
private Long clusterId;
|
||||
private String name;
|
||||
private String ip;
|
||||
private Integer adminPort;
|
||||
private Integer metricPort;
|
||||
private Integer tcpPort;
|
||||
private String status;
|
||||
private Date modifiedTime;
|
||||
|
||||
public void init() {
|
||||
status = -1;
|
||||
status = "-1";
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
@@ -52,6 +56,22 @@ public class NodeServer extends Model {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public CanalCluster getCanalCluster() {
|
||||
return canalCluster;
|
||||
}
|
||||
|
||||
public void setCanalCluster(CanalCluster canalCluster) {
|
||||
this.canalCluster = canalCluster;
|
||||
}
|
||||
|
||||
public Long getClusterId() {
|
||||
return clusterId;
|
||||
}
|
||||
|
||||
public void setClusterId(Long clusterId) {
|
||||
this.clusterId = clusterId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@@ -68,27 +88,35 @@ public class NodeServer extends Model {
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public Integer getPort() {
|
||||
return port;
|
||||
public Integer getAdminPort() {
|
||||
return adminPort;
|
||||
}
|
||||
|
||||
public void setPort(Integer port) {
|
||||
this.port = port;
|
||||
public void setAdminPort(Integer adminPort) {
|
||||
this.adminPort = adminPort;
|
||||
}
|
||||
|
||||
public Integer getPort2() {
|
||||
return port2;
|
||||
public Integer getMetricPort() {
|
||||
return metricPort;
|
||||
}
|
||||
|
||||
public void setPort2(Integer port2) {
|
||||
this.port2 = port2;
|
||||
public void setMetricPort(Integer metricPort) {
|
||||
this.metricPort = metricPort;
|
||||
}
|
||||
|
||||
public Integer getStatus() {
|
||||
public Integer getTcpPort() {
|
||||
return tcpPort;
|
||||
}
|
||||
|
||||
public void setTcpPort(Integer tcpPort) {
|
||||
this.tcpPort = tcpPort;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Integer status) {
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.alibaba.otter.canal.admin.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Pager<T> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -986577815091763517L;
|
||||
|
||||
private Long count = 0L;
|
||||
private List<T> items = new ArrayList<T>();
|
||||
private Integer page = 1;
|
||||
private Integer size = 20;
|
||||
private Long offset = 0L;
|
||||
|
||||
public Pager(){
|
||||
|
||||
}
|
||||
|
||||
public Pager(Integer page, Integer size){
|
||||
this.page = page;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public Pager(Long count, List<T> items){
|
||||
this.count = count;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "PageResult[count=" + this.count + ", items=" + this.items + "]";
|
||||
}
|
||||
|
||||
public Long getCount() {
|
||||
return this.count;
|
||||
}
|
||||
|
||||
public void setCount(Long count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public List<T> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public void setItems(List<T> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public Integer getPage() {
|
||||
if (page == null) {
|
||||
page = 1;
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
public void setPage(Integer page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
public Integer getSize() {
|
||||
if (size == null) {
|
||||
size = 20;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(Integer size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public Long getOffset() {
|
||||
offset = (long) (getPage() - 1) * (long) getSize();
|
||||
return offset;
|
||||
}
|
||||
|
||||
public void setOffset(Long offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.alibaba.otter.canal.admin.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.otter.canal.admin.model.CanalCluster;
|
||||
|
||||
public interface CanalClusterService {
|
||||
|
||||
void save(CanalCluster canalCluster);
|
||||
|
||||
CanalCluster detail(Long id);
|
||||
|
||||
void update(CanalCluster canalCluster);
|
||||
|
||||
void delete(Long id);
|
||||
|
||||
List<CanalCluster> findList(CanalCluster canalCluster);
|
||||
}
|
||||
@@ -4,7 +4,9 @@ import com.alibaba.otter.canal.admin.model.CanalConfig;
|
||||
|
||||
public interface CanalConfigService {
|
||||
|
||||
CanalConfig getCanalConfig();
|
||||
CanalConfig getCanalConfig(Long clusterId, Long serverId);
|
||||
|
||||
CanalConfig getCanalConfigSummary();
|
||||
|
||||
CanalConfig getAdapterConfig();
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package com.alibaba.otter.canal.admin.service;
|
||||
|
||||
import com.alibaba.otter.canal.admin.model.CanalInstanceConfig;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.otter.canal.admin.model.CanalInstanceConfig;
|
||||
import com.alibaba.otter.canal.admin.model.Pager;
|
||||
|
||||
/**
|
||||
* Canal实例配置信息业务层接口
|
||||
*
|
||||
@@ -13,7 +14,7 @@ import java.util.Map;
|
||||
*/
|
||||
public interface CanalInstanceService {
|
||||
|
||||
List<CanalInstanceConfig> findList(CanalInstanceConfig canalInstanceConfig);
|
||||
Pager<CanalInstanceConfig> findList(CanalInstanceConfig canalInstanceConfig, Pager<CanalInstanceConfig> pager);
|
||||
|
||||
void save(CanalInstanceConfig canalInstanceConfig);
|
||||
|
||||
@@ -26,4 +27,8 @@ public interface CanalInstanceService {
|
||||
Map<String, String> remoteInstanceLog(Long id, Long nodeId);
|
||||
|
||||
boolean remoteOperation(Long id, Long nodeId, String option);
|
||||
|
||||
boolean instanceOperation(Long id, String option);
|
||||
|
||||
List<CanalInstanceConfig> findActiveInstanceByServerId(Long serverId);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.alibaba.otter.canal.admin.service;
|
||||
|
||||
import com.alibaba.otter.canal.admin.model.NodeServer;
|
||||
import com.alibaba.otter.canal.admin.model.Pager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -14,7 +15,9 @@ public interface NodeServerService {
|
||||
|
||||
void delete(Long id);
|
||||
|
||||
List<NodeServer> findList(NodeServer nodeServer);
|
||||
List<NodeServer> findAll(NodeServer nodeServer);
|
||||
|
||||
Pager<NodeServer> findList(NodeServer nodeServer, Pager<NodeServer> pager);
|
||||
|
||||
int remoteNodeStatus(String ip, Integer port);
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.alibaba.otter.canal.admin.service;
|
||||
|
||||
import com.alibaba.otter.canal.admin.model.CanalConfig;
|
||||
import com.alibaba.otter.canal.admin.model.CanalInstanceConfig;
|
||||
|
||||
public interface PollingConfigService {
|
||||
|
||||
public boolean autoRegister(String ip, Integer adminPort, String cluster);
|
||||
|
||||
CanalConfig getChangedConfig(String ip, Integer port, String md5);
|
||||
|
||||
CanalInstanceConfig getInstancesConfig(String ip, Integer port, String md5);
|
||||
|
||||
CanalInstanceConfig getInstanceConfig(String destination, String md5);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.alibaba.otter.canal.admin.service.impl;
|
||||
|
||||
import io.ebean.Query;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
|
||||
import com.alibaba.otter.canal.admin.model.CanalCluster;
|
||||
import com.alibaba.otter.canal.admin.model.CanalInstanceConfig;
|
||||
import com.alibaba.otter.canal.admin.model.NodeServer;
|
||||
import com.alibaba.otter.canal.admin.service.CanalClusterService;
|
||||
|
||||
@Service
|
||||
public class CanalClusterServiceImpl implements CanalClusterService {
|
||||
|
||||
public void save(CanalCluster canalCluster) {
|
||||
canalCluster.save();
|
||||
}
|
||||
|
||||
public CanalCluster detail(Long id) {
|
||||
return CanalCluster.find.byId(id);
|
||||
}
|
||||
|
||||
public void update(CanalCluster canalCluster) {
|
||||
canalCluster.update("name", "zkHosts");
|
||||
}
|
||||
|
||||
public void delete(Long id) {
|
||||
// 判断集群下是否存在server信息
|
||||
int serverCnt = NodeServer.find.query().where().eq("clusterId", id).findCount();
|
||||
if (serverCnt > 0) {
|
||||
throw new ServiceException("当前集群下存在Server, 无法删除");
|
||||
}
|
||||
|
||||
// 判断集群下是否存在instance信息
|
||||
int instanceCnt = CanalInstanceConfig.find.query().where().eq("clusterId", id).findCount();
|
||||
if (instanceCnt > 0) {
|
||||
throw new ServiceException("当前集群下存在Instance配置,无法删除");
|
||||
}
|
||||
|
||||
CanalCluster canalCluster = CanalCluster.find.byId(id);
|
||||
if (canalCluster != null) {
|
||||
canalCluster.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public List<CanalCluster> findList(CanalCluster canalCluster) {
|
||||
Query<CanalCluster> query = CanalCluster.find.query();
|
||||
query.order().asc("id");
|
||||
return query.findList();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,21 @@
|
||||
package com.alibaba.otter.canal.admin.service.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
|
||||
import com.alibaba.otter.canal.admin.model.CanalConfig;
|
||||
import com.alibaba.otter.canal.admin.model.NodeServer;
|
||||
import com.alibaba.otter.canal.admin.service.CanalConfigService;
|
||||
import com.alibaba.otter.canal.protocol.SecurityUtil;
|
||||
|
||||
/**
|
||||
* Canal配置信息业务层
|
||||
@@ -14,15 +26,101 @@ import com.alibaba.otter.canal.admin.service.CanalConfigService;
|
||||
@Service
|
||||
public class CanalConfigServiceImpl implements CanalConfigService {
|
||||
|
||||
public CanalConfig getCanalConfig() {
|
||||
return CanalConfig.find.byId(1L);
|
||||
private Logger logger = LoggerFactory.getLogger(CanalConfigServiceImpl.class);
|
||||
|
||||
private static final String CANAL_GLOBAL_CONFIG = "canal.properties";
|
||||
private static final String CANAL_ADAPTER_CONFIG = "application.yml";
|
||||
|
||||
public CanalConfig getCanalConfig(Long clusterId, Long serverId) {
|
||||
CanalConfig config = null;
|
||||
if (clusterId != null && clusterId != 0) {
|
||||
config = CanalConfig.find.query().where().eq("clusterId", clusterId).findOne();
|
||||
} else if (serverId != null && serverId != 0) {
|
||||
config = CanalConfig.find.query().where().eq("serverId", serverId).findOne();
|
||||
if (config == null) {
|
||||
NodeServer nodeServer = NodeServer.find.byId(serverId);
|
||||
if (nodeServer != null) {
|
||||
Long cid = nodeServer.getClusterId();
|
||||
if (cid != null) {
|
||||
config = CanalConfig.find.query().where().eq("clusterId", cid).findOne();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new ServiceException("clusterId and serverId are all empty");
|
||||
}
|
||||
if (config == null) {
|
||||
config = new CanalConfig();
|
||||
config.setName(CANAL_GLOBAL_CONFIG);
|
||||
return config;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
public CanalConfig getCanalConfigSummary() {
|
||||
return CanalConfig.find.query()
|
||||
.setDisableLazyLoading(true)
|
||||
.select("name, modifiedTime")
|
||||
.where()
|
||||
.eq("id", 1L)
|
||||
.findOne();
|
||||
}
|
||||
|
||||
public CanalConfig getAdapterConfig() {
|
||||
return CanalConfig.find.byId(2L);
|
||||
long id = 2L;
|
||||
CanalConfig config = CanalConfig.find.byId(id);
|
||||
if (config == null) {
|
||||
String context = loadDefaultConf(CANAL_ADAPTER_CONFIG);
|
||||
if (context == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
config = new CanalConfig();
|
||||
config.setId(id);
|
||||
config.setName(CANAL_ADAPTER_CONFIG);
|
||||
config.setModifiedTime(new Date());
|
||||
config.setContent(context);
|
||||
return config;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
public void updateContent(CanalConfig canalConfig) {
|
||||
canalConfig.update("content");
|
||||
try {
|
||||
String contentMd5 = SecurityUtil.md5String(canalConfig.getContent());
|
||||
canalConfig.setContentMd5(contentMd5);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// ignore
|
||||
}
|
||||
if (canalConfig.getId() != null) {
|
||||
CanalConfig canalConfigTmp = CanalConfig.find.byId(canalConfig.getId());
|
||||
if (canalConfigTmp != null && canalConfigTmp.getClusterId() != null) {
|
||||
canalConfig.setServerId(null);
|
||||
}
|
||||
canalConfig.update("serverId", "content", "contentMd5");
|
||||
} else {
|
||||
canalConfig.save();
|
||||
}
|
||||
}
|
||||
|
||||
private String loadDefaultConf(String confFileName) {
|
||||
InputStream input = null;
|
||||
try {
|
||||
input = Thread.currentThread().getContextClassLoader().getResourceAsStream("conf/" + confFileName);
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return StringUtils.join(IOUtils.readLines(input), "\n");
|
||||
} catch (IOException e) {
|
||||
logger.error("find " + confFileName + " is error!", e);
|
||||
return null;
|
||||
} finally {
|
||||
if (input != null) {
|
||||
IOUtils.closeQuietly(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,32 @@
|
||||
package com.alibaba.otter.canal.admin.service.impl;
|
||||
|
||||
import io.ebean.Query;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.alibaba.otter.canal.admin.jmx.CanalServerMXBean;
|
||||
import com.alibaba.otter.canal.admin.jmx.JMXConnection;
|
||||
import com.alibaba.otter.canal.admin.common.Threads;
|
||||
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
|
||||
import com.alibaba.otter.canal.admin.connector.AdminConnector;
|
||||
import com.alibaba.otter.canal.admin.connector.SimpleAdminConnectors;
|
||||
import com.alibaba.otter.canal.admin.model.CanalInstanceConfig;
|
||||
import com.alibaba.otter.canal.admin.model.NodeServer;
|
||||
import com.alibaba.otter.canal.admin.model.Pager;
|
||||
import com.alibaba.otter.canal.admin.service.CanalInstanceService;
|
||||
|
||||
import io.ebean.Query;
|
||||
import com.alibaba.otter.canal.protocol.SecurityUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
* Canal实例配置信息业务层
|
||||
@@ -24,51 +37,193 @@ import io.ebean.Query;
|
||||
@Service
|
||||
public class CanalInstanceServiceImpl implements CanalInstanceService {
|
||||
|
||||
public List<CanalInstanceConfig> findList(CanalInstanceConfig canalInstanceConfig) {
|
||||
public Pager<CanalInstanceConfig> findList(CanalInstanceConfig canalInstanceConfig, Pager<CanalInstanceConfig> pager) {
|
||||
Query<CanalInstanceConfig> query = CanalInstanceConfig.find.query()
|
||||
.setDisableLazyLoading(true)
|
||||
.select("name, modifiedTime");
|
||||
.select("clusterId, serverId, name, modifiedTime")
|
||||
.fetch("canalCluster", "name")
|
||||
.fetch("nodeServer", "name,ip,adminPort");
|
||||
if (canalInstanceConfig != null) {
|
||||
if (StringUtils.isNotEmpty(canalInstanceConfig.getName())) {
|
||||
query.where().like("name", "%" + canalInstanceConfig.getName() + "%");
|
||||
}
|
||||
}
|
||||
query.order().asc("id");
|
||||
List<CanalInstanceConfig> canalInstanceConfigs = query.findList();
|
||||
|
||||
// check all canal instances running status
|
||||
List<NodeServer> nodeServers = NodeServer.find.query().findList();
|
||||
for (NodeServer nodeServer : nodeServers) {
|
||||
String runningInstances = JMXConnection
|
||||
.execute(nodeServer.getIp(), nodeServer.getPort(), CanalServerMXBean::getRunningInstances);
|
||||
if (runningInstances == null) {
|
||||
continue;
|
||||
}
|
||||
String[] instances = runningInstances.split(",");
|
||||
for (String instance : instances) {
|
||||
for (CanalInstanceConfig cig : canalInstanceConfigs) {
|
||||
if (instance.equals(cig.getName())) {
|
||||
cig.setNodeId(nodeServer.getId());
|
||||
cig.setNodeIp(nodeServer.getIp());
|
||||
break;
|
||||
}
|
||||
if (StringUtils.isNotEmpty(canalInstanceConfig.getClusterServerId())) {
|
||||
if (canalInstanceConfig.getClusterServerId().startsWith("cluster:")) {
|
||||
query.where()
|
||||
.eq("clusterId", Long.parseLong(canalInstanceConfig.getClusterServerId().substring(8)));
|
||||
} else if (canalInstanceConfig.getClusterServerId().startsWith("server:")) {
|
||||
query.where().eq("serverId", Long.parseLong(canalInstanceConfig.getClusterServerId().substring(7)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return canalInstanceConfigs;
|
||||
Query<CanalInstanceConfig> queryCnt = query.copy();
|
||||
int count = queryCnt.findCount();
|
||||
pager.setCount((long) count);
|
||||
|
||||
query.setFirstRow(pager.getOffset().intValue()).setMaxRows(pager.getSize()).order().asc("id");
|
||||
List<CanalInstanceConfig> canalInstanceConfigs = query.findList();
|
||||
pager.setItems(canalInstanceConfigs);
|
||||
|
||||
if (canalInstanceConfigs.isEmpty()) {
|
||||
return pager;
|
||||
}
|
||||
|
||||
// check all canal instances running status
|
||||
List<Future<Void>> futures = new ArrayList<>(canalInstanceConfigs.size());
|
||||
for (CanalInstanceConfig canalInstanceConfig1 : canalInstanceConfigs) {
|
||||
futures.add(Threads.executorService.submit(() -> {
|
||||
List<NodeServer> nodeServers;
|
||||
if (canalInstanceConfig1.getClusterId() != null) { // 集群模式
|
||||
nodeServers = NodeServer.find.query()
|
||||
.where()
|
||||
.eq("clusterId", canalInstanceConfig1.getClusterId())
|
||||
.findList();
|
||||
} else if (canalInstanceConfig1.getServerId() != null) { // 单机模式
|
||||
nodeServers = Collections.singletonList(canalInstanceConfig1.getNodeServer());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (NodeServer nodeServer : nodeServers) {
|
||||
String runningInstances = SimpleAdminConnectors.execute(nodeServer.getIp(),
|
||||
nodeServer.getAdminPort(),
|
||||
AdminConnector::getRunningInstances);
|
||||
if (runningInstances == null) {
|
||||
continue;
|
||||
}
|
||||
String[] instances = runningInstances.split(",");
|
||||
for (String instance : instances) {
|
||||
if (instance.equals(canalInstanceConfig1.getName())) {
|
||||
// 集群模式下server对象为空
|
||||
if (canalInstanceConfig1.getNodeServer() == null) {
|
||||
canalInstanceConfig1.setNodeServer(nodeServer);
|
||||
}
|
||||
canalInstanceConfig1.setRunningStatus("1");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}));
|
||||
}
|
||||
|
||||
futures.forEach(f -> {
|
||||
try {
|
||||
f.get(3, TimeUnit.SECONDS);
|
||||
} catch (TimeoutException | InterruptedException | ExecutionException e) {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
return pager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过Server id获取当前Server下所有运行的Instance
|
||||
*
|
||||
* @param serverId server id
|
||||
*/
|
||||
public List<CanalInstanceConfig> findActiveInstanceByServerId(Long serverId) {
|
||||
NodeServer nodeServer = NodeServer.find.byId(serverId);
|
||||
if (nodeServer == null) {
|
||||
return null;
|
||||
}
|
||||
String runningInstances = SimpleAdminConnectors.execute(nodeServer.getIp(),
|
||||
nodeServer.getAdminPort(),
|
||||
AdminConnector::getRunningInstances);
|
||||
if (runningInstances == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] instances = runningInstances.split(",");
|
||||
Object obj[] = Lists.newArrayList(instances).toArray();
|
||||
// 单机模式和集群模式区分处理
|
||||
if (nodeServer.getClusterId() != null) { // 集群模式
|
||||
List<CanalInstanceConfig> list = CanalInstanceConfig.find.query()
|
||||
.setDisableLazyLoading(true)
|
||||
.select("clusterId, serverId, name, modifiedTime")
|
||||
.where()
|
||||
// 暂停的实例也显示 .eq("status", "1")
|
||||
.in("name", obj)
|
||||
.findList();
|
||||
list.forEach(config -> config.setRunningStatus("1"));
|
||||
return list; // 集群模式直接返回当前运行的Instances
|
||||
} else { // 单机模式
|
||||
// 当前Server所配置的所有Instance
|
||||
List<CanalInstanceConfig> list = CanalInstanceConfig.find.query()
|
||||
.setDisableLazyLoading(true)
|
||||
.select("clusterId, serverId, name, modifiedTime")
|
||||
.where()
|
||||
// 暂停的实例也显示 .eq("status", "1")
|
||||
.eq("serverId", serverId)
|
||||
.findList();
|
||||
List<String> instanceList = Arrays.asList(instances);
|
||||
list.forEach(config -> {
|
||||
if (instanceList.contains(config.getName())) {
|
||||
config.setRunningStatus("1");
|
||||
}
|
||||
});
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
public void save(CanalInstanceConfig canalInstanceConfig) {
|
||||
if (StringUtils.isEmpty(canalInstanceConfig.getClusterServerId())) {
|
||||
throw new ServiceException("empty cluster or server id");
|
||||
}
|
||||
if (canalInstanceConfig.getClusterServerId().startsWith("cluster:")) {
|
||||
Long clusterId = Long.parseLong(canalInstanceConfig.getClusterServerId().substring(8));
|
||||
canalInstanceConfig.setClusterId(clusterId);
|
||||
} else if (canalInstanceConfig.getClusterServerId().startsWith("server:")) {
|
||||
Long serverId = Long.parseLong(canalInstanceConfig.getClusterServerId().substring(7));
|
||||
canalInstanceConfig.setServerId(serverId);
|
||||
}
|
||||
|
||||
try {
|
||||
String contentMd5 = SecurityUtil.md5String(canalInstanceConfig.getContent());
|
||||
canalInstanceConfig.setContentMd5(contentMd5);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
canalInstanceConfig.insert();
|
||||
}
|
||||
|
||||
public CanalInstanceConfig detail(Long id) {
|
||||
return CanalInstanceConfig.find.byId(id);
|
||||
CanalInstanceConfig canalInstanceConfig = CanalInstanceConfig.find.byId(id);
|
||||
if (canalInstanceConfig != null) {
|
||||
if (canalInstanceConfig.getClusterId() != null) {
|
||||
canalInstanceConfig.setClusterServerId("cluster:" + canalInstanceConfig.getClusterId());
|
||||
} else if (canalInstanceConfig.getServerId() != null) {
|
||||
canalInstanceConfig.setClusterServerId("server:" + canalInstanceConfig.getServerId());
|
||||
}
|
||||
}
|
||||
return canalInstanceConfig;
|
||||
}
|
||||
|
||||
public void updateContent(CanalInstanceConfig canalInstanceConfig) {
|
||||
canalInstanceConfig.update("content");
|
||||
if (StringUtils.isEmpty(canalInstanceConfig.getClusterServerId())) {
|
||||
throw new ServiceException("empty cluster or server id");
|
||||
}
|
||||
if (canalInstanceConfig.getClusterServerId().startsWith("cluster:")) {
|
||||
Long clusterId = Long.parseLong(canalInstanceConfig.getClusterServerId().substring(8));
|
||||
canalInstanceConfig.setClusterId(clusterId);
|
||||
canalInstanceConfig.setServerId(null);
|
||||
} else if (canalInstanceConfig.getClusterServerId().startsWith("server:")) {
|
||||
Long serverId = Long.parseLong(canalInstanceConfig.getClusterServerId().substring(7));
|
||||
canalInstanceConfig.setServerId(serverId);
|
||||
canalInstanceConfig.setClusterId(null);
|
||||
}
|
||||
|
||||
try {
|
||||
String contentMd5 = SecurityUtil.md5String(canalInstanceConfig.getContent());
|
||||
canalInstanceConfig.setContentMd5(contentMd5);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
canalInstanceConfig.update("content", "contentMd5", "clusterId", "serverId");
|
||||
}
|
||||
|
||||
public void delete(Long id) {
|
||||
@@ -90,9 +245,9 @@ public class CanalInstanceServiceImpl implements CanalInstanceService {
|
||||
return result;
|
||||
}
|
||||
|
||||
String log = JMXConnection.execute(nodeServer.getIp(),
|
||||
nodeServer.getPort(),
|
||||
canalServerMXBean -> canalServerMXBean.instanceLog(canalInstanceConfig.getName()));
|
||||
String log = SimpleAdminConnectors.execute(nodeServer.getIp(),
|
||||
nodeServer.getAdminPort(),
|
||||
adminConnector -> adminConnector.instanceLog(canalInstanceConfig.getName(), null, 100));
|
||||
|
||||
result.put("instance", canalInstanceConfig.getName());
|
||||
result.put("log", log);
|
||||
@@ -120,22 +275,46 @@ public class CanalInstanceServiceImpl implements CanalInstanceService {
|
||||
if (canalInstanceConfig == null) {
|
||||
return false;
|
||||
}
|
||||
Boolean resutl = null;
|
||||
Boolean result = null;
|
||||
if ("start".equals(option)) {
|
||||
resutl = JMXConnection.execute(nodeServer.getIp(),
|
||||
nodeServer.getPort(),
|
||||
canalServerMXBean -> canalServerMXBean.startInstance(canalInstanceConfig.getName()));
|
||||
if (nodeServer.getClusterId() == null) { // 非集群模式
|
||||
return instanceOperation(id, "start");
|
||||
} else {
|
||||
throw new ServiceException("集群模式不允许指定server启动");
|
||||
}
|
||||
} else if ("stop".equals(option)) {
|
||||
resutl = JMXConnection.execute(nodeServer.getIp(),
|
||||
nodeServer.getPort(),
|
||||
canalServerMXBean -> canalServerMXBean.stopInstance(canalInstanceConfig.getName()));
|
||||
if (nodeServer.getClusterId() != null) {
|
||||
// 集群模式,通知主动释放
|
||||
result = SimpleAdminConnectors.execute(nodeServer.getIp(),
|
||||
nodeServer.getAdminPort(),
|
||||
adminConnector -> adminConnector.releaseInstance(canalInstanceConfig.getName()));
|
||||
} else { // 非集群模式下直接将状态置为0
|
||||
return instanceOperation(id, "stop");
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resutl == null) {
|
||||
resutl = false;
|
||||
if (result == null) {
|
||||
result = false;
|
||||
}
|
||||
return resutl;
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean instanceOperation(Long id, String option) {
|
||||
CanalInstanceConfig canalInstanceConfig = CanalInstanceConfig.find.byId(id);
|
||||
if (canalInstanceConfig == null) {
|
||||
return false;
|
||||
}
|
||||
if ("stop".equals(option)) {
|
||||
canalInstanceConfig.setStatus("0");
|
||||
canalInstanceConfig.update("status");
|
||||
} else if ("start".equals(option)) {
|
||||
canalInstanceConfig.setStatus("1");
|
||||
canalInstanceConfig.update("status");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,29 @@
|
||||
package com.alibaba.otter.canal.admin.service.impl;
|
||||
|
||||
import io.ebean.Query;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.alibaba.otter.canal.admin.common.TemplateConfigLoader;
|
||||
import com.alibaba.otter.canal.admin.common.Threads;
|
||||
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
|
||||
import com.alibaba.otter.canal.admin.jmx.CanalServerMXBean;
|
||||
import com.alibaba.otter.canal.admin.jmx.JMXConnection;
|
||||
import com.alibaba.otter.canal.admin.connector.AdminConnector;
|
||||
import com.alibaba.otter.canal.admin.connector.SimpleAdminConnectors;
|
||||
import com.alibaba.otter.canal.admin.model.CanalConfig;
|
||||
import com.alibaba.otter.canal.admin.model.CanalInstanceConfig;
|
||||
import com.alibaba.otter.canal.admin.model.NodeServer;
|
||||
import com.alibaba.otter.canal.admin.model.Pager;
|
||||
import com.alibaba.otter.canal.admin.service.NodeServerService;
|
||||
|
||||
import io.ebean.Query;
|
||||
import com.alibaba.otter.canal.protocol.SecurityUtil;
|
||||
|
||||
/**
|
||||
* 节点信息业务层
|
||||
@@ -29,19 +34,30 @@ import io.ebean.Query;
|
||||
@Service
|
||||
public class NodeServerServiceImpl implements NodeServerService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(NodeServerServiceImpl.class);
|
||||
|
||||
public void save(NodeServer nodeServer) {
|
||||
int cnt = NodeServer.find.query()
|
||||
.where()
|
||||
.eq("ip", nodeServer.getIp())
|
||||
.eq("port", nodeServer.getPort())
|
||||
.eq("adminPort", nodeServer.getAdminPort())
|
||||
.findCount();
|
||||
if (cnt > 0) {
|
||||
throw new ServiceException("节点信息已存在");
|
||||
}
|
||||
|
||||
nodeServer.save();
|
||||
|
||||
if (nodeServer.getClusterId() == null) { // 单机模式
|
||||
CanalConfig canalConfig = new CanalConfig();
|
||||
canalConfig.setServerId(nodeServer.getId());
|
||||
String configTmp = TemplateConfigLoader.loadCanalConfig();
|
||||
canalConfig.setContent(configTmp);
|
||||
try {
|
||||
String contentMd5 = SecurityUtil.md5String(canalConfig.getContent());
|
||||
canalConfig.setContentMd5(contentMd5);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
}
|
||||
canalConfig.save();
|
||||
}
|
||||
}
|
||||
|
||||
public NodeServer detail(Long id) {
|
||||
@@ -52,25 +68,39 @@ public class NodeServerServiceImpl implements NodeServerService {
|
||||
int cnt = NodeServer.find.query()
|
||||
.where()
|
||||
.eq("ip", nodeServer.getIp())
|
||||
.eq("port", nodeServer.getPort())
|
||||
.eq("adminPort", nodeServer.getAdminPort())
|
||||
.ne("id", nodeServer.getId())
|
||||
.findCount();
|
||||
if (cnt > 0) {
|
||||
throw new ServiceException("节点信息已存在");
|
||||
}
|
||||
|
||||
nodeServer.update("name", "ip", "port", "port2");
|
||||
nodeServer.update("name", "ip", "adminPort", "tcpPort", "metricPort", "clusterId");
|
||||
}
|
||||
|
||||
public void delete(Long id) {
|
||||
NodeServer nodeServer = NodeServer.find.byId(id);
|
||||
if (nodeServer != null) {
|
||||
// 判断是否存在实例
|
||||
int cnt = CanalInstanceConfig.find.query().where().eq("serverId", nodeServer.getId()).findCount();
|
||||
if (cnt > 0) {
|
||||
throw new ServiceException("当前Server下存在Instance配置, 无法删除");
|
||||
}
|
||||
|
||||
// 同时删除配置
|
||||
CanalConfig canalConfig = CanalConfig.find.query().where().eq("serverId", id).findOne();
|
||||
if (canalConfig != null) {
|
||||
canalConfig.delete();
|
||||
}
|
||||
|
||||
nodeServer.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public List<NodeServer> findList(NodeServer nodeServer) {
|
||||
private Query<NodeServer> getBaseQuery(NodeServer nodeServer) {
|
||||
Query<NodeServer> query = NodeServer.find.query();
|
||||
query.fetch("canalCluster", "name").setDisableLazyLoading(true);
|
||||
|
||||
if (nodeServer != null) {
|
||||
if (StringUtils.isNotEmpty(nodeServer.getName())) {
|
||||
query.where().like("name", "%" + nodeServer.getName() + "%");
|
||||
@@ -78,51 +108,66 @@ public class NodeServerServiceImpl implements NodeServerService {
|
||||
if (StringUtils.isNotEmpty(nodeServer.getIp())) {
|
||||
query.where().eq("ip", nodeServer.getIp());
|
||||
}
|
||||
}
|
||||
query.order().asc("id");
|
||||
List<NodeServer> nodeServers = query.findList();
|
||||
if (nodeServers.isEmpty()) {
|
||||
return nodeServers;
|
||||
if (nodeServer.getClusterId() != null) {
|
||||
if (nodeServer.getClusterId() == -1) {
|
||||
query.where().isNull("clusterId");
|
||||
} else {
|
||||
query.where().eq("clusterId", nodeServer.getClusterId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
public List<NodeServer> findAll(NodeServer nodeServer) {
|
||||
Query<NodeServer> query = getBaseQuery(nodeServer);
|
||||
query.order().asc("id");
|
||||
return query.findList();
|
||||
}
|
||||
|
||||
public Pager<NodeServer> findList(NodeServer nodeServer, Pager<NodeServer> pager) {
|
||||
|
||||
Query<NodeServer> query = getBaseQuery(nodeServer);
|
||||
Query<NodeServer> queryCnt = query.copy();
|
||||
|
||||
int count = queryCnt.findCount();
|
||||
pager.setCount((long) count);
|
||||
|
||||
List<NodeServer> nodeServers = query.order()
|
||||
.asc("id")
|
||||
.setFirstRow(pager.getOffset().intValue())
|
||||
.setMaxRows(pager.getSize())
|
||||
.findList();
|
||||
pager.setItems(nodeServers);
|
||||
|
||||
if (nodeServers.isEmpty()) {
|
||||
return pager;
|
||||
}
|
||||
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(nodeServers.size());
|
||||
List<Future<Boolean>> futures = new ArrayList<>(nodeServers.size());
|
||||
// get all nodes status
|
||||
for (NodeServer ns : nodeServers) {
|
||||
futures.add(executorService.submit(() -> {
|
||||
int status = -1;
|
||||
JMXConnection jmxConnection = new JMXConnection(ns.getIp(), ns.getPort());
|
||||
try {
|
||||
CanalServerMXBean canalServerMXBean = jmxConnection.getCanalServerMXBean();
|
||||
status = canalServerMXBean.getStatus();
|
||||
} catch (Exception e) {
|
||||
logger.warn(e.getMessage());
|
||||
} finally {
|
||||
jmxConnection.close();
|
||||
}
|
||||
ns.setStatus(status);
|
||||
return status != -1;
|
||||
futures.add(Threads.executorService.submit(() -> {
|
||||
boolean status = SimpleAdminConnectors.execute(ns.getIp(), ns.getAdminPort(), AdminConnector::check);
|
||||
ns.setStatus(status ? "1" : "0");
|
||||
return !status;
|
||||
}));
|
||||
}
|
||||
futures.forEach(f -> {
|
||||
try {
|
||||
f.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
f.get(3, TimeUnit.SECONDS);
|
||||
} catch (TimeoutException | InterruptedException | ExecutionException e) {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
|
||||
executorService.shutdownNow();
|
||||
|
||||
return nodeServers;
|
||||
return pager;
|
||||
}
|
||||
|
||||
public int remoteNodeStatus(String ip, Integer port) {
|
||||
Integer resutl = JMXConnection.execute(ip, port, CanalServerMXBean::getStatus);
|
||||
if (resutl == null) {
|
||||
resutl = -1;
|
||||
}
|
||||
return resutl;
|
||||
boolean result = SimpleAdminConnectors.execute(ip, port, AdminConnector::check);
|
||||
return result ? 1 : 0;
|
||||
}
|
||||
|
||||
public String remoteCanalLog(Long id) {
|
||||
@@ -130,7 +175,9 @@ public class NodeServerServiceImpl implements NodeServerService {
|
||||
if (nodeServer == null) {
|
||||
return "";
|
||||
}
|
||||
return JMXConnection.execute(nodeServer.getIp(), nodeServer.getPort(), CanalServerMXBean::canalLog);
|
||||
return SimpleAdminConnectors.execute(nodeServer.getIp(),
|
||||
nodeServer.getAdminPort(),
|
||||
adminConnector -> adminConnector.canalLog(100));
|
||||
}
|
||||
|
||||
public boolean remoteOperation(Long id, String option) {
|
||||
@@ -138,18 +185,18 @@ public class NodeServerServiceImpl implements NodeServerService {
|
||||
if (nodeServer == null) {
|
||||
return false;
|
||||
}
|
||||
Boolean resutl = null;
|
||||
Boolean result = null;
|
||||
if ("start".equals(option)) {
|
||||
resutl = JMXConnection.execute(nodeServer.getIp(), nodeServer.getPort(), CanalServerMXBean::start);
|
||||
result = SimpleAdminConnectors.execute(nodeServer.getIp(), nodeServer.getAdminPort(), AdminConnector::start);
|
||||
} else if ("stop".equals(option)) {
|
||||
resutl = JMXConnection.execute(nodeServer.getIp(), nodeServer.getPort(), CanalServerMXBean::stop);
|
||||
result = SimpleAdminConnectors.execute(nodeServer.getIp(), nodeServer.getAdminPort(), AdminConnector::stop);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resutl == null) {
|
||||
resutl = false;
|
||||
if (result == null) {
|
||||
result = false;
|
||||
}
|
||||
return resutl;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.alibaba.otter.canal.admin.service.impl;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
|
||||
import com.alibaba.otter.canal.admin.model.CanalCluster;
|
||||
import com.alibaba.otter.canal.admin.model.CanalConfig;
|
||||
import com.alibaba.otter.canal.admin.model.CanalInstanceConfig;
|
||||
import com.alibaba.otter.canal.admin.model.NodeServer;
|
||||
import com.alibaba.otter.canal.admin.service.CanalClusterService;
|
||||
import com.alibaba.otter.canal.admin.service.NodeServerService;
|
||||
import com.alibaba.otter.canal.admin.service.PollingConfigService;
|
||||
import com.alibaba.otter.canal.protocol.SecurityUtil;
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
@Service
|
||||
public class PollingConfigServiceImpl implements PollingConfigService {
|
||||
|
||||
@Autowired
|
||||
NodeServerService nodeServerService;
|
||||
|
||||
@Autowired
|
||||
CanalClusterService canalClusterService;
|
||||
|
||||
public boolean autoRegister(String ip, Integer adminPort, String cluster) {
|
||||
NodeServer server = NodeServer.find.query().where().eq("ip", ip).eq("adminPort", adminPort).findOne();
|
||||
if (server == null) {
|
||||
server = new NodeServer();
|
||||
server.setName(ip);
|
||||
server.setIp(ip);
|
||||
server.setAdminPort(adminPort);
|
||||
server.setTcpPort(adminPort + 1);
|
||||
server.setMetricPort(adminPort + 2);
|
||||
if (StringUtils.isNotEmpty(cluster)) {
|
||||
CanalCluster clusterConfig = CanalCluster.find.query().where().eq("name", cluster).findOne();
|
||||
if (clusterConfig == null) {
|
||||
throw new ServiceException("auto cluster : " + cluster + " is not found.");
|
||||
}
|
||||
|
||||
server.setClusterId(clusterConfig.getId());
|
||||
}
|
||||
nodeServerService.save(server);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public CanalConfig getChangedConfig(String ip, Integer port, String md5) {
|
||||
NodeServer server = NodeServer.find.query().where().eq("ip", ip).eq("adminPort", port).findOne();
|
||||
if (server == null) {
|
||||
return null;
|
||||
}
|
||||
CanalConfig canalConfig;
|
||||
if (server.getClusterId() != null) { // 集群模式
|
||||
canalConfig = CanalConfig.find.query().where().eq("clusterId", server.getClusterId()).findOne();
|
||||
} else { // 单机模式
|
||||
canalConfig = CanalConfig.find.query().where().eq("serverId", server.getId()).findOne();
|
||||
}
|
||||
if (canalConfig == null) {
|
||||
throw new ServiceException("canal.properties config is empty");
|
||||
}
|
||||
|
||||
if (!canalConfig.getContentMd5().equals(md5)) { // 内容发生变化
|
||||
return canalConfig;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public CanalInstanceConfig getInstancesConfig(String ip, Integer port, String md5) {
|
||||
NodeServer server = NodeServer.find.query().where().eq("ip", ip).eq("adminPort", port).findOne();
|
||||
if (server == null) {
|
||||
return null;
|
||||
}
|
||||
List<CanalInstanceConfig> canalInstanceConfigs;
|
||||
if (server.getClusterId() != null) { // 集群模式
|
||||
canalInstanceConfigs = CanalInstanceConfig.find.query()
|
||||
.where()
|
||||
.eq("status", "1")
|
||||
.eq("clusterId", server.getClusterId())
|
||||
.findList(); // 取属于该集群的所有instance config
|
||||
} else { // 单机模式
|
||||
canalInstanceConfigs = CanalInstanceConfig.find.query()
|
||||
.where()
|
||||
.eq("status", "1")
|
||||
.eq("serverId", server.getId())
|
||||
.findList();
|
||||
}
|
||||
|
||||
CanalInstanceConfig canalInstanceConfig = new CanalInstanceConfig();
|
||||
List<String> instances = canalInstanceConfigs.stream()
|
||||
.map(CanalInstanceConfig::getName)
|
||||
.collect(Collectors.toList());
|
||||
String data = Joiner.on(',').join(instances);
|
||||
canalInstanceConfig.setContent(data);
|
||||
if (!StringUtils.isEmpty(md5)) {
|
||||
try {
|
||||
String newMd5 = SecurityUtil.md5String(canalInstanceConfig.getContent());
|
||||
if (StringUtils.equals(md5, newMd5)) {
|
||||
canalInstanceConfig.setContent(null);
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return canalInstanceConfig;
|
||||
}
|
||||
|
||||
public CanalInstanceConfig getInstanceConfig(String destination, String md5) {
|
||||
CanalInstanceConfig instanceConfig = CanalInstanceConfig.find.query().where().eq("name", destination).findOne();
|
||||
if (instanceConfig == null) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isEmpty(md5)) {
|
||||
return instanceConfig;
|
||||
} else {
|
||||
try {
|
||||
String newMd5 = SecurityUtil.md5String(instanceConfig.getContent());
|
||||
if (StringUtils.equals(md5, newMd5)) {
|
||||
instanceConfig.setContent(null);
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
return instanceConfig;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
package com.alibaba.otter.canal.admin.service.impl;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
|
||||
import com.alibaba.otter.canal.admin.model.User;
|
||||
import com.alibaba.otter.canal.admin.service.UserService;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.alibaba.otter.canal.protocol.SecurityUtil;
|
||||
|
||||
/**
|
||||
* 用户信息业务层
|
||||
@@ -15,26 +19,47 @@ import org.springframework.stereotype.Service;
|
||||
@Service
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
private static byte[] seeds = "canal is best!".getBytes();
|
||||
|
||||
public User find4Login(String username, String password) {
|
||||
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
|
||||
return null;
|
||||
}
|
||||
User user = User.find.query().where().eq("username", username).eq("password", password).findOne();
|
||||
if (user != null) {
|
||||
user.setPassword("");
|
||||
User user = User.find.query().where().eq("username", username).findOne();
|
||||
if (user == null) {
|
||||
throw new ServiceException("user:" + username + " auth failed!");
|
||||
}
|
||||
try {
|
||||
byte[] pass = SecurityUtil.scramble411(password.getBytes(), seeds);
|
||||
if (!SecurityUtil.scrambleServerAuth(pass, SecurityUtil.hexStr2Bytes(user.getPassword()), seeds)) {
|
||||
throw new ServiceException("user:" + user.getName() + " passwd incorrect!");
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new ServiceException("user:" + user.getName() + " auth failed!");
|
||||
}
|
||||
|
||||
user.setPassword("");
|
||||
return user;
|
||||
}
|
||||
|
||||
public void update(User user) {
|
||||
User userTmp = User.find.byId(1L);
|
||||
User userTmp = User.find.query().where().eq("username", user.getUsername()).findOne();
|
||||
if (userTmp == null) {
|
||||
throw new ServiceException();
|
||||
}
|
||||
if (!userTmp.getPassword().equals(user.getOldPassword())) {
|
||||
throw new ServiceException("错误的旧密码");
|
||||
|
||||
try {
|
||||
byte[] pass = SecurityUtil.scramble411(user.getOldPassword().getBytes(), seeds);
|
||||
if (!SecurityUtil.scrambleServerAuth(pass, SecurityUtil.hexStr2Bytes(userTmp.getPassword()), seeds)) {
|
||||
throw new ServiceException("old passwd is unmatch");
|
||||
}
|
||||
|
||||
user.setId(userTmp.getId());
|
||||
user.setPassword(SecurityUtil.scrambleGenPass(user.getPassword().getBytes()));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new ServiceException("passwd process failed");
|
||||
}
|
||||
user.setId(1L);
|
||||
|
||||
user.update("username", "nn:password");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,16 @@ spring:
|
||||
time-zone: GMT+8
|
||||
|
||||
spring.datasource:
|
||||
url: jdbc:mysql://127.0.0.1:3306/canal_manager?useUnicode=true&characterEncoding=UTF-8&useSSL=false
|
||||
username: root
|
||||
password: 121212
|
||||
driver-class-name: com.mysql.jdbc.Driver
|
||||
hikari:
|
||||
maximum-pool-size: 10
|
||||
minimum-idle: 1
|
||||
address: 127.0.0.1:3306
|
||||
database: canal_manager
|
||||
username: canal
|
||||
password: canal
|
||||
driver-class-name: com.mysql.jdbc.Driver
|
||||
url: jdbc:mysql://${spring.datasource.address}/${spring.datasource.database}?useUnicode=true&characterEncoding=UTF-8&useSSL=false
|
||||
hikari:
|
||||
maximum-pool-size: 30
|
||||
minimum-idle: 1
|
||||
|
||||
canal:
|
||||
adminUser: admin
|
||||
adminPasswd: admin
|
||||
@@ -0,0 +1,139 @@
|
||||
#################################################
|
||||
######### common argument #############
|
||||
#################################################
|
||||
# tcp bind ip
|
||||
canal.ip =
|
||||
# register ip to zookeeper
|
||||
canal.register.ip =
|
||||
canal.port = 11111
|
||||
canal.metrics.pull.port = 11112
|
||||
# canal instance user/passwd
|
||||
canal.user = canal
|
||||
canal.passwd = E3619321C1A937C46A0D8BD1DAC39F93B27D4458
|
||||
|
||||
# canal admin config
|
||||
canal.admin.manager = 127.0.0.1:8089
|
||||
canal.admin.port = 11110
|
||||
canal.admin.user = admin
|
||||
canal.admin.passwd = 4ACFE3202A5FF5CF467898FC58AAB1D615029441
|
||||
|
||||
canal.zkServers =
|
||||
# flush data to zk
|
||||
canal.zookeeper.flush.period = 1000
|
||||
canal.withoutNetty = false
|
||||
# tcp, kafka, RocketMQ
|
||||
canal.serverMode = tcp
|
||||
# flush meta cursor/parse position to file
|
||||
canal.file.data.dir = ${canal.conf.dir}
|
||||
canal.file.flush.period = 1000
|
||||
## memory store RingBuffer size, should be Math.pow(2,n)
|
||||
canal.instance.memory.buffer.size = 16384
|
||||
## memory store RingBuffer used memory unit size , default 1kb
|
||||
canal.instance.memory.buffer.memunit = 1024
|
||||
## meory store gets mode used MEMSIZE or ITEMSIZE
|
||||
canal.instance.memory.batch.mode = MEMSIZE
|
||||
canal.instance.memory.rawEntry = true
|
||||
|
||||
## detecing config
|
||||
canal.instance.detecting.enable = false
|
||||
#canal.instance.detecting.sql = insert into retl.xdual values(1,now()) on duplicate key update x=now()
|
||||
canal.instance.detecting.sql = select 1
|
||||
canal.instance.detecting.interval.time = 3
|
||||
canal.instance.detecting.retry.threshold = 3
|
||||
canal.instance.detecting.heartbeatHaEnable = false
|
||||
|
||||
# support maximum transaction size, more than the size of the transaction will be cut into multiple transactions delivery
|
||||
canal.instance.transaction.size = 1024
|
||||
# mysql fallback connected to new master should fallback times
|
||||
canal.instance.fallbackIntervalInSeconds = 60
|
||||
|
||||
# network config
|
||||
canal.instance.network.receiveBufferSize = 16384
|
||||
canal.instance.network.sendBufferSize = 16384
|
||||
canal.instance.network.soTimeout = 30
|
||||
|
||||
# binlog filter config
|
||||
canal.instance.filter.druid.ddl = true
|
||||
canal.instance.filter.query.dcl = false
|
||||
canal.instance.filter.query.dml = false
|
||||
canal.instance.filter.query.ddl = false
|
||||
canal.instance.filter.table.error = false
|
||||
canal.instance.filter.rows = false
|
||||
canal.instance.filter.transaction.entry = false
|
||||
|
||||
# binlog format/image check
|
||||
canal.instance.binlog.format = ROW,STATEMENT,MIXED
|
||||
canal.instance.binlog.image = FULL,MINIMAL,NOBLOB
|
||||
|
||||
# binlog ddl isolation
|
||||
canal.instance.get.ddl.isolation = false
|
||||
|
||||
# parallel parser config
|
||||
canal.instance.parser.parallel = true
|
||||
## concurrent thread number, default 60% available processors, suggest not to exceed Runtime.getRuntime().availableProcessors()
|
||||
#canal.instance.parser.parallelThreadSize = 16
|
||||
## disruptor ringbuffer size, must be power of 2
|
||||
canal.instance.parser.parallelBufferSize = 256
|
||||
|
||||
# table meta tsdb info
|
||||
canal.instance.tsdb.enable = true
|
||||
canal.instance.tsdb.dir = ${canal.file.data.dir:../conf}/${canal.instance.destination:}
|
||||
canal.instance.tsdb.url = jdbc:h2:${canal.instance.tsdb.dir}/h2;CACHE_SIZE=1000;MODE=MYSQL;
|
||||
canal.instance.tsdb.dbUsername = canal
|
||||
canal.instance.tsdb.dbPassword = canal
|
||||
# dump snapshot interval, default 24 hour
|
||||
canal.instance.tsdb.snapshot.interval = 24
|
||||
# purge snapshot expire , default 360 hour(15 days)
|
||||
canal.instance.tsdb.snapshot.expire = 360
|
||||
|
||||
# aliyun ak/sk , support rds/mq
|
||||
canal.aliyun.accessKey =
|
||||
canal.aliyun.secretKey =
|
||||
|
||||
#################################################
|
||||
######### destinations #############
|
||||
#################################################
|
||||
canal.destinations =
|
||||
# conf root dir
|
||||
canal.conf.dir = ../conf
|
||||
# auto scan instance dir add/remove and start/stop instance
|
||||
canal.auto.scan = true
|
||||
canal.auto.scan.interval = 5
|
||||
|
||||
canal.instance.tsdb.spring.xml = classpath:spring/tsdb/h2-tsdb.xml
|
||||
#canal.instance.tsdb.spring.xml = classpath:spring/tsdb/mysql-tsdb.xml
|
||||
|
||||
canal.instance.global.mode = manager
|
||||
canal.instance.global.lazy = false
|
||||
canal.instance.global.manager.address = ${canal.admin.manager}
|
||||
#canal.instance.global.spring.xml = classpath:spring/memory-instance.xml
|
||||
canal.instance.global.spring.xml = classpath:spring/file-instance.xml
|
||||
#canal.instance.global.spring.xml = classpath:spring/default-instance.xml
|
||||
|
||||
##################################################
|
||||
######### MQ #############
|
||||
##################################################
|
||||
canal.mq.servers = 127.0.0.1:6667
|
||||
canal.mq.retries = 0
|
||||
canal.mq.batchSize = 16384
|
||||
canal.mq.maxRequestSize = 1048576
|
||||
canal.mq.lingerMs = 100
|
||||
canal.mq.bufferMemory = 33554432
|
||||
canal.mq.canalBatchSize = 50
|
||||
canal.mq.canalGetTimeout = 100
|
||||
canal.mq.flatMessage = true
|
||||
canal.mq.compressionType = none
|
||||
canal.mq.acks = all
|
||||
#canal.mq.properties. =
|
||||
canal.mq.producerGroup = test
|
||||
# Set this value to "cloud", if you want open message trace feature in aliyun.
|
||||
canal.mq.accessChannel = local
|
||||
# aliyun mq namespace
|
||||
#canal.mq.namespace =
|
||||
|
||||
##################################################
|
||||
######### Kafka Kerberos Info #############
|
||||
##################################################
|
||||
canal.mq.kafka.kerberos.enable = false
|
||||
canal.mq.kafka.kerberos.krb5FilePath = "../conf/kerberos/krb5.conf"
|
||||
canal.mq.kafka.kerberos.jaasFilePath = "../conf/kerberos/jaas.conf"
|
||||
@@ -0,0 +1,110 @@
|
||||
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `canal_manager` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_bin */;
|
||||
|
||||
USE `canal_manager`;
|
||||
|
||||
SET NAMES utf8;
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for canal_adapter_config
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `canal_adapter_config`;
|
||||
CREATE TABLE `canal_adapter_config` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`category` varchar(45) NOT NULL,
|
||||
`name` varchar(45) NOT NULL,
|
||||
`status` varchar(45) DEFAULT NULL,
|
||||
`content` text NOT NULL,
|
||||
`modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for canal_cluster
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `canal_cluster`;
|
||||
CREATE TABLE `canal_cluster` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(63) NOT NULL,
|
||||
`zk_hosts` varchar(255) NOT NULL,
|
||||
`modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for canal_config
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `canal_config`;
|
||||
CREATE TABLE `canal_config` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`cluster_id` bigint(20) DEFAULT NULL,
|
||||
`server_id` bigint(20) DEFAULT NULL,
|
||||
`name` varchar(45) NOT NULL,
|
||||
`status` varchar(45) DEFAULT NULL,
|
||||
`content` text NOT NULL,
|
||||
`content_md5` varchar(128) NOT NULL,
|
||||
`modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `sid_UNIQUE` (`server_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for canal_instance_config
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `canal_instance_config`;
|
||||
CREATE TABLE `canal_instance_config` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`cluster_id` bigint(20) DEFAULT NULL,
|
||||
`server_id` bigint(20) DEFAULT NULL,
|
||||
`name` varchar(45) NOT NULL,
|
||||
`status` varchar(45) DEFAULT NULL,
|
||||
`content` text NOT NULL,
|
||||
`content_md5` varchar(128) DEFAULT NULL,
|
||||
`modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name_UNIQUE` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for canal_node_server
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `canal_node_server`;
|
||||
CREATE TABLE `canal_node_server` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`cluster_id` bigint(20) DEFAULT NULL,
|
||||
`name` varchar(63) NOT NULL,
|
||||
`ip` varchar(63) NOT NULL,
|
||||
`admin_port` int(11) DEFAULT NULL,
|
||||
`tcp_port` int(11) DEFAULT NULL,
|
||||
`metric_port` int(11) DEFAULT NULL,
|
||||
`status` varchar(45) DEFAULT NULL,
|
||||
`modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for canal_user
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `canal_user`;
|
||||
CREATE TABLE `canal_user` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`username` varchar(31) NOT NULL,
|
||||
`password` varchar(128) NOT NULL,
|
||||
`name` varchar(31) NOT NULL,
|
||||
`roles` varchar(31) NOT NULL,
|
||||
`introduction` varchar(255) DEFAULT NULL,
|
||||
`avatar` varchar(255) DEFAULT NULL,
|
||||
`creation_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of canal_user
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `canal_user` VALUES (1, 'admin', '6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9', 'Canal Manager', 'admin', NULL, NULL, '2019-07-14 00:05:28');
|
||||
COMMIT;
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
@@ -0,0 +1,57 @@
|
||||
#################################################
|
||||
## mysql serverId , v1.0.26+ will autoGen
|
||||
# canal.instance.mysql.slaveId=0
|
||||
|
||||
# enable gtid use true/false
|
||||
canal.instance.gtidon=false
|
||||
|
||||
# position info
|
||||
canal.instance.master.address=127.0.0.1:3306
|
||||
canal.instance.master.journal.name=
|
||||
canal.instance.master.position=
|
||||
canal.instance.master.timestamp=
|
||||
canal.instance.master.gtid=
|
||||
|
||||
# rds oss binlog
|
||||
canal.instance.rds.accesskey=
|
||||
canal.instance.rds.secretkey=
|
||||
canal.instance.rds.instanceId=
|
||||
|
||||
# table meta tsdb info
|
||||
canal.instance.tsdb.enable=true
|
||||
#canal.instance.tsdb.url=jdbc:mysql://127.0.0.1:3306/canal_tsdb
|
||||
#canal.instance.tsdb.dbUsername=canal
|
||||
#canal.instance.tsdb.dbPassword=canal
|
||||
|
||||
#canal.instance.standby.address =
|
||||
#canal.instance.standby.journal.name =
|
||||
#canal.instance.standby.position =
|
||||
#canal.instance.standby.timestamp =
|
||||
#canal.instance.standby.gtid=
|
||||
|
||||
# username/password
|
||||
canal.instance.dbUsername=canal
|
||||
canal.instance.dbPassword=canal
|
||||
canal.instance.connectionCharset = UTF-8
|
||||
# enable druid Decrypt database password
|
||||
canal.instance.enableDruid=false
|
||||
#canal.instance.pwdPublicKey=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALK4BUxdDltRRE5/zXpVEVPUgunvscYFtEip3pmLlhrWpacX7y7GCMo2/JM6LeHmiiNdH1FWgGCpUfircSwlWKUCAwEAAQ==
|
||||
|
||||
# table regex
|
||||
canal.instance.filter.regex=.*\\..*
|
||||
# table black regex
|
||||
canal.instance.filter.black.regex=
|
||||
# table field filter(format: schema1.tableName1:field1/field2,schema2.tableName2:field1/field2)
|
||||
#canal.instance.filter.field=test1.t_product:id/subject/keywords,test2.t_company:id/name/contact/ch
|
||||
# table field black filter(format: schema1.tableName1:field1/field2,schema2.tableName2:field1/field2)
|
||||
#canal.instance.filter.black.field=test1.t_product:subject/product_image,test2.t_company:id/name/contact/ch
|
||||
|
||||
# mq config
|
||||
canal.mq.topic=example
|
||||
# dynamic topic route by schema or table regex
|
||||
#canal.mq.dynamicTopic=mytest1.user,mytest2\\..*,.*\\..*
|
||||
canal.mq.partition=0
|
||||
# hash partition config
|
||||
#canal.mq.partitionsNum=3
|
||||
#canal.mq.partitionHash=test.table:id^name,.*\\..*
|
||||
#################################################
|
||||
@@ -0,0 +1,47 @@
|
||||
<configuration scan="true" scanPeriod=" 5 seconds">
|
||||
<jmxConfigurator />
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{56} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="CANAL-ROOT" class="ch.qos.logback.classic.sift.SiftingAppender">
|
||||
<discriminator>
|
||||
<Key>admin</Key>
|
||||
<DefaultValue>admin</DefaultValue>
|
||||
</discriminator>
|
||||
<sift>
|
||||
<appender name="FILE-admin" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<File>../logs/admin.log</File>
|
||||
<rollingPolicy
|
||||
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- rollover daily -->
|
||||
<fileNamePattern>../logs/admin/%d{yyyy-MM-dd}/admin-%d{yyyy-MM-dd}-%i.log.gz</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<!-- or whenever the file size reaches 100MB -->
|
||||
<maxFileSize>512MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>
|
||||
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{56} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
</sift>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
<appender-ref ref="CANAL-ROOT" />
|
||||
</root>
|
||||
|
||||
<logger name="io.ebean.SQL" additivity="false">
|
||||
<level value="INFO" />
|
||||
<appender-ref ref="STDOUT"/>
|
||||
<appender-ref ref="CANAL-ROOT" />
|
||||
</logger>
|
||||
</configuration>
|
||||
@@ -1 +1 @@
|
||||
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=/favicon.ico><title>Canal Admin</title><link href=/static/css/chunk-elementUI.18b11d0e.css rel=stylesheet><link href=/static/css/chunk-libs.5cf311f0.css rel=stylesheet><link href=/static/css/app.bb951cb3.css rel=stylesheet></head><body><noscript><strong>We're sorry but Canal Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/static/js/chunk-elementUI.667f4c87.js></script><script src=/static/js/chunk-libs.c04beefc.js></script><script>(function(e){function n(n){for(var r,c,a=n[0],f=n[1],i=n[2],h=0,d=[];h<a.length;h++)c=a[h],u[c]&&d.push(u[c][0]),u[c]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);l&&l(n);while(d.length)d.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var a=t[c];0!==u[a]&&(r=!1)}r&&(o.splice(n--,1),e=f(f.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function a(e){return f.p+"static/js/"+({}[e]||e)+"."+{"chunk-101fc062":"bc898027","chunk-137eaf06":"867259ad","chunk-238a81e9":"273bda76","chunk-37c49cbf":"64d26540","chunk-3fcdf643":"4b7133b8","chunk-555c32e2":"6c39a877","chunk-63c8061b":"7ccc9231","chunk-7279d7fc":"22670fea","chunk-4f09fed2":"ff28d88d","chunk-69386cf0":"76d77f5c","chunk-7ec889b7":"6ba68ef0","chunk-e1a839e4":"c870a02b"}[e]+".js"}function f(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.e=function(e){var n=[],t={"chunk-101fc062":1,"chunk-238a81e9":1,"chunk-37c49cbf":1,"chunk-555c32e2":1,"chunk-63c8061b":1,"chunk-7279d7fc":1,"chunk-4f09fed2":1,"chunk-69386cf0":1,"chunk-7ec889b7":1};c[e]?n.push(c[e]):0!==c[e]&&t[e]&&n.push(c[e]=new Promise(function(n,t){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-101fc062":"fad9926f","chunk-137eaf06":"31d6cfe0","chunk-238a81e9":"e8e2beee","chunk-37c49cbf":"efc21a9c","chunk-3fcdf643":"31d6cfe0","chunk-555c32e2":"9d3c5014","chunk-63c8061b":"acff1abf","chunk-7279d7fc":"84a25dbe","chunk-4f09fed2":"70ec0b86","chunk-69386cf0":"741ff14e","chunk-7ec889b7":"c0585512","chunk-e1a839e4":"31d6cfe0"}[e]+".css",u=f.p+r,o=document.getElementsByTagName("link"),a=0;a<o.length;a++){var i=o[a],h=i.getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(h===r||h===u))return n()}var d=document.getElementsByTagName("style");for(a=0;a<d.length;a++){i=d[a],h=i.getAttribute("data-href");if(h===r||h===u)return n()}var l=document.createElement("link");l.rel="stylesheet",l.type="text/css",l.onload=n,l.onerror=function(n){var r=n&&n.target&&n.target.src||u,o=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");o.code="CSS_CHUNK_LOAD_FAILED",o.request=r,delete c[e],l.parentNode.removeChild(l),t(o)},l.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(l)}).then(function(){c[e]=0}));var r=u[e];if(0!==r)if(r)n.push(r[2]);else{var o=new Promise(function(n,t){r=u[e]=[n,t]});n.push(r[2]=o);var i,h=document.createElement("script");h.charset="utf-8",h.timeout=120,f.nc&&h.setAttribute("nonce",f.nc),h.src=a(e),i=function(n){h.onerror=h.onload=null,clearTimeout(d);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var d=setTimeout(function(){i({type:"timeout",target:h})},12e4);h.onerror=h.onload=i,document.head.appendChild(h)}return Promise.all(n)},f.m=e,f.c=r,f.d=function(e,n,t){f.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},f.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,n){if(1&n&&(e=f(e)),8&n)return e;if(4&n&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)f.d(t,r,function(n){return e[n]}.bind(null,r));return t},f.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return f.d(n,"a",n),n},f.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},f.p="/",f.oe=function(e){throw console.error(e),e};var i=window["webpackJsonp"]=window["webpackJsonp"]||[],h=i.push.bind(i);i.push=n,i=i.slice();for(var d=0;d<i.length;d++)n(i[d]);var l=h;t()})([]);</script><script src=/static/js/app.b8d01331.js></script></body></html>
|
||||
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=/favicon.ico><title>Canal Admin</title><link href=/static/css/chunk-elementUI.18b11d0e.css rel=stylesheet><link href=/static/css/chunk-libs.5cf311f0.css rel=stylesheet><link href=/static/css/app.bb951cb3.css rel=stylesheet></head><body><noscript><strong>We're sorry but Canal Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/static/js/chunk-elementUI.667f4c87.js></script><script src=/static/js/chunk-libs.c04beefc.js></script><script>(function(e){function n(n){for(var c,r,o=n[0],f=n[1],i=n[2],d=0,h=[];d<o.length;d++)r=o[d],u[r]&&h.push(u[r][0]),u[r]=0;for(c in f)Object.prototype.hasOwnProperty.call(f,c)&&(e[c]=f[c]);l&&l(n);while(h.length)h.shift()();return a.push.apply(a,i||[]),t()}function t(){for(var e,n=0;n<a.length;n++){for(var t=a[n],c=!0,r=1;r<t.length;r++){var o=t[r];0!==u[o]&&(c=!1)}c&&(a.splice(n--,1),e=f(f.s=t[0]))}return e}var c={},r={runtime:0},u={runtime:0},a=[];function o(e){return f.p+"static/js/"+({}[e]||e)+"."+{"chunk-0dca2f22":"a2bc28b8","chunk-5afa45f5":"79ddcc04","chunk-5b373aad":"90a2d8e7","chunk-bd1d44ee":"8e2c4851","chunk-101fc062":"bc898027","chunk-2b9b6c5c":"cc2181b9","chunk-37c49cbf":"64d26540","chunk-49959c8b":"6d226f70","chunk-4f09fed2":"ff28d88d","chunk-55380ff2":"681c71c9","chunk-69386cf0":"76d77f5c","chunk-7ec889b7":"bd1ca803","chunk-da289616":"7def925e"}[e]+".js"}function f(n){if(c[n])return c[n].exports;var t=c[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.e=function(e){var n=[],t={"chunk-5afa45f5":1,"chunk-5b373aad":1,"chunk-bd1d44ee":1,"chunk-101fc062":1,"chunk-2b9b6c5c":1,"chunk-37c49cbf":1,"chunk-49959c8b":1,"chunk-4f09fed2":1,"chunk-69386cf0":1,"chunk-7ec889b7":1,"chunk-da289616":1};r[e]?n.push(r[e]):0!==r[e]&&t[e]&&n.push(r[e]=new Promise(function(n,t){for(var c="static/css/"+({}[e]||e)+"."+{"chunk-0dca2f22":"31d6cfe0","chunk-5afa45f5":"a34bb177","chunk-5b373aad":"b966d36b","chunk-bd1d44ee":"1528199a","chunk-101fc062":"fad9926f","chunk-2b9b6c5c":"c89f8844","chunk-37c49cbf":"efc21a9c","chunk-49959c8b":"e8e2beee","chunk-4f09fed2":"70ec0b86","chunk-55380ff2":"31d6cfe0","chunk-69386cf0":"741ff14e","chunk-7ec889b7":"c0585512","chunk-da289616":"c89f8844"}[e]+".css",u=f.p+c,a=document.getElementsByTagName("link"),o=0;o<a.length;o++){var i=a[o],d=i.getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(d===c||d===u))return n()}var h=document.getElementsByTagName("style");for(o=0;o<h.length;o++){i=h[o],d=i.getAttribute("data-href");if(d===c||d===u)return n()}var l=document.createElement("link");l.rel="stylesheet",l.type="text/css",l.onload=n,l.onerror=function(n){var c=n&&n.target&&n.target.src||u,a=new Error("Loading CSS chunk "+e+" failed.\n("+c+")");a.code="CSS_CHUNK_LOAD_FAILED",a.request=c,delete r[e],l.parentNode.removeChild(l),t(a)},l.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(l)}).then(function(){r[e]=0}));var c=u[e];if(0!==c)if(c)n.push(c[2]);else{var a=new Promise(function(n,t){c=u[e]=[n,t]});n.push(c[2]=a);var i,d=document.createElement("script");d.charset="utf-8",d.timeout=120,f.nc&&d.setAttribute("nonce",f.nc),d.src=o(e),i=function(n){d.onerror=d.onload=null,clearTimeout(h);var t=u[e];if(0!==t){if(t){var c=n&&("load"===n.type?"missing":n.type),r=n&&n.target&&n.target.src,a=new Error("Loading chunk "+e+" failed.\n("+c+": "+r+")");a.type=c,a.request=r,t[1](a)}u[e]=void 0}};var h=setTimeout(function(){i({type:"timeout",target:d})},12e4);d.onerror=d.onload=i,document.head.appendChild(d)}return Promise.all(n)},f.m=e,f.c=c,f.d=function(e,n,t){f.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},f.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,n){if(1&n&&(e=f(e)),8&n)return e;if(4&n&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var c in e)f.d(t,c,function(n){return e[n]}.bind(null,c));return t},f.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return f.d(n,"a",n),n},f.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},f.p="/",f.oe=function(e){throw console.error(e),e};var i=window["webpackJsonp"]=window["webpackJsonp"]||[],d=i.push.bind(i);i.push=n,i=i.slice();for(var h=0;h<i.length;h++)n(i[h]);var l=d;t()})([]);</script><script src=/static/js/app.6845b228.js></script></body></html>
|
||||
@@ -0,0 +1 @@
|
||||
.pagination-container[data-v-cebf2f0c]{background:#fff;padding:32px 16px}.pagination-container.hidden[data-v-cebf2f0c]{display:none}
|
||||
@@ -0,0 +1 @@
|
||||
.pagination-container[data-v-cebf2f0c]{background:#fff;padding:32px 16px}.pagination-container.hidden[data-v-cebf2f0c]{display:none}
|
||||
@@ -0,0 +1 @@
|
||||
.line[data-v-28f0cd0f]{text-align:center}
|
||||
@@ -0,0 +1 @@
|
||||
.pagination-container[data-v-38ef71f0]{background:#fff;padding:32px 16px}.pagination-container.hidden[data-v-38ef71f0]{display:none}
|
||||
@@ -1 +0,0 @@
|
||||
.line[data-v-44b63e9d]{text-align:center}
|
||||
@@ -0,0 +1 @@
|
||||
.line[data-v-2e6a6604]{text-align:center}
|
||||
@@ -0,0 +1 @@
|
||||
.line[data-v-e884438a]{text-align:center}
|
||||
@@ -1 +0,0 @@
|
||||
.line[data-v-5feb688e]{text-align:center}
|
||||
@@ -1 +0,0 @@
|
||||
.line[data-v-11496239]{text-align:center}
|
||||
@@ -0,0 +1 @@
|
||||
.line[data-v-35af5ff9]{text-align:center}
|
||||
@@ -0,0 +1 @@
|
||||
.line[data-v-5c332416]{text-align:center}
|
||||
@@ -0,0 +1 @@
|
||||
.pagination-container[data-v-38ef71f0]{background:#fff;padding:32px 16px}.pagination-container.hidden[data-v-38ef71f0]{display:none}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-101fc062"],{2017:function(e,t,s){"use strict";var n=s("3b76"),o=s.n(n);o.a},"3b76":function(e,t,s){},"562b":function(e,t,s){},"9ed6":function(e,t,s){"use strict";s.r(t);var n=function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("div",{staticClass:"login-container"},[s("el-form",{ref:"loginForm",staticClass:"login-form",attrs:{model:e.loginForm,rules:e.loginRules,"auto-complete":"on","label-position":"left"}},[s("div",{staticClass:"title-container"},[s("h3",{staticClass:"title"},[e._v("Canal Admin Login")])]),e._v(" "),s("el-form-item",{attrs:{prop:"username"}},[s("span",{staticClass:"svg-container"},[s("svg-icon",{attrs:{"icon-class":"user"}})],1),e._v(" "),s("el-input",{ref:"username",attrs:{placeholder:"Username",name:"username",type:"text",tabindex:"1","auto-complete":"on"},model:{value:e.loginForm.username,callback:function(t){e.$set(e.loginForm,"username",t)},expression:"loginForm.username"}})],1),e._v(" "),s("el-form-item",{attrs:{prop:"password"}},[s("span",{staticClass:"svg-container"},[s("svg-icon",{attrs:{"icon-class":"password"}})],1),e._v(" "),s("el-input",{key:e.passwordType,ref:"password",attrs:{type:e.passwordType,placeholder:"Password",name:"password",tabindex:"2","auto-complete":"on"},nativeOn:{keyup:function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"enter",13,t.key,"Enter")?null:e.handleLogin(t)}},model:{value:e.loginForm.password,callback:function(t){e.$set(e.loginForm,"password",t)},expression:"loginForm.password"}}),e._v(" "),s("span",{staticClass:"show-pwd",on:{click:e.showPwd}},[s("svg-icon",{attrs:{"icon-class":"password"===e.passwordType?"eye":"eye-open"}})],1)],1),e._v(" "),s("el-button",{staticStyle:{width:"100%","margin-bottom":"30px"},attrs:{loading:e.loading,type:"primary"},nativeOn:{click:function(t){return t.preventDefault(),e.handleLogin(t)}}},[e._v("Login")]),e._v(" "),s("div",{staticClass:"tips"})],1)],1)},o=[],r=s("61f7"),a={name:"Login",data:function(){var e=function(e,t,s){Object(r["b"])(t)?s():s(new Error("Please enter the correct user name"))},t=function(e,t,s){t.length<6?s(new Error("The password can not be less than 6 digits")):s()};return{loginForm:{username:"",password:""},loginRules:{username:[{required:!0,trigger:"blur",validator:e}],password:[{required:!0,trigger:"blur",validator:t}]},loading:!1,passwordType:"password",redirect:void 0}},watch:{$route:{handler:function(e){this.redirect=e.query&&e.query.redirect},immediate:!0}},methods:{showPwd:function(){var e=this;"password"===this.passwordType?this.passwordType="":this.passwordType="password",this.$nextTick(function(){e.$refs.password.focus()})},handleLogin:function(){var e=this;this.$refs.loginForm.validate(function(t){if(!t)return console.log("error submit!!"),!1;e.loading=!0,e.$store.dispatch("user/login",e.loginForm).then(function(){e.$router.push({path:e.redirect||"/"}),e.loading=!1}).catch(function(){e.loading=!1})})}}},i=a,l=(s("2017"),s("ec2d"),s("2877")),c=Object(l["a"])(i,n,o,!1,null,"31c14ebf",null);t["default"]=c.exports},ec2d:function(e,t,s){"use strict";var n=s("562b"),o=s.n(n);o.a}}]);
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2301924a"],{"10df":function(t,e,n){},1248:function(t,e,n){"use strict";n.r(e);var r=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",[n("el-form",{ref:"form",attrs:{model:t.form}},[n("div",{staticClass:"filter-container",staticStyle:{"padding-left":"10px","padding-top":"20px"}},[n("el-input",{staticClass:"filter-item",staticStyle:{width:"200px"},attrs:{placeholder:"Instance名称"},model:{value:t.form.name,callback:function(e){t.$set(t.form,"name",e)},expression:"form.name"}}),t._v(" "),n("el-select",{staticClass:"filter-item",attrs:{placeholder:"所属集群/主机"},model:{value:t.form.clusterServerId,callback:function(e){t.$set(t.form,"clusterServerId",e)},expression:"form.clusterServerId"}},t._l(t.options,function(e){return n("el-option-group",{key:e.label,attrs:{label:e.label}},t._l(e.options,function(t){return n("el-option",{key:t.value,attrs:{label:t.label,value:t.value}})}),1)}),1),t._v(" "),n("el-button",{staticClass:"filter-item",attrs:{type:"primary"},on:{click:t.onSubmit}},[t._v("保存")]),t._v(" "),n("el-button",{staticClass:"filter-item",attrs:{type:"info"},on:{click:t.onBack}},[t._v("返回")])],1),t._v(" "),n("editor",{attrs:{lang:"properties",theme:"chrome",width:"100%",height:800},on:{init:t.editorInit},model:{value:t.form.content,callback:function(e){t.$set(t.form,"content",e)},expression:"form.content"}})],1)],1)},a=[],c=(n("7f7f"),n("f546")),o=n("d631"),u={components:{editor:n("7c9e")},data:function(){return{options:[],form:{name:"",content:"",clusterServerId:""}}},created:function(){var t=this;Object(o["d"])().then(function(e){t.options=e.data})},methods:{editorInit:function(){n("2099"),n("be9d"),n("2968"),n("e0e5"),n("bb36"),n("0329"),n("95b8"),n("6a21")},onSubmit:function(){var t=this;""!==this.form.name?""!==this.form.clusterServerId?null!==this.form.content&&""!==this.form.content?this.$confirm("确定新建","确定新建",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then(function(){Object(c["a"])(t.form).then(function(e){"success"===e.data?(t.$message({message:"新建成功",type:"success"}),t.$router.push("/canalServer/canalInstances")):t.$message({message:"新建失败",type:"error"})})}):this.$message({message:"请输入配置内容",type:"error"}):this.$message({message:"请选择所属集群/主机",type:"error"}):this.$message({message:"请输入Instance名称",type:"error"})},onBack:function(){history.go(-1)}}},i=u,s=(n("2eb8"),n("2877")),l=Object(s["a"])(i,r,a,!1,null,"28f0cd0f",null);e["default"]=l.exports},"2eb8":function(t,e,n){"use strict";var r=n("10df"),a=n.n(r);a.a},d631:function(t,e,n){"use strict";n.d(e,"c",function(){return a}),n.d(e,"a",function(){return c}),n.d(e,"e",function(){return o}),n.d(e,"b",function(){return u}),n.d(e,"d",function(){return i});var r=n("b775");function a(t){return Object(r["a"])({url:"/canal/clusters",method:"get",params:t})}function c(t){return Object(r["a"])({url:"/canal/cluster",method:"post",data:t})}function o(t){return Object(r["a"])({url:"/canal/cluster",method:"put",data:t})}function u(t){return Object(r["a"])({url:"/canal/cluster/"+t,method:"delete"})}function i(){return Object(r["a"])({url:"/canal/clustersAndServers",method:"get"})}},f546:function(t,e,n){"use strict";n.d(e,"e",function(){return a}),n.d(e,"b",function(){return c}),n.d(e,"j",function(){return o}),n.d(e,"a",function(){return u}),n.d(e,"c",function(){return i}),n.d(e,"h",function(){return s}),n.d(e,"i",function(){return l}),n.d(e,"f",function(){return f}),n.d(e,"g",function(){return d}),n.d(e,"d",function(){return m});var r=n("b775");function a(t){return Object(r["a"])({url:"/canal/instances",method:"get",params:t})}function c(t){return Object(r["a"])({url:"/canal/instance/"+t,method:"get"})}function o(t){return Object(r["a"])({url:"/canal/instance",method:"put",data:t})}function u(t){return Object(r["a"])({url:"/canal/instance",method:"post",data:t})}function i(t){return Object(r["a"])({url:"/canal/instance/"+t,method:"delete"})}function s(t,e){return Object(r["a"])({url:"/canal/instance/start/"+t+"/"+e,method:"put"})}function l(t,e){return Object(r["a"])({url:"/canal/instance/stop/"+t+"/"+e,method:"put"})}function f(t,e){return Object(r["a"])({url:"/canal/instance/log/"+t+"/"+e,method:"get"})}function d(t,e){return Object(r["a"])({url:"/canal/instance/status/"+t+"?option="+e,method:"put"})}function m(t){return Object(r["a"])({url:"/canal/active/instances/"+t,method:"get"})}}}]);
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-37c49cbf"],{3671:function(t,e,n){"use strict";var a=n("afe4"),c=n.n(a);c.a},9406:function(t,e,n){"use strict";n.r(e);var a=function(){var t=this,e=t.$createElement;t._self._c;return t._m(0)},c=[function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"dashboard-container"},[n("div",{staticClass:"dashboard-text"},[t._v(" ")])])}],s=n("db72"),r=n("2f62"),u={name:"Dashboard",computed:Object(s["a"])({},Object(r["b"])(["name"])),mounted:function(){this.$router.push("/canalServer")}},i=u,o=(n("3671"),n("2877")),d=Object(o["a"])(i,a,c,!1,null,"42037c2b",null);e["default"]=d.exports},afe4:function(t,e,n){}}]);
|
||||
@@ -1 +1 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-238a81e9"],{"26fc":function(t,s,a){t.exports=a.p+"static/img/404_cloud.0f4bc32b.png"},"8cdb":function(t,s,a){"use strict";a.r(s);var e=function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"wscn-http404-container"},[a("div",{staticClass:"wscn-http404"},[t._m(0),t._v(" "),a("div",{staticClass:"bullshit"},[a("div",{staticClass:"bullshit__oops"},[t._v("OOPS!")]),t._v(" "),t._m(1),t._v(" "),a("div",{staticClass:"bullshit__headline"},[t._v(t._s(t.message))]),t._v(" "),a("div",{staticClass:"bullshit__info"},[t._v("Please check that the URL you entered is correct, or click the button below to return to the homepage.")]),t._v(" "),a("a",{staticClass:"bullshit__return-home",attrs:{href:""}},[t._v("Back to home")])])])])},c=[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"pic-404"},[e("img",{staticClass:"pic-404__parent",attrs:{src:a("a36b"),alt:"404"}}),t._v(" "),e("img",{staticClass:"pic-404__child left",attrs:{src:a("26fc"),alt:"404"}}),t._v(" "),e("img",{staticClass:"pic-404__child mid",attrs:{src:a("26fc"),alt:"404"}}),t._v(" "),e("img",{staticClass:"pic-404__child right",attrs:{src:a("26fc"),alt:"404"}})])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"bullshit__info"},[t._v("All rights reserved\n "),a("a",{staticStyle:{color:"#20a0ff"},attrs:{href:"https://wallstreetcn.com",target:"_blank"}},[t._v("wallstreetcn")])])}],i={name:"Page404",computed:{message:function(){return"The webmaster said that you can not enter this page..."}}},l=i,n=(a("97ef"),a("2877")),r=Object(n["a"])(l,e,c,!1,null,"c095f994",null);s["default"]=r.exports},"97ef":function(t,s,a){"use strict";var e=a("b51e"),c=a.n(e);c.a},a36b:function(t,s,a){t.exports=a.p+"static/img/404.a57b6f31.png"},b51e:function(t,s,a){}}]);
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-49959c8b"],{"26fc":function(t,s,a){t.exports=a.p+"static/img/404_cloud.0f4bc32b.png"},"8cdb":function(t,s,a){"use strict";a.r(s);var e=function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"wscn-http404-container"},[a("div",{staticClass:"wscn-http404"},[t._m(0),t._v(" "),a("div",{staticClass:"bullshit"},[a("div",{staticClass:"bullshit__oops"},[t._v("OOPS!")]),t._v(" "),t._m(1),t._v(" "),a("div",{staticClass:"bullshit__headline"},[t._v(t._s(t.message))]),t._v(" "),a("div",{staticClass:"bullshit__info"},[t._v("Please check that the URL you entered is correct, or click the button below to return to the homepage.")]),t._v(" "),a("a",{staticClass:"bullshit__return-home",attrs:{href:""}},[t._v("Back to home")])])])])},c=[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"pic-404"},[e("img",{staticClass:"pic-404__parent",attrs:{src:a("a36b"),alt:"404"}}),t._v(" "),e("img",{staticClass:"pic-404__child left",attrs:{src:a("26fc"),alt:"404"}}),t._v(" "),e("img",{staticClass:"pic-404__child mid",attrs:{src:a("26fc"),alt:"404"}}),t._v(" "),e("img",{staticClass:"pic-404__child right",attrs:{src:a("26fc"),alt:"404"}})])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"bullshit__info"},[t._v("All rights reserved\n "),a("a",{staticStyle:{color:"#20a0ff"},attrs:{href:"https://wallstreetcn.com",target:"_blank"}},[t._v("wallstreetcn")])])}],i={name:"Page404",computed:{message:function(){return"The webmaster said that you can not enter this page..."}}},l=i,n=(a("97ef"),a("2877")),r=Object(n["a"])(l,e,c,!1,null,"c095f994",null);s["default"]=r.exports},"97ef":function(t,s,a){"use strict";var e=a("b51e"),c=a.n(e);c.a},a36b:function(t,s,a){t.exports=a.p+"static/img/404.a57b6f31.png"},b51e:function(t,s,a){}}]);
|
||||
@@ -0,0 +1 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-49959c8b"],{"26fc":function(t,s,a){t.exports=a.p+"static/img/404_cloud.0f4bc32b.png"},"8cdb":function(t,s,a){"use strict";a.r(s);var e=function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"wscn-http404-container"},[a("div",{staticClass:"wscn-http404"},[t._m(0),t._v(" "),a("div",{staticClass:"bullshit"},[a("div",{staticClass:"bullshit__oops"},[t._v("OOPS!")]),t._v(" "),t._m(1),t._v(" "),a("div",{staticClass:"bullshit__headline"},[t._v(t._s(t.message))]),t._v(" "),a("div",{staticClass:"bullshit__info"},[t._v("Please check that the URL you entered is correct, or click the button below to return to the homepage.")]),t._v(" "),a("a",{staticClass:"bullshit__return-home",attrs:{href:""}},[t._v("Back to home")])])])])},c=[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"pic-404"},[e("img",{staticClass:"pic-404__parent",attrs:{src:a("a36b"),alt:"404"}}),t._v(" "),e("img",{staticClass:"pic-404__child left",attrs:{src:a("26fc"),alt:"404"}}),t._v(" "),e("img",{staticClass:"pic-404__child mid",attrs:{src:a("26fc"),alt:"404"}}),t._v(" "),e("img",{staticClass:"pic-404__child right",attrs:{src:a("26fc"),alt:"404"}})])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"bullshit__info"},[t._v("All rights reserved\n "),a("a",{staticStyle:{color:"#20a0ff"},attrs:{href:"https://wallstreetcn.com",target:"_blank"}},[t._v("wallstreetcn")])])}],i={name:"Page404",computed:{message:function(){return"The webmaster said that you can not enter this page..."}}},l=i,n=(a("97ef"),a("2877")),r=Object(n["a"])(l,e,c,!1,null,"c095f994",null);s["default"]=r.exports},"97ef":function(t,s,a){"use strict";var e=a("b51e"),c=a.n(e);c.a},a36b:function(t,s,a){t.exports=a.p+"static/img/404.a57b6f31.png"},b51e:function(t,s,a){}}]);
|
||||
@@ -0,0 +1 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-4f09fed2"],{"68d6":function(t,e,n){"use strict";var r=n("9d07"),o=n.n(r);o.a},"9d07":function(t,e,n){},c6ed:function(t,e,n){"use strict";n.d(e,"c",function(){return o}),n.d(e,"a",function(){return c}),n.d(e,"g",function(){return u}),n.d(e,"b",function(){return a}),n.d(e,"e",function(){return d}),n.d(e,"f",function(){return i}),n.d(e,"d",function(){return f});var r=n("b775");function o(t){return Object(r["a"])({url:"/nodeServers",method:"get",params:t})}function c(t){return Object(r["a"])({url:"/nodeServer",method:"post",data:t})}function u(t){return Object(r["a"])({url:"/nodeServer",method:"put",data:t})}function a(t){return Object(r["a"])({url:"/nodeServer/"+t,method:"delete"})}function d(t){return Object(r["a"])({url:"/nodeServer/start/"+t,method:"put"})}function i(t){return Object(r["a"])({url:"/nodeServer/stop/"+t,method:"put"})}function f(t){return Object(r["a"])({url:"/nodeServer/log/"+t,method:"get"})}},caf8:function(t,e,n){"use strict";n.r(e);var r=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",[n("el-form",{ref:"form",attrs:{model:t.form}},[n("div",{staticStyle:{"padding-left":"10px","padding-right":"10px","padding-top":"20px"}},[n("el-form-item",[t._v("\n canal.log \n "),n("el-button",{attrs:{type:"primary"},on:{click:t.onRefresh}},[t._v("刷新")]),t._v(" "),n("el-button",{attrs:{type:"info"},on:{click:t.onBack}},[t._v("返回")])],1),t._v(" "),n("el-input",{attrs:{rows:35,readonly:"readonly",type:"textarea"},model:{value:t.form.desc,callback:function(e){t.$set(t.form,"desc",e)},expression:"form.desc"}})],1)])],1)},o=[],c=n("c6ed"),u={data:function(){return{form:{desc:""}}},created:function(){this.fetchData()},methods:{fetchData:function(){var t=this;Object(c["d"])(this.$route.query.id).then(function(e){t.form.desc=e.data})},onRefresh:function(){this.fetchData()},onBack:function(){history.go(-1)}}},a=u,d=(n("68d6"),n("2877")),i=Object(d["a"])(a,r,o,!1,null,"64c3fae5",null);e["default"]=i.exports}}]);
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-555c32e2"],{1248:function(t,n,e){"use strict";e.r(n);var a=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",[e("el-form",{ref:"form",attrs:{model:t.form}},[e("div",{staticClass:"filter-container",staticStyle:{"padding-left":"10px","padding-top":"20px"}},[e("el-input",{staticClass:"filter-item",staticStyle:{width:"200px"},attrs:{placeholder:"实例名称"},model:{value:t.form.name,callback:function(n){t.$set(t.form,"name",n)},expression:"form.name"}}),t._v("\n \n "),e("el-button",{staticClass:"filter-item",attrs:{type:"primary"},on:{click:t.onSubmit}},[t._v("新建")]),t._v(" "),e("el-button",{staticClass:"filter-item",attrs:{type:"info"},on:{click:t.onBack}},[t._v("返回")])],1),t._v(" "),e("editor",{attrs:{lang:"properties",theme:"chrome",width:"100%",height:800},on:{init:t.editorInit},model:{value:t.form.content,callback:function(n){t.$set(t.form,"content",n)},expression:"form.content"}})],1)],1)},r=[],c=(e("7f7f"),e("f546")),o={components:{editor:e("7c9e")},data:function(){return{form:{name:"",content:""}}},created:function(){},methods:{editorInit:function(){e("2099"),e("be9d"),e("2968"),e("e0e5"),e("bb36"),e("0329"),e("95b8"),e("6a21")},onSubmit:function(){var t=this;""!==this.form.name?this.$confirm("确定新建","确定新建",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then(function(){Object(c["a"])(t.form).then(function(n){"success"===n.data?(t.$message({message:"新建成功",type:"success"}),t.$router.push("/canalServer/canalInstances")):t.$message({message:"新建失败",type:"error"})})}):this.$message({message:"请输入实例名称",type:"error"})},onBack:function(){history.go(-1)}}},i=o,u=(e("e943"),e("2877")),s=Object(u["a"])(i,a,r,!1,null,"44b63e9d",null);n["default"]=s.exports},"18ae":function(t,n,e){},e943:function(t,n,e){"use strict";var a=e("18ae"),r=e.n(a);r.a},f546:function(t,n,e){"use strict";e.d(n,"d",function(){return r}),e.d(n,"b",function(){return c}),e.d(n,"h",function(){return o}),e.d(n,"a",function(){return i}),e.d(n,"c",function(){return u}),e.d(n,"f",function(){return s}),e.d(n,"g",function(){return f}),e.d(n,"e",function(){return l});var a=e("b775");function r(t){return Object(a["a"])({url:"/canal/instances",method:"get",params:t})}function c(t){return Object(a["a"])({url:"/canal/instance/"+t,method:"get"})}function o(t){return Object(a["a"])({url:"/canal/instance",method:"put",data:t})}function i(t){return Object(a["a"])({url:"/canal/instance",method:"post",data:t})}function u(t){return Object(a["a"])({url:"/canal/instance/"+t,method:"delete"})}function s(t,n){return Object(a["a"])({url:"/canal/instance/start/"+t+"/"+n,method:"put"})}function f(t,n){return Object(a["a"])({url:"/canal/instance/stop/"+t+"/"+n,method:"put"})}function l(t,n){return Object(a["a"])({url:"/canal/instance/log/"+t+"/"+n,method:"get"})}}}]);
|
||||
@@ -0,0 +1 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-5afa45f5"],{"0984":function(t,e,n){},"0f47":function(t,e,n){"use strict";var o=n("0984"),r=n.n(o);r.a},aa99:function(t,e,n){"use strict";n.r(e);var o=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",[n("el-form",{ref:"form",attrs:{model:t.form}},[n("div",{staticStyle:{"padding-left":"10px","padding-top":"20px"}},[n("el-form-item",[t._v("\n "+t._s(t.form.name)+" \n "),n("el-button",{attrs:{type:"primary"},on:{click:t.onSubmit}},[t._v("保存")]),t._v(" "),n("el-button",{attrs:{type:"warning"},on:{click:t.onCancel}},[t._v("重置")]),t._v(" "),n("el-button",{attrs:{type:"success"},on:{click:t.onLoadTemplate}},[t._v("载入模板")]),t._v(" "),n("el-button",{attrs:{type:"info"},on:{click:t.onBack}},[t._v("返回")])],1)],1),t._v(" "),n("editor",{attrs:{lang:"properties",theme:"chrome",width:"100%",height:800},on:{init:t.editorInit},model:{value:t.form.content,callback:function(e){t.$set(t.form,"content",e)},expression:"form.content"}})],1)],1)},r=[],a=(n("7f7f"),n("b775"));function c(t,e){return Object(a["a"])({url:"/canal/config/"+t+"/"+e,method:"get"})}function i(t){return Object(a["a"])({url:"/canal/config",method:"put",data:t})}function s(){return Object(a["a"])({url:"/canal/config/template",method:"get"})}var u={components:{editor:n("7c9e")},data:function(){return{form:{id:null,name:"",content:"",serverId:null,clusterId:null}}},created:function(){this.loadCanalConfig()},methods:{editorInit:function(){n("2099"),n("be9d"),n("2968"),n("e0e5"),n("bb36"),n("0329"),n("95b8"),n("6a21")},loadCanalConfig:function(){var t=this,e=0,n=0;this.$route.query.clusterId?e=this.$route.query.clusterId:this.$route.query.serverId&&(n=this.$route.query.serverId),c(e,n).then(function(e){var n=e.data;t.form.id=n.id,t.form.name=n.name,t.form.content=n.content,t.form.serverId=t.$route.query.serverId,t.form.clusterId=t.$route.query.clusterId})},onSubmit:function(){var t=this;null!==this.form.content&&""!==this.form.content?this.$confirm("修改主配置可能会导致Server重启,是否继续?","确定修改",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then(function(){i(t.form).then(function(e){"success"===e.data?(t.$message({message:"保存成功",type:"success"}),t.loadCanalConfig()):t.$message({message:"保存失败",type:"error"})})}):this.$message({message:"配置内容不能为空",type:"error"})},onCancel:function(){this.loadCanalConfig()},onBack:function(){history.go(-1)},onLoadTemplate:function(){var t=this;s().then(function(e){t.form.content=e.data})}}},f=u,l=(n("0f47"),n("2877")),m=Object(l["a"])(f,o,r,!1,null,"2e6a6604",null);e["default"]=m.exports}}]);
|
||||
@@ -0,0 +1 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-5b373aad"],{1248:function(t,e,n){"use strict";n.r(e);var r=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",[n("el-form",{ref:"form",attrs:{model:t.form}},[n("div",{staticClass:"filter-container",staticStyle:{"padding-left":"10px","padding-top":"20px"}},[n("el-input",{staticClass:"filter-item",staticStyle:{width:"200px"},attrs:{placeholder:"Instance名称"},model:{value:t.form.name,callback:function(e){t.$set(t.form,"name",e)},expression:"form.name"}}),t._v(" "),n("el-select",{staticClass:"filter-item",attrs:{placeholder:"所属集群/主机"},model:{value:t.form.clusterServerId,callback:function(e){t.$set(t.form,"clusterServerId",e)},expression:"form.clusterServerId"}},t._l(t.options,function(e){return n("el-option-group",{key:e.label,attrs:{label:e.label}},t._l(e.options,function(t){return n("el-option",{key:t.value,attrs:{label:t.label,value:t.value}})}),1)}),1),t._v(" "),n("el-button",{staticClass:"filter-item",attrs:{type:"primary"},on:{click:t.onSubmit}},[t._v("保存")]),t._v(" "),n("el-button",{staticClass:"filter-item",attrs:{type:"success"},on:{click:t.onLoadTemplate}},[t._v("载入模板")]),t._v(" "),n("el-button",{staticClass:"filter-item",attrs:{type:"info"},on:{click:t.onBack}},[t._v("返回")])],1),t._v(" "),n("editor",{attrs:{lang:"properties",theme:"chrome",width:"100%",height:800},on:{init:t.editorInit},model:{value:t.form.content,callback:function(e){t.$set(t.form,"content",e)},expression:"form.content"}})],1)],1)},a=[],c=(n("7f7f"),n("f546")),o=n("d631"),u={components:{editor:n("7c9e")},data:function(){return{options:[],form:{name:"",content:"",clusterServerId:""}}},created:function(){var t=this;Object(o["d"])().then(function(e){t.options=e.data})},methods:{editorInit:function(){n("2099"),n("be9d"),n("2968"),n("e0e5"),n("bb36"),n("0329"),n("95b8"),n("6a21")},onSubmit:function(){var t=this;""!==this.form.name?""!==this.form.clusterServerId?null!==this.form.content&&""!==this.form.content?this.$confirm("确定新建","确定新建",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then(function(){Object(c["a"])(t.form).then(function(e){"success"===e.data?(t.$message({message:"新建成功",type:"success"}),t.$router.push("/canalServer/canalInstances")):t.$message({message:"新建失败",type:"error"})})}):this.$message({message:"请输入配置内容",type:"error"}):this.$message({message:"请选择所属集群/主机",type:"error"}):this.$message({message:"请输入Instance名称",type:"error"})},onBack:function(){history.go(-1)},onLoadTemplate:function(){var t=this;Object(c["f"])().then(function(e){t.form.content=e.data})}}},i=u,s=(n("890e"),n("2877")),l=Object(s["a"])(i,r,a,!1,null,"e884438a",null);e["default"]=l.exports},"41be":function(t,e,n){},"890e":function(t,e,n){"use strict";var r=n("41be"),a=n.n(r);a.a},d631:function(t,e,n){"use strict";n.d(e,"c",function(){return a}),n.d(e,"a",function(){return c}),n.d(e,"e",function(){return o}),n.d(e,"b",function(){return u}),n.d(e,"d",function(){return i});var r=n("b775");function a(t){return Object(r["a"])({url:"/canal/clusters",method:"get",params:t})}function c(t){return Object(r["a"])({url:"/canal/cluster",method:"post",data:t})}function o(t){return Object(r["a"])({url:"/canal/cluster",method:"put",data:t})}function u(t){return Object(r["a"])({url:"/canal/cluster/"+t,method:"delete"})}function i(){return Object(r["a"])({url:"/canal/clustersAndServers",method:"get"})}},f546:function(t,e,n){"use strict";n.d(e,"e",function(){return a}),n.d(e,"b",function(){return c}),n.d(e,"k",function(){return o}),n.d(e,"a",function(){return u}),n.d(e,"c",function(){return i}),n.d(e,"i",function(){return s}),n.d(e,"j",function(){return l}),n.d(e,"g",function(){return f}),n.d(e,"h",function(){return d}),n.d(e,"d",function(){return m}),n.d(e,"f",function(){return p});var r=n("b775");function a(t){return Object(r["a"])({url:"/canal/instances",method:"get",params:t})}function c(t){return Object(r["a"])({url:"/canal/instance/"+t,method:"get"})}function o(t){return Object(r["a"])({url:"/canal/instance",method:"put",data:t})}function u(t){return Object(r["a"])({url:"/canal/instance",method:"post",data:t})}function i(t){return Object(r["a"])({url:"/canal/instance/"+t,method:"delete"})}function s(t,e){return Object(r["a"])({url:"/canal/instance/start/"+t+"/"+e,method:"put"})}function l(t,e){return Object(r["a"])({url:"/canal/instance/stop/"+t+"/"+e,method:"put"})}function f(t,e){return Object(r["a"])({url:"/canal/instance/log/"+t+"/"+e,method:"get"})}function d(t,e){return Object(r["a"])({url:"/canal/instance/status/"+t+"?option="+e,method:"put"})}function m(t){return Object(r["a"])({url:"/canal/active/instances/"+t,method:"get"})}function p(){return Object(r["a"])({url:"/canal/instance/template",method:"get"})}}}]);
|
||||
@@ -1 +0,0 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-63c8061b"],{"09a8":function(n,t,e){"use strict";e.r(t);var o=function(){var n=this,t=n.$createElement,e=n._self._c||t;return e("div",[e("el-form",{ref:"form",attrs:{model:n.form}},[e("div",{staticStyle:{"padding-left":"10px","padding-top":"20px"}},[e("el-form-item",[n._v("\n "+n._s(n.form.name)+" \n "),e("el-button",{attrs:{type:"primary"},on:{click:n.onSubmit}},[n._v("修改")]),n._v(" "),e("el-button",{attrs:{type:"warning"},on:{click:n.onCancel}},[n._v("重置")])],1)],1),n._v(" "),e("editor",{attrs:{lang:"properties",theme:"chrome",width:"100%",height:800},on:{init:n.editorInit},model:{value:n.form.content,callback:function(t){n.$set(n.form,"content",t)},expression:"form.content"}})],1)],1)},a=[],i=(e("7f7f"),e("b775"));function r(){return Object(i["a"])({url:"/canal/config",method:"get"})}function c(n){return Object(i["a"])({url:"/canal/config",method:"put",data:n})}var f={components:{editor:e("7c9e")},data:function(){return{form:{id:null,name:"",content:""}}},created:function(){this.loadCanalConfig()},methods:{editorInit:function(){e("2099"),e("be9d"),e("2968"),e("e0e5"),e("bb36"),e("0329"),e("95b8"),e("6a21")},loadCanalConfig:function(){var n=this;r().then(function(t){var e=t.data;n.form.id=e.id,n.form.name=e.name,n.form.content=e.content})},onSubmit:function(){var n=this;this.$confirm("修改Canal主配置可能会导致Server重启,是否继续?","确定修改",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then(function(){c(n.form).then(function(t){"success"===t.data?(n.$message({message:"修改成功",type:"success"}),n.loadCanalConfig()):n.$message({message:"修改失败",type:"error"})})})},onCancel:function(){this.loadCanalConfig()}}},s=f,u=(e("a447"),e("2877")),l=Object(u["a"])(s,o,a,!1,null,"5feb688e",null);t["default"]=l.exports},"70a0":function(n,t,e){},a447:function(n,t,e){"use strict";var o=e("70a0"),a=e.n(o);a.a}}]);
|
||||
@@ -0,0 +1 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-69386cf0"],{2829:function(e,t,s){},"646d":function(e,t,s){"use strict";var r=s("2829"),o=s.n(r);o.a},"9fb7":function(e,t,s){"use strict";s.r(t);var r=function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("div",{staticClass:"app-container",staticStyle:{width:"600px"}},[s("el-form",{ref:"form",attrs:{rules:e.rules,model:e.form,"label-width":"120px"}},[s("el-form-item",{attrs:{label:"用户名",prop:"username"}},[s("el-input",{staticStyle:{width:"200px"},model:{value:e.form.username,callback:function(t){e.$set(e.form,"username",t)},expression:"form.username"}})],1),e._v(" "),s("el-form-item",{attrs:{label:"旧密码",prop:"oldPassword"}},[s("el-input",{staticStyle:{width:"200px"},attrs:{type:"password"},model:{value:e.form.oldPassword,callback:function(t){e.$set(e.form,"oldPassword",t)},expression:"form.oldPassword"}})],1),e._v(" "),s("el-form-item",{attrs:{label:"密码",prop:"password"}},[s("el-input",{staticStyle:{width:"200px"},attrs:{placeholder:"空为不修改密码",type:"password"},model:{value:e.form.password,callback:function(t){e.$set(e.form,"password",t)},expression:"form.password"}})],1),e._v(" "),s("el-form-item",[s("el-button",{attrs:{type:"primary"},on:{click:e.onSubmit}},[e._v("修改")]),e._v(" "),s("el-button",{on:{click:e.onCancel}},[e._v("取消")])],1)],1)],1)},o=[],a=s("c24f"),n=s("5f87"),l={data:function(){return{form:{username:"",oldPassword:"",password:null},rules:{username:[{required:!0,message:"用户名能为空",trigger:"change"}],oldPassword:[{required:!0,message:"旧密码不能为空",trigger:"change"}]}}},created:function(){this.fetchUserInfo()},methods:{fetchUserInfo:function(){var e=this;Object(a["a"])(Object(n["a"])()).then(function(t){e.form.username=t.data.username})},onSubmit:function(){var e=this;this.$refs["form"].validate(function(t){t&&Object(a["d"])(e.form).then(function(t){"success"===t.data?(e.form.oldPassword="",e.form.password=null,e.$nextTick(function(){e.$refs["form"].clearValidate()}),e.$message({message:"修改用户信息成功",type:"success"})):e.$message({message:"修改用户信息成功",type:"error"})})})},onCancel:function(){history.go(-1)}}},i=l,c=(s("646d"),s("2877")),f=Object(c["a"])(i,r,o,!1,null,"756ebb70",null);t["default"]=f.exports}}]);
|
||||
@@ -1 +0,0 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-7279d7fc"],{"926c":function(n,t,e){"use strict";var o=e("f88d"),a=e.n(o);a.a},b0a2:function(n,t,e){"use strict";e.r(t);var o=function(){var n=this,t=n.$createElement,e=n._self._c||t;return e("div",[e("el-form",{ref:"form",attrs:{model:n.form}},[e("div",{staticStyle:{"padding-left":"10px","padding-top":"20px"}},[e("el-form-item",[n._v("\n "+n._s(n.form.name)+" \n "),e("el-button",{attrs:{type:"primary"},on:{click:n.onSubmit}},[n._v("修改")]),n._v(" "),e("el-button",{attrs:{type:"warning"},on:{click:n.onCancel}},[n._v("重置")]),n._v(" "),e("el-button",{attrs:{type:"info"},on:{click:n.onBack}},[n._v("返回")])],1)],1),n._v(" "),e("editor",{attrs:{lang:"properties",theme:"chrome",width:"100%",height:800},on:{init:n.editorInit},model:{value:n.form.content,callback:function(t){n.$set(n.form,"content",t)},expression:"form.content"}})],1)],1)},a=[],c=(e("7f7f"),e("f546")),r={components:{editor:e("7c9e")},data:function(){return{form:{id:null,name:"",content:""}}},created:function(){this.loadCanalConfig()},methods:{editorInit:function(){e("2099"),e("be9d"),e("2968"),e("e0e5"),e("bb36"),e("0329"),e("95b8"),e("6a21")},loadCanalConfig:function(){var n=this;Object(c["b"])(this.$route.query.id).then(function(t){var e=t.data;n.form.id=e.id,n.form.name=e.name+"/instance.propertios",n.form.content=e.content})},onSubmit:function(){var n=this;this.$confirm("修改Canal实例配置可能会导致实例重启,是否继续?","确定修改",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then(function(){Object(c["h"])(n.form).then(function(t){"success"===t.data?(n.$message({message:"修改成功",type:"success"}),n.loadCanalConfig()):n.$message({message:"修改失败",type:"error"})})})},onCancel:function(){this.loadCanalConfig()},onBack:function(){history.go(-1)}}},i=r,u=(e("926c"),e("2877")),f=Object(u["a"])(i,o,a,!1,null,"11496239",null);t["default"]=f.exports},f546:function(n,t,e){"use strict";e.d(t,"d",function(){return a}),e.d(t,"b",function(){return c}),e.d(t,"h",function(){return r}),e.d(t,"a",function(){return i}),e.d(t,"c",function(){return u}),e.d(t,"f",function(){return f}),e.d(t,"g",function(){return s}),e.d(t,"e",function(){return l});var o=e("b775");function a(n){return Object(o["a"])({url:"/canal/instances",method:"get",params:n})}function c(n){return Object(o["a"])({url:"/canal/instance/"+n,method:"get"})}function r(n){return Object(o["a"])({url:"/canal/instance",method:"put",data:n})}function i(n){return Object(o["a"])({url:"/canal/instance",method:"post",data:n})}function u(n){return Object(o["a"])({url:"/canal/instance/"+n,method:"delete"})}function f(n,t){return Object(o["a"])({url:"/canal/instance/start/"+n+"/"+t,method:"put"})}function s(n,t){return Object(o["a"])({url:"/canal/instance/stop/"+n+"/"+t,method:"put"})}function l(n,t){return Object(o["a"])({url:"/canal/instance/log/"+n+"/"+t,method:"get"})}},f88d:function(n,t,e){}}]);
|
||||
@@ -1 +1 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-7ec889b7"],{"11ec":function(t,n,e){},"7f19":function(t,n,e){"use strict";var c=e("11ec"),r=e.n(c);r.a},"7f84":function(t,n,e){"use strict";e.r(n);var c=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",[e("el-form",{ref:"form",attrs:{model:t.form}},[e("div",{staticStyle:{"padding-left":"10px","padding-right":"10px","padding-top":"20px"}},[e("el-form-item",[t._v("\n "+t._s(t.form.instance)+" \n "),e("el-button",{attrs:{type:"primary"},on:{click:t.onRefresh}},[t._v("刷新")]),t._v(" "),e("el-button",{attrs:{type:"info"},on:{click:t.onBack}},[t._v("返回")])],1),t._v(" "),e("el-input",{attrs:{rows:35,readonly:"readonly",type:"textarea"},model:{value:t.form.desc,callback:function(n){t.$set(t.form,"desc",n)},expression:"form.desc"}})],1)])],1)},r=[],a=e("f546"),o={data:function(){return{form:{instance:"",desc:""}}},created:function(){this.fetchData()},methods:{fetchData:function(){var t=this;Object(a["e"])(this.$route.query.id,this.$route.query.nodeId).then(function(n){t.form.instance=n.data.instance+".log",t.form.desc=n.data.log})},onRefresh:function(){this.fetchData()},onBack:function(){history.go(-1)}}},u=o,i=(e("7f19"),e("2877")),f=Object(i["a"])(u,c,r,!1,null,"0b80198c",null);n["default"]=f.exports},f546:function(t,n,e){"use strict";e.d(n,"d",function(){return r}),e.d(n,"b",function(){return a}),e.d(n,"h",function(){return o}),e.d(n,"a",function(){return u}),e.d(n,"c",function(){return i}),e.d(n,"f",function(){return f}),e.d(n,"g",function(){return s}),e.d(n,"e",function(){return d});var c=e("b775");function r(t){return Object(c["a"])({url:"/canal/instances",method:"get",params:t})}function a(t){return Object(c["a"])({url:"/canal/instance/"+t,method:"get"})}function o(t){return Object(c["a"])({url:"/canal/instance",method:"put",data:t})}function u(t){return Object(c["a"])({url:"/canal/instance",method:"post",data:t})}function i(t){return Object(c["a"])({url:"/canal/instance/"+t,method:"delete"})}function f(t,n){return Object(c["a"])({url:"/canal/instance/start/"+t+"/"+n,method:"put"})}function s(t,n){return Object(c["a"])({url:"/canal/instance/stop/"+t+"/"+n,method:"put"})}function d(t,n){return Object(c["a"])({url:"/canal/instance/log/"+t+"/"+n,method:"get"})}}}]);
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-7ec889b7"],{"11ec":function(t,n,e){},"7f19":function(t,n,e){"use strict";var c=e("11ec"),r=e.n(c);r.a},"7f84":function(t,n,e){"use strict";e.r(n);var c=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",[e("el-form",{ref:"form",attrs:{model:t.form}},[e("div",{staticStyle:{"padding-left":"10px","padding-right":"10px","padding-top":"20px"}},[e("el-form-item",[t._v("\n "+t._s(t.form.instance)+" \n "),e("el-button",{attrs:{type:"primary"},on:{click:t.onRefresh}},[t._v("刷新")]),t._v(" "),e("el-button",{attrs:{type:"info"},on:{click:t.onBack}},[t._v("返回")])],1),t._v(" "),e("el-input",{attrs:{rows:35,readonly:"readonly",type:"textarea"},model:{value:t.form.desc,callback:function(n){t.$set(t.form,"desc",n)},expression:"form.desc"}})],1)])],1)},r=[],a=e("f546"),o={data:function(){return{form:{instance:"",desc:""}}},created:function(){this.fetchData()},methods:{fetchData:function(){var t=this;Object(a["f"])(this.$route.query.id,this.$route.query.nodeId).then(function(n){t.form.instance=n.data.instance+".log",t.form.desc=n.data.log})},onRefresh:function(){this.fetchData()},onBack:function(){history.go(-1)}}},u=o,i=(e("7f19"),e("2877")),f=Object(i["a"])(u,c,r,!1,null,"0b80198c",null);n["default"]=f.exports},f546:function(t,n,e){"use strict";e.d(n,"e",function(){return r}),e.d(n,"b",function(){return a}),e.d(n,"j",function(){return o}),e.d(n,"a",function(){return u}),e.d(n,"c",function(){return i}),e.d(n,"h",function(){return f}),e.d(n,"i",function(){return s}),e.d(n,"f",function(){return d}),e.d(n,"g",function(){return l}),e.d(n,"d",function(){return h});var c=e("b775");function r(t){return Object(c["a"])({url:"/canal/instances",method:"get",params:t})}function a(t){return Object(c["a"])({url:"/canal/instance/"+t,method:"get"})}function o(t){return Object(c["a"])({url:"/canal/instance",method:"put",data:t})}function u(t){return Object(c["a"])({url:"/canal/instance",method:"post",data:t})}function i(t){return Object(c["a"])({url:"/canal/instance/"+t,method:"delete"})}function f(t,n){return Object(c["a"])({url:"/canal/instance/start/"+t+"/"+n,method:"put"})}function s(t,n){return Object(c["a"])({url:"/canal/instance/stop/"+t+"/"+n,method:"put"})}function d(t,n){return Object(c["a"])({url:"/canal/instance/log/"+t+"/"+n,method:"get"})}function l(t,n){return Object(c["a"])({url:"/canal/instance/status/"+t+"?option="+n,method:"put"})}function h(t){return Object(c["a"])({url:"/canal/active/instances/"+t,method:"get"})}}}]);
|
||||
@@ -0,0 +1 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-7ec889b7"],{"11ec":function(t,n,e){},"7f19":function(t,n,e){"use strict";var c=e("11ec"),r=e.n(c);r.a},"7f84":function(t,n,e){"use strict";e.r(n);var c=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",[e("el-form",{ref:"form",attrs:{model:t.form}},[e("div",{staticStyle:{"padding-left":"10px","padding-right":"10px","padding-top":"20px"}},[e("el-form-item",[t._v("\n "+t._s(t.form.instance)+" \n "),e("el-button",{attrs:{type:"primary"},on:{click:t.onRefresh}},[t._v("刷新")]),t._v(" "),e("el-button",{attrs:{type:"info"},on:{click:t.onBack}},[t._v("返回")])],1),t._v(" "),e("el-input",{attrs:{rows:35,readonly:"readonly",type:"textarea"},model:{value:t.form.desc,callback:function(n){t.$set(t.form,"desc",n)},expression:"form.desc"}})],1)])],1)},r=[],a=e("f546"),o={data:function(){return{form:{instance:"",desc:""}}},created:function(){this.fetchData()},methods:{fetchData:function(){var t=this;Object(a["g"])(this.$route.query.id,this.$route.query.nodeId).then(function(n){t.form.instance=n.data.instance+".log",t.form.desc=n.data.log})},onRefresh:function(){this.fetchData()},onBack:function(){history.go(-1)}}},u=o,i=(e("7f19"),e("2877")),f=Object(i["a"])(u,c,r,!1,null,"0b80198c",null);n["default"]=f.exports},f546:function(t,n,e){"use strict";e.d(n,"e",function(){return r}),e.d(n,"b",function(){return a}),e.d(n,"k",function(){return o}),e.d(n,"a",function(){return u}),e.d(n,"c",function(){return i}),e.d(n,"i",function(){return f}),e.d(n,"j",function(){return s}),e.d(n,"g",function(){return d}),e.d(n,"h",function(){return l}),e.d(n,"d",function(){return m}),e.d(n,"f",function(){return h});var c=e("b775");function r(t){return Object(c["a"])({url:"/canal/instances",method:"get",params:t})}function a(t){return Object(c["a"])({url:"/canal/instance/"+t,method:"get"})}function o(t){return Object(c["a"])({url:"/canal/instance",method:"put",data:t})}function u(t){return Object(c["a"])({url:"/canal/instance",method:"post",data:t})}function i(t){return Object(c["a"])({url:"/canal/instance/"+t,method:"delete"})}function f(t,n){return Object(c["a"])({url:"/canal/instance/start/"+t+"/"+n,method:"put"})}function s(t,n){return Object(c["a"])({url:"/canal/instance/stop/"+t+"/"+n,method:"put"})}function d(t,n){return Object(c["a"])({url:"/canal/instance/log/"+t+"/"+n,method:"get"})}function l(t,n){return Object(c["a"])({url:"/canal/instance/status/"+t+"?option="+n,method:"put"})}function m(t){return Object(c["a"])({url:"/canal/active/instances/"+t,method:"get"})}function h(){return Object(c["a"])({url:"/canal/instance/template",method:"get"})}}}]);
|
||||
@@ -0,0 +1 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-98f505d0"],{"4e8a":function(t,e,n){"use strict";var o=n("727e"),r=n.n(o);r.a},"727e":function(t,e,n){},aa99:function(t,e,n){"use strict";n.r(e);var o=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",[n("el-form",{ref:"form",attrs:{model:t.form}},[n("div",{staticStyle:{"padding-left":"10px","padding-top":"20px"}},[n("el-form-item",[t._v("\n "+t._s(t.form.name)+" \n "),n("el-button",{attrs:{type:"primary"},on:{click:t.onSubmit}},[t._v("保存")]),t._v(" "),n("el-button",{attrs:{type:"warning"},on:{click:t.onCancel}},[t._v("重置")]),t._v(" "),n("el-button",{attrs:{type:"info"},on:{click:t.onBack}},[t._v("返回")])],1)],1),t._v(" "),n("editor",{attrs:{lang:"properties",theme:"chrome",width:"100%",height:800},on:{init:t.editorInit},model:{value:t.form.content,callback:function(e){t.$set(t.form,"content",e)},expression:"form.content"}})],1)],1)},r=[],a=(n("7f7f"),n("b775"));function i(t,e){return Object(a["a"])({url:"/canal/config/"+t+"/"+e,method:"get"})}function c(t){return Object(a["a"])({url:"/canal/config",method:"put",data:t})}var s={components:{editor:n("7c9e")},data:function(){return{form:{id:null,name:"",content:"",serverId:null,clusterId:null}}},created:function(){this.loadCanalConfig()},methods:{editorInit:function(){n("2099"),n("be9d"),n("2968"),n("e0e5"),n("bb36"),n("0329"),n("95b8"),n("6a21")},loadCanalConfig:function(){var t=this,e=0,n=0;this.$route.query.clusterId?e=this.$route.query.clusterId:this.$route.query.serverId&&(n=this.$route.query.serverId),i(e,n).then(function(e){var n=e.data;t.form.id=n.id,t.form.name=n.name,t.form.content=n.content,t.form.serverId=t.$route.query.serverId,t.form.clusterId=t.$route.query.clusterId})},onSubmit:function(){var t=this;null!==this.form.content&&""!==this.form.content?this.$confirm("修改主配置可能会导致Server重启,是否继续?","确定修改",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then(function(){c(t.form).then(function(e){"success"===e.data?(t.$message({message:"保存成功",type:"success"}),t.loadCanalConfig()):t.$message({message:"保存失败",type:"error"})})}):this.$message({message:"配置内容不能为空",type:"error"})},onCancel:function(){this.loadCanalConfig()},onBack:function(){history.go(-1)}}},u=s,f=(n("4e8a"),n("2877")),l=Object(f["a"])(u,o,r,!1,null,"35af5ff9",null);e["default"]=l.exports}}]);
|
||||
@@ -0,0 +1 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-bd1d44ee"],{"878d":function(t,n,e){},b0a2:function(t,n,e){"use strict";e.r(n);var r=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",[e("el-form",{ref:"form",attrs:{model:t.form}},[e("div",{staticStyle:{"padding-left":"10px","padding-top":"20px"}},[e("el-form-item",[t._v("\n "+t._s(t.form.name)+" \n "),e("el-select",{staticClass:"filter-item",attrs:{placeholder:"所属集群/主机"},model:{value:t.form.clusterServerId,callback:function(n){t.$set(t.form,"clusterServerId",n)},expression:"form.clusterServerId"}},t._l(t.options,function(n){return e("el-option-group",{key:n.label,attrs:{label:n.label}},t._l(n.options,function(t){return e("el-option",{key:t.value,attrs:{label:t.label,value:t.value}})}),1)}),1),t._v(" "),e("el-button",{attrs:{type:"primary"},on:{click:t.onSubmit}},[t._v("修改")]),t._v(" "),e("el-button",{attrs:{type:"warning"},on:{click:t.onCancel}},[t._v("重置")]),t._v(" "),e("el-button",{attrs:{type:"info"},on:{click:t.onBack}},[t._v("返回")])],1)],1),t._v(" "),e("editor",{attrs:{lang:"properties",theme:"chrome",width:"100%",height:800},on:{init:t.editorInit},model:{value:t.form.content,callback:function(n){t.$set(t.form,"content",n)},expression:"form.content"}})],1)],1)},o=[],c=(e("7f7f"),e("f546")),a=e("d631"),u={components:{editor:e("7c9e")},data:function(){return{options:[],form:{id:null,name:"",content:"",clusterServerId:""}}},created:function(){var t=this;this.loadCanalConfig(),Object(a["d"])().then(function(n){t.options=n.data})},methods:{editorInit:function(){e("2099"),e("be9d"),e("2968"),e("e0e5"),e("bb36"),e("0329"),e("95b8"),e("6a21")},loadCanalConfig:function(){var t=this;Object(c["b"])(this.$route.query.id).then(function(n){var e=n.data;t.form.id=e.id,t.form.name=e.name+"/instance.propertios",t.form.content=e.content,t.form.clusterServerId=e.clusterServerId})},onSubmit:function(){var t=this;this.$confirm("修改Instance配置可能会导致重启,是否继续?","确定修改",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then(function(){Object(c["j"])(t.form).then(function(n){"success"===n.data?(t.$message({message:"修改成功",type:"success"}),t.loadCanalConfig()):t.$message({message:"修改失败",type:"error"})})})},onCancel:function(){this.loadCanalConfig()},onBack:function(){history.go(-1)}}},i=u,l=(e("de9c"),e("2877")),s=Object(l["a"])(i,r,o,!1,null,"5c332416",null);n["default"]=s.exports},d631:function(t,n,e){"use strict";e.d(n,"c",function(){return o}),e.d(n,"a",function(){return c}),e.d(n,"e",function(){return a}),e.d(n,"b",function(){return u}),e.d(n,"d",function(){return i});var r=e("b775");function o(t){return Object(r["a"])({url:"/canal/clusters",method:"get",params:t})}function c(t){return Object(r["a"])({url:"/canal/cluster",method:"post",data:t})}function a(t){return Object(r["a"])({url:"/canal/cluster",method:"put",data:t})}function u(t){return Object(r["a"])({url:"/canal/cluster/"+t,method:"delete"})}function i(){return Object(r["a"])({url:"/canal/clustersAndServers",method:"get"})}},de9c:function(t,n,e){"use strict";var r=e("878d"),o=e.n(r);o.a},f546:function(t,n,e){"use strict";e.d(n,"e",function(){return o}),e.d(n,"b",function(){return c}),e.d(n,"j",function(){return a}),e.d(n,"a",function(){return u}),e.d(n,"c",function(){return i}),e.d(n,"h",function(){return l}),e.d(n,"i",function(){return s}),e.d(n,"f",function(){return d}),e.d(n,"g",function(){return f}),e.d(n,"d",function(){return m});var r=e("b775");function o(t){return Object(r["a"])({url:"/canal/instances",method:"get",params:t})}function c(t){return Object(r["a"])({url:"/canal/instance/"+t,method:"get"})}function a(t){return Object(r["a"])({url:"/canal/instance",method:"put",data:t})}function u(t){return Object(r["a"])({url:"/canal/instance",method:"post",data:t})}function i(t){return Object(r["a"])({url:"/canal/instance/"+t,method:"delete"})}function l(t,n){return Object(r["a"])({url:"/canal/instance/start/"+t+"/"+n,method:"put"})}function s(t,n){return Object(r["a"])({url:"/canal/instance/stop/"+t+"/"+n,method:"put"})}function d(t,n){return Object(r["a"])({url:"/canal/instance/log/"+t+"/"+n,method:"get"})}function f(t,n){return Object(r["a"])({url:"/canal/instance/status/"+t+"?option="+n,method:"put"})}function m(t){return Object(r["a"])({url:"/canal/active/instances/"+t,method:"get"})}}}]);
|
||||
@@ -0,0 +1 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-bd1d44ee"],{"878d":function(t,n,e){},b0a2:function(t,n,e){"use strict";e.r(n);var r=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",[e("el-form",{ref:"form",attrs:{model:t.form}},[e("div",{staticStyle:{"padding-left":"10px","padding-top":"20px"}},[e("el-form-item",[t._v("\n "+t._s(t.form.name)+" \n "),e("el-select",{staticClass:"filter-item",attrs:{placeholder:"所属集群/主机"},model:{value:t.form.clusterServerId,callback:function(n){t.$set(t.form,"clusterServerId",n)},expression:"form.clusterServerId"}},t._l(t.options,function(n){return e("el-option-group",{key:n.label,attrs:{label:n.label}},t._l(n.options,function(t){return e("el-option",{key:t.value,attrs:{label:t.label,value:t.value}})}),1)}),1),t._v(" "),e("el-button",{attrs:{type:"primary"},on:{click:t.onSubmit}},[t._v("修改")]),t._v(" "),e("el-button",{attrs:{type:"warning"},on:{click:t.onCancel}},[t._v("重置")]),t._v(" "),e("el-button",{attrs:{type:"info"},on:{click:t.onBack}},[t._v("返回")])],1)],1),t._v(" "),e("editor",{attrs:{lang:"properties",theme:"chrome",width:"100%",height:800},on:{init:t.editorInit},model:{value:t.form.content,callback:function(n){t.$set(t.form,"content",n)},expression:"form.content"}})],1)],1)},c=[],o=(e("7f7f"),e("f546")),a=e("d631"),u={components:{editor:e("7c9e")},data:function(){return{options:[],form:{id:null,name:"",content:"",clusterServerId:""}}},created:function(){var t=this;this.loadCanalConfig(),Object(a["d"])().then(function(n){t.options=n.data})},methods:{editorInit:function(){e("2099"),e("be9d"),e("2968"),e("e0e5"),e("bb36"),e("0329"),e("95b8"),e("6a21")},loadCanalConfig:function(){var t=this;Object(o["b"])(this.$route.query.id).then(function(n){var e=n.data;t.form.id=e.id,t.form.name=e.name+"/instance.propertios",t.form.content=e.content,t.form.clusterServerId=e.clusterServerId})},onSubmit:function(){var t=this;this.$confirm("修改Instance配置可能会导致重启,是否继续?","确定修改",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then(function(){Object(o["k"])(t.form).then(function(n){"success"===n.data?(t.$message({message:"修改成功",type:"success"}),t.loadCanalConfig()):t.$message({message:"修改失败",type:"error"})})})},onCancel:function(){this.loadCanalConfig()},onBack:function(){history.go(-1)}}},i=u,l=(e("de9c"),e("2877")),s=Object(l["a"])(i,r,c,!1,null,"5c332416",null);n["default"]=s.exports},d631:function(t,n,e){"use strict";e.d(n,"c",function(){return c}),e.d(n,"a",function(){return o}),e.d(n,"e",function(){return a}),e.d(n,"b",function(){return u}),e.d(n,"d",function(){return i});var r=e("b775");function c(t){return Object(r["a"])({url:"/canal/clusters",method:"get",params:t})}function o(t){return Object(r["a"])({url:"/canal/cluster",method:"post",data:t})}function a(t){return Object(r["a"])({url:"/canal/cluster",method:"put",data:t})}function u(t){return Object(r["a"])({url:"/canal/cluster/"+t,method:"delete"})}function i(){return Object(r["a"])({url:"/canal/clustersAndServers",method:"get"})}},de9c:function(t,n,e){"use strict";var r=e("878d"),c=e.n(r);c.a},f546:function(t,n,e){"use strict";e.d(n,"e",function(){return c}),e.d(n,"b",function(){return o}),e.d(n,"k",function(){return a}),e.d(n,"a",function(){return u}),e.d(n,"c",function(){return i}),e.d(n,"i",function(){return l}),e.d(n,"j",function(){return s}),e.d(n,"g",function(){return d}),e.d(n,"h",function(){return f}),e.d(n,"d",function(){return m}),e.d(n,"f",function(){return b});var r=e("b775");function c(t){return Object(r["a"])({url:"/canal/instances",method:"get",params:t})}function o(t){return Object(r["a"])({url:"/canal/instance/"+t,method:"get"})}function a(t){return Object(r["a"])({url:"/canal/instance",method:"put",data:t})}function u(t){return Object(r["a"])({url:"/canal/instance",method:"post",data:t})}function i(t){return Object(r["a"])({url:"/canal/instance/"+t,method:"delete"})}function l(t,n){return Object(r["a"])({url:"/canal/instance/start/"+t+"/"+n,method:"put"})}function s(t,n){return Object(r["a"])({url:"/canal/instance/stop/"+t+"/"+n,method:"put"})}function d(t,n){return Object(r["a"])({url:"/canal/instance/log/"+t+"/"+n,method:"get"})}function f(t,n){return Object(r["a"])({url:"/canal/instance/status/"+t+"?option="+n,method:"put"})}function m(t){return Object(r["a"])({url:"/canal/active/instances/"+t,method:"get"})}function b(){return Object(r["a"])({url:"/canal/instance/template",method:"get"})}}}]);
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,42 @@
|
||||
package com.alibaba.otter.canal.admin;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.alibaba.otter.canal.admin.connector.SimpleAdminConnector;
|
||||
|
||||
@Ignore
|
||||
public class SimpleAdminConnectorTest {
|
||||
|
||||
@Test
|
||||
public void testSimple() {
|
||||
SimpleAdminConnector connector = new SimpleAdminConnector("127.0.0.1", 11110, "admin", "admin");
|
||||
connector.connect();
|
||||
System.out.println("check 1 : " + connector.check());
|
||||
System.out.println("getRunning Before stop 1 : " + connector.getRunningInstances());
|
||||
System.out.println("stop 1: " + connector.stop());
|
||||
System.out.println("getRunning After stop 1 : " + connector.getRunningInstances());
|
||||
System.out.println("check 1 : " + connector.check());
|
||||
System.out.println("listFile 1 : " + connector.listCanalLog());
|
||||
System.out.println("getFile 1 : " + connector.canalLog(10));
|
||||
connector.disconnect();
|
||||
|
||||
connector.connect();
|
||||
System.out.println("check 2 : " + connector.check());
|
||||
System.out.println("get Running Before start : " + connector.getRunningInstances());
|
||||
System.out.println("start 2 : " + connector.start());
|
||||
System.out.println("get Running After start : " + connector.getRunningInstances());
|
||||
System.out.println("check 2 : " + connector.check());
|
||||
System.out.println("check after before example 2 : " + connector.checkInstance("example"));
|
||||
System.out.println("stop example 2 : " + connector.stopInstance("example"));
|
||||
System.out.println("check example 2 : " + connector.checkInstance("example"));
|
||||
System.out.println("start example 2 : " + connector.startInstance("example"));
|
||||
System.out.println("check after start example 2 : " + connector.checkInstance("example"));
|
||||
System.out.println("listFile 2 : " + connector.listCanalLog());
|
||||
System.out.println("getFile 2 : " + connector.canalLog(10));
|
||||
|
||||
System.out.println("listFile 3 : " + connector.listInstanceLog("example"));
|
||||
System.out.println("getFile 3 : " + connector.instanceLog("example", "example.log", 10));
|
||||
connector.disconnect();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>canal-admin</artifactId>
|
||||
<groupId>com.alibaba.otter</groupId>
|
||||
<version>1.1.4-SNAPSHOT</version>
|
||||
<version>1.1.4</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -20,11 +18,11 @@
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<id>npm</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>env</name>
|
||||
<value>release</value>
|
||||
<value>npm</value>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
|
||||
46
canal-admin/canal-admin-ui/src/api/canalCluster.js
Normal file
46
canal-admin/canal-admin-ui/src/api/canalCluster.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function getCanalClusters(params) {
|
||||
return request({
|
||||
url: '/canal/clusters',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export function addCanalCluster(data) {
|
||||
return request({
|
||||
url: '/canal/cluster',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function canalClusterDetail(id) {
|
||||
return request({
|
||||
url: '/canal/cluster/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function updateCanalCluster(data) {
|
||||
return request({
|
||||
url: '/canal/cluster',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteCanalCluster(id) {
|
||||
return request({
|
||||
url: '/canal/cluster/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export function getClustersAndServers() {
|
||||
return request({
|
||||
url: '/canal/clustersAndServers',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function getCanalConfig() {
|
||||
export function getCanalConfig(clusterId, serverId) {
|
||||
return request({
|
||||
url: '/canal/config',
|
||||
url: '/canal/config/' + clusterId + '/' + serverId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@@ -14,3 +14,10 @@ export function updateCanalConfig(data) {
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getTemplateConfig() {
|
||||
return request({
|
||||
url: '/canal/config/template',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -58,3 +58,24 @@ export function instanceLog(id, nodeId) {
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function instanceStatus(id, option) {
|
||||
return request({
|
||||
url: '/canal/instance/status/' + id + '?option=' + option,
|
||||
method: 'put'
|
||||
})
|
||||
}
|
||||
|
||||
export function getActiveInstances(serverId) {
|
||||
return request({
|
||||
url: '/canal/active/instances/' + serverId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getTemplateInstance() {
|
||||
return request({
|
||||
url: '/canal/instance/template',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
101
canal-admin/canal-admin-ui/src/components/Pagination/index.vue
Normal file
101
canal-admin/canal-admin-ui/src/components/Pagination/index.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div :class="{'hidden':hidden}" class="pagination-container">
|
||||
<el-pagination
|
||||
:background="background"
|
||||
:current-page.sync="currentPage"
|
||||
:page-size.sync="pageSize"
|
||||
:layout="layout"
|
||||
:page-sizes="pageSizes"
|
||||
:total="total"
|
||||
v-bind="$attrs"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { scrollTo } from '@/utils/scrollTo'
|
||||
|
||||
export default {
|
||||
name: 'Pagination',
|
||||
props: {
|
||||
total: {
|
||||
required: true,
|
||||
type: Number
|
||||
},
|
||||
page: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 20
|
||||
},
|
||||
pageSizes: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [10, 20, 30, 50]
|
||||
}
|
||||
},
|
||||
layout: {
|
||||
type: String,
|
||||
default: 'total, sizes, prev, pager, next, jumper'
|
||||
},
|
||||
background: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
autoScroll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
hidden: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentPage: {
|
||||
get() {
|
||||
return this.page
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:page', val)
|
||||
}
|
||||
},
|
||||
pageSize: {
|
||||
get() {
|
||||
return this.limit
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:limit', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSizeChange(val) {
|
||||
this.$emit('pagination', { page: this.currentPage, limit: val })
|
||||
if (this.autoScroll) {
|
||||
scrollTo(0, 800)
|
||||
}
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.$emit('pagination', { page: val, limit: this.pageSize })
|
||||
if (this.autoScroll) {
|
||||
scrollTo(0, 800)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pagination-container {
|
||||
background: #fff;
|
||||
padding: 32px 16px;
|
||||
}
|
||||
.pagination-container.hidden {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -77,49 +77,56 @@ export const constantRoutes = [
|
||||
meta: { title: 'Canal Server', icon: 'example' },
|
||||
children: [
|
||||
{
|
||||
path: 'nodeServers',
|
||||
name: '节点状态',
|
||||
component: () => import('@/views/canalServer/NodeServer'),
|
||||
meta: { title: '节点管理', icon: 'tree' }
|
||||
path: 'canalClusters',
|
||||
name: 'Canal 集群管理',
|
||||
component: () => import('@/views/canalServer/CanalCluster'),
|
||||
meta: { title: '集群管理', icon: 'tree' }
|
||||
},
|
||||
{
|
||||
path: 'config',
|
||||
name: 'Canal主配置',
|
||||
component: () => import('@/views/canalServer/Config'),
|
||||
meta: { title: 'Canal主配置', icon: 'form' }
|
||||
path: 'nodeServers',
|
||||
name: 'Server 状态',
|
||||
component: () => import('@/views/canalServer/NodeServer'),
|
||||
meta: { title: 'Server 管理', icon: 'form' }
|
||||
},
|
||||
{
|
||||
path: 'nodeServer/config',
|
||||
name: 'Server 配置',
|
||||
component: () => import('@/views/canalServer/CanalConfig'),
|
||||
meta: { title: 'Server 配置' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'canalInstances',
|
||||
name: '实例管理',
|
||||
name: 'Instance 管理',
|
||||
component: () => import('@/views/canalServer/CanalInstance'),
|
||||
meta: { title: '实例管理', icon: 'nested' }
|
||||
meta: { title: 'Instance 管理', icon: 'nested' }
|
||||
},
|
||||
{
|
||||
path: 'canalInstance/add',
|
||||
name: '新建实例配置',
|
||||
name: '新建Instance配置',
|
||||
component: () => import('@/views/canalServer/CanalInstanceAdd'),
|
||||
meta: { title: '新建实例配置' },
|
||||
meta: { title: '新建Instance配置' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'canalInstance/modify',
|
||||
name: '修改实例配置',
|
||||
name: '修改Instance配置',
|
||||
component: () => import('@/views/canalServer/CanalInstanceUpdate'),
|
||||
meta: { title: '修改实例配置' },
|
||||
meta: { title: '修改Instance配置' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'nodeServer/log',
|
||||
name: 'Canal日志',
|
||||
name: 'Server 日志',
|
||||
component: () => import('@/views/canalServer/CanalLogDetail'),
|
||||
meta: { title: 'Canal日志' },
|
||||
meta: { title: 'Server 日志' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'canalInstance/log',
|
||||
name: 'Canal Instance日志',
|
||||
name: 'Instance 日志',
|
||||
component: () => import('@/views/canalServer/CanalInstanceLogDetail'),
|
||||
meta: { title: 'Canal Instance日志' },
|
||||
meta: { title: 'Instance 日志' },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
|
||||
50
canal-admin/canal-admin-ui/src/utils/scrollTo.js
Normal file
50
canal-admin/canal-admin-ui/src/utils/scrollTo.js
Normal file
@@ -0,0 +1,50 @@
|
||||
Math.easeInOutQuad = function(t, b, c, d) {
|
||||
t /= d / 2
|
||||
if (t < 1) {
|
||||
return c / 2 * t * t + b
|
||||
}
|
||||
t--
|
||||
return -c / 2 * (t * (t - 2) - 1) + b
|
||||
}
|
||||
|
||||
// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
|
||||
var requestAnimFrame = (function() {
|
||||
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
|
||||
})()
|
||||
|
||||
// because it's so fucking difficult to detect the scrolling element, just move them all
|
||||
function move(amount) {
|
||||
document.documentElement.scrollTop = amount
|
||||
document.body.parentNode.scrollTop = amount
|
||||
document.body.scrollTop = amount
|
||||
}
|
||||
|
||||
function position() {
|
||||
return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
|
||||
}
|
||||
|
||||
export function scrollTo(to, duration, callback) {
|
||||
const start = position()
|
||||
const change = to - start
|
||||
const increment = 20
|
||||
let currentTime = 0
|
||||
duration = (typeof (duration) === 'undefined') ? 500 : duration
|
||||
var animateScroll = function() {
|
||||
// increment the time
|
||||
currentTime += increment
|
||||
// find the value with the quadratic in-out easing function
|
||||
var val = Math.easeInOutQuad(currentTime, start, change, duration)
|
||||
// move the document.body
|
||||
move(val)
|
||||
// do the animation unless its over
|
||||
if (currentTime < duration) {
|
||||
requestAnimFrame(animateScroll)
|
||||
} else {
|
||||
if (callback && typeof (callback) === 'function') {
|
||||
// the animation is done so lets callback
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
animateScroll()
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="filter-container">
|
||||
<!-- <el-input v-model="listQuery.name" placeholder="Server 名称" style="width: 200px;" class="filter-item" />
|
||||
<el-input v-model="listQuery.ip" placeholder="Server IP" style="width: 200px;" class="filter-item" />
|
||||
<el-button class="filter-item" type="primary" icon="el-icon-search" plain @click="fetchData()">查询</el-button> -->
|
||||
<el-button class="filter-item" type="primary" @click="handleCreate()">新建集群</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
:data="list"
|
||||
element-loading-text="Loading"
|
||||
border
|
||||
fit
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column label="集群名称" min-width="200" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.name }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="ZK地址" min-width="300" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.zkHosts }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" prop="created_at" label="操作" min-width="150">
|
||||
<template slot-scope="scope">
|
||||
<el-dropdown trigger="click">
|
||||
<el-button type="primary" size="mini">
|
||||
操作<i class="el-icon-arrow-down el-icon--right" />
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item @click.native="handleConfig(scope.row)">主配置</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="handleUpdate(scope.row)">修改集群</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="handleDelete(scope.row)">删除集群</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="handleView(scope.row)">查看Server</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-dialog :visible.sync="dialogFormVisible" :title="textMap[dialogStatus]" width="600px">
|
||||
<el-form ref="dataForm" :rules="rules" :model="canalCluster" label-position="left" label-width="120px" style="width: 400px; margin-left:30px;">
|
||||
<el-form-item label="集群名称" prop="name">
|
||||
<el-input v-model="canalCluster.name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="ZK地址" prop="zkHosts">
|
||||
<el-input v-model="canalCluster.zkHosts" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="dataOperation()">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { addCanalCluster, getCanalClusters, updateCanalCluster, deleteCanalCluster } from '@/api/canalCluster'
|
||||
|
||||
export default {
|
||||
filters: {
|
||||
statusFilter(status) {
|
||||
const statusMap = {
|
||||
'1': 'success',
|
||||
'0': 'gray',
|
||||
'-1': 'danger'
|
||||
}
|
||||
return statusMap[status]
|
||||
},
|
||||
statusLabel(status) {
|
||||
const statusMap = {
|
||||
'1': '启动',
|
||||
'0': '停止',
|
||||
'-1': '断开'
|
||||
}
|
||||
return statusMap[status]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: null,
|
||||
listLoading: true,
|
||||
listQuery: {
|
||||
name: '',
|
||||
ip: ''
|
||||
},
|
||||
dialogFormVisible: false,
|
||||
textMap: {
|
||||
create: '新建集群信息',
|
||||
update: '修改集群信息'
|
||||
},
|
||||
canalCluster: {
|
||||
id: null,
|
||||
name: null,
|
||||
zkHosts: null
|
||||
},
|
||||
rules: {
|
||||
name: [{ required: true, message: '集群名称不能为空', trigger: 'change' }],
|
||||
zkHosts: [{ required: true, message: 'zk地址不能为空', trigger: 'change' }]
|
||||
},
|
||||
dialogStatus: 'create'
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
fetchData() {
|
||||
this.listLoading = true
|
||||
getCanalClusters(this.listQuery).then(res => {
|
||||
this.list = res.data
|
||||
}).finally(() => {
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
resetModel() {
|
||||
this.canalCluster = {
|
||||
id: null,
|
||||
name: null,
|
||||
zkHosts: null
|
||||
}
|
||||
},
|
||||
handleCreate() {
|
||||
this.resetModel()
|
||||
this.dialogStatus = 'create'
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
dataOperation() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.dialogStatus === 'create') {
|
||||
addCanalCluster(this.canalCluster).then(res => {
|
||||
this.operationRes(res)
|
||||
})
|
||||
}
|
||||
if (this.dialogStatus === 'update') {
|
||||
updateCanalCluster(this.canalCluster).then(res => {
|
||||
this.operationRes(res)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
operationRes(res) {
|
||||
if (res.data === 'success') {
|
||||
this.fetchData()
|
||||
this.dialogFormVisible = false
|
||||
this.$message({
|
||||
message: this.textMap[this.dialogStatus] + '成功',
|
||||
type: 'success'
|
||||
})
|
||||
} else {
|
||||
this.$message({
|
||||
message: this.textMap[this.dialogStatus] + '失败',
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
},
|
||||
handleView(row) {
|
||||
this.$router.push('/canalServer/nodeServers?clusterId=' + row.id)
|
||||
},
|
||||
handleConfig(row) {
|
||||
this.$router.push('/canalServer/nodeServer/config?clusterId=' + row.id)
|
||||
},
|
||||
handleUpdate(row) {
|
||||
this.resetModel()
|
||||
this.canalCluster = Object.assign({}, row)
|
||||
this.dialogStatus = 'update'
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
handleDelete(row) {
|
||||
this.$confirm('删除集群信息会导致服务停止', '确定删除集群信息', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
deleteCanalCluster(row.id).then((res) => {
|
||||
if (res.data === 'success') {
|
||||
this.fetchData()
|
||||
this.$message({
|
||||
message: '删除集群信息成功',
|
||||
type: 'success'
|
||||
})
|
||||
} else {
|
||||
this.$message({
|
||||
message: '删除集群信息失败',
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -4,8 +4,10 @@
|
||||
<div style="padding-left: 10px;padding-top: 20px;">
|
||||
<el-form-item>
|
||||
{{ form.name }}
|
||||
<el-button type="primary" @click="onSubmit">修改</el-button>
|
||||
<el-button type="primary" @click="onSubmit">保存</el-button>
|
||||
<el-button type="warning" @click="onCancel">重置</el-button>
|
||||
<el-button type="success" @click="onLoadTemplate">载入模板</el-button>
|
||||
<el-button type="info" @click="onBack">返回</el-button>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<editor v-model="form.content" lang="properties" theme="chrome" width="100%" :height="800" @init="editorInit" />
|
||||
@@ -14,7 +16,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCanalConfig, updateCanalConfig } from '@/api/canalConfig'
|
||||
import { getCanalConfig, updateCanalConfig, getTemplateConfig } from '@/api/canalConfig'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -25,7 +27,9 @@ export default {
|
||||
form: {
|
||||
id: null,
|
||||
name: '',
|
||||
content: ''
|
||||
content: '',
|
||||
serverId: null,
|
||||
clusterId: null
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -44,16 +48,32 @@ export default {
|
||||
require('brace/snippets/javascript')
|
||||
},
|
||||
loadCanalConfig() {
|
||||
getCanalConfig().then(response => {
|
||||
let clusterId = 0
|
||||
let serverId = 0
|
||||
if (this.$route.query.clusterId) {
|
||||
clusterId = this.$route.query.clusterId
|
||||
} else if (this.$route.query.serverId) {
|
||||
serverId = this.$route.query.serverId
|
||||
}
|
||||
getCanalConfig(clusterId, serverId).then(response => {
|
||||
const data = response.data
|
||||
this.form.id = data.id
|
||||
this.form.name = data.name
|
||||
this.form.content = data.content
|
||||
this.form.serverId = this.$route.query.serverId
|
||||
this.form.clusterId = this.$route.query.clusterId
|
||||
})
|
||||
},
|
||||
onSubmit() {
|
||||
if (this.form.content === null || this.form.content === '') {
|
||||
this.$message({
|
||||
message: '配置内容不能为空',
|
||||
type: 'error'
|
||||
})
|
||||
return
|
||||
}
|
||||
this.$confirm(
|
||||
'修改Canal主配置可能会导致Server重启,是否继续?',
|
||||
'修改主配置可能会导致Server重启,是否继续?',
|
||||
'确定修改',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
@@ -64,13 +84,13 @@ export default {
|
||||
updateCanalConfig(this.form).then(response => {
|
||||
if (response.data === 'success') {
|
||||
this.$message({
|
||||
message: '修改成功',
|
||||
message: '保存成功',
|
||||
type: 'success'
|
||||
})
|
||||
this.loadCanalConfig()
|
||||
} else {
|
||||
this.$message({
|
||||
message: '修改失败',
|
||||
message: '保存失败',
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
@@ -79,6 +99,14 @@ export default {
|
||||
},
|
||||
onCancel() {
|
||||
this.loadCanalConfig()
|
||||
},
|
||||
onBack() {
|
||||
history.go(-1)
|
||||
},
|
||||
onLoadTemplate() {
|
||||
getTemplateConfig().then(res => {
|
||||
this.form.content = res.data
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,16 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="filter-container">
|
||||
<el-input v-model="listQuery.name" placeholder="实例名称" style="width: 200px;" class="filter-item" />
|
||||
<el-button class="filter-item" type="primary" icon="el-icon-search" plain @click="fetchData()">查询</el-button>
|
||||
<el-input v-model="listQuery.name" placeholder="Instance 名称" style="width: 200px;" class="filter-item" />
|
||||
<el-select v-model="listQuery.clusterServerId" placeholder="所属集群/主机" class="filter-item">
|
||||
<el-option key="" label="所属集群/主机" value="" />
|
||||
<el-option-group v-for="group in options" :key="group.label" :label="group.label">
|
||||
<el-option v-for="item in group.options" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-option-group>
|
||||
</el-select>
|
||||
<el-button class="filter-item" type="primary" icon="el-icon-search" plain @click="queryData()">查询</el-button>
|
||||
|
||||
<el-button class="filter-item" type="primary" @click="handleCreate()">新建实例</el-button>
|
||||
<el-button class="filter-item" type="primary" @click="handleCreate()">新建 Instance</el-button>
|
||||
<el-button class="filter-item" type="info" @click="fetchData()">刷新列表</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
@@ -15,14 +21,30 @@
|
||||
fit
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column label="实例名称" min-width="200" align="center">
|
||||
<el-table-column label="Instance 名称" min-width="200" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.name }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="运行节点" min-width="200" align="center">
|
||||
<el-table-column label="所属集群" min-width="200" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.nodeIp }}</span>
|
||||
<span v-if="scope.row.canalCluster !== null">
|
||||
{{ scope.row.canalCluster.name }}
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="所属主机" min-width="200" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.nodeServer !== null">
|
||||
{{ scope.row.nodeServer.name }}
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column class-name="status-col" label="状态" min-width="150" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.runningStatus | statusFilter">{{ scope.row.runningStatus | statusLabel }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="修改时间" min-width="200" align="center">
|
||||
@@ -37,43 +59,39 @@
|
||||
操作<i class="el-icon-arrow-down el-icon--right" />
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item @click.native="handleUpdate(scope.row)">修改实例</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="handleDelete(scope.row)">删除实例</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="handleStart(scope.row)">启动服务</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="handleStop(scope.row)">停止服务</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="handleLog(scope.row)">日志详情</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="handleUpdate(scope.row)">修改</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="handleDelete(scope.row)">删除</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="handleStart(scope.row)">启动</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="handleStop(scope.row)">停止</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="handleLog(scope.row)">日志</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-dialog :visible.sync="dialogFormVisible" title="确定启动服务" width="400px">
|
||||
<el-form ref="dataForm" :rules="rules" :model="nodeModel" label-position="left" label-width="80px" style="width: 350px; margin-left:30px;">
|
||||
<el-form-item label="选择节点" prop="nodeId">
|
||||
<el-select v-model="nodeModel.id" placeholder="选择节点">
|
||||
<el-option v-for="item in nodeServices" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="doStartInstance()">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<pagination v-show="count>0" :total="count" :page.sync="listQuery.page" :limit.sync="listQuery.size" @pagination="fetchData()" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCanalInstances, deleteCanalInstance, startInstance, stopInstance } from '@/api/canalInstance'
|
||||
import { getNodeServers } from '@/api/nodeServer'
|
||||
import { getCanalInstances, deleteCanalInstance, instanceStatus } from '@/api/canalInstance'
|
||||
import Pagination from '@/components/Pagination'
|
||||
import { getClustersAndServers } from '@/api/canalCluster'
|
||||
|
||||
export default {
|
||||
components: { Pagination },
|
||||
filters: {
|
||||
statusFilter(status) {
|
||||
const statusMap = {
|
||||
published: 'success',
|
||||
draft: 'gray',
|
||||
deleted: 'danger'
|
||||
'1': 'success',
|
||||
'0': 'gray'
|
||||
}
|
||||
return statusMap[status]
|
||||
},
|
||||
statusLabel(status) {
|
||||
const statusMap = {
|
||||
'1': '启动',
|
||||
'0': '停止'
|
||||
}
|
||||
return statusMap[status]
|
||||
}
|
||||
@@ -84,26 +102,37 @@ export default {
|
||||
listLoading: true,
|
||||
dialogFormVisible: false,
|
||||
nodeServices: [],
|
||||
count: 0,
|
||||
options: [],
|
||||
listQuery: {
|
||||
name: ''
|
||||
name: '',
|
||||
clusterServerId: '',
|
||||
page: 1,
|
||||
size: 20
|
||||
},
|
||||
currentId: null,
|
||||
nodeModel: {
|
||||
id: null
|
||||
},
|
||||
rules: {
|
||||
id: [{ required: true, message: '请选择节点', trigger: 'change' }]
|
||||
id: [{ required: true, message: '请选择运行Server', trigger: 'change' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
getClustersAndServers().then((res) => {
|
||||
this.options = res.data
|
||||
})
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
queryData() {
|
||||
this.listQuery.page = 1
|
||||
this.fetchData()
|
||||
},
|
||||
fetchData() {
|
||||
this.listLoading = true
|
||||
getCanalInstances(this.listQuery).then(res => {
|
||||
this.list = res.data
|
||||
this.list = res.data.items
|
||||
this.count = res.data.count
|
||||
}).finally(() => {
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
@@ -114,7 +143,7 @@ export default {
|
||||
this.$router.push('/canalServer/canalInstance/modify?id=' + row.id)
|
||||
},
|
||||
handleDelete(row) {
|
||||
this.$confirm('删除实例配置会导致Canal实例停止', '确定删除实例信息', {
|
||||
this.$confirm('删除Instance配置会导致停止', '确定删除Instance信息', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
@@ -123,12 +152,12 @@ export default {
|
||||
if (res.data === 'success') {
|
||||
this.fetchData()
|
||||
this.$message({
|
||||
message: '删除实例信息成功',
|
||||
message: '删除Instance信息成功',
|
||||
type: 'success'
|
||||
})
|
||||
} else {
|
||||
this.$message({
|
||||
message: '删除实例点信息失败',
|
||||
message: '删除Instance信息失败',
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
@@ -136,60 +165,51 @@ export default {
|
||||
})
|
||||
},
|
||||
handleStart(row) {
|
||||
if (row.nodeId !== null) {
|
||||
this.$message({ message: '当前实例不是停止状态,无法启动', type: 'error' })
|
||||
return
|
||||
}
|
||||
|
||||
this.currentId = row.id
|
||||
this.nodeModel.id = null
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
|
||||
getNodeServers().then((res) => {
|
||||
this.nodeServices = res.data
|
||||
this.dialogFormVisible = true
|
||||
})
|
||||
},
|
||||
doStartInstance() {
|
||||
startInstance(this.currentId, this.nodeModel.id).then((res) => {
|
||||
if (res.data) {
|
||||
this.fetchData()
|
||||
this.$message({
|
||||
message: '启动成功',
|
||||
type: 'success'
|
||||
})
|
||||
this.dialogFormVisible = false
|
||||
} else {
|
||||
this.$message({
|
||||
message: '启动实例服务出现异常',
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleStop(row) {
|
||||
if (row.nodeId === null) {
|
||||
this.$message({ message: '当前实例不是启动状态,无法停止', type: 'error' })
|
||||
return
|
||||
}
|
||||
this.$confirm('停止Canal Instance: ' + row.name, '确定停止实例服务', {
|
||||
// if (row.runningStatus === '1') {
|
||||
// this.$message({ message: '当前Instance已处于启动状态!', type: 'error' })
|
||||
// return
|
||||
// }
|
||||
this.$confirm('启动Instance: ' + row.name, '确定启动Instance服务', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
stopInstance(row.id, row.nodeId).then((res) => {
|
||||
instanceStatus(row.id, 'start').then((res) => {
|
||||
if (res.data) {
|
||||
this.fetchData()
|
||||
this.$message({
|
||||
message: '停止成功',
|
||||
message: '启动成功, 稍后请刷新列表查看状态',
|
||||
type: 'success'
|
||||
})
|
||||
} else {
|
||||
this.$message({
|
||||
message: '停止实例服务出现异常',
|
||||
message: '启动Instance出现异常',
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
handleStop(row) {
|
||||
// if (row.runningStatus === '0') {
|
||||
// this.$message({ message: '当前Instance已处于停止状态!', type: 'error' })
|
||||
// return
|
||||
// }
|
||||
this.$confirm('停止Instance: ' + row.name, '确定停止Instance服务', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
instanceStatus(row.id, 'stop').then((res) => {
|
||||
if (res.data) {
|
||||
this.fetchData()
|
||||
this.$message({
|
||||
message: '停止成功, 稍后请刷新列表查看状态',
|
||||
type: 'success'
|
||||
})
|
||||
} else {
|
||||
this.$message({
|
||||
message: '停止Instance出现异常',
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
@@ -198,10 +218,10 @@ export default {
|
||||
},
|
||||
handleLog(row) {
|
||||
if (row.nodeId === null) {
|
||||
this.$message({ message: '当前实例不是启动状态,无法查看日志', type: 'warning' })
|
||||
this.$message({ message: '当前Instance不是启动状态,无法查看日志', type: 'warning' })
|
||||
return
|
||||
}
|
||||
this.$router.push('canalInstance/log?id=' + row.id + '&nodeId=' + row.nodeId)
|
||||
this.$router.push('canalInstance/log?id=' + row.id + '&nodeId=' + row.nodeServer.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user