diff --git a/client/pom.xml b/client/pom.xml index d8fa433d5be3..0eb77389e9d5 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -28,6 +28,11 @@ 4.23.0.0-SNAPSHOT + + org.apache.cloudstack + cloud-plugin-hackerbook-feature + ${project.version} + javax.servlet javax.servlet-api diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42200to42300.sql b/engine/schema/src/main/resources/META-INF/db/schema-42200to42300.sql index c1f1bb2c094d..48b7e9ce640a 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42200to42300.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42200to42300.sql @@ -18,3 +18,17 @@ --; -- Schema upgrade from 4.22.0.0 to 4.23.0.0 --; +CREATE TABLE IF NOT EXISTS `cloud`.`coffee` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) UNIQUE, + `name` varchar(255) NOT NULL, + `offering` varchar(40) NOT NULL, + `size` varchar(40) NOT NULL, + `state` varchar(40) NOT NULL, + `account_id` bigint unsigned NOT NULL, + `created` datetime NOT NULL COMMENT 'date of creation', + `removed` datetime COMMENT 'date of removal', + PRIMARY KEY (`id`), + KEY (`uuid`), + KEY `i_coffee` (`name`, `account_id`, `created`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/plugins/hackerbook/feature/pom.xml b/plugins/hackerbook/feature/pom.xml new file mode 100644 index 000000000000..a88f5657c4aa --- /dev/null +++ b/plugins/hackerbook/feature/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + cloud-plugin-hackerbook-feature + Apache CloudStack Plugin - HackerBook Coffee Feature + + org.apache.cloudstack + cloudstack-plugins + 4.23.0.0-SNAPSHOT + ../../pom.xml + + + + org.apache.cloudstack + cloud-api + ${project.version} + + + org.apache.cloudstack + cloud-utils + ${project.version} + + + + 11 + 11 + UTF-8 + + diff --git a/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/Coffee.java b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/Coffee.java new file mode 100644 index 000000000000..7fb706f03a5d --- /dev/null +++ b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/Coffee.java @@ -0,0 +1,79 @@ +/* + * 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; + +/** + * Represents a coffee order in the system. + * @since 4.23.0.0 + */ +public interface Coffee extends InternalIdentity, Identity { + + /** + * Size options for coffee orders. + */ + enum Size { + SMALL, + MEDIUM, + LARGE + } + + /** + * Available coffee offerings/types. + */ + enum Offering { + ESPRESSO, + CAPPUCCINO, + MOCHA, + LATTE + } + + /** + * Lifecycle states for a coffee order. + */ + enum State { + CREATED, + BREWING, + BREWED + } + + /** + * Returns the name of the coffee order. + * @return the coffee name + */ + String getName(); + + /** + * Returns the type of coffee ordered. + * @return the coffee offering type + */ + Offering getOffering(); + + /** + * Returns the size of the coffee order. + * @return the coffee size + */ + Size getSize(); + + /** + * Returns the current state of the coffee order. + * @return the coffee state + */ + State getState(); +} diff --git a/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/CoffeeManager.java b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/CoffeeManager.java new file mode 100644 index 000000000000..ee97063f3a1a --- /dev/null +++ b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/CoffeeManager.java @@ -0,0 +1,42 @@ +/* + * 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; + +import org.apache.cloudstack.api.command.CreateCoffeeCmd; +import org.apache.cloudstack.api.command.ListCoffeeCmd; +import org.apache.cloudstack.api.command.RemoveCoffeeCmd; +import org.apache.cloudstack.api.command.UpdateCoffeeCmd; +import org.apache.cloudstack.api.response.CoffeeResponse; + +import java.util.List; + +public interface CoffeeManager { + Coffee createCoffee(CreateCoffeeCmd cmd); + + List listCoffees(ListCoffeeCmd cmd); + + Coffee updateCoffee(UpdateCoffeeCmd cmd); + + boolean removeCoffee(RemoveCoffeeCmd cmd); + + CoffeeResponse createCoffeeResponse(Coffee coffee); + + List createCoffeeResponses(List coffees); +} diff --git a/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/command/CreateCoffeeCmd.java b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/command/CreateCoffeeCmd.java new file mode 100644 index 000000000000..a98a9209cc51 --- /dev/null +++ b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/command/CreateCoffeeCmd.java @@ -0,0 +1,124 @@ +/* + * 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; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.Coffee; +import org.apache.cloudstack.api.CoffeeManager; +import org.apache.cloudstack.api.response.CoffeeResponse; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.context.CallContext; + +import javax.inject.Inject; + +@APICommand( + name = CreateCoffeeCmd.APINAME, + description = "Creates a new coffee order", + responseObject = CoffeeResponse.class, + since = "4.23.0.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User} +) +public class CreateCoffeeCmd extends BaseAsyncCreateCmd { + public static final String APINAME = "createCoffee"; + + @Inject + private CoffeeManager coffeeManager; + + private Coffee coffee; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, + type = CommandType.STRING, + required = true, + description = "name of the coffee order") + private String name; + + @Parameter(name = "offering", + type = CommandType.STRING, + required = true, + description = "type of coffee (ESPRESSO, CAPPUCCINO, MOCHA, LATTE)") + private String offering; + + @Parameter(name = "size", + type = CommandType.STRING, + required = true, + description = "size of coffee (SMALL, MEDIUM, LARGE)") + private String size; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getName() { + return name; + } + + public String getOffering() { + return offering; + } + + public String getSize() { + return size; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void create() { + coffee = coffeeManager.createCoffee(this); + + if (coffee != null) { + setEntityId(coffee.getId()); + setEntityUuid(coffee.getUuid()); + } + } + + @Override + public void execute() { + CoffeeResponse response = coffeeManager.createCoffeeResponse(coffee); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public String getEventType() { + return "COFFEE.CREATE"; + } + + @Override + public String getEventDescription() { + return "Creating coffee: " + name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/command/ListCoffeeCmd.java b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/command/ListCoffeeCmd.java new file mode 100644 index 000000000000..1099553e538e --- /dev/null +++ b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/command/ListCoffeeCmd.java @@ -0,0 +1,108 @@ +/* + * 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; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.Coffee; +import org.apache.cloudstack.api.CoffeeManager; +import org.apache.cloudstack.api.response.CoffeeResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.context.CallContext; + +import javax.inject.Inject; +import java.util.List; + +@APICommand( + name = ListCoffeeCmd.APINAME, + description = "Lists all coffees with optional filters", + responseObject = CoffeeResponse.class, + since = "4.23.0.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User} +) +public class ListCoffeeCmd extends BaseListCmd { + public static final String APINAME = "listCoffees"; + + @Inject + private CoffeeManager coffeeManager; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, + type = CommandType.LONG, + required = false, + description = "the ID of the coffee order") + private Long id; + + @Parameter(name = "offering", + type = CommandType.STRING, + required = false, + description = "type of coffee (ESPRESSO, CAPPUCCINO, MOCHA, LATTE)") + private String offering; + + @Parameter(name = "size", + type = CommandType.STRING, + required = false, + description = "size of coffee (SMALL, MEDIUM, LARGE)") + private String size; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getOffering() { + return offering; + } + + public String getSize() { + return size; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + List coffees = coffeeManager.listCoffees(this); + List responseList = coffeeManager.createCoffeeResponses(coffees); + + ListResponse listResponse = new ListResponse<>(); + listResponse.setResponses(responseList, responseList.size()); + listResponse.setResponseName(getCommandName()); + setResponseObject(listResponse); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/command/RemoveCoffeeCmd.java b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/command/RemoveCoffeeCmd.java new file mode 100644 index 000000000000..05bbeca3e3d9 --- /dev/null +++ b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/command/RemoveCoffeeCmd.java @@ -0,0 +1,125 @@ +/* + * 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; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.CoffeeManager; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.context.CallContext; + +import javax.inject.Inject; +import java.util.List; + +@APICommand( + name = RemoveCoffeeCmd.APINAME, + description = "Removes a coffee order or multiple coffee orders", + responseObject = SuccessResponse.class, + since = "4.23.0.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User} +) +public class RemoveCoffeeCmd extends BaseAsyncCmd { + public static final String APINAME = "removeCoffee"; + + @Inject + private CoffeeManager coffeeManager; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, + type = CommandType.LONG, + required = false, + description = "the ID of the coffee order to remove") + private Long id; + + @Parameter(name = "ids", + type = CommandType.LIST, + collectionType = CommandType.LONG, + required = false, + description = "the IDs of coffee orders to remove") + private List ids; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public List getIds() { + return ids; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + boolean result = coffeeManager.removeCoffee(this); + + SuccessResponse response = new SuccessResponse(); + + if (result) { + response.setSuccess(true); + if (id != null) { + response.setDisplayText("Successfully removed coffee order: " + id); + } else if (ids != null && !ids.isEmpty()) { + response.setDisplayText("Successfully removed " + ids.size() + " coffee orders"); + } else { + response.setDisplayText("Coffee removal completed"); + } + } else { + response.setSuccess(false); + response.setDisplayText("Failed to remove coffee order"); + } + + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public String getEventType() { + return "COFFEE.REMOVE"; + } + + @Override + public String getEventDescription() { + if (id != null) { + return "Removing coffee: " + id; + } else if (ids != null) { + return "Removing " + ids.size() + " coffees"; + } + return "Removing coffee"; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/command/UpdateCoffeeCmd.java b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/command/UpdateCoffeeCmd.java new file mode 100644 index 000000000000..1e450927b4d7 --- /dev/null +++ b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/command/UpdateCoffeeCmd.java @@ -0,0 +1,103 @@ +/* + * 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; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.Coffee; +import org.apache.cloudstack.api.CoffeeManager; +import org.apache.cloudstack.api.response.CoffeeResponse; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.context.CallContext; + +import javax.inject.Inject; + +@APICommand( + name = UpdateCoffeeCmd.APINAME, + description = "Updates an existing coffee order", + responseObject = CoffeeResponse.class, + since = "4.23.0.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User} +) +public class UpdateCoffeeCmd extends BaseAsyncCmd { + public static final String APINAME = "updateCoffee"; + + @Inject + private CoffeeManager coffeeManager; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, + type = CommandType.LONG, + required = true, + description = "the ID of the coffee order") + private Long id; + + @Parameter(name = "size", + type = CommandType.STRING, + required = false, + description = "new size of coffee (SMALL, MEDIUM, LARGE)") + private String size; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getSize() { + return size; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + Coffee coffee = coffeeManager.updateCoffee(this); + CoffeeResponse response = coffeeManager.createCoffeeResponse(coffee); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public String getEventType() { + return "COFFEE.UPDATE"; + } + + @Override + public String getEventDescription() { + return "Updating coffee: " + id; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/response/CoffeeResponse.java b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/response/CoffeeResponse.java new file mode 100644 index 000000000000..79ad10d9148c --- /dev/null +++ b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/api/response/CoffeeResponse.java @@ -0,0 +1,56 @@ +// 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.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.api.Coffee; +import org.apache.cloudstack.api.ApiConstants; +import com.cloud.serializer.Param; + +@EntityReference(value = Coffee.class) +public class CoffeeResponse extends BaseResponse { + public static final String OFFERING = "offering"; + public static final String SIZE = "size"; + + @SerializedName(ApiConstants.ID) + @Param(description = "the coffee ID") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "the coffee name") + private String name; + + @SerializedName(OFFERING) + @Param(description = "the type of coffee") + private String offering; + + @SerializedName(SIZE) + @Param(description = "the size of coffee") + private String size; + + @SerializedName(ApiConstants.STATE) + @Param(description = "current coffee state") + private String state; + + public void setId(String id) { this.id = id; } + public void setName(String name) { this.name = name; } + public void setOffering(String offering) { this.offering = offering; } + public void setSize(String size) { this.size = size; } + public void setState(String state) { this.state = state; } +} diff --git a/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/feature/CoffeeManagerImpl.java b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/feature/CoffeeManagerImpl.java new file mode 100644 index 000000000000..8835678146d0 --- /dev/null +++ b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/feature/CoffeeManagerImpl.java @@ -0,0 +1,264 @@ +/* + * 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.feature; + +import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.component.PluggableService; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.cloudstack.poll.BackgroundPollManager; +import org.apache.cloudstack.poll.BackgroundPollTask; +import org.apache.cloudstack.api.Coffee; +import org.apache.cloudstack.api.CoffeeManager; +import org.apache.cloudstack.api.command.CreateCoffeeCmd; +import org.apache.cloudstack.api.command.ListCoffeeCmd; +import org.apache.cloudstack.api.command.RemoveCoffeeCmd; +import org.apache.cloudstack.api.command.UpdateCoffeeCmd; +import org.apache.cloudstack.api.response.CoffeeResponse; +import org.apache.cloudstack.feature.dao.CoffeeDao; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class CoffeeManagerImpl extends ManagerBase implements CoffeeManager, Configurable, PluggableService { + protected Logger logger = LogManager.getLogger(getClass()); + + @Inject + private CoffeeDao _coffeeDao; + + @Inject + private BackgroundPollManager _backgroundPollManager; + + private static final ConfigKey CoffeeTTLInterval = new ConfigKey( + "Advanced", + Long.class, + "coffee.ttl.interval", + "600", + "The max time in seconds after which coffee becomes stale.", + true, + ConfigKey.Scope.Zone + ); + + private static final ConfigKey CoffeeGCInterval = new ConfigKey( + "Advanced", + Long.class, + "coffee.gc.interval", + "300", + "The interval in seconds at which the coffee garbage collection task runs.", + true, + ConfigKey.Scope.Zone + ); + + private static final class CoffeeGCTask extends ManagedContextRunnable implements BackgroundPollTask { + private final CoffeeManager coffeeManager; + + private CoffeeGCTask(CoffeeManager coffeeManager) { + this.coffeeManager = coffeeManager; + } + + @Override + protected void runInContext() { + try { + if (logger.isTraceEnabled()) { + logger.trace("Coffee GC task is running..."); + } + + final Long ttl = CoffeeTTLInterval.value(); + + logger.info("Coffee GC task executed. TTL: " + ttl + " seconds"); + + } catch (final Throwable t) { + logger.error("Error trying to run Coffee GC task", t); + } + } + + @Override + public Long getDelay() { + return CoffeeGCInterval.value() * 1000L; + } + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + super.configure(name, params); + logger.info("CoffeeManager is being configured"); + _backgroundPollManager.submitTask(new CoffeeGCTask(this)); + logger.info("Coffee GC background task has been scheduled"); + return true; + } + + @Override + public boolean start() { + logger.info("CoffeeManager is starting"); + return true; + } + + @Override + public boolean stop() { + logger.info("CoffeeManager is stopping"); + return true; + } + + @Override + public List> getCommands() { + final List> cmdList = new ArrayList<>(); + cmdList.add(CreateCoffeeCmd.class); + cmdList.add(ListCoffeeCmd.class); + cmdList.add(UpdateCoffeeCmd.class); + cmdList.add(RemoveCoffeeCmd.class); + return cmdList; + } + + @Override + public String getConfigComponentName() { + return CoffeeManager.class.getSimpleName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[]{ + CoffeeTTLInterval, + CoffeeGCInterval + }; + } + + @Override + public CoffeeResponse createCoffeeResponse(Coffee coffee) { + CoffeeResponse response = new CoffeeResponse(); + response.setId(coffee.getUuid()); + response.setName(coffee.getName()); + response.setOffering(coffee.getOffering().name()); + response.setSize(coffee.getSize().name()); + response.setState(coffee.getState().name()); + response.setObjectName("coffee"); + return response; + } + + @Override + public List createCoffeeResponses(List coffees) { + List responses = new ArrayList<>(); + for (Coffee coffee : coffees) { + responses.add(createCoffeeResponse(coffee)); + } + return responses; + } + + @Override + public Coffee createCoffee(CreateCoffeeCmd cmd) { + logger.info("Creating coffee: " + cmd.getName()); + + Coffee.Offering offering = Coffee.Offering.valueOf(cmd.getOffering()); + Coffee.Size size = Coffee.Size.valueOf(cmd.getSize()); + + CoffeeVO coffee = new CoffeeVO(cmd.getName(), offering, size, 1L); + coffee = _coffeeDao.persist(coffee); + + logger.debug("Created coffee with ID: " + coffee.getId() + ", UUID: " + coffee.getUuid()); + return coffee; + } + + @Override + public List listCoffees(ListCoffeeCmd cmd) { + logger.info("Listing coffees"); + + List coffees = _coffeeDao.listAll(); + + Long id = cmd.getId(); + String offering = cmd.getOffering(); + String size = cmd.getSize(); + + List filteredCoffeeList = new ArrayList<>(); + + for (CoffeeVO coffee : coffees) { + boolean isCoffeeFound = true; + + if (id != null && coffee.getId() != id) { + isCoffeeFound = false; + } + + if (offering != null && !coffee.getOffering().name().equalsIgnoreCase(offering)) { + isCoffeeFound = false; + } + + if (size != null && !coffee.getSize().name().equalsIgnoreCase(size)) { + isCoffeeFound = false; + } + + if (isCoffeeFound) { + filteredCoffeeList.add(coffee); + } + } + + logger.debug("Returning " + filteredCoffeeList.size() + " coffees"); + return filteredCoffeeList; + } + + @Override + public Coffee updateCoffee(UpdateCoffeeCmd cmd) { + logger.info("Updating coffee with ID: " + cmd.getId()); + + long id = cmd.getId(); + CoffeeVO coffee = _coffeeDao.findById(id); + + if (coffee == null) { + throw new CloudRuntimeException("Coffee with ID " + id + " not found"); + } + + if (cmd.getSize() != null) { + coffee.setSize(Coffee.Size.valueOf(cmd.getSize())); + } + + _coffeeDao.update(id, coffee); + + logger.debug("Updated coffee: " + coffee.getName()); + return coffee; + } + + @Override + public boolean removeCoffee(RemoveCoffeeCmd cmd) { + if (cmd.getId() != null) { + logger.info("Removing coffee with ID: " + cmd.getId()); + long id = cmd.getId(); + + boolean result = _coffeeDao.remove(id); + + if (!result) { + throw new CloudRuntimeException("Failed to remove coffee with ID " + id); + } + + return true; + } else if (cmd.getIds() != null) { + logger.info("Removing " + cmd.getIds().size() + " coffees"); + for (Long id : cmd.getIds()) { + _coffeeDao.remove(id); + } + return true; + } + + return false; + } +} diff --git a/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/feature/CoffeeVO.java b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/feature/CoffeeVO.java new file mode 100644 index 000000000000..850345bbbe3b --- /dev/null +++ b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/feature/CoffeeVO.java @@ -0,0 +1,173 @@ +// 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.feature; + +import org.apache.cloudstack.api.Coffee; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; +import java.util.UUID; + +@Entity +@Table(name = "coffee") +public class CoffeeVO implements Coffee { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "name") + private String name; + + @Column(name = "offering") + @Enumerated(value = EnumType.STRING) + private Offering offering; + + @Column(name = "size") + @Enumerated(value = EnumType.STRING) + private Size size; + + @Column(name = "state") + @Enumerated(value = EnumType.STRING) + private State state = State.CREATED; + + @Column(name = "account_id") + private long accountId; + + @Column(name = "created") + private Date created; + + @Column(name = "removed") + private Date removed; + + public CoffeeVO() { + this.uuid = UUID.randomUUID().toString(); + } + + public CoffeeVO(String name, Offering offering, Size size, long accountId) { + this(); + this.name = name; + this.offering = offering; + this.size = size; + this.accountId = accountId; + this.state = State.CREATED; + this.created = new Date(); + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public String getName() { + return name; + } + + @Override + public Offering getOffering() { + return offering; + } + + @Override + public Size getSize() { + return size; + } + + @Override + public State getState() { + return state; + } + + public long getAccountId() { + return accountId; + } + + public Date getCreated() { + return created; + } + + public Date getRemoved() { + return removed; + } + + public void setId(long id) { + this.id = id; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public void setName(String name) { + this.name = name; + } + + public void setOffering(Offering offering) { + this.offering = offering; + } + + public void setSize(Size size) { + this.size = size; + } + + public void setState(State state) { + this.state = state; + } + + public void setAccountId(long accountId) { + this.accountId = accountId; + } + + public void setCreated(Date created) { + this.created = created; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + @Override + public String toString() { + return "CoffeeVO{" + + "id=" + id + + ", uuid='" + uuid + '\'' + + ", name='" + name + '\'' + + ", offering=" + offering + + ", size=" + size + + ", state=" + state + + ", accountId=" + accountId + + '}'; + } +} diff --git a/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/feature/dao/CoffeeDao.java b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/feature/dao/CoffeeDao.java new file mode 100644 index 000000000000..567392c6dcec --- /dev/null +++ b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/feature/dao/CoffeeDao.java @@ -0,0 +1,24 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.feature.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.feature.CoffeeVO; + +public interface CoffeeDao extends GenericDao { +} diff --git a/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/feature/dao/CoffeeDaoImpl.java b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/feature/dao/CoffeeDaoImpl.java new file mode 100644 index 000000000000..c584b0221a61 --- /dev/null +++ b/plugins/hackerbook/feature/src/main/java/org/apache/cloudstack/feature/dao/CoffeeDaoImpl.java @@ -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.feature.dao; + +import com.cloud.utils.db.GenericDaoBase; +import org.apache.cloudstack.feature.CoffeeVO; +import org.springframework.stereotype.Component; + +@Component +public class CoffeeDaoImpl extends GenericDaoBase implements CoffeeDao { + + public CoffeeDaoImpl() { + super(); + } +} diff --git a/plugins/hackerbook/feature/src/main/resources/META-INF/cloudstack/feature/module.properties b/plugins/hackerbook/feature/src/main/resources/META-INF/cloudstack/feature/module.properties new file mode 100644 index 000000000000..69a1fd028abd --- /dev/null +++ b/plugins/hackerbook/feature/src/main/resources/META-INF/cloudstack/feature/module.properties @@ -0,0 +1,18 @@ +# 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. +name=feature +parent=backend diff --git a/plugins/hackerbook/feature/src/main/resources/META-INF/cloudstack/feature/spring-feature-context.xml b/plugins/hackerbook/feature/src/main/resources/META-INF/cloudstack/feature/spring-feature-context.xml new file mode 100644 index 000000000000..75db0aa7c013 --- /dev/null +++ b/plugins/hackerbook/feature/src/main/resources/META-INF/cloudstack/feature/spring-feature-context.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/plugins/pom.xml b/plugins/pom.xml index e7d13871285e..c95e4651f269 100755 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -140,6 +140,7 @@ storage/object/ceph storage/object/cloudian storage/object/simulator + hackerbook/feature storage-allocators/random diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py index e41a04ff2e1b..34970180e542 100644 --- a/tools/apidoc/gen_toc.py +++ b/tools/apidoc/gen_toc.py @@ -273,7 +273,8 @@ 'Extensions' : 'Extension', 'CustomAction' : 'Extension', 'CustomActions' : 'Extension', - 'ImportVmTask': 'Import VM Task' + 'ImportVmTask': 'Import VM Task', + 'Coffee': 'Coffee' }