Skip to content

Commit

Permalink
CLOUDSTACK-9957 Annotations (apache#2181)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
DaanHoogland authored Oct 13, 2017
1 parent 189b0e4 commit a379230
Show file tree
Hide file tree
Showing 24 changed files with 1,215 additions and 67 deletions.
17 changes: 11 additions & 6 deletions api/src/com/cloud/event/EventTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@
import java.util.HashMap;
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.Pod;
import com.cloud.dc.StorageNetworkIpRange;
Expand Down Expand Up @@ -75,6 +69,12 @@
import com.cloud.vm.Nic;
import com.cloud.vm.NicSecondaryIp;
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 {

Expand Down Expand Up @@ -569,6 +569,9 @@ public class EventTypes {
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_ANNOTATION_CREATE = "ANNOTATION.CREATE";
public static final String EVENT_ANNOTATION_REMOVE = "ANNOTATION.REMOVE";


static {

Expand Down Expand Up @@ -953,6 +956,8 @@ public class EventTypes {
entityEventDetails.put(EVENT_NETSCALER_SERVICEPACKAGE_ADD, "NETSCALER.SERVICEPACKAGE.CREATE");
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) {
Expand Down
37 changes: 37 additions & 0 deletions api/src/org/apache/cloudstack/annotation/Annotation.java
Original file line number Diff line number Diff line change
@@ -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();
}
49 changes: 49 additions & 0 deletions api/src/org/apache/cloudstack/annotation/AnnotationService.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
}
4 changes: 4 additions & 0 deletions api/src/org/apache/cloudstack/api/ApiConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class ApiConstants {
public static final String ADDRESS = "address";
public static final String ALGORITHM = "algorithm";
public static final String ALLOCATED_ONLY = "allocatedonly";
public static final String ANNOTATION = "annotation";
public static final String API_KEY = "apikey";
public static final String USER_API_KEY = "userapikey";
public static final String APPLIED = "applied";
Expand Down Expand Up @@ -680,6 +681,9 @@ public class ApiConstants {
+ " 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.";

public static final String HAS_ANNOTATION = "hasannotation";
public static final String LAST_ANNOTATED = "lastannotated";

public enum HostDetails {
all, capacity, events, stats, min;
}
Expand Down
53 changes: 27 additions & 26 deletions api/src/org/apache/cloudstack/api/BaseCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,6 @@

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.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
Expand Down Expand Up @@ -78,11 +52,35 @@
import com.cloud.user.AccountService;
import com.cloud.user.DomainService;
import com.cloud.user.ResourceLimitService;
import com.cloud.utils.HttpUtils;
import com.cloud.utils.ReflectUtil;
import com.cloud.utils.db.EntityManager;
import com.cloud.utils.db.UUIDManager;
import com.cloud.vm.UserVmService;
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 {
private static final Logger s_logger = Logger.getLogger(BaseCmd.class.getName());
Expand All @@ -93,6 +91,7 @@ public abstract class BaseCmd {
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");
protected static final Map<Class<?>, List<Field>> fieldsForCmdClass = new HashMap<Class<?>, List<Field>>();

public static enum HTTPMethod {
GET, POST, PUT, DELETE
}
Expand Down Expand Up @@ -192,6 +191,8 @@ public static enum CommandType {
public AlertService _alertSvc;
@Inject
public UUIDManager _uuidMgr;
@Inject
public AnnotationService annotationService;

public abstract void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException,
ResourceAllocationException, NetworkRuleConflictException;
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading

0 comments on commit a379230

Please sign in to comment.