diff --git a/api/src/main/java/org/apache/cloudstack/agent/ApplianceAgentService.java b/api/src/main/java/org/apache/cloudstack/agent/ApplianceAgentService.java new file mode 100644 index 000000000000..6195feae8009 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/agent/ApplianceAgentService.java @@ -0,0 +1,24 @@ +// 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. + +package org.apache.cloudstack.agent; + +import org.apache.cloudstack.api.command.admin.agent.LivePatchCmd; + +public interface ApplianceAgentService { + String livePatch(LivePatchCmd cmd); +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/agent/LivePatchCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/agent/LivePatchCmd.java new file mode 100644 index 000000000000..7302528da7a8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/agent/LivePatchCmd.java @@ -0,0 +1,96 @@ +// 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. + +package org.apache.cloudstack.api.command.admin.agent; + + +import javax.inject.Inject; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.agent.ApplianceAgentService; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.SystemVmResponse; + +import com.cloud.user.Account; +import com.google.common.base.Strings; + +@APICommand(name = LivePatchCmd.APINAME, + description = "Live Patches Appliance Agent", + responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.13.0", + authorized = {RoleType.Admin}) +public class LivePatchCmd extends BaseCmd { + public static final String APINAME = "livePatch"; + + @Inject + ApplianceAgentService applianceAgentService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, required = true, entityType = SystemVmResponse.class, + validations = {ApiArgValidator.PositiveNumber}, + description = "The ID of the system VM appliance to live patch") + private Long id; + + @Parameter(name = ApiConstants.TOKEN, type = CommandType.STRING, + description = "Ping token") + private String token; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getToken() { + return token; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() { + String reply = applianceAgentService.livePatch(this); + SuccessResponse response = new SuccessResponse(); + response.setDisplayText(reply); + response.setSuccess(!Strings.isNullOrEmpty(reply)); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} diff --git a/framework/appliance-agent/agent/agent.go b/framework/appliance-agent/agent/agent.go new file mode 100644 index 000000000000..30bf5c05263c --- /dev/null +++ b/framework/appliance-agent/agent/agent.go @@ -0,0 +1,34 @@ +// Virtual Appliance Agent +package main + +import ( + "context" + "log" + "net" + + "google.golang.org/grpc" + pb "./virtualappliance" +) + +const ( + port = ":8200" +) + +type server struct{} + +func (s *server) Ping(ctx context.Context, in *pb.PingRequest) (*pb.PingResponse, error) { + log.Printf("Received: %v", in.Message) + return &pb.PingResponse{Message: "Pong " + in.Message}, nil +} + +func main() { + lis, err := net.Listen("tcp", port) + if err != nil { + log.Fatalf("failed to listen: %v", err) + } + s := grpc.NewServer() + pb.RegisterApplianceAgentServer(s, &server{}) + if err := s.Serve(lis); err != nil { + log.Fatalf("failed to serve: %v", err) + } +} diff --git a/framework/appliance-agent/agent/testclient.go b/framework/appliance-agent/agent/testclient.go new file mode 100644 index 000000000000..189ca374f7c2 --- /dev/null +++ b/framework/appliance-agent/agent/testclient.go @@ -0,0 +1,37 @@ +package main + +import ( + "context" + "log" + "os" + "time" + + "google.golang.org/grpc" + pb "./virtualappliance" +) + +const ( + address = "localhost:50051" + defaultName = "client123" +) + +func main() { + conn, err := grpc.Dial(address, grpc.WithInsecure()) + if err != nil { + log.Fatalf("did not connect: %v", err) + } + defer conn.Close() + c := pb.NewApplianceAgentClient(conn) + + name := defaultName + if len(os.Args) > 1 { + name = os.Args[1] + } + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + r, err := c.Ping(ctx, &pb.PingRequest{Message: name}) + if err != nil { + log.Fatalf("could not ping due to: %v", err) + } + log.Printf("Pinging: %s", r.Message) +} diff --git a/framework/appliance-agent/agent/virtualappliance/VirtualAppliance.pb.go b/framework/appliance-agent/agent/virtualappliance/VirtualAppliance.pb.go new file mode 100644 index 000000000000..628e148327f4 --- /dev/null +++ b/framework/appliance-agent/agent/virtualappliance/VirtualAppliance.pb.go @@ -0,0 +1,206 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: VirtualAppliance.proto + +package virtualappliance + +import ( + context "context" + fmt "fmt" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type PingRequest struct { + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PingRequest) Reset() { *m = PingRequest{} } +func (m *PingRequest) String() string { return proto.CompactTextString(m) } +func (*PingRequest) ProtoMessage() {} +func (*PingRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_1dcc0045f14d07fe, []int{0} +} + +func (m *PingRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PingRequest.Unmarshal(m, b) +} +func (m *PingRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PingRequest.Marshal(b, m, deterministic) +} +func (m *PingRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_PingRequest.Merge(m, src) +} +func (m *PingRequest) XXX_Size() int { + return xxx_messageInfo_PingRequest.Size(m) +} +func (m *PingRequest) XXX_DiscardUnknown() { + xxx_messageInfo_PingRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_PingRequest proto.InternalMessageInfo + +func (m *PingRequest) GetMessage() string { + if m != nil { + return m.Message + } + return "" +} + +type PingResponse struct { + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PingResponse) Reset() { *m = PingResponse{} } +func (m *PingResponse) String() string { return proto.CompactTextString(m) } +func (*PingResponse) ProtoMessage() {} +func (*PingResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1dcc0045f14d07fe, []int{1} +} + +func (m *PingResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PingResponse.Unmarshal(m, b) +} +func (m *PingResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PingResponse.Marshal(b, m, deterministic) +} +func (m *PingResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_PingResponse.Merge(m, src) +} +func (m *PingResponse) XXX_Size() int { + return xxx_messageInfo_PingResponse.Size(m) +} +func (m *PingResponse) XXX_DiscardUnknown() { + xxx_messageInfo_PingResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_PingResponse proto.InternalMessageInfo + +func (m *PingResponse) GetMessage() string { + if m != nil { + return m.Message + } + return "" +} + +func init() { + proto.RegisterType((*PingRequest)(nil), "virtualappliance.PingRequest") + proto.RegisterType((*PingResponse)(nil), "virtualappliance.PingResponse") +} + +func init() { proto.RegisterFile("VirtualAppliance.proto", fileDescriptor_1dcc0045f14d07fe) } + +var fileDescriptor_1dcc0045f14d07fe = []byte{ + // 184 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x0b, 0xcb, 0x2c, 0x2a, + 0x29, 0x4d, 0xcc, 0x71, 0x2c, 0x28, 0xc8, 0xc9, 0x4c, 0xcc, 0x4b, 0x4e, 0xd5, 0x2b, 0x28, 0xca, + 0x2f, 0xc9, 0x17, 0x12, 0x28, 0x83, 0x88, 0x27, 0xc2, 0xc4, 0x95, 0xd4, 0xb9, 0xb8, 0x03, 0x32, + 0xf3, 0xd2, 0x83, 0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x24, 0xb8, 0xd8, 0x73, 0x53, 0x8b, + 0x8b, 0x13, 0xd3, 0x53, 0x25, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0x60, 0x5c, 0x25, 0x0d, 0x2e, + 0x1e, 0x88, 0xc2, 0xe2, 0x82, 0xfc, 0xbc, 0xe2, 0x54, 0xdc, 0x2a, 0x8d, 0x22, 0xb9, 0xf8, 0xe0, + 0xf6, 0x3a, 0xa6, 0xa7, 0xe6, 0x95, 0x08, 0xb9, 0x73, 0xb1, 0x80, 0xf4, 0x0a, 0xc9, 0xea, 0xa1, + 0xdb, 0xaf, 0x87, 0x64, 0xb9, 0x94, 0x1c, 0x2e, 0x69, 0x88, 0x95, 0x4a, 0x0c, 0x4e, 0xee, 0x5c, + 0xfa, 0xf9, 0x45, 0xe9, 0x7a, 0x89, 0x05, 0x89, 0xc9, 0x19, 0xa9, 0x7a, 0xc9, 0x39, 0xf9, 0xa5, + 0x29, 0xc5, 0x25, 0x89, 0xc9, 0xd9, 0x98, 0x1a, 0xc1, 0xfe, 0x4d, 0x2a, 0x4d, 0x73, 0x12, 0x42, + 0x0f, 0x8a, 0x00, 0xa7, 0x00, 0xc6, 0x24, 0x36, 0xb0, 0xbc, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, + 0xa7, 0xe9, 0x61, 0x15, 0x29, 0x01, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// ApplianceAgentClient is the client API for ApplianceAgent service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type ApplianceAgentClient interface { + Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) +} + +type applianceAgentClient struct { + cc *grpc.ClientConn +} + +func NewApplianceAgentClient(cc *grpc.ClientConn) ApplianceAgentClient { + return &applianceAgentClient{cc} +} + +func (c *applianceAgentClient) Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) { + out := new(PingResponse) + err := c.cc.Invoke(ctx, "/virtualappliance.ApplianceAgent/Ping", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ApplianceAgentServer is the server API for ApplianceAgent service. +type ApplianceAgentServer interface { + Ping(context.Context, *PingRequest) (*PingResponse, error) +} + +// UnimplementedApplianceAgentServer can be embedded to have forward compatible implementations. +type UnimplementedApplianceAgentServer struct { +} + +func (*UnimplementedApplianceAgentServer) Ping(ctx context.Context, req *PingRequest) (*PingResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented") +} + +func RegisterApplianceAgentServer(s *grpc.Server, srv ApplianceAgentServer) { + s.RegisterService(&_ApplianceAgent_serviceDesc, srv) +} + +func _ApplianceAgent_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PingRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApplianceAgentServer).Ping(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/virtualappliance.ApplianceAgent/Ping", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApplianceAgentServer).Ping(ctx, req.(*PingRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _ApplianceAgent_serviceDesc = grpc.ServiceDesc{ + ServiceName: "virtualappliance.ApplianceAgent", + HandlerType: (*ApplianceAgentServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Ping", + Handler: _ApplianceAgent_Ping_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "VirtualAppliance.proto", +} diff --git a/framework/appliance-agent/gen.sh b/framework/appliance-agent/gen.sh new file mode 100755 index 000000000000..9091141d8336 --- /dev/null +++ b/framework/appliance-agent/gen.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -x +protoc -I src/main/proto VirtualAppliance.proto --go_out=plugins=grpc:agent/virtualappliance +(cd agent; go build agent.go; cp agent ../../../systemvm/debian/opt/cloud/bin/) diff --git a/framework/appliance-agent/pom.xml b/framework/appliance-agent/pom.xml new file mode 100644 index 000000000000..246fb3950594 --- /dev/null +++ b/framework/appliance-agent/pom.xml @@ -0,0 +1,102 @@ + + + 4.0.0 + cloud-framework-appliance-agent + Apache CloudStack Framework - Appliance Agent + + org.apache.cloudstack + cloudstack-framework + 4.13.0.0-SNAPSHOT + ../pom.xml + + + + org.apache.cloudstack + cloud-utils + ${project.version} + + + org.apache.cloudstack + cloud-framework-ca + ${project.version} + + + org.apache.cloudstack + cloud-api + ${project.version} + + + com.google.protobuf + protobuf-java + 3.7.1 + + + com.google.protobuf + protobuf-java-util + 3.7.1 + + + io.grpc + grpc-netty + 1.20.0 + runtime + + + io.grpc + grpc-protobuf + 1.20.0 + + + io.grpc + grpc-stub + 1.20.0 + + + + + + kr.motd.maven + os-maven-plugin + 1.5.0.Final + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.5.0 + + com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier} + grpc-java + io.grpc:protoc-gen-grpc-java:1.20.0:exe:${os.detected.classifier} + + + + + compile + compile-custom + + + + + + + diff --git a/framework/appliance-agent/src/main/java/org/apache/cloudstack/virtualappliance/ApplianceAgentRpcClient.java b/framework/appliance-agent/src/main/java/org/apache/cloudstack/virtualappliance/ApplianceAgentRpcClient.java new file mode 100644 index 000000000000..c80555646d65 --- /dev/null +++ b/framework/appliance-agent/src/main/java/org/apache/cloudstack/virtualappliance/ApplianceAgentRpcClient.java @@ -0,0 +1,45 @@ +// 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. + +package org.apache.cloudstack.virtualappliance; + +import org.apache.cloudstack.virtualappliance.protobuf.ApplianceAgentGrpc; +import org.apache.cloudstack.virtualappliance.protobuf.PingRequest; +import org.apache.cloudstack.virtualappliance.protobuf.PingResponse; + +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; + +public class ApplianceAgentRpcClient { + ApplianceAgentGrpc.ApplianceAgentBlockingStub blockingStub; + + public ApplianceAgentRpcClient() { + ManagedChannel managedChannel = ManagedChannelBuilder + .forAddress("localhost", 8200).usePlaintext().build(); + blockingStub = ApplianceAgentGrpc.newBlockingStub(managedChannel); + } + + public ApplianceAgentRpcClient(String host, Integer port) { + ManagedChannel managedChannel = ManagedChannelBuilder + .forAddress(host, port).usePlaintext().build(); + blockingStub = ApplianceAgentGrpc.newBlockingStub(managedChannel); + } + + public PingResponse ping(final PingRequest request) { + return blockingStub.ping(request); + } +} diff --git a/framework/appliance-agent/src/main/proto/VirtualAppliance.proto b/framework/appliance-agent/src/main/proto/VirtualAppliance.proto new file mode 100644 index 000000000000..73751b469557 --- /dev/null +++ b/framework/appliance-agent/src/main/proto/VirtualAppliance.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "org.apache.cloudstack.virtualappliance.protobuf"; +option java_outer_classname = "VirtualAppliancePB"; + +package virtualappliance; + +service ApplianceAgent { + rpc Ping (PingRequest) returns (PingResponse) {} +} + +message PingRequest { + string message = 1; +} + +message PingResponse { + string message = 1; +} diff --git a/framework/appliance-agent/src/test/java/org/apache/cloudstack/virtualappliance/ApplianceAgentRpcClientTest.java b/framework/appliance-agent/src/test/java/org/apache/cloudstack/virtualappliance/ApplianceAgentRpcClientTest.java new file mode 100644 index 000000000000..de14fd84dc3b --- /dev/null +++ b/framework/appliance-agent/src/test/java/org/apache/cloudstack/virtualappliance/ApplianceAgentRpcClientTest.java @@ -0,0 +1,35 @@ +// 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. +package org.apache.cloudstack.virtualappliance; + +import org.apache.cloudstack.virtualappliance.protobuf.PingRequest; +import org.apache.cloudstack.virtualappliance.protobuf.PingResponse; +import org.junit.Assert; + +public class ApplianceAgentRpcClientTest { + + //@Test + public void ping() { + ApplianceAgentRpcClient client = new ApplianceAgentRpcClient("172.20.20.12", 8200); + String message = "GRPC based Agent Works!"; + PingRequest request = PingRequest.newBuilder() + .setMessage(message) + .build(); + PingResponse response = client.ping(request); + Assert.assertEquals("Pong " + message, response.getMessage()); + } +} diff --git a/framework/pom.xml b/framework/pom.xml index c42bb3c24c75..8c64500fb531 100644 --- a/framework/pom.xml +++ b/framework/pom.xml @@ -45,6 +45,7 @@ agent-lb + appliance-agent ca cluster config diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartCommandWrapper.java index da9f1f3b3d1a..f3839f0fc761 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartCommandWrapper.java @@ -118,9 +118,14 @@ public Answer execute(final StartCommand command, final LibvirtComputingResource // try to patch and SSH into the systemvm for up to 5 minutes for (int count = 0; count < 10; count++) { // wait and try passCmdLine for 30 seconds at most for CLOUDSTACK-2823 - libvirtComputingResource.passCmdLine(vmName, vmSpec.getBootArgs()); - // check router is up? - final VirtualRoutingResource virtRouterResource = libvirtComputingResource.getVirtRouterResource(); + if (libvirtComputingResource.passCmdLine(vmName, vmSpec.getBootArgs())) { + break; + } + } + + final VirtualRoutingResource virtRouterResource = libvirtComputingResource.getVirtRouterResource(); + // check if the router is up? + for (int count = 0; count < 60; count++) { final boolean result = virtRouterResource.connect(controlIp, 1, 5000); if (result) { break; diff --git a/pom.xml b/pom.xml index 44f0cbc1e2a0..e559f5437f8b 100644 --- a/pom.xml +++ b/pom.xml @@ -760,7 +760,7 @@ ${project.basedir} **\/*.java - **\/deps\/,**\/test\/,**\/target\/,**\/bin\/,**\/*.xml,**\/*.ini,**\/*.sh,**\/*.bat,**\/XmlToHtmlConverter*,**\/generated-sources\/**\/* + **\/deps\/,**\/test\/,**\/target\/,**\/bin\/,**\/*.xml,**\/*.ini,**\/*.sh,**\/*.bat,**\/XmlToHtmlConverter*,**\/generated-sources\/**\/*,**\/protobuf\/ diff --git a/server/pom.xml b/server/pom.xml index c0ee1197dcb1..ef14b5186893 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -64,6 +64,11 @@ cloud-framework-ca ${project.version} + + org.apache.cloudstack + cloud-framework-appliance-agent + ${project.version} + org.apache.cloudstack cloud-framework-jobs diff --git a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java index 68c12333c5e3..238171f4db3c 100644 --- a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java @@ -620,7 +620,7 @@ public void updateKeyPairs() { s_logger.error("Cannot read the private key file", e); throw new CloudRuntimeException("Cannot read the private key file"); } - String privateKey = new String(arr1).trim(); + String privateKey = new String(arr1).trim() + "\n"; byte[] arr2 = new byte[4094]; // configuration table column value size try (DataInputStream dis = new DataInputStream(new FileInputStream(pubkeyfile))) { dis.readFully(arr2); @@ -630,7 +630,7 @@ public void updateKeyPairs() { s_logger.warn("Cannot read the public key file", e); throw new CloudRuntimeException("Cannot read the public key file"); } - String publicKey = new String(arr2).trim(); + String publicKey = new String(arr2).trim() + "\n"; final String insertSql1 = "INSERT INTO `cloud`.`configuration` (category, instance, component, name, value, description) " + diff --git a/server/src/main/java/org/apache/cloudstack/agent/ApplianceAgentManagerImpl.java b/server/src/main/java/org/apache/cloudstack/agent/ApplianceAgentManagerImpl.java new file mode 100644 index 000000000000..e83da3cafe02 --- /dev/null +++ b/server/src/main/java/org/apache/cloudstack/agent/ApplianceAgentManagerImpl.java @@ -0,0 +1,80 @@ +// 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. + +package org.apache.cloudstack.agent; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.command.admin.agent.LivePatchCmd; +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.cloudstack.virtualappliance.ApplianceAgentRpcClient; +import org.apache.cloudstack.virtualappliance.protobuf.PingRequest; +import org.apache.cloudstack.virtualappliance.protobuf.PingResponse; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.Networks; +import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.component.PluggableService; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.dao.VMInstanceDao; +import com.google.common.base.Strings; + +public class ApplianceAgentManagerImpl extends ManagerBase implements PluggableService, ApplianceAgentService { + + @Inject + VMInstanceDao vmInstanceDao; + + @Inject + private NetworkOrchestrationService networkManager; + + @Override + public List> getCommands() { + return Arrays.asList(LivePatchCmd.class); + } + + @Override + public String livePatch(LivePatchCmd cmd) { + final Long vmId = cmd.getId(); + final String message = cmd.getToken(); + final VMInstanceVO vmInstance = vmInstanceDao.findByIdTypes(vmId, VirtualMachine.Type.ConsoleProxy, VirtualMachine.Type.DomainRouter, VirtualMachine.Type.SecondaryStorageVm); + + if (vmInstance == null) { + throw new InvalidParameterValueException("Unable to find a system vm with the requested ID)"); + } + + final Map accessDetails = networkManager.getSystemVMAccessDetails(vmInstance); + final String applianceManagementAddress = accessDetails.get(Networks.TrafficType.Public.name()); + + if (Strings.isNullOrEmpty(applianceManagementAddress)) { + throw new CloudRuntimeException("Unable to set system vm management IP for system vm"); + } + + // TODO: exception handling, checks etc. + ApplianceAgentRpcClient client = new ApplianceAgentRpcClient(applianceManagementAddress, 8200); + PingRequest request = PingRequest.newBuilder() + .setMessage(message) + .build(); + PingResponse response = client.ping(request); + return response.getMessage(); + } +} diff --git a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml index 2f67c4248d35..b2ce0614d9d0 100644 --- a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml +++ b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml @@ -300,4 +300,7 @@ + + + diff --git a/systemvm/debian/etc/iptables/iptables-router b/systemvm/debian/etc/iptables/iptables-router index 9851ee7dbd96..1594f5fa3f72 100644 --- a/systemvm/debian/etc/iptables/iptables-router +++ b/systemvm/debian/etc/iptables/iptables-router @@ -38,6 +38,7 @@ COMMIT -A INPUT -i eth0 -p tcp -m tcp --dport 53 -j ACCEPT -A INPUT -i eth1 -p tcp -m tcp -m state --state NEW,ESTABLISHED --dport 3922 -j ACCEPT -A INPUT -i eth0 -p tcp -m tcp -m state --state NEW --dport 80 -j ACCEPT +-A INPUT -p tcp -m tcp -m state --state NEW,ESTABLISHED --dport 8200 -j ACCEPT -A FORWARD -i eth0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT -A FORWARD -i eth2 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT -A FORWARD -i eth0 -o eth0 -m state --state NEW -j ACCEPT diff --git a/systemvm/debian/etc/iptables/iptables-secstorage b/systemvm/debian/etc/iptables/iptables-secstorage index 019aaa0bfb40..0d53f48c3b3d 100755 --- a/systemvm/debian/etc/iptables/iptables-secstorage +++ b/systemvm/debian/etc/iptables/iptables-secstorage @@ -33,4 +33,5 @@ COMMIT -A INPUT -p icmp --icmp-type 13 -j DROP -A INPUT -p icmp -j ACCEPT -A INPUT -i eth0 -p tcp -m state --state NEW --dport 3922 -j ACCEPT +-A INPUT -p tcp -m tcp -m state --state NEW,ESTABLISHED --dport 8200 -j ACCEPT COMMIT diff --git a/systemvm/debian/etc/iptables/iptables-vpcrouter b/systemvm/debian/etc/iptables/iptables-vpcrouter index e6237c5a1cd8..4d8716277952 100644 --- a/systemvm/debian/etc/iptables/iptables-vpcrouter +++ b/systemvm/debian/etc/iptables/iptables-vpcrouter @@ -29,6 +29,7 @@ COMMIT -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -i eth0 -p tcp -m tcp -m state --state NEW,ESTABLISHED --dport 3922 -j ACCEPT +-A INPUT -p tcp -m tcp -m state --state NEW,ESTABLISHED --dport 8200 -j ACCEPT -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT COMMIT diff --git a/systemvm/debian/opt/cloud/bin/setup/router.sh b/systemvm/debian/opt/cloud/bin/setup/router.sh index 6bb3b6f21461..e1fe8cd9646f 100755 --- a/systemvm/debian/opt/cloud/bin/setup/router.sh +++ b/systemvm/debian/opt/cloud/bin/setup/router.sh @@ -96,7 +96,9 @@ setup_router() { sed -i "s/-A INPUT -i eth0 -p tcp -m tcp --dport 53 -j ACCEPT/-A INPUT -i eth0 -p tcp -m tcp --dport 53 -s $DHCP_RANGE\/$CIDR_SIZE -j ACCEPT/g" /etc/iptables/rules.v4 # Setup hourly logrotate - mv -n /etc/cron.daily/logrotate /etc/cron.hourly 2>&1 + if [ -f /etc/cron.daily/logrotate ]; then + mv -n /etc/cron.daily/logrotate /etc/cron.hourly 2>&1 + fi } routing_svcs diff --git a/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh b/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh index af111adc6ba7..9e6cdb7b74b6 100755 --- a/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh +++ b/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh @@ -108,8 +108,10 @@ EOF echo 0 > /var/cache/cloud/dnsmasq_managed_lease fi - #setup hourly logrotate - mv -n /etc/cron.daily/logrotate /etc/cron.hourly 2>&1 + # Setup hourly logrotate + if [ -f /etc/cron.daily/logrotate ]; then + mv -n /etc/cron.daily/logrotate /etc/cron.hourly 2>&1 + fi } routing_svcs diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py index 1b2a979d443b..99ecb6f46eef 100644 --- a/tools/apidoc/gen_toc.py +++ b/tools/apidoc/gen_toc.py @@ -192,6 +192,7 @@ 'Sioc' : 'Sioc', 'Diagnostics': 'Diagnostics', 'Management': 'Management', + 'livePatch': 'Router', }