diff --git a/build/docker/Dockerfile.guacd b/build/docker/Dockerfile.guacd new file mode 100644 index 0000000000..6bfb4f4a04 --- /dev/null +++ b/build/docker/Dockerfile.guacd @@ -0,0 +1,212 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# +# Dockerfile for guacamole-server +# + +# The Alpine Linux image that should be used as the basis for the guacd image +ARG ALPINE_BASE_IMAGE=latest +FROM alpine:${ALPINE_BASE_IMAGE} AS builder + +ARG TARGETPLATFORM + +RUN sed -i 's!https://dl-cdn.alpinelinux.org/!https://mirrors.ustc.edu.cn/!g' /etc/apk/repositories + +# Install build dependencies +RUN apk add --no-cache \ + autoconf \ + automake \ + build-base \ + cairo-dev \ + cmake \ + cunit-dev \ + git \ + grep \ + libjpeg-turbo-dev \ + libpng-dev \ + libtool \ + libwebp-dev \ + make \ + openssl1.1-compat-dev \ + pango-dev \ + pulseaudio-dev \ + util-linux-dev + +# Copy source to container for sake of build +ARG BUILD_DIR=/tmp/guacamole-server +ARG GUAC_VER=1.5.3 +RUN git clone --branch=${GUAC_VER} https://github.com/apache/guacamole-server.git /tmp/guacamole-server + +# +# Base directory for installed build artifacts. +# +# NOTE: Due to limitations of the Docker image build process, this value is +# duplicated in an ARG in the second stage of the build. +# +ARG PREFIX_DIR=/opt/guacamole + +# +# Automatically select the latest versions of each core protocol support +# library (these can be overridden at build time if a specific version is +# needed) +# +ARG WITH_FREERDP='2(\.\d+)+' +ARG WITH_LIBSSH2='libssh2-\d+(\.\d+)+' +ARG WITH_LIBTELNET='\d+(\.\d+)+' +ARG WITH_LIBVNCCLIENT='LibVNCServer-\d+(\.\d+)+' +ARG WITH_LIBWEBSOCKETS='v\d+(\.\d+)+' + +# +# Default build options for each core protocol support library, as well as +# guacamole-server itself (these can be overridden at build time if different +# options are needed) +# + +ARG FREERDP_OPTS="\ + -DBUILTIN_CHANNELS=OFF \ + -DCHANNEL_URBDRC=OFF \ + -DWITH_ALSA=OFF \ + -DWITH_CAIRO=ON \ + -DWITH_CHANNELS=ON \ + -DWITH_CLIENT=ON \ + -DWITH_CUPS=OFF \ + -DWITH_DIRECTFB=OFF \ + -DWITH_FFMPEG=OFF \ + -DWITH_GSM=OFF \ + -DWITH_GSSAPI=OFF \ + -DWITH_IPP=OFF \ + -DWITH_JPEG=ON \ + -DWITH_LIBSYSTEMD=OFF \ + -DWITH_MANPAGES=OFF \ + -DWITH_OPENH264=OFF \ + -DWITH_OPENSSL=ON \ + -DWITH_OSS=OFF \ + -DWITH_PCSC=OFF \ + -DWITH_PULSE=OFF \ + -DWITH_SERVER=OFF \ + -DWITH_SERVER_INTERFACE=OFF \ + -DWITH_SHADOW_MAC=OFF \ + -DWITH_SHADOW_X11=OFF \ + -DWITH_SSE2=ON \ + -DWITH_WAYLAND=OFF \ + -DWITH_X11=OFF \ + -DWITH_X264=OFF \ + -DWITH_XCURSOR=ON \ + -DWITH_XEXT=ON \ + -DWITH_XI=OFF \ + -DWITH_XINERAMA=OFF \ + -DWITH_XKBFILE=ON \ + -DWITH_XRENDER=OFF \ + -DWITH_XTEST=OFF \ + -DWITH_XV=OFF \ + -DWITH_ZLIB=ON" + +ARG GUACAMOLE_SERVER_OPTS="\ + --disable-guaclog" + +ARG LIBSSH2_OPTS="\ + -DBUILD_EXAMPLES=OFF \ + -DBUILD_SHARED_LIBS=ON" + +ARG LIBTELNET_OPTS="\ + --disable-static \ + --disable-util" + +ARG LIBVNCCLIENT_OPTS="" + +ARG LIBWEBSOCKETS_OPTS="\ + -DDISABLE_WERROR=ON \ + -DLWS_WITHOUT_SERVER=ON \ + -DLWS_WITHOUT_TESTAPPS=ON \ + -DLWS_WITHOUT_TEST_CLIENT=ON \ + -DLWS_WITHOUT_TEST_PING=ON \ + -DLWS_WITHOUT_TEST_SERVER=ON \ + -DLWS_WITHOUT_TEST_SERVER_EXTPOLL=ON \ + -DLWS_WITH_STATIC=OFF" + +# Build guacamole-server and its core protocol library dependencies +RUN echo "$TARGETPLATFORM" +RUN if [ "$TARGETPLATFORM" = "linux/amd64" ]; \ + then FREERDP_OPTS="${FREERDP_OPTS} -DWITH_SSE2=ON" && echo "SSE2 active"; \ + else FREERDP_OPTS="${FREERDP_OPTS} -DWITH_SSE2=OFF" && echo "SSE2 disabled"; \ + fi && ${BUILD_DIR}/src/guacd-docker/bin/build-all.sh + + +# Record the packages of all runtime library dependencies +RUN ${BUILD_DIR}/src/guacd-docker/bin/list-dependencies.sh \ + ${PREFIX_DIR}/sbin/guacd \ + ${PREFIX_DIR}/lib/libguac-client-*.so \ + ${PREFIX_DIR}/lib/freerdp2/*guac*.so \ + > ${PREFIX_DIR}/DEPENDENCIES + +# Use same Alpine version as the base for the runtime image +FROM alpine:${ALPINE_BASE_IMAGE} + +RUN sed -i 's!https://dl-cdn.alpinelinux.org/!https://mirrors.ustc.edu.cn/!g' /etc/apk/repositories +# +# Base directory for installed build artifacts. See also the +# CMD directive at the end of this build stage. +# +# NOTE: Due to limitations of the Docker image build process, this value is +# duplicated in an ARG in the first stage of the build. +# +ARG PREFIX_DIR=/opt/guacamole + +# Runtime environment +ENV LC_ALL=C.UTF-8 +ENV LD_LIBRARY_PATH=${PREFIX_DIR}/lib +ENV GUACD_LOG_LEVEL=info + +# Copy build artifacts into this stage +COPY --from=builder ${PREFIX_DIR} ${PREFIX_DIR} + +# Bring runtime environment up to date and install runtime dependencies +RUN apk add --no-cache \ + ca-certificates \ + ghostscript \ + netcat-openbsd \ + shadow \ + terminus-font \ + ttf-dejavu \ + ttf-liberation \ + util-linux-login && \ + xargs apk add --no-cache < ${PREFIX_DIR}/DEPENDENCIES + +# Checks the operating status every 5 minutes with a timeout of 5 seconds +HEALTHCHECK --interval=5m --timeout=5s CMD nc -z 127.0.0.1 4822 || exit 1 + +# Create a new user guacd +ARG UID=1000 +ARG GID=1000 +RUN groupadd --gid $GID guacd +RUN useradd --system --create-home --shell /sbin/nologin --uid $UID --gid $GID guacd + +# Run with user guacd +USER guacd + +# Expose the default listener port +EXPOSE 4822 + +# Start guacd, listening on port 0.0.0.0:4822 +# +# Note the path here MUST correspond to the value specified in the +# PREFIX_DIR build argument. +# +CMD /opt/guacamole/sbin/guacd -b 0.0.0.0 -L $GUACD_LOG_LEVEL -f diff --git a/build/docker/Makefile b/build/docker/Makefile index bc4b2b4402..3926b10616 100644 --- a/build/docker/Makefile +++ b/build/docker/Makefile @@ -64,3 +64,13 @@ host-image: LBAGENT_BASE_VERSION = v0.0.3 lbagent-base: $(DOCKER_BUILDX)/lbagent-base:$(LBAGENT_BASE_VERSION) -f ./Dockerfile.lbagent-base . + +GUACD_VERSION=1.5.3 +guacd: + docker buildx build --platform linux/arm64 --push \ + -t registry.cn-beijing.aliyuncs.com/yunionio/guacd:$(GUACD_VERSION)-arm64 -f ./Dockerfile.guacd . + docker buildx build --platform linux/amd64 --push \ + -t registry.cn-beijing.aliyuncs.com/yunionio/guacd:$(GUACD_VERSION)-amd64 -f ./Dockerfile.guacd . + docker buildx imagetools create -t registry.cn-beijing.aliyuncs.com/yunionio/guacd:$(GUACD_VERSION) \ + registry.cn-beijing.aliyuncs.com/yunionio/guacd:$(GUACD_VERSION)-amd64 \ + registry.cn-beijing.aliyuncs.com/yunionio/guacd:$(GUACD_VERSION)-arm64 diff --git a/cmd/climc/shell/compute/webconsole.go b/cmd/climc/shell/compute/webconsole.go index a87e381922..f0544b0766 100644 --- a/cmd/climc/shell/compute/webconsole.go +++ b/cmd/climc/shell/compute/webconsole.go @@ -133,4 +133,14 @@ func init() { handleResult(s, args.WebConsoleOptions, ret) return nil }) + + R(&o.WebConsoleServerRdpOptions{}, "webconsole-server-rdp", "Connect server remote graphic console by rdp", func(s *mcclient.ClientSession, args *o.WebConsoleServerRdpOptions) error { + ret, err := webconsole.WebConsole.DoServerRDPConnect(s, args.ID, nil) + if err != nil { + return err + } + handleResult(s, args.WebConsoleOptions, ret) + return nil + }) + } diff --git a/pkg/apis/compute/sslcertificate.go b/pkg/apis/compute/sslcertificate.go index 454a79e58b..82a68e27fa 100644 --- a/pkg/apis/compute/sslcertificate.go +++ b/pkg/apis/compute/sslcertificate.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package compute import "yunion.io/x/onecloud/pkg/apis" diff --git a/pkg/apis/monitor/datasource.go b/pkg/apis/monitor/datasource.go index f36b3428ec..3352c48f70 100644 --- a/pkg/apis/monitor/datasource.go +++ b/pkg/apis/monitor/datasource.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package monitor import "yunion.io/x/onecloud/pkg/apis" diff --git a/pkg/apis/webconsole/consts.go b/pkg/apis/webconsole/consts.go index 1f6c940c2d..5fdaabfc86 100644 --- a/pkg/apis/webconsole/consts.go +++ b/pkg/apis/webconsole/consts.go @@ -21,6 +21,7 @@ const ( const ( VNC = "vnc" + RDP = "rdp" ALIYUN = "aliyun" QCLOUD = "qcloud" OPENSTACK = "openstack" diff --git a/pkg/cloudcommon/tsdb/doc.go b/pkg/cloudcommon/tsdb/doc.go index c3746f4421..070ac00d4f 100644 --- a/pkg/cloudcommon/tsdb/doc.go +++ b/pkg/cloudcommon/tsdb/doc.go @@ -1 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package tsdb // import "yunion.io/x/onecloud/pkg/cloudcommon/tsdb" diff --git a/pkg/cloudcommon/tsdb/tsdb.go b/pkg/cloudcommon/tsdb/tsdb.go index 41bf2d1370..0aec6f4218 100644 --- a/pkg/cloudcommon/tsdb/tsdb.go +++ b/pkg/cloudcommon/tsdb/tsdb.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package tsdb import ( diff --git a/pkg/compute/models/sslcertificate.go b/pkg/compute/models/sslcertificate.go index 10efed1601..9284759dde 100644 --- a/pkg/compute/models/sslcertificate.go +++ b/pkg/compute/models/sslcertificate.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package models import ( diff --git a/pkg/mcclient/modules/webconsole/mod_webconsole.go b/pkg/mcclient/modules/webconsole/mod_webconsole.go index 367a90d038..9aa8d115d4 100644 --- a/pkg/mcclient/modules/webconsole/mod_webconsole.go +++ b/pkg/mcclient/modules/webconsole/mod_webconsole.go @@ -153,6 +153,10 @@ func (m WebConsoleManager) DoServerConnect(s *mcclient.ClientSession, id string, return m.DoConnect(s, "server", id, "", params) } +func (m WebConsoleManager) DoServerRDPConnect(s *mcclient.ClientSession, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) { + return m.DoConnect(s, "server-rdp", id, "", params) +} + func (m WebConsoleManager) DoClimcSshConnect(s *mcclient.ClientSession, ip string, port int) (jsonutils.JSONObject, error) { data := jsonutils.Marshal(map[string]interface{}{ "username": "root", diff --git a/pkg/mcclient/options/webconsole.go b/pkg/mcclient/options/webconsole.go index 2f01c289ca..14aa8c211d 100644 --- a/pkg/mcclient/options/webconsole.go +++ b/pkg/mcclient/options/webconsole.go @@ -92,3 +92,17 @@ type WebConsoleServerOptions struct { WebConsoleOptions ID string `help:"Server id or name"` } + +type WebConsoleServerRdpOptions struct { + WebConsoleOptions + ID string `help:"Server id or name"` + + HOST string + Port *int + Username *string + Password *string + + Width *int + Height *int + Dpi *int +} diff --git a/pkg/monitor/datasource/datasource.go b/pkg/monitor/datasource/datasource.go index fda82d59e8..c2f4908852 100644 --- a/pkg/monitor/datasource/datasource.go +++ b/pkg/monitor/datasource/datasource.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package datasource import ( diff --git a/pkg/monitor/datasource/doc.go b/pkg/monitor/datasource/doc.go index 6514f6dca9..6ac694657b 100644 --- a/pkg/monitor/datasource/doc.go +++ b/pkg/monitor/datasource/doc.go @@ -1 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package datasource // import "yunion.io/x/onecloud/pkg/monitor/datasource" diff --git a/pkg/monitor/tsdb/driver/influxdb/helper_test.go b/pkg/monitor/tsdb/driver/influxdb/helper_test.go index 94d388a1dd..3dba10c18e 100644 --- a/pkg/monitor/tsdb/driver/influxdb/helper_test.go +++ b/pkg/monitor/tsdb/driver/influxdb/helper_test.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package influxdb import ( diff --git a/pkg/monitor/tsdb/driver/victoriametrics/client.go b/pkg/monitor/tsdb/driver/victoriametrics/client.go index 1abd9c1a3d..b3c45edf98 100644 --- a/pkg/monitor/tsdb/driver/victoriametrics/client.go +++ b/pkg/monitor/tsdb/driver/victoriametrics/client.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package victoriametrics import ( diff --git a/pkg/monitor/tsdb/driver/victoriametrics/client_test.go b/pkg/monitor/tsdb/driver/victoriametrics/client_test.go index 5fa3210cb1..f6ab3f5fc5 100644 --- a/pkg/monitor/tsdb/driver/victoriametrics/client_test.go +++ b/pkg/monitor/tsdb/driver/victoriametrics/client_test.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package victoriametrics func newTestClient(ep string) (Client, error) { diff --git a/pkg/monitor/tsdb/driver/victoriametrics/doc.go b/pkg/monitor/tsdb/driver/victoriametrics/doc.go index 48d12494d0..a997f83f13 100644 --- a/pkg/monitor/tsdb/driver/victoriametrics/doc.go +++ b/pkg/monitor/tsdb/driver/victoriametrics/doc.go @@ -1 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package victoriametrics // import "yunion.io/x/onecloud/pkg/monitor/tsdb/driver/victoriametrics" diff --git a/pkg/monitor/tsdb/driver/victoriametrics/result_parser.go b/pkg/monitor/tsdb/driver/victoriametrics/result_parser.go index e199162fe0..a35ea7223a 100644 --- a/pkg/monitor/tsdb/driver/victoriametrics/result_parser.go +++ b/pkg/monitor/tsdb/driver/victoriametrics/result_parser.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package victoriametrics import ( diff --git a/pkg/monitor/tsdb/driver/victoriametrics/result_parser_test.go b/pkg/monitor/tsdb/driver/victoriametrics/result_parser_test.go index 662bbe5221..92df56471a 100644 --- a/pkg/monitor/tsdb/driver/victoriametrics/result_parser_test.go +++ b/pkg/monitor/tsdb/driver/victoriametrics/result_parser_test.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package victoriametrics import ( diff --git a/pkg/monitor/tsdb/driver/victoriametrics/vm.go b/pkg/monitor/tsdb/driver/victoriametrics/vm.go index a4a334458c..af61ad80c8 100644 --- a/pkg/monitor/tsdb/driver/victoriametrics/vm.go +++ b/pkg/monitor/tsdb/driver/victoriametrics/vm.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package victoriametrics import ( diff --git a/pkg/monitor/tsdb/driver/victoriametrics/vm_test.go b/pkg/monitor/tsdb/driver/victoriametrics/vm_test.go index f6a07c3634..592623f617 100644 --- a/pkg/monitor/tsdb/driver/victoriametrics/vm_test.go +++ b/pkg/monitor/tsdb/driver/victoriametrics/vm_test.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package victoriametrics /*import ( diff --git a/pkg/webconsole/guac/doc.go b/pkg/webconsole/guac/doc.go new file mode 100644 index 0000000000..e279368df2 --- /dev/null +++ b/pkg/webconsole/guac/doc.go @@ -0,0 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package guac // import "yunion.io/x/onecloud/pkg/webconsole/guac" diff --git a/pkg/webconsole/guac/guac.go b/pkg/webconsole/guac/guac.go new file mode 100644 index 0000000000..43cf78032d --- /dev/null +++ b/pkg/webconsole/guac/guac.go @@ -0,0 +1,56 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package guac + +import ( + "fmt" + "net" +) + +func NewGuacamoleTunnel(host string, port int, user, password string, id string, w, h, dpi int) (*GuacamoleTunnel, error) { + opts := NewGuacOptions() + opts.ConnectionId = id + opts.Protocol = "rdp" + if w > 0 && h > 0 { + opts.OptimalScreenHeight = h + opts.OptimalScreenWidth = w + } + if dpi > 0 { + opts.OptimalResolution = dpi + } + opts.AudioMimetypes = []string{"audio/L16", "rate=44100", "channels=2"} + opts.Parameters = map[string]string{ + "scheme": opts.Protocol, + "hostname": host, + "port": fmt.Sprintf("%d", port), + "ignore-cert": "true", + "security": "", + "username": user, + "password": password, + } + conn, err := net.Dial("tcp", "127.0.0.1:4822") + if err != nil { + return nil, err + } + ret := &GuacamoleTunnel{ + conn: conn, + opts: opts, + } + err = ret.Handshake() + if err != nil { + return nil, err + } + return ret, nil +} diff --git a/pkg/webconsole/guac/instruction.go b/pkg/webconsole/guac/instruction.go new file mode 100644 index 0000000000..ec49030ddb --- /dev/null +++ b/pkg/webconsole/guac/instruction.go @@ -0,0 +1,84 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package guac + +import ( + "fmt" + "strconv" + + "yunion.io/x/pkg/errors" +) + +type Instruction struct { + Opcode string + Args []string +} + +func NewInstruction(op string, args ...string) *Instruction { + return &Instruction{ + Opcode: op, + Args: args, + } +} + +func (i *Instruction) String() string { + ret := fmt.Sprintf("%d.%s", len(i.Opcode), i.Opcode) + for _, value := range i.Args { + ret += fmt.Sprintf(",%d.%s", len(value), value) + } + ret += ";" + return ret +} + +func parse(buf []byte) ([]*Instruction, []byte, error) { + ret := []*Instruction{} + if len(buf) == 0 { + return ret, []byte{}, nil + } + start, opcode, args := 0, "", []string{} + begin := 0 + for i := 0; i < len(buf); i++ { + switch buf[i] { + case ',': + start = i + 1 + case '.': + length, err := strconv.Atoi(string(buf[start:i])) + if err != nil { + return nil, []byte{}, errors.Wrapf(err, "Atoi(%s)", string(buf[start:i])) + } + if i+length+1 > len(buf) { + return ret, buf[begin:], nil + } + str := string(buf[i+1 : i+length+1]) + if len(opcode) == 0 { + opcode = str + } else { + args = append(args, str) + } + i += length + case ';': + start = i + 1 + begin = i + 1 + instruction := NewInstruction(opcode, args...) + ret = append(ret, instruction) + opcode = "" + args = []string{} + } + } + if buf[len(buf)-1] != ';' { + return ret, buf[begin:], nil + } + return ret, []byte{}, nil +} diff --git a/pkg/webconsole/guac/instruction_test.go b/pkg/webconsole/guac/instruction_test.go new file mode 100644 index 0000000000..3518e7b971 --- /dev/null +++ b/pkg/webconsole/guac/instruction_test.go @@ -0,0 +1,69 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package guac + +import ( + "testing" + + "yunion.io/x/log" +) + +func TestInstruction(t *testing.T) { + cases := []struct { + instructionStr string + left string + want []*Instruction + }{ + {"5.ready,37.$59a9eff3-ef1a-4758-8be2-0f1d06b4c8be;", "", []*Instruction{NewInstruction("ready", "$59a9eff3-ef1a-4758-8be2-0f1d06b4c8be")}}, + {"5.audio,1.1,31.audio/L16;rate=44100,channels=2;4.size,1.0,4.1648,3.991;4.size,2.-1,2.11,2.16;3.img,1.3,2.12,2.-1,9.image/png,1.0,1.0;4.blob,1.3,232.iVBORw0KGgoAAAANSUhEUgAAAAsAAAAQCAYAAADAvYV+AAAABmJLR0QA/wD/AP+gvaeTAAAAYklEQVQokY2RQQ4AIQgDW+L/v9y9qCEsIJ4QZggoJAnDYwAwFQwASI4EO8FEMH95CRYTnfCDOyGFK6GEM6GFo7AqKI4sSSsCJH1X+roFkKdjueABX/On77lz2uGtr6pj9okfTeJQAYVaxnMAAAAASUVORK5CYII=;3.end,1.3;6.cursor,1.0,1.0,2.-1,1.0,1.0,2.11,2.16;", "", []*Instruction{ + NewInstruction("audio", "1", "audio/L16;rate=44100,channels=2"), + NewInstruction("size", "0", "1648", "991"), + NewInstruction("size", "-1", "11", "16"), + NewInstruction("img", "3", "12", "-1", "image/png", "0", "0"), + NewInstruction("blob", "3", "iVBORw0KGgoAAAANSUhEUgAAAAsAAAAQCAYAAADAvYV+AAAABmJLR0QA/wD/AP+gvaeTAAAAYklEQVQokY2RQQ4AIQgDW+L/v9y9qCEsIJ4QZggoJAnDYwAwFQwASI4EO8FEMH95CRYTnfCDOyGFK6GEM6GFo7AqKI4sSSsCJH1X+roFkKdjueABX/On77lz2uGtr6pj9okfTeJQAYVaxnMAAAAASUVORK5CYII="), + NewInstruction("end", "3"), + NewInstruction("cursor", "0", "0", "-1", "0", "0", "11", "16"), + }}, + {"4.copy,2.-2,1.0,1.0,2.64,2.64,2.14,1.0,3.640,3.576;4.copy,2.-2,1.0,1.0,2.64,2.64,2.14,1.0,3.704,3.576;4.copy,2.-2,1.0,1.0,2.64,2.64,2.14,1.0,3.7", + "4.copy,2.-2,1.0,1.0,2.64,2.64,2.14,1.0,3.7", + []*Instruction{ + NewInstruction("copy", "-2", "0", "0", "64", "64", "14", "0", "640", "576"), + NewInstruction("copy", "-2", "0", "0", "64", "64", "14", "0", "704", "576"), + }}, + {"4.copy,2.-6,1.0,1.0,2.64,2.64,2.14,1.0,3.640,3.576;4.copy,2.-6,1", "4.copy,2.-6,1", []*Instruction{ + NewInstruction("copy", "-6", "0", "0", "64", "64", "14", "0", "640", "576"), + }}, + } + + for _, c := range cases { + t.Run("test instruction", func(t *testing.T) { + instructions, left, err := parse([]byte(c.instructionStr)) + if err != nil { + t.Fatalf("parse %s error: %v", c.instructionStr, err) + } + if c.left != string(left) { + log.Fatalf("want %s left, got %s", c.left, string(left)) + } + if len(instructions) != len(c.want) { + log.Fatalf("want %d instructions, parse %d", len(c.want), len(instructions)) + } + for i := range instructions { + if instructions[i].String() != c.want[i].String() { + log.Fatalf("%s not equals %s", instructions[i].String(), c.want[i].String()) + } + } + }) + } +} diff --git a/pkg/webconsole/guac/options.go b/pkg/webconsole/guac/options.go new file mode 100644 index 0000000000..2fd0aa9518 --- /dev/null +++ b/pkg/webconsole/guac/options.go @@ -0,0 +1,39 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package guac + +type GuacOptions struct { + ConnectionId string + Protocol string + Parameters map[string]string + OptimalScreenWidth int + OptimalScreenHeight int + OptimalResolution int + AudioMimetypes []string + VideoMimetypes []string + ImageMimetypes []string +} + +func NewGuacOptions() *GuacOptions { + return &GuacOptions{ + Parameters: map[string]string{}, + OptimalScreenWidth: 1024, + OptimalScreenHeight: 768, + OptimalResolution: 96, + AudioMimetypes: []string{}, + VideoMimetypes: []string{}, + ImageMimetypes: []string{}, + } +} diff --git a/pkg/webconsole/guac/tunnel.go b/pkg/webconsole/guac/tunnel.go new file mode 100644 index 0000000000..51d0f727c7 --- /dev/null +++ b/pkg/webconsole/guac/tunnel.go @@ -0,0 +1,172 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package guac + +import ( + "fmt" + "io" + "net" + + "yunion.io/x/pkg/errors" +) + +type GuacamoleTunnel struct { + opts *GuacOptions + conn net.Conn + + instructs chan *Instruction + err chan error + + stopChan chan bool + + isStart bool +} + +func (self *GuacamoleTunnel) Write(data []byte) (int, error) { + return self.conn.Write(data) +} + +func (self *GuacamoleTunnel) ReadOne() (*Instruction, error) { + for { + select { + case instruct := <-self.instructs: + return instruct, nil + default: + } + } +} + +func (self *GuacamoleTunnel) Start() error { + return self.start() +} + +func (self *GuacamoleTunnel) start() error { + if self.isStart { + return nil + } + go func() { + self.isStart = true + self.instructs = make(chan *Instruction, 10) + self.stopChan = make(chan bool) + self.err = make(chan error) + var buf []byte = make([]byte, 4*1024*1024) + var left []byte = []byte{} + for { + select { + case <-self.stopChan: + close(self.instructs) + close(self.err) + return + default: + n, err := self.conn.Read(buf) + if err != nil && err != io.EOF { + self.err <- err + return + } + instructions, _left, err := parse(append(left, buf[:n]...)) + if err != nil { + self.err <- err + return + } + left = _left + for i := range instructions { + self.instructs <- instructions[i] + } + } + } + }() + return nil +} + +func (self *GuacamoleTunnel) Stop() { + self.stopChan <- true +} + +func (self *GuacamoleTunnel) Wait() error { + err := <-self.err + return err +} + +func (self *GuacamoleTunnel) Handshake() error { + selectArg := self.opts.Protocol + if len(self.opts.ConnectionId) > 0 { + selectArg = self.opts.ConnectionId + } + + err := self.start() + if err != nil { + return err + } + + _, err = self.Write([]byte(NewInstruction("select", selectArg).String())) + if err != nil { + return errors.Wrapf(err, "select") + } + + args, err := self.ReadOne() + if err != nil { + return errors.Wrapf(err, "ReadOne") + } + + if args.Opcode != "args" { + return errors.Errorf("Invalid instruct %s", args.String()) + } + + for i, arg := range args.Args { + args.Args[i] = self.opts.Parameters[arg] + } + + _, err = self.Write([]byte(NewInstruction("size", + fmt.Sprintf("%v", self.opts.OptimalScreenWidth), + fmt.Sprintf("%v", self.opts.OptimalScreenHeight), + fmt.Sprintf("%v", self.opts.OptimalResolution), + ).String())) + if err != nil { + return errors.Wrapf(err, "set size") + } + + _, err = self.Write([]byte(NewInstruction("audio", self.opts.AudioMimetypes...).String())) + if err != nil { + return errors.Wrapf(err, "set audio") + } + _, err = self.Write([]byte(NewInstruction("video", self.opts.VideoMimetypes...).String())) + if err != nil { + return errors.Wrapf(err, "set video") + } + _, err = self.Write([]byte(NewInstruction("image", self.opts.ImageMimetypes...).String())) + if err != nil { + return errors.Wrapf(err, "set image") + } + _, err = self.Write([]byte(NewInstruction("connect", args.Args...).String())) + if err != nil { + return errors.Wrapf(err, "connect %s", args.Args) + } + + ready, err := self.ReadOne() + if err != nil { + return errors.Wrapf(err, "read ready") + } + + if ready.Opcode != "ready" { + return fmt.Errorf("invalid ready instruction %s", ready.String()) + } + + if len(ready.Args) == 0 { + return fmt.Errorf("no connection id received") + } + + self.opts.ConnectionId = ready.Args[0] + return nil +} diff --git a/pkg/webconsole/handlers.go b/pkg/webconsole/handlers.go index 82b891f907..29e2c64f53 100644 --- a/pkg/webconsole/handlers.go +++ b/pkg/webconsole/handlers.go @@ -56,6 +56,7 @@ func InitHandlers(app *appsrv.Application) { app.AddHandler("POST", ApiPathPrefix+"baremetal/", auth.Authenticate(handleBaremetalShell)) app.AddHandler("POST", ApiPathPrefix+"ssh/", auth.Authenticate(handleSshShell)) app.AddHandler("POST", ApiPathPrefix+"server/", auth.Authenticate(handleServerRemoteConsole)) + app.AddHandler("POST", ApiPathPrefix+"server-rdp/", auth.Authenticate(handleServerRemoteRDPConsole)) for _, man := range []db.IModelManager{ models.GetCommandLogManager(), @@ -255,6 +256,22 @@ func handleServerRemoteConsole(ctx context.Context, w http.ResponseWriter, r *ht } } +func handleServerRemoteRDPConsole(ctx context.Context, w http.ResponseWriter, r *http.Request) { + env, err := fetchCloudEnv(ctx, w, r) + if err != nil { + httperrors.GeneralServerError(ctx, w, err) + return + } + query := env.Body + srvId := env.Params[""] + info, err := session.NewRemoteRDPConsoleInfoByCloud(env.ClientSessin, srvId, query) + if err != nil { + httperrors.GeneralServerError(ctx, w, err) + return + } + handleDataSession(ctx, info, w, url.Values{"password": {info.GetPassword()}}, true) +} + func responsePublicCloudConsole(ctx context.Context, info *session.RemoteConsoleInfo, w http.ResponseWriter) { params, err := info.GetConnectParams() if err != nil { diff --git a/pkg/webconsole/server/rdp_server.go b/pkg/webconsole/server/rdp_server.go new file mode 100644 index 0000000000..0472eccf03 --- /dev/null +++ b/pkg/webconsole/server/rdp_server.go @@ -0,0 +1,176 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package server + +import ( + "io" + "net/http" + "time" + + "github.com/gorilla/websocket" + + "yunion.io/x/log" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/gotypes" + + "yunion.io/x/onecloud/pkg/webconsole/guac" + "yunion.io/x/onecloud/pkg/webconsole/options" + "yunion.io/x/onecloud/pkg/webconsole/session" +) + +type RDPServer struct { + Session *session.SSession + + Host string + Port int + Username string + Password string + ConnectionId string + + Width int + Height int + Dpi int +} + +func NewRDPServer(s *session.SSession) (*RDPServer, error) { + info := s.ISessionData.(*session.RemoteRDPConsoleInfo) + server := &RDPServer{ + Session: s, + Host: info.Host, + Port: info.Port, + Username: info.Username, + Password: info.Password, + ConnectionId: info.ConnectionId, + + Width: info.Width, + Height: info.Height, + Dpi: info.Dpi, + } + return server, nil +} + +func (s *RDPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { + var up = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + CheckOrigin: func(r *http.Request) bool { + return true + }, + } + + ws, err := up.Upgrade(w, r, http.Header{"Sec-Websocket-Protocol": []string{ + r.Header.Get("Sec-Websocket-Protocol"), + }}) + if err != nil { + log.Errorf("upgrade error: %v", err) + return + } + + defer ws.Close() + + tunnel, err := guac.NewGuacamoleTunnel(s.Host, s.Port, s.Username, s.Password, s.ConnectionId, s.Width, s.Height, s.Dpi) + if err != nil { + log.Errorf("NewGuacamoleTunnel error: %v", err) + return + } + + err = tunnel.Start() + if err != nil { + log.Errorf("Start error: %v", err) + return + } + + done := make(chan bool, 3) + stop := make(chan bool) + timer := time.NewTimer(time.Microsecond * 100) + setDone := func() { + done <- true + } + + go func() { + defer setDone() + + for { + ins, err := tunnel.ReadOne() + if err != nil { + log.Errorf("read one error: %v", err) + return + } + if options.Options.SshSessionTimeoutMinutes > 0 && timer != nil { + timer.Reset(time.Duration(options.Options.SshSessionTimeoutMinutes) * time.Minute) + } + if !gotypes.IsNil(ins) { + err = ws.WriteMessage(websocket.TextMessage, []byte(ins.String())) + if err != nil { + log.Errorf("Failed writing to guacd %s: %v", ins.String(), err) + return + } + } + } + }() + + go func() { + defer setDone() + defer tunnel.Stop() + + for { + _, p, err := ws.ReadMessage() + if err != nil { + if errors.Cause(err) != io.EOF { + log.Errorf("read message error %v", err) + return + } + continue + } + if options.Options.SshSessionTimeoutMinutes > 0 && timer != nil { + timer.Reset(time.Duration(options.Options.SshSessionTimeoutMinutes) * time.Minute) + } + _, err = tunnel.Write(p) + if err != nil { + log.Errorf("Failed writing to guacd: %v", err) + return + } + } + }() + + go func() { + if options.Options.SshSessionTimeoutMinutes > 0 { + timer.Reset(time.Duration(options.Options.SshSessionTimeoutMinutes) * time.Minute) + } + defer timer.Stop() + defer setDone() + + for { + select { + case <-stop: + return + case <-timer.C: + if options.Options.SshSessionTimeoutMinutes > 0 { + return + } + timer.Reset(time.Microsecond * 100) + } + } + }() + + err = tunnel.Wait() + if err != nil { + log.Errorf("wait error: %v", err) + } + + <-done + stop <- true + log.Infof("rdp %s@%s:%d complete", s.Username, s.Host, s.Port) +} diff --git a/pkg/webconsole/server/server.go b/pkg/webconsole/server/server.go index dbd55eda96..9601537300 100644 --- a/pkg/webconsole/server/server.go +++ b/pkg/webconsole/server/server.go @@ -71,6 +71,8 @@ func (s *ConnectionServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { srv, err = NewWebsocketProxyServer(sessionObj) case session.WS: srv, err = NewSshServer(sessionObj) + case session.RDP: + srv, err = NewRDPServer(sessionObj) default: srv, err = NewTTYServer(sessionObj) } diff --git a/pkg/webconsole/session/remote_console.go b/pkg/webconsole/session/remote_console.go index 4005c2c6a5..69ac093960 100644 --- a/pkg/webconsole/session/remote_console.go +++ b/pkg/webconsole/session/remote_console.go @@ -31,6 +31,7 @@ import ( const ( VNC = api.VNC + RDP = api.RDP ALIYUN = api.ALIYUN QCLOUD = api.QCLOUD OPENSTACK = api.OPENSTACK diff --git a/pkg/webconsole/session/remote_console_rdp.go b/pkg/webconsole/session/remote_console_rdp.go new file mode 100644 index 0000000000..e7286351aa --- /dev/null +++ b/pkg/webconsole/session/remote_console_rdp.go @@ -0,0 +1,107 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package session + +import ( + "os/exec" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/gotypes" + + api "yunion.io/x/onecloud/pkg/apis/webconsole" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute" + "yunion.io/x/onecloud/pkg/webconsole/recorder" +) + +type RemoteRDPConsoleInfo struct { + Host string + Port int + Username string + Password string + ConnectionId string + + Width int + Height int + Dpi int + s *mcclient.ClientSession +} + +func NewRemoteRDPConsoleInfoByCloud(s *mcclient.ClientSession, serverId string, query jsonutils.JSONObject) (*RemoteRDPConsoleInfo, error) { + info := &RemoteRDPConsoleInfo{s: s} + if !gotypes.IsNil(query) { + query.Unmarshal(&info) + } + if len(info.Host) == 0 { + return nil, httperrors.NewMissingParameterError("host") + } + if (len(info.Password) == 0 || len(info.Username) == 0) && len(info.ConnectionId) == 0 { + ret, err := modules.Servers.PerformAction(s, serverId, "login-info", jsonutils.NewDict()) + if err != nil { + return nil, err + } + info.Password, _ = ret.GetString("password") + info.Username, _ = ret.GetString("username") + } + if info.Port == 0 { + info.Port = 3389 + } + return info, nil +} + +func (info *RemoteRDPConsoleInfo) GetProtocol() string { + return api.RDP +} + +func (info *RemoteRDPConsoleInfo) GetCommand() *exec.Cmd { + return nil +} + +func (info *RemoteRDPConsoleInfo) Cleanup() error { + return nil +} + +func (info *RemoteRDPConsoleInfo) Connect() error { + return nil +} + +func (info *RemoteRDPConsoleInfo) Scan(byte, func(string)) { + return +} + +func (info *RemoteRDPConsoleInfo) IsNeedLogin() (bool, error) { + return false, nil +} + +func (info *RemoteRDPConsoleInfo) GetClientSession() *mcclient.ClientSession { + return info.s +} + +func (info *RemoteRDPConsoleInfo) GetConnectParams() (string, error) { + return "", nil +} + +func (info *RemoteRDPConsoleInfo) GetPassword() string { + return info.Password +} + +func (info *RemoteRDPConsoleInfo) GetId() string { + return "" +} + +func (info *RemoteRDPConsoleInfo) GetRecordObject() *recorder.Object { + return nil +}