Merge branch 'master' into feature/ceilometer
diff --git a/xos/configurations/cord/README-VTN.md b/xos/configurations/cord/README-VTN.md
index a8feeac..2d9b7aa 100644
--- a/xos/configurations/cord/README-VTN.md
+++ b/xos/configurations/cord/README-VTN.md
@@ -4,7 +4,7 @@
 
 VTN doesn't seem to like cloudlab's networks (flat-net-1, ext-net, etc). I've placed a script in xos/scripts/ called destroy-all-networks.sh that will automate tearing down all of cloudlab's neutron networks.
 
-    cd xos/scripts
+    cd xos/tools
     ./destroy-all-networks.sh
 
 inside the xos container, update the configuration. Make sure to restart Openstack Synchronizer afterward. Might be a good idea to restart the XOS UI as well:
@@ -61,7 +61,7 @@
     ovs-vsctl del-br br-flat-lan-1
     ip addr add <addr-that-was-assinged-to-flat-lan-1> dev br-int
     ip link set br-int up
-    ip route add <network-that-was-assigned-to-flat-lan-1> dev br-int
+    ip route add <network-that-was-assigned-to-flat-lan-1>/24 dev br-int
     
 To get the management network working, we need to create management network template, slice, and network. configurations/cord/vtn.yaml will do this for you. Then add a connection to the management network for any slice that needs management connectivity. Note the subnet that gets assigned to the management network. Management-gateway-ip is the .1 address on the subnet. On the compute node:
 
diff --git a/xos/configurations/cord/ceilometer.yaml b/xos/configurations/cord/ceilometer.yaml
index 35922cf..6fcd132 100644
--- a/xos/configurations/cord/ceilometer.yaml
+++ b/xos/configurations/cord/ceilometer.yaml
@@ -58,6 +58,52 @@
               default: 33333
               description: sFlow publish subscribe api listening port
 
+    tosca.nodes.CeilometerService:
+        derived_from: tosca.nodes.Root
+        description: >
+            XOS Ceilometer Service
+        capabilities:
+            scalable:
+                type: tosca.capabilities.Scalable
+            service:
+                type: tosca.capabilities.xos.Service
+        properties:
+            kind:
+                type: string
+                default: generic
+                description: Type of service.
+            view_url:
+                type: string
+                required: false
+                description: URL to follow when icon is clicked in the Service Directory.
+            icon_url:
+                type: string
+                required: false
+                description: ICON to display in the Service Directory.
+            enabled:
+                type: boolean
+                default: true
+            published:
+                type: boolean
+                default: true
+                description: If True then display this Service in the Service Directory.
+            public_key:
+                type: string
+                required: false
+                description: Public key to install into Instances to allows Services to SSH into them.
+            private_key_fn:
+                type: string
+                required: false
+                description: Location of private key file
+            versionNumber:
+                type: string
+                required: false
+                description: Version number of Service.
+            ceilometer_pub_sub_url:
+                type: string
+                required: false
+                description: REST URL of ceilometer PUB/SUB component
+
     tosca.nodes.CeilometerTenant:
         derived_from: tosca.nodes.Root
         description: >
@@ -71,11 +117,12 @@
 topology_template:
   node_templates:
     service_ceilometer:
-      type: tosca.nodes.Service
+      type: tosca.nodes.CeilometerService
       requirements:
       properties:
           view_url: /admin/ceilometer/ceilometerservice/$id$/
           kind: ceilometer
+          ceilometer_pub_sub_url: http://10.11.10.1:4455/
 #          public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
 #      artifacts:
 #          pubkey: /opt/xos/synchronizers/vcpe/vcpe_public_key
diff --git a/xos/configurations/cord/openstack_ceilometer_patch.tar.gz b/xos/configurations/cord/openstack_ceilometer_patch.tar.gz
index 6a6ffa7..2c4f02c 100644
--- a/xos/configurations/cord/openstack_ceilometer_patch.tar.gz
+++ b/xos/configurations/cord/openstack_ceilometer_patch.tar.gz
Binary files differ
diff --git a/xos/core/xoslib/methods/ceilometerview.py b/xos/core/xoslib/methods/ceilometerview.py
index a633819..f26d84a 100644
--- a/xos/core/xoslib/methods/ceilometerview.py
+++ b/xos/core/xoslib/methods/ceilometerview.py
@@ -57,6 +57,9 @@
                 else:
                     logger.warn("SRIKANTH: Slice %(slice)s is not associated with any service" % {'slice':cs.slice.name})
                     tenantmap[cs.tenant_id]["service"] = "Other"
