Skip to content

Commit c2eb3ec

Browse files
committed
CLOUDSTACK-8677: Call-home functionality for CloudStack
With this commit the Management Server will be default generate a anonymous Usage report every 7 (seven) days and submit this information back to the Apache CloudStack project. These anonymous reports do NOT contain any information about Instance names, subnets, etc. It only contains numbers about how CloudStack is being used. This information is vital for the project to gain more insight in how CloudStack is being used. Users can turn the reporting off by setting usage.report.interval to 0 (zero)
1 parent fddf59f commit c2eb3ec

12 files changed

Lines changed: 875 additions & 3 deletions

File tree

core/src/com/cloud/agent/transport/ArrayTypeAdaptor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ public T[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContex
8080
T cmd = (T)_gson.fromJson(entry.getValue(), clazz);
8181
cmds.add(cmd);
8282
}
83-
Class<?> type = ((Class<?>)typeOfT).getComponentType();
83+
Class<?> type = (Class<?>)com.google.gson.internal.$Gson$Types.getArrayComponentType(typeOfT);
8484
T[] ts = (T[])Array.newInstance(type, cmds.size());
8585
return cmds.toArray(ts);
8686
}
87-
}
87+
}

engine/schema/src/com/cloud/upgrade/dao/VersionDao.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,15 @@
1616
// under the License.
1717
package com.cloud.upgrade.dao;
1818

19+
import java.util.List;
20+
1921
import com.cloud.upgrade.dao.VersionVO.Step;
2022
import com.cloud.utils.db.GenericDao;
2123

2224
public interface VersionDao extends GenericDao<VersionVO, Long> {
2325
VersionVO findByVersion(String version, Step step);
2426

2527
String getCurrentVersion();
28+
29+
List<VersionVO> getAllVersions();
2630
}

engine/schema/src/com/cloud/upgrade/dao/VersionDaoImpl.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,13 @@ public String getCurrentVersion() {
154154
}
155155

156156
}
157+
158+
@Override
159+
@DB
160+
public List<VersionVO> getAllVersions() {
161+
SearchCriteria<VersionVO> sc = AllFieldsSearch.create();
162+
sc.setParameters("step", "Complete");
163+
164+
return listBy(sc);
165+
}
157166
}

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@
7373
<cs.jasypt.version>1.9.2</cs.jasypt.version>
7474
<cs.trilead.version>1.0.0-build217</cs.trilead.version>
7575
<cs.ehcache.version>2.6.9</cs.ehcache.version>
76-
<cs.gson.version>1.7.2</cs.gson.version>
7776
<cs.guava-testlib.version>18.0</cs.guava-testlib.version>
77+
<cs.gson.version>2.3.1</cs.gson.version>
7878
<cs.guava.version>18.0</cs.guava.version>
7979
<cs.xapi.version>6.2.0-3.1</cs.xapi.version>
8080
<cs.httpclient.version>4.5</cs.httpclient.version>

reporter/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# CloudStack Usage Report
2+
3+
This directory contains the CloudStack reporter webservice used by the Apache CloudStack project
4+
to gather anonymous statistical information about CloudStack deployments.
5+
6+
Since version 4.7 the management server sends out a anonymized Usage Report out to the
7+
project every 7 days.
8+
9+
This information is used to gain information about how CloudStack is being used.
10+
11+
Turning this Usage Reporting functionality off can be done in the Global Settings by setting
12+
'usage.report.interval' to 0.
13+
14+
For more information visit http://cloudstack.apache.org/call-home.html
15+
16+
# The webservice
17+
The Python Flask application in this directory is the webservice running on https://reporting.cloudstack.apache.org/report
18+
and stores all the incoming information in a ElasticSearch database.
19+
20+
Since Apache CloudStack is Open Source we show not only how we generate the report, but also how we process it.

