You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@brooklyn.apache.org by Graeme-Miller <gi...@git.apache.org> on 2017/12/05 11:54:49 UTC

[GitHub] brooklyn-server pull request #908: Added a DashboardAggregator

GitHub user Graeme-Miller opened a pull request:

    https://github.com/apache/brooklyn-server/pull/908

    Added a DashboardAggregator

    Added a DashboardAggregator. This aggregator will pull together dashboard sensors from it's children and merge them together. This however, does not touch the owner or environment sensors as there is no good way to aggregate them.

You can merge this pull request into a Git repository by running:

    $ git pull https://github.com/Graeme-Miller/brooklyn-server add_dashboard_aggregator

Alternatively you can review and apply these changes as the patch at:

    https://github.com/apache/brooklyn-server/pull/908.patch

To close this pull request, make a commit to your master/trunk branch
with (at least) the following in the commit message:

    This closes #908
    
----
commit 972ae5c6fa562adc2a09de7954f4e41bfe50024a
Author: graeme.miller <gr...@cloudsoftcorp.com>
Date:   2017-12-05T11:53:00Z

    Added a DashboardAggregator

----


---

[GitHub] brooklyn-server pull request #908: Added a DashboardAggregator

Posted by aledsage <gi...@git.apache.org>.
Github user aledsage commented on a diff in the pull request:

    https://github.com/apache/brooklyn-server/pull/908#discussion_r154934710
  
    --- Diff: core/src/main/java/org/apache/brooklyn/enricher/stock/aggregator/AggregationJob.java ---
    @@ -0,0 +1,158 @@
    +/*
    + * 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.brooklyn.enricher.stock.aggregator;
    +
    +import java.util.ArrayList;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +import java.util.Map;
    +
    +import org.apache.commons.lang3.StringUtils;
    +
    +import org.apache.brooklyn.api.entity.Entity;
    +import org.apache.brooklyn.core.sensor.BasicAttributeSensorAndConfigKey;
    +
    +public final class AggregationJob implements Runnable {
    +
    +    public static BasicAttributeSensorAndConfigKey<Map<String, String>> DASHBOARD_COST_PER_MONTH = new BasicAttributeSensorAndConfigKey(Map.class, "dashboard.costPerMonth", "A map of VM ID to monthly cost. Please note if the same VM ID is reported more than once, only one of the sensors will be propagated");
    +    public static BasicAttributeSensorAndConfigKey<List<Map<String, String>>> DASHBOARD_HA_PRIMARIES = new BasicAttributeSensorAndConfigKey(List.class, "dashboard.ha.primaries", "A list of high availability primaries "); //TODO: find out what this is and add a better description
    +    public static BasicAttributeSensorAndConfigKey<List<Map<String, String>>> DASHBOARD_LICENSES = new BasicAttributeSensorAndConfigKey(List.class, "dashboard.licenses", "The licences in use for the running entities. This is a list of maps. The map should contain two keys: product and key");
    +    public static BasicAttributeSensorAndConfigKey<Map<String, List<Map<String, Object>>>> DASHBOARD_LOCATIONS = new BasicAttributeSensorAndConfigKey(Map.class, "dashboard.locations", "Locations in which the VMs are running. A map where the key is category of location (e.g. server) and the value is a list of maps containing name/icon/count");
    +    public static BasicAttributeSensorAndConfigKey<List<Map<String, String>>> DASHBOARD_POLICY_HIGHLIGHTS = new BasicAttributeSensorAndConfigKey(List.class, "dashboard.policyHighlights", "Highlights from policies. List of Masps, where each map should contain text and category");
    +
    +    private final Entity entity;
    +
    +    private static final String NAME_STRING = "name";
    +    private static final String COUNT_STRING = "count";
    +
    +    public AggregationJob(Entity entity) {
    +        this.entity = entity;
    +    }
    +
    +    private void updateFromConfig(Entity entity, BasicAttributeSensorAndConfigKey<List<Map<String, String>>> sensorAndConfigKey, List<Map<String, String>> list) {
    +        List<Map<String, String>> listFromEntity = entity.sensors().get(sensorAndConfigKey);
    +        if (listFromEntity == null || listFromEntity.isEmpty()) {
    +            listFromEntity = entity.config().get(sensorAndConfigKey);
    +        }
    +
    +        if (listFromEntity != null) {
    +            list.addAll(listFromEntity);
    +        }
    +    }
    +
    +    @Override
    +    public void run() {
    +        HashMap<String, String> costPerMonth = new HashMap<>();
    +        List<Map<String, String>> haPrimaries = new ArrayList<>();
    +        List<Map<String, String>> dashboardLicences = new ArrayList<>();
    +        Map<String, List<Map<String, Object>>> dashboardLocations = new HashMap<>();
    +        List<Map<String, String>> dashboardPolicyHighlights = new ArrayList<>();
    +
    +        entity.getChildren().forEach(childEntity -> scanEntity(childEntity, costPerMonth, haPrimaries, dashboardLicences, dashboardLocations, dashboardPolicyHighlights));
    +        entity.sensors().set(DASHBOARD_COST_PER_MONTH, costPerMonth);
    +        entity.sensors().set(DASHBOARD_HA_PRIMARIES, haPrimaries);
    +        entity.sensors().set(DASHBOARD_LICENSES, dashboardLicences);
    +        entity.sensors().set(DASHBOARD_LOCATIONS, dashboardLocations);
    +        entity.sensors().set(DASHBOARD_POLICY_HIGHLIGHTS, dashboardPolicyHighlights);
    +    }
    +
    +    protected void scanEntity(Entity entity,
    +                              Map<String, String> costPerMonth,
    +                              List<Map<String, String>> haPrimaries,
    +                              List<Map<String, String>> licences,
    +                              Map<String, List<Map<String, Object>>> locations,
    +                              List<Map<String, String>> policyHighlights) {
    +
    +        updateFromConfig(entity, DASHBOARD_HA_PRIMARIES, haPrimaries);
    +        updateFromConfig(entity, DASHBOARD_LICENSES, licences);
    +        updateFromConfig(entity, DASHBOARD_POLICY_HIGHLIGHTS, policyHighlights);
    +
    +        //Cost per month
    +        Map<String, String> costPerMonthFromEntity = entity.sensors().get(DASHBOARD_COST_PER_MONTH);
    +        if (costPerMonthFromEntity == null || costPerMonthFromEntity.isEmpty()) {
    +            costPerMonthFromEntity = entity.config().get(DASHBOARD_COST_PER_MONTH);
    +        }
    +
    +        if (costPerMonthFromEntity != null) {
    +            costPerMonth.putAll(costPerMonthFromEntity);
    +        }
    +
    +        //Locations merge
    +        //We are merging a Map, of Lists of Maps
    +        //The outer map is location type, e.g. servers to a list of data for that type
    +        //The inner map should contain the data e.g. name/icon/count.
    +
    +        //Further complicating this is that the Map you get back from sensors/config doesn't conform to the generic expectations.
    +        //I.E. even if you type this as Map<String, List<Map<String, String>>> you will still get back an integer from
    +        //the inner map- hence all the weird casting bellow.
    +        Map<String, List<Map<String, Object>>> outerLocationMapFromEntity = entity.sensors().get(DASHBOARD_LOCATIONS);
    +        if (outerLocationMapFromEntity == null || outerLocationMapFromEntity.isEmpty()) {
    +            outerLocationMapFromEntity = entity.config().get(DASHBOARD_LOCATIONS);
    +        }
    +
    +        if (outerLocationMapFromEntity != null) {
    +            //loop through outer maps
    +            outerLocationMapFromEntity.forEach((outerLocationMapFromEntityKey, outerLocationMapFromEntityValue) -> {
    +                boolean found = false;
    +                for (Map.Entry<String, List<Map<String, Object>>> outerLocationMapFromMethodParam : locations.entrySet()) {
    +                    if (StringUtils.equals(outerLocationMapFromMethodParam.getKey(), outerLocationMapFromEntityKey)) {
    +                        found = true;
    +
    +                        //loop through list
    +                        Iterator<Map<String, Object>> listIteratorFromEntity = outerLocationMapFromEntityValue.iterator();
    +                        while (listIteratorFromEntity.hasNext()) {
    +                            Map<String, Object> innerMapFromEntity = listIteratorFromEntity.next();
    +                            boolean foundInner = false;
    +
    +                            //loop through inner map and merge
    +                            for (Map<String, Object> innerMapFromMethodParam : outerLocationMapFromMethodParam.getValue()) {
    +                                if (StringUtils.equals((String) innerMapFromEntity.get(NAME_STRING), (String) innerMapFromMethodParam.get(NAME_STRING))) {
    +
    +                                    innerMapFromMethodParam.put(COUNT_STRING, (int) innerMapFromEntity.get(COUNT_STRING) + (int) innerMapFromMethodParam.get(COUNT_STRING));
    +                                    foundInner = true;
    +                                    break;
    +                                }
    +                            }
    +
    +                            //If the entity has a "name" not found in the method param then add it to the method param
    +                            if (!foundInner) {
    +                                outerLocationMapFromMethodParam.getValue().add(new HashMap<>(innerMapFromEntity));
    +                            }
    +                        }
    +                    }
    +
    +                }
    +
    +                //If the entity has an entry in the outer map that isn't in the method param, then add it
    +                if (!found) {
    +                    ArrayList clonedList = new ArrayList();
    +                    outerLocationMapFromEntityValue.forEach(mapToClone -> {
    +                        clonedList.add(new HashMap<>(mapToClone));
    +
    +                    });
    +                    locations.put(outerLocationMapFromEntityKey, clonedList);
    +                }
    +
    +            });
    +        }
    +
    +        entity.getChildren().forEach(childEntity -> scanEntity(childEntity, costPerMonth, haPrimaries, licences, locations, policyHighlights));
    --- End diff --
    
    The recursion here feels strange - if we have a cluster with a `costPerMonth` sensor, would you expect that to already be an aggregate of its children? Or would you expect to add that and also look at all its children to add each of those individually as well?
    
    I think you could argue it either way - it will sometimes feel surprising no matter which way round you choose (e.g. if I have a software process with child software processes - e.g. where the child installs a new relic agent - then I'd expect to take license info from the parent software process and the child software process).


---

[GitHub] brooklyn-server pull request #908: Added a DashboardAggregator

Posted by aledsage <gi...@git.apache.org>.
Github user aledsage commented on a diff in the pull request:

    https://github.com/apache/brooklyn-server/pull/908#discussion_r157783475
  
    --- Diff: core/src/main/java/org/apache/brooklyn/enricher/stock/aggregator/DashboardAggregator.java ---
    @@ -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.brooklyn.enricher.stock.aggregator;
    +
    +import java.util.Map;
    +import java.util.concurrent.Callable;
    +
    +import org.apache.brooklyn.api.entity.EntityLocal;
    +import org.apache.brooklyn.api.mgmt.Task;
    +import org.apache.brooklyn.core.enricher.AbstractEnricher;
    +import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
    +import org.apache.brooklyn.core.sensor.BasicAttributeSensorAndConfigKey;
    +import org.apache.brooklyn.util.core.task.ScheduledTask;
    +import org.apache.brooklyn.util.core.task.Tasks;
    +import org.apache.brooklyn.util.time.Duration;
    +
    +/**
    + * The DashboardAggregator is an enricher that combines config/sensor values from the children of the entity it is attached to.
    + * The combined values are set as sensors on the entity that it is attached to. Preference is given to the child entities sensors values,
    + * but if none are set, config values are used instead.
    + *
    + * The reason that this exists is to provide high level summary information, that could be useful to display on a dashboard.
    + * Whilst brooklyn itself has no such dashboard, we can imagine this entity being used in that fashion.
    + *
    + * Please note, that the DashboardAggregator will aggregate all children of the entity it is attached to, even intermediate level children.
    --- End diff --
    
    I'd say "all descendants" rather than "all children".


---

[GitHub] brooklyn-server pull request #908: Added a DashboardAggregator

Posted by aledsage <gi...@git.apache.org>.
Github user aledsage commented on a diff in the pull request:

    https://github.com/apache/brooklyn-server/pull/908#discussion_r154934843
  
    --- Diff: core/src/main/java/org/apache/brooklyn/enricher/stock/aggregator/DashboardAggregator.java ---
    @@ -0,0 +1,51 @@
    +/*
    + * 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.brooklyn.enricher.stock.aggregator;
    +
    +import java.util.concurrent.Callable;
    +
    +import org.apache.brooklyn.api.entity.EntityLocal;
    +import org.apache.brooklyn.api.mgmt.Task;
    +import org.apache.brooklyn.core.enricher.AbstractEnricher;
    +import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
    +import org.apache.brooklyn.util.core.task.ScheduledTask;
    +import org.apache.brooklyn.util.core.task.Tasks;
    +import org.apache.brooklyn.util.time.Duration;
    +
    +public class DashboardAggregator extends AbstractEnricher {
    +
    +    @SuppressWarnings("unchecked")
    +    @Override
    +    public void setEntity(EntityLocal entity) {
    +        super.setEntity(entity);
    +
    +        Callable<Task<?>> taskFactory = () -> Tasks.builder()
    +                .dynamic(false)
    +                .body(new AggregationJob(entity))
    +                .displayName("DashboardAggregator task")
    +                .tag(BrooklynTaskTags.TRANSIENT_TASK_TAG)
    +                .description("Retrieves and aggregates sensor values")
    +                .build();
    +
    +        ScheduledTask task = ScheduledTask.builder(taskFactory).period(Duration.seconds(1)).displayName("scheduled:[DashboardAggregator task]").tagTransient().build();
    --- End diff --
    
    I'd make the period configurable.


---

[GitHub] brooklyn-server issue #908: Added a DashboardAggregator

Posted by aledsage <gi...@git.apache.org>.
Github user aledsage commented on the issue:

    https://github.com/apache/brooklyn-server/pull/908
  
    Thanks @Graeme-Miller - merging now.


---

[GitHub] brooklyn-server pull request #908: Added a DashboardAggregator

Posted by aledsage <gi...@git.apache.org>.
Github user aledsage commented on a diff in the pull request:

    https://github.com/apache/brooklyn-server/pull/908#discussion_r154931647
  
    --- Diff: core/src/main/java/org/apache/brooklyn/enricher/stock/aggregator/AggregationJob.java ---
    @@ -0,0 +1,158 @@
    +/*
    + * 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.brooklyn.enricher.stock.aggregator;
    +
    +import java.util.ArrayList;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +import java.util.Map;
    +
    +import org.apache.commons.lang3.StringUtils;
    +
    +import org.apache.brooklyn.api.entity.Entity;
    +import org.apache.brooklyn.core.sensor.BasicAttributeSensorAndConfigKey;
    +
    +public final class AggregationJob implements Runnable {
    +
    +    public static BasicAttributeSensorAndConfigKey<Map<String, String>> DASHBOARD_COST_PER_MONTH = new BasicAttributeSensorAndConfigKey(Map.class, "dashboard.costPerMonth", "A map of VM ID to monthly cost. Please note if the same VM ID is reported more than once, only one of the sensors will be propagated");
    +    public static BasicAttributeSensorAndConfigKey<List<Map<String, String>>> DASHBOARD_HA_PRIMARIES = new BasicAttributeSensorAndConfigKey(List.class, "dashboard.ha.primaries", "A list of high availability primaries "); //TODO: find out what this is and add a better description
    +    public static BasicAttributeSensorAndConfigKey<List<Map<String, String>>> DASHBOARD_LICENSES = new BasicAttributeSensorAndConfigKey(List.class, "dashboard.licenses", "The licences in use for the running entities. This is a list of maps. The map should contain two keys: product and key");
    +    public static BasicAttributeSensorAndConfigKey<Map<String, List<Map<String, Object>>>> DASHBOARD_LOCATIONS = new BasicAttributeSensorAndConfigKey(Map.class, "dashboard.locations", "Locations in which the VMs are running. A map where the key is category of location (e.g. server) and the value is a list of maps containing name/icon/count");
    --- End diff --
    
    Preference for splitting really long lines over multiple lines.


---

[GitHub] brooklyn-server issue #908: Added a DashboardAggregator

Posted by Graeme-Miller <gi...@git.apache.org>.
Github user Graeme-Miller commented on the issue:

    https://github.com/apache/brooklyn-server/pull/908
  
    test failure fixed, only thing left to decide is whether this is the correct place or not


---

[GitHub] brooklyn-server pull request #908: Added a DashboardAggregator

Posted by aledsage <gi...@git.apache.org>.
Github user aledsage commented on a diff in the pull request:

    https://github.com/apache/brooklyn-server/pull/908#discussion_r154935928
  
    --- Diff: core/src/test/java/org/apache/brooklyn/enricher/stock/aggregator/AggregationJobTest.java ---
    @@ -0,0 +1,423 @@
    +/*
    + * 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.brooklyn.enricher.stock.aggregator;
    +
    +import static org.apache.brooklyn.enricher.stock.aggregator.AggregationJob.DASHBOARD_COST_PER_MONTH;
    +import static org.apache.brooklyn.enricher.stock.aggregator.AggregationJob.DASHBOARD_HA_PRIMARIES;
    +import static org.apache.brooklyn.enricher.stock.aggregator.AggregationJob.DASHBOARD_LICENSES;
    +import static org.apache.brooklyn.enricher.stock.aggregator.AggregationJob.DASHBOARD_LOCATIONS;
    +import static org.apache.brooklyn.enricher.stock.aggregator.AggregationJob.DASHBOARD_POLICY_HIGHLIGHTS;
    +import static org.testng.Assert.assertEquals;
    +
    +import java.util.ArrayList;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +
    +import org.testng.annotations.BeforeMethod;
    +import org.testng.annotations.Test;
    +
    +import org.apache.brooklyn.api.entity.EntitySpec;
    +import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
    +import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
    +import org.apache.brooklyn.core.test.entity.TestApplication;
    +import org.apache.brooklyn.core.test.entity.TestEntity;
    +
    +public class AggregationJobTest {
    +
    +    TestApplication testApplication;
    +    TestEntity parentEntity;
    +    TestEntity childEntityOne;
    +    TestEntity childEntityTwo;
    +
    +    AggregationJob aggregationJob;
    +
    +    LocalManagementContext localManagementContext = LocalManagementContextForTests.newInstance();
    +
    +    @BeforeMethod
    --- End diff --
    
    Preference to always use `@BeforeMethod(alwaysRun=true)`. Then if someone marks one of the tests as `groups="Integeration"` for some reason, it still gets the setup method run.


---

[GitHub] brooklyn-server pull request #908: Added a DashboardAggregator

Posted by aledsage <gi...@git.apache.org>.
Github user aledsage commented on a diff in the pull request:

    https://github.com/apache/brooklyn-server/pull/908#discussion_r154936196
  
    --- Diff: core/src/test/java/org/apache/brooklyn/enricher/stock/aggregator/AggregationJobTest.java ---
    @@ -0,0 +1,423 @@
    +/*
    + * 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.brooklyn.enricher.stock.aggregator;
    +
    +import static org.apache.brooklyn.enricher.stock.aggregator.AggregationJob.DASHBOARD_COST_PER_MONTH;
    +import static org.apache.brooklyn.enricher.stock.aggregator.AggregationJob.DASHBOARD_HA_PRIMARIES;
    +import static org.apache.brooklyn.enricher.stock.aggregator.AggregationJob.DASHBOARD_LICENSES;
    +import static org.apache.brooklyn.enricher.stock.aggregator.AggregationJob.DASHBOARD_LOCATIONS;
    +import static org.apache.brooklyn.enricher.stock.aggregator.AggregationJob.DASHBOARD_POLICY_HIGHLIGHTS;
    +import static org.testng.Assert.assertEquals;
    +
    +import java.util.ArrayList;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +
    +import org.testng.annotations.BeforeMethod;
    +import org.testng.annotations.Test;
    +
    +import org.apache.brooklyn.api.entity.EntitySpec;
    +import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
    +import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
    +import org.apache.brooklyn.core.test.entity.TestApplication;
    +import org.apache.brooklyn.core.test.entity.TestEntity;
    +
    +public class AggregationJobTest {
    --- End diff --
    
    I'd extend `BrooklynAppUnitTestSupport` (and override `setUp()`).


---

[GitHub] brooklyn-server pull request #908: Added a DashboardAggregator

Posted by aledsage <gi...@git.apache.org>.
Github user aledsage commented on a diff in the pull request:

    https://github.com/apache/brooklyn-server/pull/908#discussion_r154935450
  
    --- Diff: core/src/main/java/org/apache/brooklyn/enricher/stock/aggregator/DashboardAggregator.java ---
    @@ -0,0 +1,51 @@
    +/*
    + * 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.brooklyn.enricher.stock.aggregator;
    +
    +import java.util.concurrent.Callable;
    +
    +import org.apache.brooklyn.api.entity.EntityLocal;
    +import org.apache.brooklyn.api.mgmt.Task;
    +import org.apache.brooklyn.core.enricher.AbstractEnricher;
    +import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
    +import org.apache.brooklyn.util.core.task.ScheduledTask;
    +import org.apache.brooklyn.util.core.task.Tasks;
    +import org.apache.brooklyn.util.time.Duration;
    +
    +public class DashboardAggregator extends AbstractEnricher {
    --- End diff --
    
    Should also implement `destroy()`, to cancel the `ScheduledTask`.


---

[GitHub] brooklyn-server pull request #908: Added a DashboardAggregator

Posted by aledsage <gi...@git.apache.org>.
Github user aledsage commented on a diff in the pull request:

    https://github.com/apache/brooklyn-server/pull/908#discussion_r154932730
  
    --- Diff: core/src/main/java/org/apache/brooklyn/enricher/stock/aggregator/AggregationJob.java ---
    @@ -0,0 +1,158 @@
    +/*
    + * 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.brooklyn.enricher.stock.aggregator;
    +
    +import java.util.ArrayList;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +import java.util.Map;
    +
    +import org.apache.commons.lang3.StringUtils;
    +
    +import org.apache.brooklyn.api.entity.Entity;
    +import org.apache.brooklyn.core.sensor.BasicAttributeSensorAndConfigKey;
    +
    +public final class AggregationJob implements Runnable {
    +
    +    public static BasicAttributeSensorAndConfigKey<Map<String, String>> DASHBOARD_COST_PER_MONTH = new BasicAttributeSensorAndConfigKey(Map.class, "dashboard.costPerMonth", "A map of VM ID to monthly cost. Please note if the same VM ID is reported more than once, only one of the sensors will be propagated");
    +    public static BasicAttributeSensorAndConfigKey<List<Map<String, String>>> DASHBOARD_HA_PRIMARIES = new BasicAttributeSensorAndConfigKey(List.class, "dashboard.ha.primaries", "A list of high availability primaries "); //TODO: find out what this is and add a better description
    +    public static BasicAttributeSensorAndConfigKey<List<Map<String, String>>> DASHBOARD_LICENSES = new BasicAttributeSensorAndConfigKey(List.class, "dashboard.licenses", "The licences in use for the running entities. This is a list of maps. The map should contain two keys: product and key");
    +    public static BasicAttributeSensorAndConfigKey<Map<String, List<Map<String, Object>>>> DASHBOARD_LOCATIONS = new BasicAttributeSensorAndConfigKey(Map.class, "dashboard.locations", "Locations in which the VMs are running. A map where the key is category of location (e.g. server) and the value is a list of maps containing name/icon/count");
    +    public static BasicAttributeSensorAndConfigKey<List<Map<String, String>>> DASHBOARD_POLICY_HIGHLIGHTS = new BasicAttributeSensorAndConfigKey(List.class, "dashboard.policyHighlights", "Highlights from policies. List of Masps, where each map should contain text and category");
    +
    +    private final Entity entity;
    +
    +    private static final String NAME_STRING = "name";
    +    private static final String COUNT_STRING = "count";
    +
    +    public AggregationJob(Entity entity) {
    +        this.entity = entity;
    +    }
    +
    +    private void updateFromConfig(Entity entity, BasicAttributeSensorAndConfigKey<List<Map<String, String>>> sensorAndConfigKey, List<Map<String, String>> list) {
    +        List<Map<String, String>> listFromEntity = entity.sensors().get(sensorAndConfigKey);
    +        if (listFromEntity == null || listFromEntity.isEmpty()) {
    +            listFromEntity = entity.config().get(sensorAndConfigKey);
    +        }
    +
    +        if (listFromEntity != null) {
    +            list.addAll(listFromEntity);
    +        }
    +    }
    +
    +    @Override
    +    public void run() {
    +        HashMap<String, String> costPerMonth = new HashMap<>();
    +        List<Map<String, String>> haPrimaries = new ArrayList<>();
    +        List<Map<String, String>> dashboardLicences = new ArrayList<>();
    +        Map<String, List<Map<String, Object>>> dashboardLocations = new HashMap<>();
    +        List<Map<String, String>> dashboardPolicyHighlights = new ArrayList<>();
    +
    +        entity.getChildren().forEach(childEntity -> scanEntity(childEntity, costPerMonth, haPrimaries, dashboardLicences, dashboardLocations, dashboardPolicyHighlights));
    +        entity.sensors().set(DASHBOARD_COST_PER_MONTH, costPerMonth);
    +        entity.sensors().set(DASHBOARD_HA_PRIMARIES, haPrimaries);
    +        entity.sensors().set(DASHBOARD_LICENSES, dashboardLicences);
    +        entity.sensors().set(DASHBOARD_LOCATIONS, dashboardLocations);
    +        entity.sensors().set(DASHBOARD_POLICY_HIGHLIGHTS, dashboardPolicyHighlights);
    +    }
    +
    +    protected void scanEntity(Entity entity,
    --- End diff --
    
    Don't like that the `entity` parameter hides the `entity` field (and means something different).


---

[GitHub] brooklyn-server pull request #908: Added a DashboardAggregator

Posted by Graeme-Miller <gi...@git.apache.org>.
Github user Graeme-Miller commented on a diff in the pull request:

    https://github.com/apache/brooklyn-server/pull/908#discussion_r156983984
  
    --- Diff: core/src/main/java/org/apache/brooklyn/enricher/stock/aggregator/AggregationJob.java ---
    @@ -0,0 +1,158 @@
    +/*
    + * 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.brooklyn.enricher.stock.aggregator;
    +
    +import java.util.ArrayList;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +import java.util.Map;
    +
    +import org.apache.commons.lang3.StringUtils;
    +
    +import org.apache.brooklyn.api.entity.Entity;
    +import org.apache.brooklyn.core.sensor.BasicAttributeSensorAndConfigKey;
    +
    +public final class AggregationJob implements Runnable {
    +
    +    public static BasicAttributeSensorAndConfigKey<Map<String, String>> DASHBOARD_COST_PER_MONTH = new BasicAttributeSensorAndConfigKey(Map.class, "dashboard.costPerMonth", "A map of VM ID to monthly cost. Please note if the same VM ID is reported more than once, only one of the sensors will be propagated");
    +    public static BasicAttributeSensorAndConfigKey<List<Map<String, String>>> DASHBOARD_HA_PRIMARIES = new BasicAttributeSensorAndConfigKey(List.class, "dashboard.ha.primaries", "A list of high availability primaries "); //TODO: find out what this is and add a better description
    +    public static BasicAttributeSensorAndConfigKey<List<Map<String, String>>> DASHBOARD_LICENSES = new BasicAttributeSensorAndConfigKey(List.class, "dashboard.licenses", "The licences in use for the running entities. This is a list of maps. The map should contain two keys: product and key");
    +    public static BasicAttributeSensorAndConfigKey<Map<String, List<Map<String, Object>>>> DASHBOARD_LOCATIONS = new BasicAttributeSensorAndConfigKey(Map.class, "dashboard.locations", "Locations in which the VMs are running. A map where the key is category of location (e.g. server) and the value is a list of maps containing name/icon/count");
    +    public static BasicAttributeSensorAndConfigKey<List<Map<String, String>>> DASHBOARD_POLICY_HIGHLIGHTS = new BasicAttributeSensorAndConfigKey(List.class, "dashboard.policyHighlights", "Highlights from policies. List of Masps, where each map should contain text and category");
    +
    +    private final Entity entity;
    +
    +    private static final String NAME_STRING = "name";
    +    private static final String COUNT_STRING = "count";
    +
    +    public AggregationJob(Entity entity) {
    +        this.entity = entity;
    +    }
    +
    +    private void updateFromConfig(Entity entity, BasicAttributeSensorAndConfigKey<List<Map<String, String>>> sensorAndConfigKey, List<Map<String, String>> list) {
    +        List<Map<String, String>> listFromEntity = entity.sensors().get(sensorAndConfigKey);
    +        if (listFromEntity == null || listFromEntity.isEmpty()) {
    +            listFromEntity = entity.config().get(sensorAndConfigKey);
    +        }
    +
    +        if (listFromEntity != null) {
    +            list.addAll(listFromEntity);
    +        }
    +    }
    +
    +    @Override
    +    public void run() {
    +        HashMap<String, String> costPerMonth = new HashMap<>();
    +        List<Map<String, String>> haPrimaries = new ArrayList<>();
    +        List<Map<String, String>> dashboardLicences = new ArrayList<>();
    +        Map<String, List<Map<String, Object>>> dashboardLocations = new HashMap<>();
    +        List<Map<String, String>> dashboardPolicyHighlights = new ArrayList<>();
    +
    +        entity.getChildren().forEach(childEntity -> scanEntity(childEntity, costPerMonth, haPrimaries, dashboardLicences, dashboardLocations, dashboardPolicyHighlights));
    +        entity.sensors().set(DASHBOARD_COST_PER_MONTH, costPerMonth);
    +        entity.sensors().set(DASHBOARD_HA_PRIMARIES, haPrimaries);
    +        entity.sensors().set(DASHBOARD_LICENSES, dashboardLicences);
    +        entity.sensors().set(DASHBOARD_LOCATIONS, dashboardLocations);
    +        entity.sensors().set(DASHBOARD_POLICY_HIGHLIGHTS, dashboardPolicyHighlights);
    +    }
    +
    +    protected void scanEntity(Entity entity,
    +                              Map<String, String> costPerMonth,
    +                              List<Map<String, String>> haPrimaries,
    +                              List<Map<String, String>> licences,
    +                              Map<String, List<Map<String, Object>>> locations,
    +                              List<Map<String, String>> policyHighlights) {
    +
    +        updateFromConfig(entity, DASHBOARD_HA_PRIMARIES, haPrimaries);
    +        updateFromConfig(entity, DASHBOARD_LICENSES, licences);
    +        updateFromConfig(entity, DASHBOARD_POLICY_HIGHLIGHTS, policyHighlights);
    +
    +        //Cost per month
    +        Map<String, String> costPerMonthFromEntity = entity.sensors().get(DASHBOARD_COST_PER_MONTH);
    +        if (costPerMonthFromEntity == null || costPerMonthFromEntity.isEmpty()) {
    +            costPerMonthFromEntity = entity.config().get(DASHBOARD_COST_PER_MONTH);
    +        }
    +
    +        if (costPerMonthFromEntity != null) {
    +            costPerMonth.putAll(costPerMonthFromEntity);
    +        }
    +
    +        //Locations merge
    +        //We are merging a Map, of Lists of Maps
    +        //The outer map is location type, e.g. servers to a list of data for that type
    +        //The inner map should contain the data e.g. name/icon/count.
    +
    +        //Further complicating this is that the Map you get back from sensors/config doesn't conform to the generic expectations.
    +        //I.E. even if you type this as Map<String, List<Map<String, String>>> you will still get back an integer from
    +        //the inner map- hence all the weird casting bellow.
    +        Map<String, List<Map<String, Object>>> outerLocationMapFromEntity = entity.sensors().get(DASHBOARD_LOCATIONS);
    --- End diff --
    
    That hasn't been agreed yet, but your suggestion seems sensible


---

[GitHub] brooklyn-server pull request #908: Added a DashboardAggregator

Posted by aledsage <gi...@git.apache.org>.
Github user aledsage commented on a diff in the pull request:

    https://github.com/apache/brooklyn-server/pull/908#discussion_r157784517
  
    --- Diff: core/src/main/java/org/apache/brooklyn/enricher/stock/aggregator/AggregationJob.java ---
    @@ -0,0 +1,172 @@
    +/*
    + * 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.brooklyn.enricher.stock.aggregator;
    +
    +import java.util.ArrayList;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +import java.util.Map;
    +
    +import org.apache.commons.lang3.StringUtils;
    +
    +import org.apache.brooklyn.api.entity.Entity;
    +import org.apache.brooklyn.core.sensor.BasicAttributeSensorAndConfigKey;
    +
    +public final class AggregationJob implements Runnable {
    --- End diff --
    
    Let's mark this `@Beta` - same for the other classes.


---

[GitHub] brooklyn-server issue #908: Added a DashboardAggregator

Posted by aledsage <gi...@git.apache.org>.
Github user aledsage commented on the issue:

    https://github.com/apache/brooklyn-server/pull/908
  
    Test failure looks real:
    ```
    java.lang.IllegalStateException: Management context no longer running
    	at org.apache.brooklyn.enricher.stock.aggregator.AggregationJobTest.testSetup(AggregationJobTest.java:55)
    ```


---

[GitHub] brooklyn-server pull request #908: Added a DashboardAggregator

Posted by aledsage <gi...@git.apache.org>.
Github user aledsage commented on a diff in the pull request:

    https://github.com/apache/brooklyn-server/pull/908#discussion_r154933984
  
    --- Diff: core/src/main/java/org/apache/brooklyn/enricher/stock/aggregator/AggregationJob.java ---
    @@ -0,0 +1,158 @@
    +/*
    + * 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.brooklyn.enricher.stock.aggregator;
    +
    +import java.util.ArrayList;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +import java.util.Map;
    +
    +import org.apache.commons.lang3.StringUtils;
    +
    +import org.apache.brooklyn.api.entity.Entity;
    +import org.apache.brooklyn.core.sensor.BasicAttributeSensorAndConfigKey;
    +
    +public final class AggregationJob implements Runnable {
    +
    +    public static BasicAttributeSensorAndConfigKey<Map<String, String>> DASHBOARD_COST_PER_MONTH = new BasicAttributeSensorAndConfigKey(Map.class, "dashboard.costPerMonth", "A map of VM ID to monthly cost. Please note if the same VM ID is reported more than once, only one of the sensors will be propagated");
    +    public static BasicAttributeSensorAndConfigKey<List<Map<String, String>>> DASHBOARD_HA_PRIMARIES = new BasicAttributeSensorAndConfigKey(List.class, "dashboard.ha.primaries", "A list of high availability primaries "); //TODO: find out what this is and add a better description
    +    public static BasicAttributeSensorAndConfigKey<List<Map<String, String>>> DASHBOARD_LICENSES = new BasicAttributeSensorAndConfigKey(List.class, "dashboard.licenses", "The licences in use for the running entities. This is a list of maps. The map should contain two keys: product and key");
    +    public static BasicAttributeSensorAndConfigKey<Map<String, List<Map<String, Object>>>> DASHBOARD_LOCATIONS = new BasicAttributeSensorAndConfigKey(Map.class, "dashboard.locations", "Locations in which the VMs are running. A map where the key is category of location (e.g. server) and the value is a list of maps containing name/icon/count");
    +    public static BasicAttributeSensorAndConfigKey<List<Map<String, String>>> DASHBOARD_POLICY_HIGHLIGHTS = new BasicAttributeSensorAndConfigKey(List.class, "dashboard.policyHighlights", "Highlights from policies. List of Masps, where each map should contain text and category");
    +
    +    private final Entity entity;
    +
    +    private static final String NAME_STRING = "name";
    +    private static final String COUNT_STRING = "count";
    +
    +    public AggregationJob(Entity entity) {
    +        this.entity = entity;
    +    }
    +
    +    private void updateFromConfig(Entity entity, BasicAttributeSensorAndConfigKey<List<Map<String, String>>> sensorAndConfigKey, List<Map<String, String>> list) {
    +        List<Map<String, String>> listFromEntity = entity.sensors().get(sensorAndConfigKey);
    +        if (listFromEntity == null || listFromEntity.isEmpty()) {
    +            listFromEntity = entity.config().get(sensorAndConfigKey);
    +        }
    +
    +        if (listFromEntity != null) {
    +            list.addAll(listFromEntity);
    +        }
    +    }
    +
    +    @Override
    +    public void run() {
    +        HashMap<String, String> costPerMonth = new HashMap<>();
    +        List<Map<String, String>> haPrimaries = new ArrayList<>();
    +        List<Map<String, String>> dashboardLicences = new ArrayList<>();
    +        Map<String, List<Map<String, Object>>> dashboardLocations = new HashMap<>();
    +        List<Map<String, String>> dashboardPolicyHighlights = new ArrayList<>();
    +
    +        entity.getChildren().forEach(childEntity -> scanEntity(childEntity, costPerMonth, haPrimaries, dashboardLicences, dashboardLocations, dashboardPolicyHighlights));
    +        entity.sensors().set(DASHBOARD_COST_PER_MONTH, costPerMonth);
    +        entity.sensors().set(DASHBOARD_HA_PRIMARIES, haPrimaries);
    +        entity.sensors().set(DASHBOARD_LICENSES, dashboardLicences);
    +        entity.sensors().set(DASHBOARD_LOCATIONS, dashboardLocations);
    +        entity.sensors().set(DASHBOARD_POLICY_HIGHLIGHTS, dashboardPolicyHighlights);
    +    }
    +
    +    protected void scanEntity(Entity entity,
    +                              Map<String, String> costPerMonth,
    +                              List<Map<String, String>> haPrimaries,
    +                              List<Map<String, String>> licences,
    +                              Map<String, List<Map<String, Object>>> locations,
    +                              List<Map<String, String>> policyHighlights) {
    +
    +        updateFromConfig(entity, DASHBOARD_HA_PRIMARIES, haPrimaries);
    +        updateFromConfig(entity, DASHBOARD_LICENSES, licences);
    +        updateFromConfig(entity, DASHBOARD_POLICY_HIGHLIGHTS, policyHighlights);
    +
    +        //Cost per month
    +        Map<String, String> costPerMonthFromEntity = entity.sensors().get(DASHBOARD_COST_PER_MONTH);
    +        if (costPerMonthFromEntity == null || costPerMonthFromEntity.isEmpty()) {
    +            costPerMonthFromEntity = entity.config().get(DASHBOARD_COST_PER_MONTH);
    +        }
    +
    +        if (costPerMonthFromEntity != null) {
    +            costPerMonth.putAll(costPerMonthFromEntity);
    +        }
    +
    +        //Locations merge
    +        //We are merging a Map, of Lists of Maps
    +        //The outer map is location type, e.g. servers to a list of data for that type
    +        //The inner map should contain the data e.g. name/icon/count.
    +
    +        //Further complicating this is that the Map you get back from sensors/config doesn't conform to the generic expectations.
    +        //I.E. even if you type this as Map<String, List<Map<String, String>>> you will still get back an integer from
    +        //the inner map- hence all the weird casting bellow.
    +        Map<String, List<Map<String, Object>>> outerLocationMapFromEntity = entity.sensors().get(DASHBOARD_LOCATIONS);
    --- End diff --
    
    I assumed if we were going to look at locations, we'd do `entity.getLocations()` or some such. Is it expected that some enricher is added at each leaf node will set the `DASHBOARD_LOCATIONS` sensor on that entity?


---

[GitHub] brooklyn-server pull request #908: Added a DashboardAggregator

Posted by aledsage <gi...@git.apache.org>.
Github user aledsage commented on a diff in the pull request:

    https://github.com/apache/brooklyn-server/pull/908#discussion_r157783869
  
    --- Diff: core/src/main/java/org/apache/brooklyn/enricher/stock/aggregator/DashboardAggregator.java ---
    @@ -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.brooklyn.enricher.stock.aggregator;
    +
    +import java.util.Map;
    +import java.util.concurrent.Callable;
    +
    +import org.apache.brooklyn.api.entity.EntityLocal;
    +import org.apache.brooklyn.api.mgmt.Task;
    +import org.apache.brooklyn.core.enricher.AbstractEnricher;
    +import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
    +import org.apache.brooklyn.core.sensor.BasicAttributeSensorAndConfigKey;
    +import org.apache.brooklyn.util.core.task.ScheduledTask;
    +import org.apache.brooklyn.util.core.task.Tasks;
    +import org.apache.brooklyn.util.time.Duration;
    +
    +/**
    + * The DashboardAggregator is an enricher that combines config/sensor values from the children of the entity it is attached to.
    + * The combined values are set as sensors on the entity that it is attached to. Preference is given to the child entities sensors values,
    + * but if none are set, config values are used instead.
    + *
    + * The reason that this exists is to provide high level summary information, that could be useful to display on a dashboard.
    + * Whilst brooklyn itself has no such dashboard, we can imagine this entity being used in that fashion.
    + *
    + * Please note, that the DashboardAggregator will aggregate all children of the entity it is attached to, even intermediate level children.
    + * Therefore, please only ever attach one DashboardAggregator to the top most entity.
    + *
    + * For a detailed list of the config that is combined, please see the AggregationJob class.
    + *
    + */
    +public class DashboardAggregator extends AbstractEnricher {
    +
    +    private ScheduledTask task;
    +
    +    public static BasicAttributeSensorAndConfigKey<Duration> DASHBOARD_COST_PER_MONTH = new BasicAttributeSensorAndConfigKey(Duration.class,
    +            "dashboard.period",
    +            "The amount of time to wait between aggregation jobs",
    +            Duration.seconds(1));
    +
    +    @SuppressWarnings("unchecked")
    +    @Override
    +    public void setEntity(EntityLocal entity) {
    +        super.setEntity(entity);
    +
    +        Duration duration = config().get(DASHBOARD_COST_PER_MONTH);
    +
    +        Callable<Task<?>> taskFactory = () -> Tasks.builder()
    +                .dynamic(false)
    +                .body(new AggregationJob(entity))
    +                .displayName("DashboardAggregator task")
    +                .tag(BrooklynTaskTags.TRANSIENT_TASK_TAG)
    +                .description("Retrieves and aggregates sensor values")
    +                .build();
    +
    +        task = ScheduledTask.builder(taskFactory).period(duration).displayName("scheduled:[DashboardAggregator task]").tagTransient().build();
    +        this.getManagementContext().getExecutionManager().submit(task);
    +
    +    }
    +
    +    @Override
    +    public void destroy() {
    +        super.destroy();
    +        task.cancel();
    --- End diff --
    
    guard with `if (task != null)`.


---

[GitHub] brooklyn-server pull request #908: Added a DashboardAggregator

Posted by asfgit <gi...@git.apache.org>.
Github user asfgit closed the pull request at:

    https://github.com/apache/brooklyn-server/pull/908


---