mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
CLOUDSTACK-9957 Annotations (#2181)
* annotations on hosts * Adding marvin tests * rebase error * review comments * context for owner * review * illegal entity test * entityType check on input * Annotation events * rebase issues
This commit is contained in:
parent
189b0e4487
commit
a379230e8e
@ -19,12 +19,6 @@ package com.cloud.event;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.cloudstack.acl.Role;
|
|
||||||
import org.apache.cloudstack.acl.RolePermission;
|
|
||||||
import org.apache.cloudstack.config.Configuration;
|
|
||||||
import org.apache.cloudstack.ha.HAConfig;
|
|
||||||
import org.apache.cloudstack.usage.Usage;
|
|
||||||
|
|
||||||
import com.cloud.dc.DataCenter;
|
import com.cloud.dc.DataCenter;
|
||||||
import com.cloud.dc.Pod;
|
import com.cloud.dc.Pod;
|
||||||
import com.cloud.dc.StorageNetworkIpRange;
|
import com.cloud.dc.StorageNetworkIpRange;
|
||||||
@ -75,6 +69,12 @@ import com.cloud.user.User;
|
|||||||
import com.cloud.vm.Nic;
|
import com.cloud.vm.Nic;
|
||||||
import com.cloud.vm.NicSecondaryIp;
|
import com.cloud.vm.NicSecondaryIp;
|
||||||
import com.cloud.vm.VirtualMachine;
|
import com.cloud.vm.VirtualMachine;
|
||||||
|
import org.apache.cloudstack.acl.Role;
|
||||||
|
import org.apache.cloudstack.acl.RolePermission;
|
||||||
|
import org.apache.cloudstack.annotation.Annotation;
|
||||||
|
import org.apache.cloudstack.config.Configuration;
|
||||||
|
import org.apache.cloudstack.ha.HAConfig;
|
||||||
|
import org.apache.cloudstack.usage.Usage;
|
||||||
|
|
||||||
public class EventTypes {
|
public class EventTypes {
|
||||||
|
|
||||||
@ -569,6 +569,9 @@ public class EventTypes {
|
|||||||
public static final String EVENT_NETSCALER_VM_START = "NETSCALERVM.START";
|
public static final String EVENT_NETSCALER_VM_START = "NETSCALERVM.START";
|
||||||
public static final String EVENT_NETSCALER_VM_STOP = "NETSCALERVM.STOP";
|
public static final String EVENT_NETSCALER_VM_STOP = "NETSCALERVM.STOP";
|
||||||
|
|
||||||
|
public static final String EVENT_ANNOTATION_CREATE = "ANNOTATION.CREATE";
|
||||||
|
public static final String EVENT_ANNOTATION_REMOVE = "ANNOTATION.REMOVE";
|
||||||
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
|
||||||
@ -953,6 +956,8 @@ public class EventTypes {
|
|||||||
entityEventDetails.put(EVENT_NETSCALER_SERVICEPACKAGE_ADD, "NETSCALER.SERVICEPACKAGE.CREATE");
|
entityEventDetails.put(EVENT_NETSCALER_SERVICEPACKAGE_ADD, "NETSCALER.SERVICEPACKAGE.CREATE");
|
||||||
entityEventDetails.put(EVENT_NETSCALER_SERVICEPACKAGE_DELETE, "NETSCALER.SERVICEPACKAGE.DELETE");
|
entityEventDetails.put(EVENT_NETSCALER_SERVICEPACKAGE_DELETE, "NETSCALER.SERVICEPACKAGE.DELETE");
|
||||||
|
|
||||||
|
entityEventDetails.put(EVENT_ANNOTATION_CREATE, Annotation.class);
|
||||||
|
entityEventDetails.put(EVENT_ANNOTATION_REMOVE, Annotation.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getEntityForEvent(String eventName) {
|
public static String getEntityForEvent(String eventName) {
|
||||||
|
|||||||
37
api/src/org/apache/cloudstack/annotation/Annotation.java
Normal file
37
api/src/org/apache/cloudstack/annotation/Annotation.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// 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.annotation;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.Identity;
|
||||||
|
import org.apache.cloudstack.api.InternalIdentity;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public interface Annotation extends InternalIdentity, Identity {
|
||||||
|
|
||||||
|
String getAnnotation();
|
||||||
|
|
||||||
|
String getEntityUuid();
|
||||||
|
|
||||||
|
AnnotationService.EntityType getEntityType();
|
||||||
|
|
||||||
|
String getUserUuid();
|
||||||
|
|
||||||
|
Date getCreated();
|
||||||
|
|
||||||
|
Date getRemoved();
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
// 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.annotation;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.command.admin.annotation.AddAnnotationCmd;
|
||||||
|
import org.apache.cloudstack.api.command.admin.annotation.ListAnnotationsCmd;
|
||||||
|
import org.apache.cloudstack.api.command.admin.annotation.RemoveAnnotationCmd;
|
||||||
|
import org.apache.cloudstack.api.response.AnnotationResponse;
|
||||||
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
|
|
||||||
|
public interface AnnotationService {
|
||||||
|
ListResponse<AnnotationResponse> searchForAnnotations(ListAnnotationsCmd cmd);
|
||||||
|
|
||||||
|
AnnotationResponse addAnnotation(AddAnnotationCmd addAnnotationCmd);
|
||||||
|
AnnotationResponse addAnnotation(String text, EntityType type, String uuid);
|
||||||
|
|
||||||
|
AnnotationResponse removeAnnotation(RemoveAnnotationCmd removeAnnotationCmd);
|
||||||
|
|
||||||
|
enum EntityType {
|
||||||
|
HOST("host"), DOMAIN("domain"), VM("vm_instance");
|
||||||
|
private String tableName;
|
||||||
|
|
||||||
|
EntityType(String tableName) {
|
||||||
|
this.tableName = tableName;
|
||||||
|
}
|
||||||
|
static public boolean contains(String representation) {
|
||||||
|
try {
|
||||||
|
/* EntityType tiep = */ valueOf(representation);
|
||||||
|
return true;
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -25,6 +25,7 @@ public class ApiConstants {
|
|||||||
public static final String ADDRESS = "address";
|
public static final String ADDRESS = "address";
|
||||||
public static final String ALGORITHM = "algorithm";
|
public static final String ALGORITHM = "algorithm";
|
||||||
public static final String ALLOCATED_ONLY = "allocatedonly";
|
public static final String ALLOCATED_ONLY = "allocatedonly";
|
||||||
|
public static final String ANNOTATION = "annotation";
|
||||||
public static final String API_KEY = "apikey";
|
public static final String API_KEY = "apikey";
|
||||||
public static final String USER_API_KEY = "userapikey";
|
public static final String USER_API_KEY = "userapikey";
|
||||||
public static final String APPLIED = "applied";
|
public static final String APPLIED = "applied";
|
||||||
@ -680,6 +681,9 @@ public class ApiConstants {
|
|||||||
+ " representing the java supported algorithm, i.e. MD5 or SHA-256. Note that java does not\n"
|
+ " representing the java supported algorithm, i.e. MD5 or SHA-256. Note that java does not\n"
|
||||||
+ " contain an algorithm called SHA256 or one called sha-256, only SHA-256.";
|
+ " contain an algorithm called SHA256 or one called sha-256, only SHA-256.";
|
||||||
|
|
||||||
|
public static final String HAS_ANNOTATION = "hasannotation";
|
||||||
|
public static final String LAST_ANNOTATED = "lastannotated";
|
||||||
|
|
||||||
public enum HostDetails {
|
public enum HostDetails {
|
||||||
all, capacity, events, stats, min;
|
all, capacity, events, stats, min;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,32 +17,6 @@
|
|||||||
|
|
||||||
package org.apache.cloudstack.api;
|
package org.apache.cloudstack.api;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import com.cloud.utils.HttpUtils;
|
|
||||||
import org.apache.cloudstack.acl.RoleService;
|
|
||||||
import org.apache.log4j.Logger;
|
|
||||||
|
|
||||||
import org.apache.cloudstack.acl.RoleType;
|
|
||||||
import org.apache.cloudstack.affinity.AffinityGroupService;
|
|
||||||
import org.apache.cloudstack.alert.AlertService;
|
|
||||||
import org.apache.cloudstack.context.CallContext;
|
|
||||||
import org.apache.cloudstack.network.element.InternalLoadBalancerElementService;
|
|
||||||
import org.apache.cloudstack.network.lb.ApplicationLoadBalancerService;
|
|
||||||
import org.apache.cloudstack.network.lb.InternalLoadBalancerVMService;
|
|
||||||
import org.apache.cloudstack.query.QueryService;
|
|
||||||
import org.apache.cloudstack.usage.UsageService;
|
|
||||||
|
|
||||||
import com.cloud.configuration.ConfigurationService;
|
import com.cloud.configuration.ConfigurationService;
|
||||||
import com.cloud.exception.ConcurrentOperationException;
|
import com.cloud.exception.ConcurrentOperationException;
|
||||||
import com.cloud.exception.InsufficientCapacityException;
|
import com.cloud.exception.InsufficientCapacityException;
|
||||||
@ -78,11 +52,35 @@ import com.cloud.user.Account;
|
|||||||
import com.cloud.user.AccountService;
|
import com.cloud.user.AccountService;
|
||||||
import com.cloud.user.DomainService;
|
import com.cloud.user.DomainService;
|
||||||
import com.cloud.user.ResourceLimitService;
|
import com.cloud.user.ResourceLimitService;
|
||||||
|
import com.cloud.utils.HttpUtils;
|
||||||
import com.cloud.utils.ReflectUtil;
|
import com.cloud.utils.ReflectUtil;
|
||||||
import com.cloud.utils.db.EntityManager;
|
import com.cloud.utils.db.EntityManager;
|
||||||
import com.cloud.utils.db.UUIDManager;
|
import com.cloud.utils.db.UUIDManager;
|
||||||
import com.cloud.vm.UserVmService;
|
import com.cloud.vm.UserVmService;
|
||||||
import com.cloud.vm.snapshot.VMSnapshotService;
|
import com.cloud.vm.snapshot.VMSnapshotService;
|
||||||
|
import org.apache.cloudstack.acl.RoleService;
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.affinity.AffinityGroupService;
|
||||||
|
import org.apache.cloudstack.alert.AlertService;
|
||||||
|
import org.apache.cloudstack.annotation.AnnotationService;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.network.element.InternalLoadBalancerElementService;
|
||||||
|
import org.apache.cloudstack.network.lb.ApplicationLoadBalancerService;
|
||||||
|
import org.apache.cloudstack.network.lb.InternalLoadBalancerVMService;
|
||||||
|
import org.apache.cloudstack.query.QueryService;
|
||||||
|
import org.apache.cloudstack.usage.UsageService;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public abstract class BaseCmd {
|
public abstract class BaseCmd {
|
||||||
private static final Logger s_logger = Logger.getLogger(BaseCmd.class.getName());
|
private static final Logger s_logger = Logger.getLogger(BaseCmd.class.getName());
|
||||||
@ -93,6 +91,7 @@ public abstract class BaseCmd {
|
|||||||
public static Pattern newInputDateFormat = Pattern.compile("[\\d]+-[\\d]+-[\\d]+ [\\d]+:[\\d]+:[\\d]+");
|
public static Pattern newInputDateFormat = Pattern.compile("[\\d]+-[\\d]+-[\\d]+ [\\d]+:[\\d]+:[\\d]+");
|
||||||
private static final DateFormat s_outputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
|
private static final DateFormat s_outputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
|
||||||
protected static final Map<Class<?>, List<Field>> fieldsForCmdClass = new HashMap<Class<?>, List<Field>>();
|
protected static final Map<Class<?>, List<Field>> fieldsForCmdClass = new HashMap<Class<?>, List<Field>>();
|
||||||
|
|
||||||
public static enum HTTPMethod {
|
public static enum HTTPMethod {
|
||||||
GET, POST, PUT, DELETE
|
GET, POST, PUT, DELETE
|
||||||
}
|
}
|
||||||
@ -192,6 +191,8 @@ public abstract class BaseCmd {
|
|||||||
public AlertService _alertSvc;
|
public AlertService _alertSvc;
|
||||||
@Inject
|
@Inject
|
||||||
public UUIDManager _uuidMgr;
|
public UUIDManager _uuidMgr;
|
||||||
|
@Inject
|
||||||
|
public AnnotationService annotationService;
|
||||||
|
|
||||||
public abstract void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException,
|
public abstract void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException,
|
||||||
ResourceAllocationException, NetworkRuleConflictException;
|
ResourceAllocationException, NetworkRuleConflictException;
|
||||||
|
|||||||
@ -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.api.command.admin.annotation;
|
||||||
|
|
||||||
|
import com.cloud.exception.ConcurrentOperationException;
|
||||||
|
import com.cloud.exception.InsufficientCapacityException;
|
||||||
|
import com.cloud.exception.NetworkRuleConflictException;
|
||||||
|
import com.cloud.exception.ResourceAllocationException;
|
||||||
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.annotation.AnnotationService;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.BaseCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.response.AnnotationResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
|
||||||
|
@APICommand(name = AddAnnotationCmd.APINAME, description = "add an annotation.", responseObject = AnnotationResponse.class,
|
||||||
|
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.11", authorized = {RoleType.Admin})
|
||||||
|
public class AddAnnotationCmd extends BaseCmd {
|
||||||
|
|
||||||
|
public static final String APINAME = "addAnnotation";
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ANNOTATION, type = CommandType.STRING, description = "the annotation text")
|
||||||
|
private String annotation;
|
||||||
|
@Parameter(name = ApiConstants.ENTITY_TYPE, type = CommandType.STRING, description = "the entity type (only HOST is allowed atm)")
|
||||||
|
private String entityType;
|
||||||
|
@Parameter(name = ApiConstants.ENTITY_ID, type = CommandType.STRING, description = "the id of the entity to annotate")
|
||||||
|
private String entityUuid;
|
||||||
|
|
||||||
|
public String getAnnotation() {
|
||||||
|
return annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnnotationService.EntityType getEntityType() {
|
||||||
|
return AnnotationService.EntityType.valueOf(entityType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEntityUuid() {
|
||||||
|
return entityUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute()
|
||||||
|
throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException,
|
||||||
|
NetworkRuleConflictException {
|
||||||
|
Preconditions.checkNotNull(entityUuid,"I have to have an entity to set an annotation on!");
|
||||||
|
Preconditions.checkState(AnnotationService.EntityType.contains(entityType),(java.lang.String)"'%s' is ot a valid EntityType to put annotations on", entityType);
|
||||||
|
AnnotationResponse annotationResponse = annotationService.addAnnotation(this);
|
||||||
|
annotationResponse.setResponseName(getCommandName());
|
||||||
|
this.setResponseObject(annotationResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
return CallContext.current().getCallingAccount().getAccountId();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
// 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.annotation;
|
||||||
|
|
||||||
|
import com.cloud.exception.ConcurrentOperationException;
|
||||||
|
import com.cloud.exception.InsufficientCapacityException;
|
||||||
|
import com.cloud.exception.NetworkRuleConflictException;
|
||||||
|
import com.cloud.exception.ResourceAllocationException;
|
||||||
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
|
import com.cloud.utils.StringUtils;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.BaseCmd;
|
||||||
|
import org.apache.cloudstack.api.BaseListCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.response.AnnotationResponse;
|
||||||
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
|
|
||||||
|
@APICommand(name = ListAnnotationsCmd.APINAME, description = "Lists annotations.", responseObject = AnnotationResponse.class,
|
||||||
|
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.11", authorized = {RoleType.Admin})
|
||||||
|
public class ListAnnotationsCmd extends BaseListCmd {
|
||||||
|
|
||||||
|
public static final String APINAME = "listAnnotations";
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ID, type = CommandType.STRING, description = "the id of the annotation")
|
||||||
|
private String uuid;
|
||||||
|
@Parameter(name = ApiConstants.ENTITY_TYPE, type = CommandType.STRING, description = "the entity type")
|
||||||
|
private String entityType;
|
||||||
|
@Parameter(name = ApiConstants.ENTITY_ID, type = CommandType.STRING, description = "the id of the entity for which to show annotations")
|
||||||
|
private String entityUuid;
|
||||||
|
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEntityType() {
|
||||||
|
return entityType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEntityUuid() {
|
||||||
|
return entityUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void execute()
|
||||||
|
throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException,
|
||||||
|
NetworkRuleConflictException {
|
||||||
|
// preconditions to check:
|
||||||
|
// if entity type is null entity uuid can not have a value
|
||||||
|
Preconditions.checkArgument(StringUtils.isNotBlank(entityType) ? ! StringUtils.isNotBlank(uuid) : true,
|
||||||
|
"I can search for an anotation on an entity or for a specific annotation, not both");
|
||||||
|
// if uuid has a value entity type and entity uuid can not have a value
|
||||||
|
Preconditions.checkArgument(StringUtils.isNotBlank(uuid) ? entityType == null && entityUuid == null : true,
|
||||||
|
"I will either search for a specific annotation or for annotations on an entity, not both");
|
||||||
|
|
||||||
|
ListResponse<AnnotationResponse> response = annotationService.searchForAnnotations(this);
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
this.setResponseObject(response);
|
||||||
|
response.setObjectName("annotations");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public String getCommandName() {
|
||||||
|
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
// 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.annotation;
|
||||||
|
|
||||||
|
import com.cloud.exception.ConcurrentOperationException;
|
||||||
|
import com.cloud.exception.InsufficientCapacityException;
|
||||||
|
import com.cloud.exception.NetworkRuleConflictException;
|
||||||
|
import com.cloud.exception.ResourceAllocationException;
|
||||||
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.BaseCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.response.AnnotationResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
|
||||||
|
@APICommand(name = RemoveAnnotationCmd.APINAME, description = "remove an annotation.", responseObject = AnnotationResponse.class,
|
||||||
|
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.11", authorized = {RoleType.Admin})
|
||||||
|
public class RemoveAnnotationCmd extends BaseCmd {
|
||||||
|
|
||||||
|
public static final String APINAME = "removeAnnotation";
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ID, type = CommandType.STRING, required = true, description = "the id of the annotation")
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute()
|
||||||
|
throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException,
|
||||||
|
NetworkRuleConflictException {
|
||||||
|
AnnotationResponse annotationResponse = annotationService.removeAnnotation(this);
|
||||||
|
annotationResponse.setResponseName(getCommandName());
|
||||||
|
this.setResponseObject(annotationResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
return CallContext.current().getCallingAccount().getAccountId();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,10 +16,10 @@
|
|||||||
// under the License.
|
// under the License.
|
||||||
package org.apache.cloudstack.api.command.admin.host;
|
package org.apache.cloudstack.api.command.admin.host;
|
||||||
|
|
||||||
import java.util.List;
|
import com.cloud.host.Host;
|
||||||
|
import com.cloud.user.Account;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.annotation.AnnotationService;
|
||||||
import org.apache.cloudstack.api.APICommand;
|
import org.apache.cloudstack.api.APICommand;
|
||||||
import org.apache.cloudstack.api.ApiConstants;
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
import org.apache.cloudstack.api.ApiErrorCode;
|
import org.apache.cloudstack.api.ApiErrorCode;
|
||||||
@ -28,9 +28,9 @@ import org.apache.cloudstack.api.Parameter;
|
|||||||
import org.apache.cloudstack.api.ServerApiException;
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
import org.apache.cloudstack.api.response.GuestOSCategoryResponse;
|
import org.apache.cloudstack.api.response.GuestOSCategoryResponse;
|
||||||
import org.apache.cloudstack.api.response.HostResponse;
|
import org.apache.cloudstack.api.response.HostResponse;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import com.cloud.host.Host;
|
import java.util.List;
|
||||||
import com.cloud.user.Account;
|
|
||||||
|
|
||||||
@APICommand(name = "updateHost", description = "Updates a host.", responseObject = HostResponse.class,
|
@APICommand(name = "updateHost", description = "Updates a host.", responseObject = HostResponse.class,
|
||||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||||
@ -62,6 +62,9 @@ public class UpdateHostCmd extends BaseCmd {
|
|||||||
@Parameter(name = ApiConstants.URL, type = CommandType.STRING, description = "the new uri for the secondary storage: nfs://host/path")
|
@Parameter(name = ApiConstants.URL, type = CommandType.STRING, description = "the new uri for the secondary storage: nfs://host/path")
|
||||||
private String url;
|
private String url;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ANNOTATION, type = CommandType.STRING, description = "Add an annotation to this host", since = "4.11", authorized = {RoleType.Admin})
|
||||||
|
private String annotation;
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
/////////////////// Accessors ///////////////////////
|
/////////////////// Accessors ///////////////////////
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
@ -86,6 +89,10 @@ public class UpdateHostCmd extends BaseCmd {
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getAnnotation() {
|
||||||
|
return annotation;
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
/////////////// API Implementation///////////////////
|
/////////////// API Implementation///////////////////
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
@ -109,6 +116,9 @@ public class UpdateHostCmd extends BaseCmd {
|
|||||||
Host result;
|
Host result;
|
||||||
try {
|
try {
|
||||||
result = _resourceService.updateHost(this);
|
result = _resourceService.updateHost(this);
|
||||||
|
if(getAnnotation() != null) {
|
||||||
|
annotationService.addAnnotation(getAnnotation(), AnnotationService.EntityType.HOST, result.getUuid());
|
||||||
|
}
|
||||||
HostResponse hostResponse = _responseGenerator.createHostResponse(result);
|
HostResponse hostResponse = _responseGenerator.createHostResponse(result);
|
||||||
hostResponse.setResponseName(getCommandName());
|
hostResponse.setResponseName(getCommandName());
|
||||||
this.setResponseObject(hostResponse);
|
this.setResponseObject(hostResponse);
|
||||||
|
|||||||
@ -0,0 +1,121 @@
|
|||||||
|
// 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.response;
|
||||||
|
|
||||||
|
import com.cloud.serializer.Param;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import org.apache.cloudstack.annotation.Annotation;
|
||||||
|
import org.apache.cloudstack.annotation.AnnotationService;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.BaseResponse;
|
||||||
|
import org.apache.cloudstack.api.EntityReference;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.11
|
||||||
|
*/
|
||||||
|
@EntityReference(value = Annotation.class)
|
||||||
|
public class AnnotationResponse extends BaseResponse {
|
||||||
|
@SerializedName(ApiConstants.ID)
|
||||||
|
@Param(description = "the (uu)id of the annotation")
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.ENTITY_TYPE)
|
||||||
|
@Param(description = "the type of the annotated entity")
|
||||||
|
private String entityType;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.ENTITY_ID)
|
||||||
|
@Param(description = "the (uu)id of the entitiy to which this annotation pertains")
|
||||||
|
private String entityUuid;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.ANNOTATION)
|
||||||
|
@Param(description = "the contents of the annotation")
|
||||||
|
private String annotation;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.USER_ID)
|
||||||
|
@Param(description = "The (uu)id of the user that entered the annotation")
|
||||||
|
private String userUuid;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.CREATED)
|
||||||
|
@Param(description = "the creation timestamp for this annotation")
|
||||||
|
private Date created;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.REMOVED)
|
||||||
|
@Param(description = "the removal timestamp for this annotation")
|
||||||
|
private Date removed;
|
||||||
|
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUuid(String uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEntityType() {
|
||||||
|
return entityType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEntityType(String entityType) {
|
||||||
|
this.entityType = entityType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEntityType(AnnotationService.EntityType entityType) {
|
||||||
|
this.entityType = entityType.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEntityUuid() {
|
||||||
|
return entityUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEntityUuid(String entityUuid) {
|
||||||
|
this.entityUuid = entityUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAnnotation() {
|
||||||
|
return annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAnnotation(String annotation) {
|
||||||
|
this.annotation = annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserUuid() {
|
||||||
|
return userUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserUuid(String userUuid) {
|
||||||
|
this.userUuid = userUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getCreated() {
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreated(Date created) {
|
||||||
|
this.created = created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getRemoved() {
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRemoved(Date removed) {
|
||||||
|
this.removed = removed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -231,6 +231,17 @@ public class HostResponse extends BaseResponse {
|
|||||||
@Param(description = "Host details in key/value pairs.", since = "4.5")
|
@Param(description = "Host details in key/value pairs.", since = "4.5")
|
||||||
private Map details;
|
private Map details;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.ANNOTATION)
|
||||||
|
@Param(description = "the last annotation set on this host by an admin", since = "4.11")
|
||||||
|
private String annotation;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.LAST_ANNOTATED)
|
||||||
|
@Param(description = "the last time this host was annotated", since = "4.11")
|
||||||
|
private Date lastAnnotated;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.USERNAME)
|
||||||
|
@Param(description = "the admin that annotated this host", since = "4.11")
|
||||||
|
private String username;
|
||||||
|
|
||||||
// Default visibility to support accessing the details from unit tests
|
// Default visibility to support accessing the details from unit tests
|
||||||
Map getDetails() {
|
Map getDetails() {
|
||||||
@ -458,6 +469,18 @@ public class HostResponse extends BaseResponse {
|
|||||||
this.haHost = haHost;
|
this.haHost = haHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAnnotation(String annotation) {
|
||||||
|
this.annotation = annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastAnnotated(Date lastAnnotated) {
|
||||||
|
this.lastAnnotated = lastAnnotated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
public void setDetails(Map details) {
|
public void setDetails(Map details) {
|
||||||
|
|
||||||
if (details == null) {
|
if (details == null) {
|
||||||
|
|||||||
@ -353,4 +353,5 @@
|
|||||||
<bean id="LBHealthCheckPolicyDetailsDaoImpl" class="org.apache.cloudstack.resourcedetail.dao.LBHealthCheckPolicyDetailsDaoImpl" />
|
<bean id="LBHealthCheckPolicyDetailsDaoImpl" class="org.apache.cloudstack.resourcedetail.dao.LBHealthCheckPolicyDetailsDaoImpl" />
|
||||||
<bean id="outOfBandManagementDaoImpl" class="org.apache.cloudstack.outofbandmanagement.dao.OutOfBandManagementDaoImpl" />
|
<bean id="outOfBandManagementDaoImpl" class="org.apache.cloudstack.outofbandmanagement.dao.OutOfBandManagementDaoImpl" />
|
||||||
<bean id="GuestOsDetailsDaoImpl" class="org.apache.cloudstack.resourcedetail.dao.GuestOsDetailsDaoImpl" />
|
<bean id="GuestOsDetailsDaoImpl" class="org.apache.cloudstack.resourcedetail.dao.GuestOsDetailsDaoImpl" />
|
||||||
|
<bean id="annotationDaoImpl" class="org.apache.cloudstack.annotation.dao.AnnotationDaoImpl" />
|
||||||
</beans>
|
</beans>
|
||||||
|
|||||||
@ -0,0 +1,154 @@
|
|||||||
|
// 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.annotation;
|
||||||
|
|
||||||
|
import com.cloud.utils.db.GenericDao;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.11
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "annotations")
|
||||||
|
public class AnnotationVO implements Annotation {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "id")
|
||||||
|
private long id;
|
||||||
|
|
||||||
|
@Column(name = "uuid")
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
@Column(name = "annotation")
|
||||||
|
private String annotation;
|
||||||
|
|
||||||
|
@Column(name = "entity_uuid")
|
||||||
|
private String entityUuid;
|
||||||
|
|
||||||
|
@Column(name = "entity_type")
|
||||||
|
private AnnotationService.EntityType entityType;
|
||||||
|
|
||||||
|
@Column(name = "user_uuid")
|
||||||
|
private String userUuid;
|
||||||
|
|
||||||
|
@Column(name = GenericDao.CREATED_COLUMN)
|
||||||
|
private Date created;
|
||||||
|
|
||||||
|
@Column(name = GenericDao.REMOVED_COLUMN)
|
||||||
|
private Date removed;
|
||||||
|
|
||||||
|
// construct
|
||||||
|
public AnnotationVO() {
|
||||||
|
this.uuid = UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnnotationVO(String text, AnnotationService.EntityType type, String uuid) {
|
||||||
|
this();
|
||||||
|
setAnnotation(text);
|
||||||
|
setEntityType(type);
|
||||||
|
setEntityUuid(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnnotationVO(String text, String type, String uuid) {
|
||||||
|
this();
|
||||||
|
setAnnotation(text);
|
||||||
|
setEntityType(type);
|
||||||
|
setEntityUuid(uuid);
|
||||||
|
}
|
||||||
|
// access
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAnnotation() {
|
||||||
|
return annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEntityUuid() {
|
||||||
|
return entityUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnnotationService.EntityType getEntityType() {
|
||||||
|
return entityType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUserUuid() {
|
||||||
|
return userUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getCreated() {
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getRemoved() {
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUuid(String uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAnnotation(String annotation) {
|
||||||
|
this.annotation = annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEntityUuid(String entityUuid) {
|
||||||
|
this.entityUuid = entityUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEntityType(String entityType) {
|
||||||
|
this.entityType = AnnotationService.EntityType.valueOf(entityType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEntityType(AnnotationService.EntityType entityType) {
|
||||||
|
this.entityType = entityType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserUuid(String userUuid) {
|
||||||
|
this.userUuid = userUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreated(Date created) {
|
||||||
|
this.created = created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRemoved(Date removed) {
|
||||||
|
this.removed = removed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
// 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.annotation.dao;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.annotation.AnnotationVO;
|
||||||
|
import com.cloud.utils.db.GenericDao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.11
|
||||||
|
*/
|
||||||
|
public interface AnnotationDao extends GenericDao<AnnotationVO, Long> {
|
||||||
|
public List<AnnotationVO> findByEntityType(String entityType);
|
||||||
|
public List<AnnotationVO> findByEntity(String entityType, String entityUuid);
|
||||||
|
}
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
// 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.annotation.dao;
|
||||||
|
|
||||||
|
import com.cloud.utils.db.GenericDaoBase;
|
||||||
|
import com.cloud.utils.db.SearchBuilder;
|
||||||
|
import com.cloud.utils.db.SearchCriteria;
|
||||||
|
import org.apache.cloudstack.annotation.AnnotationVO;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.1
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class AnnotationDaoImpl extends GenericDaoBase<AnnotationVO, Long> implements AnnotationDao {
|
||||||
|
private final SearchBuilder<AnnotationVO> AnnotationSearchByType;
|
||||||
|
private final SearchBuilder<AnnotationVO> AnnotationSearchByTypeAndUuid;
|
||||||
|
|
||||||
|
public AnnotationDaoImpl() {
|
||||||
|
super();
|
||||||
|
AnnotationSearchByType = createSearchBuilder();
|
||||||
|
AnnotationSearchByType.and("entityType", AnnotationSearchByType.entity().getEntityType(), SearchCriteria.Op.EQ);
|
||||||
|
AnnotationSearchByType.done();
|
||||||
|
AnnotationSearchByTypeAndUuid = createSearchBuilder();
|
||||||
|
AnnotationSearchByTypeAndUuid.and("entityType", AnnotationSearchByTypeAndUuid.entity().getEntityType(), SearchCriteria.Op.EQ);
|
||||||
|
AnnotationSearchByTypeAndUuid.and("entityUuid", AnnotationSearchByTypeAndUuid.entity().getEntityUuid(), SearchCriteria.Op.EQ);
|
||||||
|
AnnotationSearchByTypeAndUuid.done();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public List<AnnotationVO> findByEntityType(String entityType) {
|
||||||
|
SearchCriteria<AnnotationVO> sc = createSearchCriteria();
|
||||||
|
sc.addAnd("entityType", SearchCriteria.Op.EQ, entityType);
|
||||||
|
return listBy(sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public List<AnnotationVO> findByEntity(String entityType, String entityUuid) {
|
||||||
|
SearchCriteria<AnnotationVO> sc = createSearchCriteria();
|
||||||
|
sc.addAnd("entityType", SearchCriteria.Op.EQ, entityType);
|
||||||
|
sc.addAnd("entityUuid", SearchCriteria.Op.EQ, entityUuid);
|
||||||
|
return listBy(sc, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -283,6 +283,7 @@
|
|||||||
<property name="gslbServiceProviders" value="#{gslbServiceProvidersRegistry.registered}" />
|
<property name="gslbServiceProviders" value="#{gslbServiceProvidersRegistry.registered}" />
|
||||||
</bean>
|
</bean>
|
||||||
<bean id="certServiceImpl" class="org.apache.cloudstack.network.ssl.CertServiceImpl" />
|
<bean id="certServiceImpl" class="org.apache.cloudstack.network.ssl.CertServiceImpl" />
|
||||||
|
|
||||||
<bean id="imageStoreUploadMonitorImpl" class="com.cloud.storage.ImageStoreUploadMonitorImpl" />
|
<bean id="imageStoreUploadMonitorImpl" class="com.cloud.storage.ImageStoreUploadMonitorImpl" />
|
||||||
|
|
||||||
<!-- the new CA manager -->
|
<!-- the new CA manager -->
|
||||||
@ -290,4 +291,6 @@
|
|||||||
<property name="caProviders" value="#{caProvidersRegistry.registered}" />
|
<property name="caProviders" value="#{caProvidersRegistry.registered}" />
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="annotationService" class="org.apache.cloudstack.annotation.AnnotationManagerImpl" />
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
|||||||
@ -35,8 +35,6 @@ import org.apache.cloudstack.api.response.HostForMigrationResponse;
|
|||||||
import org.apache.cloudstack.api.response.HostResponse;
|
import org.apache.cloudstack.api.response.HostResponse;
|
||||||
import org.apache.cloudstack.api.response.VgpuResponse;
|
import org.apache.cloudstack.api.response.VgpuResponse;
|
||||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||||
import org.apache.cloudstack.ha.HAResource;
|
|
||||||
import org.apache.cloudstack.ha.dao.HAConfigDao;
|
|
||||||
import org.apache.cloudstack.outofbandmanagement.dao.OutOfBandManagementDao;
|
import org.apache.cloudstack.outofbandmanagement.dao.OutOfBandManagementDao;
|
||||||
|
|
||||||
import com.cloud.api.ApiDBUtils;
|
import com.cloud.api.ApiDBUtils;
|
||||||
@ -52,6 +50,9 @@ import com.cloud.utils.db.GenericDaoBase;
|
|||||||
import com.cloud.utils.db.SearchBuilder;
|
import com.cloud.utils.db.SearchBuilder;
|
||||||
import com.cloud.utils.db.SearchCriteria;
|
import com.cloud.utils.db.SearchCriteria;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.ha.HAResource;
|
||||||
|
import org.apache.cloudstack.ha.dao.HAConfigDao;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class HostJoinDaoImpl extends GenericDaoBase<HostJoinVO, Long> implements HostJoinDao {
|
public class HostJoinDaoImpl extends GenericDaoBase<HostJoinVO, Long> implements HostJoinDao {
|
||||||
public static final Logger s_logger = Logger.getLogger(HostJoinDaoImpl.class);
|
public static final Logger s_logger = Logger.getLogger(HostJoinDaoImpl.class);
|
||||||
@ -244,6 +245,9 @@ public class HostJoinDaoImpl extends GenericDaoBase<HostJoinVO, Long> implements
|
|||||||
hostResponse.setJobId(host.getJobUuid());
|
hostResponse.setJobId(host.getJobUuid());
|
||||||
hostResponse.setJobStatus(host.getJobStatus());
|
hostResponse.setJobStatus(host.getJobStatus());
|
||||||
}
|
}
|
||||||
|
hostResponse.setAnnotation(host.getAnnotation());
|
||||||
|
hostResponse.setLastAnnotated(host.getLastAnnotated ());
|
||||||
|
hostResponse.setUsername(host.getUsername());
|
||||||
|
|
||||||
hostResponse.setObjectName("host");
|
hostResponse.setObjectName("host");
|
||||||
|
|
||||||
|
|||||||
@ -27,15 +27,15 @@ import javax.persistence.Table;
|
|||||||
import javax.persistence.Temporal;
|
import javax.persistence.Temporal;
|
||||||
import javax.persistence.TemporalType;
|
import javax.persistence.TemporalType;
|
||||||
|
|
||||||
import org.apache.cloudstack.api.Identity;
|
|
||||||
import org.apache.cloudstack.api.InternalIdentity;
|
|
||||||
|
|
||||||
import com.cloud.host.Host.Type;
|
import com.cloud.host.Host.Type;
|
||||||
import com.cloud.host.Status;
|
import com.cloud.host.Status;
|
||||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||||
import com.cloud.org.Cluster;
|
import com.cloud.org.Cluster;
|
||||||
import com.cloud.resource.ResourceState;
|
import com.cloud.resource.ResourceState;
|
||||||
|
import com.cloud.utils.StringUtils;
|
||||||
import com.cloud.utils.db.GenericDao;
|
import com.cloud.utils.db.GenericDao;
|
||||||
|
import org.apache.cloudstack.api.Identity;
|
||||||
|
import org.apache.cloudstack.api.InternalIdentity;
|
||||||
import org.apache.cloudstack.ha.HAConfig;
|
import org.apache.cloudstack.ha.HAConfig;
|
||||||
import org.apache.cloudstack.outofbandmanagement.OutOfBandManagement;
|
import org.apache.cloudstack.outofbandmanagement.OutOfBandManagement;
|
||||||
|
|
||||||
@ -192,6 +192,15 @@ public class HostJoinVO extends BaseViewVO implements InternalIdentity, Identity
|
|||||||
@Column(name = "job_status")
|
@Column(name = "job_status")
|
||||||
private int jobStatus;
|
private int jobStatus;
|
||||||
|
|
||||||
|
@Column(name = "annotation")
|
||||||
|
private String annotation;
|
||||||
|
|
||||||
|
@Column(name = "last_annotated")
|
||||||
|
private Date lastAnnotated;
|
||||||
|
|
||||||
|
@Column(name = "username")
|
||||||
|
private String username;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getId() {
|
public long getId() {
|
||||||
return this.id;
|
return this.id;
|
||||||
@ -377,4 +386,20 @@ public class HostJoinVO extends BaseViewVO implements InternalIdentity, Identity
|
|||||||
public String getTag() {
|
public String getTag() {
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getAnnotation() {
|
||||||
|
return annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLastAnnotated() {
|
||||||
|
return lastAnnotated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAnnotated() {
|
||||||
|
return StringUtils.isNotBlank(annotation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,149 @@
|
|||||||
|
// 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.annotation;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import com.cloud.event.ActionEvent;
|
||||||
|
import com.cloud.event.EventTypes;
|
||||||
|
import com.cloud.utils.component.ManagerBase;
|
||||||
|
import com.cloud.utils.component.PluggableService;
|
||||||
|
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
||||||
|
import org.apache.cloudstack.api.command.admin.annotation.AddAnnotationCmd;
|
||||||
|
import org.apache.cloudstack.api.command.admin.annotation.ListAnnotationsCmd;
|
||||||
|
import org.apache.cloudstack.api.command.admin.annotation.RemoveAnnotationCmd;
|
||||||
|
import org.apache.cloudstack.api.response.AnnotationResponse;
|
||||||
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.11
|
||||||
|
*/
|
||||||
|
public final class AnnotationManagerImpl extends ManagerBase implements AnnotationService, PluggableService {
|
||||||
|
public static final Logger LOGGER = Logger.getLogger(AnnotationManagerImpl.class);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private AnnotationDao annotationDao;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListResponse<AnnotationResponse> searchForAnnotations(ListAnnotationsCmd cmd) {
|
||||||
|
List<AnnotationVO> annotations = getAnnotationsForApiCmd(cmd);
|
||||||
|
List<AnnotationResponse> annotationResponses = convertAnnotationsToResponses(annotations);
|
||||||
|
return createAnnotationsResponseList(annotationResponses);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ActionEvent(eventType = EventTypes.EVENT_ANNOTATION_CREATE, eventDescription = "creating an annotation on an entity")
|
||||||
|
public AnnotationResponse addAnnotation(AddAnnotationCmd addAnnotationCmd) {
|
||||||
|
return addAnnotation(addAnnotationCmd.getAnnotation(), addAnnotationCmd.getEntityType(), addAnnotationCmd.getEntityUuid());
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnnotationResponse addAnnotation(String text, EntityType type, String uuid) {
|
||||||
|
CallContext ctx = CallContext.current();
|
||||||
|
String userUuid = ctx.getCallingUserUuid();
|
||||||
|
|
||||||
|
AnnotationVO annotation = new AnnotationVO(text, type, uuid);
|
||||||
|
annotation.setUserUuid(userUuid);
|
||||||
|
annotation = annotationDao.persist(annotation);
|
||||||
|
return createAnnotationResponse(annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ActionEvent(eventType = EventTypes.EVENT_ANNOTATION_REMOVE, eventDescription = "removing an annotation on an entity")
|
||||||
|
public AnnotationResponse removeAnnotation(RemoveAnnotationCmd removeAnnotationCmd) {
|
||||||
|
String uuid = removeAnnotationCmd.getUuid();
|
||||||
|
if(LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug("marking annotation removed: " + uuid);
|
||||||
|
}
|
||||||
|
AnnotationVO annotation = annotationDao.findByUuid(uuid);
|
||||||
|
annotationDao.remove(annotation.getId());
|
||||||
|
return createAnnotationResponse(annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<AnnotationVO> getAnnotationsForApiCmd(ListAnnotationsCmd cmd) {
|
||||||
|
List<AnnotationVO> annotations;
|
||||||
|
if(cmd.getUuid() != null) {
|
||||||
|
annotations = new ArrayList<>();
|
||||||
|
String uuid = cmd.getUuid().toString();
|
||||||
|
if(LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug("getting single annotation by uuid: " + uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
annotations.add(annotationDao.findByUuid(uuid));
|
||||||
|
} else if( ! (cmd.getEntityType() == null || cmd.getEntityType().isEmpty()) ) {
|
||||||
|
String type = cmd.getEntityType();
|
||||||
|
if(LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug("getting annotations for type: " + type);
|
||||||
|
}
|
||||||
|
if (cmd.getEntityUuid() != null) {
|
||||||
|
String uuid = cmd.getEntityUuid().toString();
|
||||||
|
if(LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug("getting annotations for entity: " + uuid);
|
||||||
|
}
|
||||||
|
annotations = annotationDao.findByEntity(type,cmd.getEntityUuid().toString());
|
||||||
|
} else {
|
||||||
|
annotations = annotationDao.findByEntityType(type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug("getting all annotations");
|
||||||
|
}
|
||||||
|
annotations = annotationDao.listAll();
|
||||||
|
}
|
||||||
|
return annotations;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<AnnotationResponse> convertAnnotationsToResponses(List<AnnotationVO> annotations) {
|
||||||
|
List<AnnotationResponse> annotationResponses = new ArrayList<>();
|
||||||
|
for (AnnotationVO annotation : annotations) {
|
||||||
|
annotationResponses.add(createAnnotationResponse(annotation));
|
||||||
|
}
|
||||||
|
return annotationResponses;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ListResponse<AnnotationResponse> createAnnotationsResponseList(List<AnnotationResponse> annotationResponses) {
|
||||||
|
ListResponse<AnnotationResponse> listResponse = new ListResponse<>();
|
||||||
|
listResponse.setResponses(annotationResponses);
|
||||||
|
return listResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AnnotationResponse createAnnotationResponse(AnnotationVO annotation) {
|
||||||
|
AnnotationResponse response = new AnnotationResponse();
|
||||||
|
response.setUuid(annotation.getUuid());
|
||||||
|
response.setEntityType(annotation.getEntityType());
|
||||||
|
response.setEntityUuid(annotation.getEntityUuid());
|
||||||
|
response.setAnnotation(annotation.getAnnotation());
|
||||||
|
response.setUserUuid(annotation.getUserUuid());
|
||||||
|
response.setCreated(annotation.getCreated());
|
||||||
|
response.setRemoved(annotation.getRemoved());
|
||||||
|
response.setObjectName("annotation");
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public List<Class<?>> getCommands() {
|
||||||
|
final List<Class<?>> cmdList = new ArrayList<>();
|
||||||
|
cmdList.add(AddAnnotationCmd.class);
|
||||||
|
cmdList.add(ListAnnotationsCmd.class);
|
||||||
|
cmdList.add(RemoveAnnotationCmd.class);
|
||||||
|
return cmdList;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -159,10 +159,46 @@ CREATE TABLE IF NOT EXISTS `cloud`.`ha_config` (
|
|||||||
|
|
||||||
DELETE from `cloud`.`configuration` where name='outofbandmanagement.sync.interval';
|
DELETE from `cloud`.`configuration` where name='outofbandmanagement.sync.interval';
|
||||||
|
|
||||||
|
-- Annotations specifc changes following
|
||||||
|
CREATE TABLE IF NOT EXISTS `cloud`.`annotations` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`uuid` varchar(40) UNIQUE,
|
||||||
|
`annotation` text,
|
||||||
|
`entity_uuid` varchar(40),
|
||||||
|
`entity_type` varchar(32),
|
||||||
|
`user_uuid` varchar(40),
|
||||||
|
`created` datetime COMMENT 'date of creation',
|
||||||
|
`removed` datetime COMMENT 'date of removal',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY (`uuid`),
|
||||||
|
KEY `i_entity` (`entity_uuid`, `entity_type`, `created`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
DROP VIEW IF EXISTS `cloud`.`last_annotation_view`;
|
||||||
|
CREATE VIEW `last_annotation_view` AS
|
||||||
|
SELECT
|
||||||
|
`annotations`.`uuid` AS `uuid`,
|
||||||
|
`annotations`.`annotation` AS `annotation`,
|
||||||
|
`annotations`.`entity_uuid` AS `entity_uuid`,
|
||||||
|
`annotations`.`entity_type` AS `entity_type`,
|
||||||
|
`annotations`.`user_uuid` AS `user_uuid`,
|
||||||
|
`annotations`.`created` AS `created`,
|
||||||
|
`annotations`.`removed` AS `removed`
|
||||||
|
FROM
|
||||||
|
`annotations`
|
||||||
|
WHERE
|
||||||
|
`annotations`.`created` IN (SELECT
|
||||||
|
MAX(`annotations`.`created`)
|
||||||
|
FROM
|
||||||
|
`annotations`
|
||||||
|
WHERE
|
||||||
|
`annotations`.`removed` IS NULL
|
||||||
|
GROUP BY `annotations`.`entity_uuid`);
|
||||||
|
|
||||||
-- Host HA changes:
|
-- Host HA changes:
|
||||||
DROP VIEW IF EXISTS `cloud`.`host_view`;
|
DROP VIEW IF EXISTS `cloud`.`host_view`;
|
||||||
CREATE VIEW `cloud`.`host_view` AS
|
CREATE VIEW `cloud`.`host_view` AS
|
||||||
select
|
SELECT
|
||||||
host.id,
|
host.id,
|
||||||
host.uuid,
|
host.uuid,
|
||||||
host.name,
|
host.name,
|
||||||
@ -210,37 +246,46 @@ CREATE VIEW `cloud`.`host_view` AS
|
|||||||
oobm.power_state AS `oobm_power_state`,
|
oobm.power_state AS `oobm_power_state`,
|
||||||
ha_config.enabled AS `ha_enabled`,
|
ha_config.enabled AS `ha_enabled`,
|
||||||
ha_config.ha_state AS `ha_state`,
|
ha_config.ha_state AS `ha_state`,
|
||||||
ha_config.provider AS `ha_provider`
|
ha_config.provider AS `ha_provider`,
|
||||||
from
|
`last_annotation_view`.`annotation` AS `annotation`,
|
||||||
|
`last_annotation_view`.`created` AS `last_annotated`,
|
||||||
|
`user`.`username` AS `username`
|
||||||
|
FROM
|
||||||
`cloud`.`host`
|
`cloud`.`host`
|
||||||
left join
|
LEFT JOIN
|
||||||
`cloud`.`cluster` ON host.cluster_id = cluster.id
|
`cloud`.`cluster` ON host.cluster_id = cluster.id
|
||||||
left join
|
LEFT JOIN
|
||||||
`cloud`.`data_center` ON host.data_center_id = data_center.id
|
`cloud`.`data_center` ON host.data_center_id = data_center.id
|
||||||
left join
|
LEFT JOIN
|
||||||
`cloud`.`host_pod_ref` ON host.pod_id = host_pod_ref.id
|
`cloud`.`host_pod_ref` ON host.pod_id = host_pod_ref.id
|
||||||
left join
|
LEFT JOIN
|
||||||
`cloud`.`host_details` ON host.id = host_details.host_id
|
`cloud`.`host_details` ON host.id = host_details.host_id
|
||||||
and host_details.name = 'guest.os.category.id'
|
AND host_details.name = 'guest.os.category.id'
|
||||||
left join
|
LEFT JOIN
|
||||||
`cloud`.`guest_os_category` ON guest_os_category.id = CONVERT ( host_details.value, UNSIGNED )
|
`cloud`.`guest_os_category` ON guest_os_category.id = CONVERT ( host_details.value, UNSIGNED )
|
||||||
left join
|
LEFT JOIN
|
||||||
`cloud`.`host_tags` ON host_tags.host_id = host.id
|
`cloud`.`host_tags` ON host_tags.host_id = host.id
|
||||||
left join
|
LEFT JOIN
|
||||||
`cloud`.`op_host_capacity` mem_caps ON host.id = mem_caps.host_id
|
`cloud`.`op_host_capacity` mem_caps ON host.id = mem_caps.host_id
|
||||||
and mem_caps.capacity_type = 0
|
AND mem_caps.capacity_type = 0
|
||||||
left join
|
LEFT JOIN
|
||||||
`cloud`.`op_host_capacity` cpu_caps ON host.id = cpu_caps.host_id
|
`cloud`.`op_host_capacity` cpu_caps ON host.id = cpu_caps.host_id
|
||||||
and cpu_caps.capacity_type = 1
|
AND cpu_caps.capacity_type = 1
|
||||||
left join
|
LEFT JOIN
|
||||||
`cloud`.`async_job` ON async_job.instance_id = host.id
|
`cloud`.`async_job` ON async_job.instance_id = host.id
|
||||||
and async_job.instance_type = 'Host'
|
AND async_job.instance_type = 'Host'
|
||||||
and async_job.job_status = 0
|
AND async_job.job_status = 0
|
||||||
left join
|
LEFT JOIN
|
||||||
`cloud`.`oobm` ON oobm.host_id = host.id
|
`cloud`.`oobm` ON oobm.host_id = host.id
|
||||||
left join
|
left join
|
||||||
`cloud`.`ha_config` ON ha_config.resource_id=host.id
|
`cloud`.`ha_config` ON ha_config.resource_id=host.id
|
||||||
and ha_config.resource_type='Host';
|
and ha_config.resource_type='Host'
|
||||||
|
LEFT JOIN
|
||||||
|
`cloud`.`last_annotation_view` ON `last_annotation_view`.`entity_uuid` = `host`.`uuid`
|
||||||
|
LEFT JOIN
|
||||||
|
`cloud`.`user` ON `user`.`uuid` = `last_annotation_view`.`user_uuid`;
|
||||||
|
-- End Of Annotations specific changes
|
||||||
|
|
||||||
|
|
||||||
-- Out-of-band management driver for nested-cloudstack
|
-- Out-of-band management driver for nested-cloudstack
|
||||||
ALTER TABLE `cloud`.`oobm` MODIFY COLUMN port VARCHAR(255);
|
ALTER TABLE `cloud`.`oobm` MODIFY COLUMN port VARCHAR(255);
|
||||||
|
|||||||
178
test/integration/smoke/test_host_annotations.py
Normal file
178
test/integration/smoke/test_host_annotations.py
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
# 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.
|
||||||
|
""" BVT tests for Hosts and Clusters
|
||||||
|
"""
|
||||||
|
#Import Local Modules
|
||||||
|
import marvin
|
||||||
|
from marvin.cloudstackTestCase import *
|
||||||
|
from marvin.cloudstackAPI import *
|
||||||
|
from marvin.lib.utils import *
|
||||||
|
from marvin.lib.base import *
|
||||||
|
from marvin.lib.common import *
|
||||||
|
from marvin.lib.utils import (random_gen)
|
||||||
|
from nose.plugins.attrib import attr
|
||||||
|
|
||||||
|
#Import System modules
|
||||||
|
import time
|
||||||
|
|
||||||
|
_multiprocess_shared_ = True
|
||||||
|
|
||||||
|
class TestHostAnnotations(cloudstackTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.apiclient = self.testClient.getApiClient()
|
||||||
|
self.services = self.testClient.getParsedTestDataConfig()
|
||||||
|
self.zone = get_zone(self.apiclient, self.testClient.getZoneForTests())
|
||||||
|
self.host = list_hosts(self.apiclient,
|
||||||
|
zoneid=self.zone.id,
|
||||||
|
type='Routing')[0]
|
||||||
|
self.cleanup = []
|
||||||
|
self.added_annotations = []
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
try:
|
||||||
|
#Clean up
|
||||||
|
cleanup_resources(self.apiclient, self.cleanup)
|
||||||
|
self.cleanAnnotations()
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Warning: Exception during cleanup : %s" % e)
|
||||||
|
return
|
||||||
|
|
||||||
|
def cleanAnnotations(self):
|
||||||
|
"""Remove annotations"""
|
||||||
|
for annotation in self.added_annotations:
|
||||||
|
self.removeAnnotation(annotation.annotation.id)
|
||||||
|
|
||||||
|
def addAnnotation(self, annotation):
|
||||||
|
cmd = addAnnotation.addAnnotationCmd()
|
||||||
|
cmd.entityid = self.host.id
|
||||||
|
cmd.entitytype = "HOST"
|
||||||
|
cmd.annotation = annotation
|
||||||
|
|
||||||
|
self.added_annotations.append(self.apiclient.addAnnotation(cmd))
|
||||||
|
|
||||||
|
return self.added_annotations[-1]
|
||||||
|
|
||||||
|
def removeAnnotation(self, id):
|
||||||
|
cmd = removeAnnotation.removeAnnotationCmd()
|
||||||
|
cmd.id = id
|
||||||
|
|
||||||
|
return self.apiclient.removeAnnotation(cmd)
|
||||||
|
|
||||||
|
def getHostAnnotation(self, hostId):
|
||||||
|
host = list_hosts(self.apiclient,
|
||||||
|
zoneid=self.zone.id,
|
||||||
|
type='Routing')[0]
|
||||||
|
return host.annotation
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_01_add_annotation(self):
|
||||||
|
"""Testing the addAnnotations API ability to add an annoatation per host"""
|
||||||
|
self.addAnnotation("annotation1")
|
||||||
|
self.assertEqual(self.added_annotations[-1].annotation.annotation, "annotation1")
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_02_add_multiple_annotations(self):
|
||||||
|
"""Testing the addAnnotations API ability to add an annoatation per host
|
||||||
|
when there are annotations already.
|
||||||
|
And only the last one stands as annotation attribute on host level."""
|
||||||
|
self.addAnnotation("annotation1")
|
||||||
|
self.assertEqual(self.added_annotations[-1].annotation.annotation, "annotation1")
|
||||||
|
|
||||||
|
# Adds sleep of 1 second just to be sure next annotation will not be created in the same second.
|
||||||
|
time.sleep(1)
|
||||||
|
self.addAnnotation("annotation2")
|
||||||
|
self.assertEqual(self.added_annotations[-1].annotation.annotation, "annotation2")
|
||||||
|
|
||||||
|
# Adds sleep of 1 second just to be sure next annotation will not be created in the same second.
|
||||||
|
time.sleep(1)
|
||||||
|
self.addAnnotation("annotation3")
|
||||||
|
self.assertEqual(self.added_annotations[-1].annotation.annotation, "annotation3")
|
||||||
|
|
||||||
|
#Check that the last one is visible in host details
|
||||||
|
self.assertEqual(self.getHostAnnotation(self.host.id), "annotation3")
|
||||||
|
print
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_03_user_role_dont_see_annotations(self):
|
||||||
|
"""Testing the annotations api are restricted to users"""
|
||||||
|
|
||||||
|
self.addAnnotation("annotation1")
|
||||||
|
self.assertEqual(self.added_annotations[-1].annotation.annotation, "annotation1")
|
||||||
|
|
||||||
|
self.account = Account.create(
|
||||||
|
self.apiclient,
|
||||||
|
self.services["account"],
|
||||||
|
)
|
||||||
|
self.cleanup.append(self.account)
|
||||||
|
|
||||||
|
userApiClient = self.testClient.getUserApiClient(self.account.name, 'ROOT', 'User')
|
||||||
|
|
||||||
|
cmd = addAnnotation.addAnnotationCmd()
|
||||||
|
cmd.entityid = self.host.id
|
||||||
|
cmd.entitytype = "HOST"
|
||||||
|
cmd.annotation = "test"
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.added_annotations.append(userApiClient.addAnnotation(cmd))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.fail("AddAnnotation is allowed for User")
|
||||||
|
|
||||||
|
cmd = listAnnotations.listAnnotationsCmd()
|
||||||
|
try:
|
||||||
|
userApiClient.listAnnotations(cmd)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.fail("ListAnnotations is allowed for User")
|
||||||
|
|
||||||
|
cmd = removeAnnotation.removeAnnotationCmd()
|
||||||
|
cmd.id = self.added_annotations[-1].annotation.id
|
||||||
|
try:
|
||||||
|
userApiClient.removeAnnotation(cmd)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.fail("RemoveAnnotation is allowed for User")
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_04_remove_annotations(self):
|
||||||
|
"""Testing the deleteAnnotation API ability to delete annotation"""
|
||||||
|
self.addAnnotation("annotation1")
|
||||||
|
self.removeAnnotation(self.added_annotations[-1].annotation.id)
|
||||||
|
del self.added_annotations[-1]
|
||||||
|
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_05_add_annotation_for_invvalid_entityType(self):
|
||||||
|
cmd = addAnnotation.addAnnotationCmd()
|
||||||
|
cmd.entityid = self.host.id
|
||||||
|
cmd.entitytype = "BLA"
|
||||||
|
cmd.annotation = annotation
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.apiclient.addAnnotation(cmd)
|
||||||
|
except CloudstackAPIException as f:
|
||||||
|
log.debug("error message %s" % f)
|
||||||
|
else:
|
||||||
|
self.fail("AddAnnotation is allowed for on an unknown entityType")
|
||||||
|
|
||||||
|
return self.added_annotations[-1]
|
||||||
@ -181,6 +181,9 @@ known_categories = {
|
|||||||
'deleteServicePackageOffering' : 'Load Balancer',
|
'deleteServicePackageOffering' : 'Load Balancer',
|
||||||
'destroyNsVpx' : 'Load Balancer',
|
'destroyNsVpx' : 'Load Balancer',
|
||||||
'startNsVpx' : 'Load Balancer',
|
'startNsVpx' : 'Load Balancer',
|
||||||
|
'listAnnotations' : 'Annotations',
|
||||||
|
'addAnnotation' : 'Annotations',
|
||||||
|
'removeAnnotation' : 'Annotations',
|
||||||
'CA': 'Certificate'
|
'CA': 'Certificate'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -427,6 +427,8 @@ var dictionary = {"ICMP.code":"ICMP Code",
|
|||||||
"label.allocated":"Allocated",
|
"label.allocated":"Allocated",
|
||||||
"label.allocation.state":"Allocation State",
|
"label.allocation.state":"Allocation State",
|
||||||
"label.allow":"Allow",
|
"label.allow":"Allow",
|
||||||
|
"label.annotated.by":"Annotator",
|
||||||
|
"label.annotation":"Annotation",
|
||||||
"label.anti.affinity":"Anti-affinity",
|
"label.anti.affinity":"Anti-affinity",
|
||||||
"label.anti.affinity.group":"Anti-affinity Group",
|
"label.anti.affinity.group":"Anti-affinity Group",
|
||||||
"label.anti.affinity.groups":"Anti-affinity Groups",
|
"label.anti.affinity.groups":"Anti-affinity Groups",
|
||||||
@ -944,6 +946,7 @@ var dictionary = {"ICMP.code":"ICMP Code",
|
|||||||
"label.lang.polish":"Polish",
|
"label.lang.polish":"Polish",
|
||||||
"label.lang.russian":"Russian",
|
"label.lang.russian":"Russian",
|
||||||
"label.lang.spanish":"Spanish",
|
"label.lang.spanish":"Spanish",
|
||||||
|
"label.last.annotated":"Last annotation date",
|
||||||
"label.last.disconnected":"Last Disconnected",
|
"label.last.disconnected":"Last Disconnected",
|
||||||
"label.last.name":"Last Name",
|
"label.last.name":"Last Name",
|
||||||
"label.lastname.lower":"lastname",
|
"label.lastname.lower":"lastname",
|
||||||
|
|||||||
@ -16064,6 +16064,9 @@
|
|||||||
if (args.data.oscategoryid != null && args.data.oscategoryid.length > 0)
|
if (args.data.oscategoryid != null && args.data.oscategoryid.length > 0)
|
||||||
array1.push("&osCategoryId=" + args.data.oscategoryid);
|
array1.push("&osCategoryId=" + args.data.oscategoryid);
|
||||||
|
|
||||||
|
if (args.data.annotation != null && args.data.annotation.length > 0)
|
||||||
|
array1.push("&annotation=" + args.data.annotation);
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: createURL("updateHost&id=" + args.context.hosts[0].id + array1.join("")),
|
url: createURL("updateHost&id=" + args.context.hosts[0].id + array1.join("")),
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
@ -17073,6 +17076,17 @@
|
|||||||
ipaddress: {
|
ipaddress: {
|
||||||
label: 'label.ip.address'
|
label: 'label.ip.address'
|
||||||
},
|
},
|
||||||
|
annotation: {
|
||||||
|
label: 'label.annotation',
|
||||||
|
isEditable: true
|
||||||
|
},
|
||||||
|
lastannotated: {
|
||||||
|
label: 'label.last.annotated',
|
||||||
|
converter: cloudStack.converters.toLocalDate
|
||||||
|
},
|
||||||
|
username: {
|
||||||
|
label: 'label.annotated.by'
|
||||||
|
},
|
||||||
disconnected: {
|
disconnected: {
|
||||||
label: 'label.last.disconnected'
|
label: 'label.last.disconnected'
|
||||||
},
|
},
|
||||||
@ -17099,12 +17113,17 @@
|
|||||||
if (item && item.outofbandmanagement) {
|
if (item && item.outofbandmanagement) {
|
||||||
item.powerstate = item.outofbandmanagement.powerstate;
|
item.powerstate = item.outofbandmanagement.powerstate;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item && item.hostha) {
|
if (item && item.hostha) {
|
||||||
item.hastate = item.hostha.hastate;
|
item.hastate = item.hostha.hastate;
|
||||||
item.haprovider = item.hostha.haprovider;
|
item.haprovider = item.hostha.haprovider;
|
||||||
item.haenabled = item.hostha.haenable;
|
item.haenabled = item.hostha.haenable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item.annotation = item.annotation;
|
||||||
|
item.lastannotated = item.lastannotated;
|
||||||
|
item.username = item.username;
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: createURL("listDedicatedHosts&hostid=" + args.context.hosts[0].id),
|
url: createURL("listDedicatedHosts&hostid=" + args.context.hosts[0].id),
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user