+    #TEMPORARY WORK AROUND: There are some resource in network like whitebox switches does not belong to a specific tenant.
+    #They are all associated with "default_admin_tenant" tenant
+    tenantmap["default_admin_tenant"] = {"slice": "default_admin_tenant", "service": "Other"}
     return tenantmap
 
 def build_url(path, q, params=None):
@@ -1094,9 +1097,19 @@
         tenant_ceilometer_url = getTenantCeilometerProxyURL(request.user)
         if (not tenant_ceilometer_url):
             raise XOSMissingField("Tenant ceilometer URL is missing")
+
+        tenant_id = request.QUERY_PARAMS.get('tenant', None)
+        resource_id = request.QUERY_PARAMS.get('resource', None)
+
+        query = []
+        if tenant_id:
+            query.extend(make_query(tenant_id=tenant_id))
+        if resource_id:
+            query.extend(make_query(resource_id=resource_id))
+
         tenant_map = getTenantControllerTenantMap(request.user)
-        resource_map = get_resource_map(request, ceilometer_url=tenant_ceilometer_url)
-        meters = Meters(request, ceilometer_url=tenant_ceilometer_url, tenant_map=tenant_map, resource_map=resource_map)
+        resource_map = get_resource_map(request, ceilometer_url=tenant_ceilometer_url, query=query)
+        meters = Meters(request, ceilometer_url=tenant_ceilometer_url, query=query, tenant_map=tenant_map, resource_map=resource_map)
         services = {
             _('Nova'): meters.list_nova(),
             _('Neutron'): meters.list_neutron(),
@@ -1165,7 +1178,7 @@
             return Response(row)
 
         #Statistics query for all meter
-        resource_map = get_resource_map(request, ceilometer_url=tenant_ceilometer_url)
+        resource_map = get_resource_map(request, ceilometer_url=tenant_ceilometer_url, query=query)
         meters = Meters(request, ceilometer_url=tenant_ceilometer_url, query=query, tenant_map=tenant_map, resource_map=resource_map)
         services = {
             _('Nova'): meters.list_nova(),
@@ -1239,6 +1252,24 @@
                      sample["resource_name"] = sample["resource_id"]
         return Response(samples)
 
+class XOSSliceServiceList(APIView):
+    method_kind = "list"
+    method_name = "xos-slice-service-mapping"
+
+    def get(self, request, format=None):
+        if (not request.user.is_authenticated()):
+            raise PermissionDenied("You must be authenticated in order to use this API")
+        tenant_map = getTenantControllerTenantMap(request.user)
+        service_map={}
+        for k,v in tenant_map.iteritems():
+            if not (v['service'] in service_map.keys()):
+                service_map[v['service']] = {}
+                service_map[v['service']]['service'] = v['service']
+                service_map[v['service']]['slices'] = []
+            slice_details = {'slice':v['slice'], 'project_id':k}
+            service_map[v['service']]['slices'].append(slice_details)
+        return Response(service_map.values())
+
 class XOSInstanceStatisticsList(APIView):
     method_kind = "list"
     method_name = "xos-instance-statistics"
@@ -1295,9 +1326,15 @@
             #Statistics query for all meter
             resource_map = get_resource_map(request, ceilometer_url=tenant_ceilometer_url, query=query)
             meters = Meters(request, ceilometer_url=tenant_ceilometer_url, query=query, tenant_map=tenant_map, resource_map=resource_map)
+            exclude_nova_meters_info = [ "instance", "instance:<type>", "disk.read.requests", "disk.write.requests",
+                "disk.read.bytes", "disk.write.bytes", "disk.read.requests.rate", "disk.write.requests.rate", "disk.read.bytes.rate",
+                "disk.write.bytes.rate", "disk.root.size", "disk.ephemeral.size"]
+            exclude_neutron_meters_info = [ 'network.create', 'network.update', 'subnet.create',
+                'subnet.update', 'port.create', 'port.update', 'router.create', 'router.update',
+                'ip.floating.create', 'ip.floating.update']
             services = {
-                _('Nova'): meters.list_nova(),
-                _('Neutron'): meters.list_neutron(),
+                _('Nova'): meters.list_nova(except_meters=exclude_nova_meters_info),
+                _('Neutron'): meters.list_neutron(except_meters=exclude_neutron_meters_info),
                 _('VCPE'): meters.list_vcpe(),
                 _('SDN'): meters.list_sdn(),
             }
diff --git a/xos/services/ceilometer/admin.py b/xos/services/ceilometer/admin.py
index 73b205e..062a2ae 100644
--- a/xos/services/ceilometer/admin.py
+++ b/xos/services/ceilometer/admin.py
@@ -18,15 +18,32 @@
 from django.core.urlresolvers import reverse
 from django.contrib.admin.utils import quote
 
+class CeilometerServiceForm(forms.ModelForm):
+    ceilometer_pub_sub_url = forms.CharField(required=False, max_length=1024, help_text="REST URL of ceilometer PUB/SUB component in http://IP:port/ format")
+
+    def __init__(self,*args,**kwargs):
+        super (CeilometerServiceForm,self ).__init__(*args,**kwargs)
+        if self.instance:
+            # fields for the attributes
+            self.fields['ceilometer_pub_sub_url'].initial = self.instance.ceilometer_pub_sub_url
+
+    def save(self, commit=True):
+        self.instance.ceilometer_pub_sub_url = self.cleaned_data.get("ceilometer_pub_sub_url")
+        return super(CeilometerServiceForm, self).save(commit=commit)
+
+    class Meta:
+        model = CeilometerService
+
 class CeilometerServiceAdmin(ReadOnlyAwareAdmin):
     model = CeilometerService
     verbose_name = "Ceilometer Service"
     verbose_name_plural = "Ceilometer Service"
     list_display = ("backend_status_icon", "name", "enabled")
     list_display_links = ('backend_status_icon', 'name', )
-    fieldsets = [(None, {'fields': ['backend_status_text', 'name','enabled','versionNumber', 'description',"view_url","icon_url" ], 'classes':['suit-tab suit-tab-general']})]
+    fieldsets = [(None, {'fields': ['backend_status_text', 'name','enabled','versionNumber', 'description','ceilometer_pub_sub_url', "view_url","icon_url" ], 'classes':['suit-tab suit-tab-general']})]
     readonly_fields = ('backend_status_text', )
     inlines = [SliceInline,ServiceAttrAsTabInline,ServicePrivilegeInline]
+    form = CeilometerServiceForm
 
     extracontext_registered_admins = True
 
diff --git a/xos/services/ceilometer/models.py b/xos/services/ceilometer/models.py
index 91d9716..42734de 100644
--- a/xos/services/ceilometer/models.py
+++ b/xos/services/ceilometer/models.py
@@ -22,6 +22,14 @@
         verbose_name = "Ceilometer Service"
         proxy = True
 
+    @property
+    def ceilometer_pub_sub_url(self):
+        return self.get_attribute("ceilometer_pub_sub_url", None)
+
+    @ceilometer_pub_sub_url.setter
+    def ceilometer_pub_sub_url(self, value):
+        self.set_attribute("ceilometer_pub_sub_url", value)
+
 class MonitoringChannel(TenantWithContainer):   # aka 'CeilometerTenant'
     class Meta:
         proxy = True
diff --git a/xos/synchronizers/monitoring_channel/steps/sync_monitoringchannel.py b/xos/synchronizers/monitoring_channel/steps/sync_monitoringchannel.py
index db7d318..2c0ba10 100644
--- a/xos/synchronizers/monitoring_channel/steps/sync_monitoringchannel.py
+++ b/xos/synchronizers/monitoring_channel/steps/sync_monitoringchannel.py
@@ -10,7 +10,7 @@
 from synchronizers.base.ansible import run_template_ssh
 from synchronizers.base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible
 from core.models import Service, Slice
-from services.ceilometer.models import MonitoringChannel
+from services.ceilometer.models import CeilometerService, MonitoringChannel
 from xos.logger import Logger, logging
 
 parentdir = os.path.join(os.path.dirname(__file__),"..")
@@ -42,6 +42,13 @@
         #   2) Ceilometer API service endpoint URL if running externally
         #   3) Credentials to access Ceilometer API service
 
+        ceilometer_services = CeilometerService.get_service_objects().filter(id=o.provider_service.id)
+        if not ceilometer_services:
+            raise "No associated Ceilometer service"
+        ceilometer_service = ceilometer_services[0]
+        ceilometer_pub_sub_url = ceilometer_service.ceilometer_pub_sub_url
+        if not ceilometer_pub_sub_url:
+            ceilometer_pub_sub_url = ''
         instance = self.get_instance(o)
 
         try:
@@ -55,6 +62,7 @@
                   "admin_user":instance.controller.admin_user,
                   "admin_password":instance.controller.admin_password,
                   "admin_tenant":instance.controller.admin_tenant,
+                  "ceilometer_pub_sub_url": ceilometer_pub_sub_url,
                   "full_setup": full_setup}
 
         return fields
diff --git a/xos/synchronizers/monitoring_channel/steps/sync_monitoringchannel.yaml b/xos/synchronizers/monitoring_channel/steps/sync_monitoringchannel.yaml
index cedf3a4..6cda511 100644
--- a/xos/synchronizers/monitoring_channel/steps/sync_monitoringchannel.yaml
+++ b/xos/synchronizers/monitoring_channel/steps/sync_monitoringchannel.yaml
@@ -16,6 +16,7 @@
       ceilometer_client_acess_ip: {{ ceilometer_ip }}
       ceilometer_client_acess_mac: {{ ceilometer_mac }}
       ceilometer_host_port: {{ ceilometer_port }}
+      ceilometer_pub_sub_url: {{ ceilometer_pub_sub_url }}
       allowed_tenant_ids:
         {% for allowed_tenant_id in allowed_tenant_ids %}
         - {{ allowed_tenant_id }}
diff --git a/xos/synchronizers/monitoring_channel/steps/sync_sflowservice.yaml b/xos/synchronizers/monitoring_channel/steps/sync_sflowservice.yaml
index b0b3f50..8d853a2 100644
--- a/xos/synchronizers/monitoring_channel/steps/sync_sflowservice.yaml
+++ b/xos/synchronizers/monitoring_channel/steps/sync_sflowservice.yaml
@@ -53,7 +53,7 @@
       image: srikanthvavila/sflowpubsub
       expose:
       - {{ sflow_api_port }}
-      - {{ sflow_port }}
+      - {{ sflow_port }}/udp
       ports:
       - "{{ sflow_port }}:{{ sflow_port }}/udp"
       - "{{ sflow_api_port }}:{{ sflow_api_port }}"
diff --git a/xos/synchronizers/monitoring_channel/templates/ceilometer_proxy_config.j2 b/xos/synchronizers/monitoring_channel/templates/ceilometer_proxy_config.j2
index 4c712f1..bd6c521 100644
--- a/xos/synchronizers/monitoring_channel/templates/ceilometer_proxy_config.j2
+++ b/xos/synchronizers/monitoring_channel/templates/ceilometer_proxy_config.j2
@@ -7,6 +7,7 @@
 admin_user={{ admin_user }}
 admin_tenant={{ admin_tenant }}
 admin_password={{ admin_password }}
+ceilometer_pub_sub_url={{ ceilometer_pub_sub_url }}
 
 [allowed_tenants]
 {% if allowed_tenant_ids %}
diff --git a/xos/synchronizers/monitoring_channel/templates/ceilometer_proxy_server.py b/xos/synchronizers/monitoring_channel/templates/ceilometer_proxy_server.py
index 62f0804..c81b941 100644
--- a/xos/synchronizers/monitoring_channel/templates/ceilometer_proxy_server.py
+++ b/xos/synchronizers/monitoring_channel/templates/ceilometer_proxy_server.py
@@ -7,6 +7,7 @@
 import logging
 import urllib
 import urllib2
+from urlparse import urlparse
 from wsgilog import WsgiLog
 
 web.config.debug=False
@@ -252,6 +253,11 @@
     def POST(self):
         global config
         parse_ceilometer_proxy_config()
+        ceilometer_pub_sub_url = config.get('default', 'ceilometer_pub_sub_url')
+        url = urlparse(ceilometer_pub_sub_url)
+        if (not url.scheme) or (not url.netloc): 
+             raise Exception("Ceilometer PUB/SUB URL not set")
+        ceilometer_pub_sub_url = url.scheme + "://" + url.netloc + "/subscribe"
         data_str = unicode(web.data(),'iso-8859-1')
         post_data = json.loads(data_str)
         final_query=[]
@@ -262,7 +268,7 @@
              raise Exception("Not allowed to subscribe to any meters")
         post_data["query"] = final_query
         #TODO: The PUB/SUB url needs to be read from config
-        put_request = urllib2.Request("http://10.11.10.1:4455/subscribe", json.dumps(post_data))
+        put_request = urllib2.Request(ceilometer_pub_sub_url, json.dumps(post_data))
         put_request.get_method = lambda: 'SUB'
         put_request.add_header('Content-Type', 'application/json')
         response = urllib2.urlopen(put_request)
@@ -270,9 +276,14 @@
         return json.dumps(response_text)
 
     def DELETE(self):
+        ceilometer_pub_sub_url = config.get('default', 'ceilometer_pub_sub_url')
+        url = urlparse(ceilometer_pub_sub_url)
+        if (not url.scheme) or (not url.netloc): 
+             raise Exception("Ceilometer PUB/SUB URL not set")
+        ceilometer_pub_sub_url = url.scheme + "://" + url.netloc + "/unsubscribe"
         data_str = web.data()
         #TODO: The PUB/SUB url needs to be read from config
-        put_request = urllib2.Request("http://10.11.10.1:4455/unsubscribe", data_str)
+        put_request = urllib2.Request(ceilometer_pub_sub_url, data_str)
         put_request.get_method = lambda: 'UNSUB'
         put_request.add_header('Content-Type', 'application/json')
         response = urllib2.urlopen(put_request)
diff --git a/xos/synchronizers/openstack/steps/sync_controller_slice_privileges.py b/xos/synchronizers/openstack/steps/sync_controller_slice_privileges.py
index b78e4a0..e5513b0 100644
--- a/xos/synchronizers/openstack/steps/sync_controller_slice_privileges.py
+++ b/xos/synchronizers/openstack/steps/sync_controller_slice_privileges.py
@@ -18,7 +18,7 @@
     observes=ControllerSlicePrivilege
     playbook = 'sync_controller_users.yaml'
 
-    def map_inputs(self, controller_slice_privilege):
+    def map_sync_inputs(self, controller_slice_privilege):
         if not controller_slice_privilege.controller.admin_user:
             logger.info("controller %r has no admin_user, skipping" % controller_slice_privilege.controller)
             return
diff --git a/xos/synchronizers/vcpe/steps/sync_vcpetenant.yaml b/xos/synchronizers/vcpe/steps/sync_vcpetenant.yaml
index b9b4b9e..d887547 100644
--- a/xos/synchronizers/vcpe/steps/sync_vcpetenant.yaml
+++ b/xos/synchronizers/vcpe/steps/sync_vcpetenant.yaml
@@ -100,13 +100,13 @@
 #  - name: DEBUG
 #    debug: var=cron_job_pids_count.stdout
 
-  - name: make sure ~/bin exists
-    file: path=~/bin state=directory owner=root group=root
-    when: cron_job_pids_count.stdout == "0"
+#  - name: make sure ~/bin exists
+#    file: path=~/bin state=directory owner=root group=root
+#    when: cron_job_pids_count.stdout == "0"
 
   - name: Copy cron job to destination
     copy: src=/opt/xos/synchronizers/vcpe/vcpe_stats_notifier.py
-      dest=~/bin/vcpe_stats_notifier.py
+      dest=/usr/local/sbin/vcpe_stats_notifier.py
     when: cron_job_pids_count.stdout == "0"
 
   - name: install python-kombu
@@ -114,7 +114,7 @@
     when: cron_job_pids_count.stdout == "0"
 
   - name: Initiate vcpe_stats_notifier cron job
-    command: python ~/bin/vcpe_stats_notifier.py --keystone_tenant_id={{ keystone_tenant_id }} --keystone_user_id={{ keystone_user_id }} --rabbit_user={{ rabbit_user }} --rabbit_password={{ rabbit_password }} --rabbit_host={{ rabbit_host }} --vcpeservice_rabbit_exchange='vcpeservice'
+    command: sudo python /usr/local/sbin/vcpe_stats_notifier.py --keystone_tenant_id={{ keystone_tenant_id }} --keystone_user_id={{ keystone_user_id }} --rabbit_user={{ rabbit_user }} --rabbit_password={{ rabbit_password }} --rabbit_host={{ rabbit_host }} --vcpeservice_rabbit_exchange='vcpeservice'
     async: 9999999999999999
     poll: 0
     when: cron_job_pids_count.stdout == "0"
diff --git a/xos/synchronizers/vcpe/vcpe_stats_notifier.py b/xos/synchronizers/vcpe/vcpe_stats_notifier.py
index d726e3c..4d2cc76 100644
--- a/xos/synchronizers/vcpe/vcpe_stats_notifier.py
+++ b/xos/synchronizers/vcpe/vcpe_stats_notifier.py
@@ -8,6 +8,7 @@
 import time, threading
 import sys, getopt
 import logging
+import os
 
 
 logfile = "vcpe_stats_notifier.log"
@@ -21,8 +22,8 @@
 handler.setFormatter(formatter)
 logger.addHandler(handler)
 
-def extract_dns_stats_from_all_vcpes():
-    p = subprocess.Popen('docker ps', shell=True, stdout=subprocess.PIPE) 
+def get_all_docker_containers():
+    p = subprocess.Popen('docker ps --no-trunc', shell=True, stdout=subprocess.PIPE) 
     firstline = True
     dockercontainers = {}
     while True:
@@ -37,6 +38,56 @@
                 container_fields = {}
                 container_fields['id'] = fields[0]
                 dockercontainers[fields[-1]] = container_fields
+    return dockercontainers
+
+def extract_compute_stats_from_all_vcpes(dockercontainers):
+    for k,v in dockercontainers.iteritems():
+        cmd = 'sudo docker stats --no-stream=true ' + v['id'] 
+        p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) 
+        firstline = True
+        while True:
+            out = p.stdout.readline()
+            if out == '' and p.poll() != None:
+                break
+            if out != '':
+                if firstline is True:
+                    firstline = False
+                else:
+                    fields = out.split()
+                    #['CONTAINER_ID', 'CPU%', 'MEMUSE', 'UNITS', '/', 'MEMLIMIT', 'UNITS', 'MEM%', 'NET I/O', 'UNITS', '/', 'NET I/O LIMIT', 'UNITS', 'BLOCK I/O', 'UNITS', '/', 'BLOCK I/O LIMIT', 'UNITS']
+                    v['cpu_util'] = fields[1][:-1]
+                    if fields[6] == 'GB':
+                       v['memory'] = str(float(fields[5]) * 1000)
+                    else:
+                       v['memory'] = fields[5]
+                    if fields[3] == 'GB':
+                       v['memory_usage'] = str(float(fields[2]) * 1000)
+                    else:
+                       v['memory_usage'] = fields[2]
+        v['network_stats'] = []
+        for intf in ['eth0', 'eth1']:
+            cmd = 'sudo docker exec ' + v['id'] + ' ifconfig ' + intf
+            p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
+            out,err = p.communicate()
+            if out:
+                intf_stats = {}
+                m = re.search("RX bytes:(\d+)", str(out))
+                if m:
+                    intf_stats['rx_bytes'] = m.group(1)
+                m = re.search("TX bytes:(\d+)", str(out))
+                if m:
+                    intf_stats['tx_bytes'] = m.group(1)
+                m = re.search("RX packets:(\d+)", str(out))
+                if m:
+                    intf_stats['rx_packets'] = m.group(1)
+                m = re.search("TX packets:(\d+)", str(out))
+                if m:
+                    intf_stats['tx_packets'] = m.group(1)
+                if intf_stats:
+                    intf_stats['intf'] = intf
+                    v['network_stats'].append(intf_stats)
+
+def extract_dns_stats_from_all_vcpes(dockercontainers):
     for k,v in dockercontainers.iteritems():
          cmd = 'docker exec ' + v['id'] + ' killall -10 dnsmasq'
          p = subprocess.Popen (cmd, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
@@ -112,9 +163,11 @@
 
      logger.debug('publish_cpe_stats invoked')
 
-     cpe_container_stats = extract_dns_stats_from_all_vcpes()
+     dockercontainers = get_all_docker_containers()
+     cpe_container_compute_stats = extract_compute_stats_from_all_vcpes(dockercontainers)
+     cpe_container_dns_stats = extract_dns_stats_from_all_vcpes(dockercontainers)
 
-     for k,v in cpe_container_stats.iteritems():
+     for k,v in cpe_container_dns_stats.iteritems():
           msg = {'event_type': 'vcpe', 
                  'message_id':six.text_type(uuid.uuid4()),
                  'publisher_id': cpe_publisher_id,
@@ -128,6 +181,29 @@
           producer.publish(msg)
           logger.debug('Publishing vcpe event: %s', msg)
 
+          compute_payload = {}
+          if 'cpu_util' in v:
+               compute_payload['cpu_util']= v['cpu_util']
+          if 'memory' in v:
+               compute_payload['memory']= v['memory']
+          if 'memory_usage' in v:
+               compute_payload['memory_usage']= v['memory_usage']
+          if ('network_stats' in v) and (v['network_stats']):
+               compute_payload['network_stats']= v['network_stats']
+          if compute_payload:
+               compute_payload['vcpe_id'] = k
+               compute_payload['user_id'] = keystone_user_id
+               compute_payload['tenant_id'] = keystone_tenant_id
+               msg = {'event_type': 'vcpe.compute.stats', 
+                      'message_id':six.text_type(uuid.uuid4()),
+                      'publisher_id': cpe_publisher_id,
+                      'timestamp':datetime.datetime.now().isoformat(),
+                      'priority':'INFO',
+                      'payload': compute_payload 
+                     }
+               producer.publish(msg)
+               logger.debug('Publishing vcpe.dns.cache.size event: %s', msg)
+
           if 'cache_size' in v:
                msg = {'event_type': 'vcpe.dns.cache.size', 
                       'message_id':six.text_type(uuid.uuid4()),
diff --git a/xos/tosca/resources/ceilometerservice.py b/xos/tosca/resources/ceilometerservice.py
new file mode 100644
index 0000000..e77fa55
--- /dev/null
+++ b/xos/tosca/resources/ceilometerservice.py
@@ -0,0 +1,41 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+
+from core.models import ServiceAttribute
+from services.ceilometer.models import CeilometerService
+
+from service import XOSService
+
+class XOSCeilometerService(XOSService):
+    provides = "tosca.nodes.CeilometerService"
+    xos_model = CeilometerService
+    copyin_props = ["view_url", "icon_url", "enabled", "published", "public_key", "versionNumber", "ceilometer_pub_sub_url"]
+
+    def set_service_attr(self, obj, prop_name, value):
+        value = self.try_intrinsic_function(value)
+        if value:
+            attrs = ServiceAttribute.objects.filter(service=obj, name=prop_name)
+            if attrs:
+                attr = attrs[0]
+                if attr.value != value:
+                    self.info("updating attribute %s" % prop_name)
+                    attr.value = value
+                    attr.save()
+            else:
+                self.info("adding attribute %s" % prop_name)
+                ta = ServiceAttribute(service=obj, name=prop_name, value=value)
+                ta.save()
+
+    def postprocess(self, obj):
+        props = self.nodetemplate.get_properties()
+        for (k,d) in props.items():
+            v = d.value
+            if k.startswith("config_"):
+                self.set_service_attr(obj, k, v)
+            elif k.startswith("rest_"):
+                self.set_service_attr(obj, k, v)
+