reporter/usage-report-collector.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env python
2+
# Licensed to the Apache Software Foundation (ASF) under one
3+
# or more contributor license agreements. See the NOTICE file
4+
# distributed with this work for additional information
5+
# regarding copyright ownership. The ASF licenses this file
6+
# to you under the Apache License, Version 2.0 (the
7+
# "License"); you may not use this file except in compliance
8+
# with the License. You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing,
13+
# software distributed under the License is distributed on an
14+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
# KIND, either express or implied. See the License for the
16+
# specific language governing permissions and limitations
17+
# under the License.
18+
19+
from flask import abort, Flask, request, Response
20+
from elasticsearch import Elasticsearch
21+
import json
22+
import time
23+
24+
def json_response(response):
25+
return json.dumps(response, indent=2) + "\n", 200, {'Content-Type': 'application/json; charset=utf-8'}
26+
27+
def generate_app(config=None):
28+
app = Flask(__name__)
29+
30+
@app.route('/report/<unique_id>', methods=['POST'])
31+
def report(unique_id):
32+
# We expect JSON data, so if the Content-Type doesn't match JSON data we throw an error
33+
if 'Content-Type' in request.headers:
34+
if request.headers['Content-Type'] != 'application/json':
35+
abort(417, "No or incorrect Content-Type header was supplied")
36+
37+
index = "cloudstack-%s" % time.strftime("%Y.%m.%d", time.gmtime())
38+
timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
39+
40+
es = Elasticsearch()
41+
es.indices.create(index=index, ignore=400)
42+
43+
report = json.loads(request.data)
44+
report["unique_id"] = unique_id
45+
report["timestamp"] = timestamp
46+
47+
es.index(index=index, doc_type="usage-report", body=json.dumps(report), timestamp=timestamp, refresh=True)
48+
49+
response = {}
50+
return json_response(response)
51+
52+
return app
53+
54+
55+
app = generate_app()
56+
57+
# Only run the App if this script is invoked from a Shell
58+
if __name__ == '__main__':
59+
app.debug = True
60+
app.run(host='0.0.0.0', port=8088)
61+
62+
# Otherwise provide a variable called 'application' for mod_wsgi
63+
else:
64+
application = app

server/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,16 @@
140140
<artifactId>opensaml</artifactId>
141141
<version>${cs.opensaml.version}</version>
142142
</dependency>
143+
<dependency>
144+
<groupId>com.google.code.gson</groupId>
145+
<artifactId>gson</artifactId>
146+
<version>${cs.gson.version}</version>
147+
</dependency>
148+
<dependency>
149+
<groupId>com.google.guava</groupId>
150+
<artifactId>guava</artifactId>
151+
<version>${cs.guava.version}</version>
152+
</dependency>
143153
</dependencies>
144154
<build>
145155
<testResources>

server/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@
224224

225225
<bean id="statsCollector" class="com.cloud.server.StatsCollector" />
226226

227+
<bean id="usageReporter" class="org.apache.cloudstack.report.UsageReporter" />
228+
227229
<bean id="storagePoolAutomationImpl" class="com.cloud.storage.StoragePoolAutomationImpl" />
228230

229231
<bean id="domainManagerImpl" class="com.cloud.user.DomainManagerImpl" />
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.report;
18+
19+
import com.google.gson.TypeAdapter;
20+
import com.google.gson.stream.JsonReader;
21+
import com.google.gson.stream.JsonWriter;
22+
import com.google.common.util.concurrent.AtomicLongMap;
23+
import java.util.Map;
24+
import java.io.IOException;
25+
26+
public class AtomicGsonAdapter extends TypeAdapter<AtomicLongMap> {
27+
28+
public AtomicLongMap<Object> read(JsonReader reader) throws IOException {
29+
reader.nextNull();
30+
return null;
31+
}
32+
33+
public void write(JsonWriter writer, AtomicLongMap value) throws IOException {
34+
if (value == null) {
35+
writer.nullValue();
36+
return;
37+
}
38+
39+
@SuppressWarnings("unchecked")
40+
Map <String, Long> map = value.asMap();
41+
42+
writer.beginObject();
43+
for (Map.Entry<String, Long> entry : map.entrySet()) {
44+
writer.name(entry.getKey()).value(entry.getValue());
45+
}
46+
writer.endObject();
47+
}
48+
}

0 commit comments

Comments
 (0)