Merge branch 'master' of bitbucket.org:corddesign/voltha
diff --git a/.gitignore b/.gitignore
index 0abf43a..f49da55 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,15 @@
 
 # Virtualenv
 venv
+venv-darwin
+venv-linux
 
 # Temp data dir for testing
 tmp
+
+# Any vi swap files
+*.swp
+
+# Protobuf output files
+voltha/core/protos/*.desc
+voltha/core/protos/*_pb2.py
diff --git a/BUILD.md b/BUILD.md
index c92f238..53a3656 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -22,6 +22,8 @@
 make vagrant
 vagrant ssh # the rest to be executed inside the vagrant VM
 cd /voltha
+. env.sh
+make fetch
 make
 ```
 
diff --git a/Makefile b/Makefile
index d009f74..8a2a3a1 100644
--- a/Makefile
+++ b/Makefile
@@ -74,9 +74,15 @@
 vagrant:
 	vagrant up
 
-build: fetch utest
+build: utest build-protos
 	docker build -t cord/voltha -f Dockerfile .
 
+build-protos:
+	make -C voltha/core/protos
+
+install-protoc:
+	make -C voltha/core/protos install-protoc
+
 clean:
 	find voltha -name '*.pyc' | xargs rm -f
 
diff --git a/Vagrantfile b/Vagrantfile
index f9acea9..2508217 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -16,8 +16,9 @@
     d.vm.network "private_network", ip: "10.100.198.220"
     d.vm.provision :shell, path: "ansible/scripts/bootstrap_ansible.sh"
     d.vm.provision :shell, inline: "PYTHONUNBUFFERED=1 ansible-playbook /voltha/ansible/voltha.yml -c local"
+    d.vm.provision :shell, inline: "cd /voltha && source env.sh && make install-protoc"
     d.vm.provider "virtualbox" do |v|
-      v.memory = 2048
+      v.memory = 4096
     end
   end
 
diff --git a/experiments/addressbook.proto b/experiments/addressbook.proto
new file mode 100644
index 0000000..bfdceea
--- /dev/null
+++ b/experiments/addressbook.proto
@@ -0,0 +1,33 @@
+// See README.txt for information and build instructions.
+
+syntax = "proto3";
+
+package tutorial;
+
+option java_package = "com.example.tutorial";
+option java_outer_classname = "AddressBookProtos";
+option csharp_namespace = "Google.Protobuf.Examples.AddressBook";
+
+message Person {
+  string name = 1;
+  int32 id = 2;        // Unique ID number for this person.
+  string email = 3;
+
+  enum PhoneType {
+    MOBILE = 0;
+    HOME = 1;
+    WORK = 2;
+  }
+
+  message PhoneNumber {
+    string number = 1;
+    PhoneType type = 2;
+  }
+
+  repeated PhoneNumber phones = 4;
+}
+
+// Our address book file is just one of these.
+message AddressBook {
+  repeated Person people = 1;
+}
diff --git a/experiments/addressbook_pb2.py b/experiments/addressbook_pb2.py
new file mode 100644
index 0000000..e545fb3
--- /dev/null
+++ b/experiments/addressbook_pb2.py
@@ -0,0 +1,206 @@
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: addressbook.proto
+
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf import descriptor_pb2
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='addressbook.proto',
+  package='tutorial',
+  syntax='proto3',
+  serialized_pb=b'\n\x11\x61\x64\x64ressbook.proto\x12\x08tutorial\"\xd5\x01\n\x06Person\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\x05\x12\r\n\x05\x65mail\x18\x03 \x01(\t\x12,\n\x06phones\x18\x04 \x03(\x0b\x32\x1c.tutorial.Person.PhoneNumber\x1aG\n\x0bPhoneNumber\x12\x0e\n\x06number\x18\x01 \x01(\t\x12(\n\x04type\x18\x02 \x01(\x0e\x32\x1a.tutorial.Person.PhoneType\"+\n\tPhoneType\x12\n\n\x06MOBILE\x10\x00\x12\x08\n\x04HOME\x10\x01\x12\x08\n\x04WORK\x10\x02\"/\n\x0b\x41\x64\x64ressBook\x12 \n\x06people\x18\x01 \x03(\x0b\x32\x10.tutorial.PersonBP\n\x14\x63om.example.tutorialB\x11\x41\x64\x64ressBookProtos\xaa\x02$Google.Protobuf.Examples.AddressBookb\x06proto3'
+)
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+
+
+_PERSON_PHONETYPE = _descriptor.EnumDescriptor(
+  name='PhoneType',
+  full_name='tutorial.Person.PhoneType',
+  filename=None,
+  file=DESCRIPTOR,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='MOBILE', index=0, number=0,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='HOME', index=1, number=1,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='WORK', index=2, number=2,
+      options=None,
+      type=None),
+  ],
+  containing_type=None,
+  options=None,
+  serialized_start=202,
+  serialized_end=245,
+)
+_sym_db.RegisterEnumDescriptor(_PERSON_PHONETYPE)
+
+
+_PERSON_PHONENUMBER = _descriptor.Descriptor(
+  name='PhoneNumber',
+  full_name='tutorial.Person.PhoneNumber',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='number', full_name='tutorial.Person.PhoneNumber.number', index=0,
+      number=1, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='type', full_name='tutorial.Person.PhoneNumber.type', index=1,
+      number=2, type=14, cpp_type=8, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=129,
+  serialized_end=200,
+)
+
+_PERSON = _descriptor.Descriptor(
+  name='Person',
+  full_name='tutorial.Person',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='name', full_name='tutorial.Person.name', index=0,
+      number=1, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='id', full_name='tutorial.Person.id', index=1,
+      number=2, type=5, cpp_type=1, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='email', full_name='tutorial.Person.email', index=2,
+      number=3, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='phones', full_name='tutorial.Person.phones', index=3,
+      number=4, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[_PERSON_PHONENUMBER, ],
+  enum_types=[
+    _PERSON_PHONETYPE,
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=32,
+  serialized_end=245,
+)
+
+
+_ADDRESSBOOK = _descriptor.Descriptor(
+  name='AddressBook',
+  full_name='tutorial.AddressBook',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='people', full_name='tutorial.AddressBook.people', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=247,
+  serialized_end=294,
+)
+
+_PERSON_PHONENUMBER.fields_by_name['type'].enum_type = _PERSON_PHONETYPE
+_PERSON_PHONENUMBER.containing_type = _PERSON
+_PERSON.fields_by_name['phones'].message_type = _PERSON_PHONENUMBER
+_PERSON_PHONETYPE.containing_type = _PERSON
+_ADDRESSBOOK.fields_by_name['people'].message_type = _PERSON
+DESCRIPTOR.message_types_by_name['Person'] = _PERSON
+DESCRIPTOR.message_types_by_name['AddressBook'] = _ADDRESSBOOK
+
+Person = _reflection.GeneratedProtocolMessageType('Person', (_message.Message,), dict(
+
+  PhoneNumber = _reflection.GeneratedProtocolMessageType('PhoneNumber', (_message.Message,), dict(
+    DESCRIPTOR = _PERSON_PHONENUMBER,
+    __module__ = 'addressbook_pb2'
+    # @@protoc_insertion_point(class_scope:tutorial.Person.PhoneNumber)
+    ))
+  ,
+  DESCRIPTOR = _PERSON,
+  __module__ = 'addressbook_pb2'
+  # @@protoc_insertion_point(class_scope:tutorial.Person)
+  ))
+_sym_db.RegisterMessage(Person)
+_sym_db.RegisterMessage(Person.PhoneNumber)
+
+AddressBook = _reflection.GeneratedProtocolMessageType('AddressBook', (_message.Message,), dict(
+  DESCRIPTOR = _ADDRESSBOOK,
+  __module__ = 'addressbook_pb2'
+  # @@protoc_insertion_point(class_scope:tutorial.AddressBook)
+  ))
+_sym_db.RegisterMessage(AddressBook)
+
+
+DESCRIPTOR.has_options = True
+DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), b'\n\024com.example.tutorialB\021AddressBookProtos\252\002$Google.Protobuf.Examples.AddressBook')
+# @@protoc_insertion_point(module_scope)
diff --git a/experiments/encoing_test.py b/experiments/encoing_test.py
new file mode 100644
index 0000000..d7ddbfc
--- /dev/null
+++ b/experiments/encoing_test.py
@@ -0,0 +1,300 @@
+#!/usr/bin/env python
+
+"""
+We use this module to time-test various alternatives for building,
+serializing, and de-serializing objects.
+"""
+
+import time
+from copy import copy, deepcopy
+
+import addressbook_pb2
+from random import randint
+from uuid import uuid4
+from simplejson import dumps, loads, JSONEncoder
+
+
+class ProtoBufs(object):
+
+    FILENAME = 'addressbook.bin.pb'
+
+    @staticmethod
+    def makeAddressBook(n):
+
+        addressbook = addressbook_pb2.AddressBook()
+
+        for i in xrange(n):
+            person = addressbook.people.add()
+            person.id = i
+            person.name = uuid4().get_hex()
+            person.email = person.name + '@abc.com'
+
+            phone1 = person.phones.add()
+            phone1.number = str(randint(1000000000, 7999999999))
+            phone1.type = addressbook_pb2.Person.HOME
+
+            phone2 = person.phones.add()
+            phone2.number = str(randint(1000000000, 7999999999))
+            phone2.type = addressbook_pb2.Person.MOBILE
+
+        return addressbook
+
+    @staticmethod
+    def serialize(addressbook):
+        return addressbook.SerializeToString()
+
+    @staticmethod
+    def deserialize(str):
+        addressbook = addressbook_pb2.AddressBook()
+        addressbook.ParseFromString(str)
+        return addressbook
+
+    @staticmethod
+    def diff(addressbook1, addressbook2):
+        assert isinstance(addressbook1, addressbook_pb2.AddressBook)
+        assert isinstance(addressbook2, addressbook_pb2.AddressBook)
+        assert addressbook1 == addressbook2
+
+
+class JsonWithPythonData(object):
+
+    FILENAME = 'addressbook.native.json'
+
+    @staticmethod
+    def makeAddressBook(n):
+
+        addressbook = dict(people=[])
+        people = addressbook['people']
+
+        for i in xrange(n):
+            name = uuid4().get_hex()
+            people.append(dict(
+                id=i,
+                name=name,
+                email=name + '@abc.com',
+                phones=[
+                    dict(
+                        number=str(randint(1000000000, 7999999999)),
+                        type='HOME'
+                    ),
+                    dict(
+                        number=str(randint(1000000000, 7999999999)),
+                        type='MOBILE'
+                    )
+                ]
+            ))
+
+        return addressbook
+
+    @staticmethod
+    def serialize(addressbook):
+        return dumps(addressbook)
+
+    @staticmethod
+    def deserialize(str):
+        return loads(str)
+
+    @staticmethod
+    def diff(addressbook1, addressbook2):
+        assert isinstance(addressbook1, dict)
+        assert isinstance(addressbook2, dict)
+        assert addressbook1 == addressbook2
+
+
+class JsonWithPythonClasses(object):
+
+    class JsonEncoded(object):
+        def __eq__(self, other):
+            return self.__dict__ == other.__dict__
+
+    class Phone(JsonEncoded):
+
+        def __init__(self, number, type):
+            self.number = number
+            self.type = type
+
+        @property
+        def number(self):
+            return self._type
+        @number.setter
+        def number(self, number):
+            assert isinstance(number, str)
+            self._number = number
+
+        @property
+        def type(self):
+            return self._type
+        @number.setter
+        def type(self, type):
+            assert isinstance(type, str)
+            self._type = type
+
+    class Person(JsonEncoded):
+
+        def __init__(self, id, name, email, phones=list()):
+            self.id = id
+            self.name = name
+            self.email = email
+            self.phones = phones
+
+        @property
+        def id(self):
+            return self._id
+        @id.setter
+        def id(self, id):
+            assert isinstance(id, int)
+            self._id = id
+
+        @property
+        def name(self):
+            return self._name
+        @name.setter
+        def name(self, name):
+            assert isinstance(name, str)
+            self._name = name
+
+        @property
+        def email(self):
+            return self._email
+        @email.setter
+        def email(self, email):
+            assert isinstance(email, str)
+            self._email = email
+
+        @property
+        def phones(self):
+            return self._phones
+        @phones.setter
+        def phones(self, phones):
+            assert isinstance(phones, list)
+            self._phones = phones
+
+
+    class AddressBook(JsonEncoded):
+
+        def __init__(self, people=list()):
+            self.people = people
+
+        @property
+        def people(self):
+            return self._people
+        @people.setter
+        def people(self, people):
+            assert isinstance(people, list)
+            self._people = people
+
+    cls_map = {
+        Phone.__name__: Phone,
+        Person.__name__: Person,
+        AddressBook.__name__: AddressBook
+    }
+
+    class CustomEncoder(JSONEncoder):
+        def default(self, o):
+            if isinstance(o, JsonWithPythonClasses.JsonEncoded):
+                d = dict((k.strip('_'), v) for k, v in o.__dict__.iteritems())
+                d['_class'] = o.__class__.__name__
+                return d
+            return super(self).default(o)
+
+    @staticmethod
+    def as_object(dct):
+        if '_class' in dct and dct['_class'] in JsonWithPythonClasses.cls_map:
+            kw = deepcopy(dct)
+            cls_name = kw.pop('_class')
+            cls = JsonWithPythonClasses.cls_map[cls_name]
+            return cls(**kw)
+        return dct
+
+    FILENAME = 'addressbook.class.json'
+
+    @staticmethod
+    def makeAddressBook(n):
+
+        addressbook = JsonWithPythonClasses.AddressBook()
+        people = addressbook.people
+
+        for i in xrange(n):
+            name = uuid4().get_hex()
+            person = JsonWithPythonClasses.Person(
+                id=i,
+                name=name,
+                email=name + '@abc.com',
+                phones=[
+                    JsonWithPythonClasses.Phone(
+                        number=str(randint(1000000000, 7999999999)),
+                        type='HOME'
+                    ),
+                    JsonWithPythonClasses.Phone(
+                        number=str(randint(1000000000, 7999999999)),
+                        type='MOBILE'
+                    )
+                ]
+            )
+            people.append(person)
+
+        return addressbook
+
+    @staticmethod
+    def serialize(addressbook):
+        return dumps(addressbook, cls=JsonWithPythonClasses.CustomEncoder)
+
+    @staticmethod
+    def deserialize(str):
+        return loads(str, object_hook=JsonWithPythonClasses.as_object)
+
+    @staticmethod
+    def diff(addressbook1, addressbook2):
+        assert isinstance(addressbook1, JsonWithPythonClasses.AddressBook)
+        assert isinstance(addressbook2, JsonWithPythonClasses.AddressBook)
+        assert len(addressbook1.people) == len(addressbook2.people)
+        for i in xrange(len(addressbook1.people)):
+            assert addressbook1.people[i] == addressbook2.people[i], \
+                '\n%s\n!=\n%s' % (addressbook1.people[i].__dict__,
+                              addressbook2.people[i].__dict__)
+        assert addressbook1 == addressbook2
+
+
+def timetest(cls, n):
+
+    generator = cls()
+
+    # generate addressbook
+
+    t = time.time()
+    addressbook = generator.makeAddressBook(n)
+    t_make = time.time() - t
+
+    # serialize addressbook to string and save it to file
+    t = time.time()
+    str = generator.serialize(addressbook)
+    t_serialize = time.time() - t
+    size = len(str)
+
+    with open(cls.FILENAME, 'wb') as f:
+        f.write(str)
+
+    # deserialize by reading it back from file
+    t = time.time()
+    addressbook2 = generator.deserialize(str)
+    t_deserialize = time.time() - t
+
+    generator.diff(addressbook, addressbook2)
+
+    print "%-30s %12lf   %12lf   %12lf   %10d" % \
+          (cls.__name__,
+           1e6 * t_make / n,
+           1e6 * t_serialize / n,
+           1e6 * t_deserialize / n,
+           size / n)
+
+
+def run_tests(n):
+    print "%-30s %12s   %12s   %12s   %10s" % \
+          ('Method', 'Gen [us]', 'Ser [us]', 'Des [us]', 'Size [bytes]')
+    timetest(ProtoBufs, n)
+    timetest(JsonWithPythonData, n)
+    timetest(JsonWithPythonClasses, n)
+
+
+run_tests(10000)
diff --git a/third_party/googleapis/LICENSE b/third_party/googleapis/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/third_party/googleapis/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
diff --git a/third_party/googleapis/google/api/annotations.proto b/third_party/googleapis/google/api/annotations.proto
new file mode 100644
index 0000000..cbd18b8
--- /dev/null
+++ b/third_party/googleapis/google/api/annotations.proto
@@ -0,0 +1,29 @@
+// Copyright (c) 2015, Google Inc.
+//
+// Licensed 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.
+
+syntax = "proto3";
+
+package google.api;
+
+import "google/api/http.proto";
+import "google/protobuf/descriptor.proto";
+
+option java_multiple_files = true;
+option java_outer_classname = "AnnotationsProto";
+option java_package = "com.google.api";
+
+extend google.protobuf.MethodOptions {
+  // See `HttpRule`.
+  HttpRule http = 72295728;
+}
diff --git a/third_party/googleapis/google/api/http.proto b/third_party/googleapis/google/api/http.proto
new file mode 100644
index 0000000..ce07aa1
--- /dev/null
+++ b/third_party/googleapis/google/api/http.proto
@@ -0,0 +1,127 @@
+// Copyright (c) 2015, Google Inc.
+//
+// Licensed 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.
+
+syntax = "proto3";
+
+package google.api;
+
+option java_multiple_files = true;
+option java_outer_classname = "HttpProto";
+option java_package = "com.google.api";
+
+
+// `HttpRule` defines the mapping of an RPC method to one or more HTTP REST API
+// methods. The mapping determines what portions of the request message are
+// populated from the path, query parameters, or body of the HTTP request.  The
+// mapping is typically specified as an `google.api.http` annotation, see
+// "google/api/annotations.proto" for details.
+//
+// The mapping consists of a mandatory field specifying a path template and an
+// optional `body` field specifying what data is represented in the HTTP request
+// body. The field name for the path indicates the HTTP method. Example:
+//
+// ```
+// package google.storage.v2;
+//
+// import "google/api/annotations.proto";
+//
+// service Storage {
+//   rpc CreateObject(CreateObjectRequest) returns (Object) {
+//     option (google.api.http) {
+//       post: "/v2/{bucket_name=buckets/*}/objects"
+//       body: "object"
+//     };
+//   };
+// }
+// ```
+//
+// Here `bucket_name` and `object` bind to fields of the request message
+// `CreateObjectRequest`.
+//
+// The rules for mapping HTTP path, query parameters, and body fields
+// to the request message are as follows:
+//
+// 1. The `body` field specifies either `*` or a field path, or is
+//    omitted. If omitted, it assumes there is no HTTP body.
+// 2. Leaf fields (recursive expansion of nested messages in the
+//    request) can be classified into three types:
+//     (a) Matched in the URL template.
+//     (b) Covered by body (if body is `*`, everything except (a) fields;
+//         else everything under the body field)
+//     (c) All other fields.
+// 3. URL query parameters found in the HTTP request are mapped to (c) fields.
+// 4. Any body sent with an HTTP request can contain only (b) fields.
+//
+// The syntax of the path template is as follows:
+//
+//     Template = "/" Segments [ Verb ] ;
+//     Segments = Segment { "/" Segment } ;
+//     Segment  = "*" | "**" | LITERAL | Variable ;
+//     Variable = "{" FieldPath [ "=" Segments ] "}" ;
+//     FieldPath = IDENT { "." IDENT } ;
+//     Verb     = ":" LITERAL ;
+//
+// `*` matches a single path component, `**` zero or more path components, and
+// `LITERAL` a constant.  A `Variable` can match an entire path as specified
+// again by a template; this nested template must not contain further variables.
+// If no template is given with a variable, it matches a single path component.
+// The notation `{var}` is henceforth equivalent to `{var=*}`.
+//
+// Use CustomHttpPattern to specify any HTTP method that is not included in the
+// pattern field, such as HEAD, or "*" to leave the HTTP method unspecified for
+// a given URL path rule. The wild-card rule is useful for services that provide
+// content to Web (HTML) clients.
+message HttpRule {
+
+  // Determines the URL pattern is matched by this rules. This pattern can be
+  // used with any of the {get|put|post|delete|patch} methods. A custom method
+  // can be defined using the 'custom' field.
+  oneof pattern {
+    // Used for listing and getting information about resources.
+    string get = 2;
+
+    // Used for updating a resource.
+    string put = 3;
+
+    // Used for creating a resource.
+    string post = 4;
+
+    // Used for deleting a resource.
+    string delete = 5;
+
+    // Used for updating a resource.
+    string patch = 6;
+
+    // Custom pattern is used for defining custom verbs.
+    CustomHttpPattern custom = 8;
+  }
+
+  // The name of the request field whose value is mapped to the HTTP body, or
+  // `*` for mapping all fields not captured by the path pattern to the HTTP
+  // body.
+  string body = 7;
+
+  // Additional HTTP bindings for the selector. Nested bindings must not
+  // specify a selector and must not contain additional bindings.
+  repeated HttpRule additional_bindings = 11;
+}
+
+// A custom pattern is used for defining custom HTTP verb.
+message CustomHttpPattern {
+  // The name of this custom HTTP verb.
+  string kind = 1;
+
+  // The path matched by this custom verb.
+  string path = 2;
+}
diff --git a/voltha/core/apis.py b/voltha/core/apis.py
new file mode 100644
index 0000000..14f80db
--- /dev/null
+++ b/voltha/core/apis.py
@@ -0,0 +1,77 @@
+#
+# Copyright 2016 the original author or authors.
+#
+# Licensed 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.
+#
+
+"""API definition file"""
+
+
+class CoreApi(object):
+    pass
+
+
+class Health(CoreApi):
+
+    def get(self):
+        """
+        Query the health of this Voltha instance.
+        ---
+        tags:
+        - admin
+        parameters:
+        responses:
+          200:
+            description: Indicates healthy state
+          503:
+            description: Overloaded
+          500:
+            description: Server in faulty state
+        """
+        return None
+
+
+class DeviceGroup(CoreApi):
+    """
+    schemas:
+      device_group_summary:
+        schema:
+          id: DeviceGroup
+          properties:
+            id:
+              type: string
+              description: Unique identifier of device group
+            assigned_to:
+              type: string
+              description: Unique instance_id of Voltha instance handling group
+            device_count:
+              type: int
+              description: Number of devices in the device group
+    """
+
+    def list(self):
+        """
+        Return the list of device groups managed by Voltha
+        ---
+        tags:
+        - admin
+        parameters:
+        responses:
+          200:
+            description: List of device groups
+            schema:
+              type: array
+              items:
+                $ref: '#/definitions/DeviceGroup
+        """
+
diff --git a/voltha/core/protos/Makefile b/voltha/core/protos/Makefile
new file mode 100644
index 0000000..5466e37
--- /dev/null
+++ b/voltha/core/protos/Makefile
@@ -0,0 +1,75 @@
+#
+# Copyright 2016 the original author or authors.
+#
+# Licensed 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.
+#
+
+# Makefile to build all protobuf and gRPC related artifacts
+
+ifeq ($(VOLTHA_BASE)_set,_set)
+  $(error To get started, please source the env.sh file from Voltha top level directory)
+endif
+
+default: build
+
+PROTO_FILES := $(wildcard *.proto)
+PROTO_PB2_FILES := $(foreach f,$(PROTO_FILES),$(subst .proto,_pb2.py,$(f)))
+PROTO_DESC_FILES := $(foreach f,$(PROTO_FILES),$(subst .proto,.desc,$(f)))
+
+PROTOC_PREFIX := /usr/local
+PROTOC_LIBDIR := $(PROTOC_PREFIX)/lib
+
+PROTOC := $(PROTOC_PREFIX)/bin/protoc
+
+PROTOC_VERSION := "3.0.2"
+PROTOC_DOWNLOAD_PREFIX := "https://github.com/google/protobuf/releases/download"
+PROTOC_DIR := protobuf-$(PROTOC_VERSION)
+PROTOC_TARBALL := protobuf-python-$(PROTOC_VERSION).tar.gz
+PROTOC_DOWNLOAD_URI := $(PROTOC_DOWNLOAD_PREFIX)/v$(PROTOC_VERSION)/$(PROTOC_TARBALL)
+PROTOC_BUILD_TMP_DIR := "/tmp/protobuf-build-$(shell uname -s | tr '[:upper:]' '[:lower:]')"
+
+build: $(PROTOC) $(PROTO_PB2_FILES)
+
+%_pb2.py: %.proto Makefile
+	@echo "Building protocol buffer artifacts from $<"
+	env LD_LIBRARY_PATH=$(PROTOC_LIBDIR) $(PROTOC) -I. -I$(VOLTHA_BASE)/third_party/googleapis --python_out=. --descriptor_set_out=$(basename $<).desc --include_source_info $<
+
+clean:
+	rm -f $(PROTO_PB2_FILES) $(PROTO_DESC_FILES)
+
+$(PROTOC):
+	@echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+	@echo "It looks like you don't have protocol buffer tools installed."
+	@echo "To install the protocol buffer toolchain, you can run:"
+	@echo "    make install-protoc"
+	@echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+
+install-protoc: $(PROTOC)
+	@echo "Downloading and installing protocol buffer support."
+	@echo "Installation will require sodo priviledges"
+	@echo "This will take a few minutes."
+	mkdir -p $(PROTOC_BUILD_TMP_DIR)
+	@echo "We ask for sudo credentials now so we can install at the end"; \
+	sudo echo "Thanks"; \
+	    cd $(PROTOC_BUILD_TMP_DIR); \
+	    wget $(PROTOC_DOWNLOAD_URI); \
+	    tar xzvf $(PROTOC_TARBALL); \
+	    cd $(PROTOC_DIR); \
+	    ./configure --prefix=$(PROTOC_PREFIX); \
+	    make; \
+	    sudo make install
+
+uninstall-protoc:
+	cd $(PROTOC_BUILD_TMP_DIR)/$(PROTOC_DIR); \
+	    sudo make uninstall
+
diff --git a/voltha/core/protos/voltha.proto b/voltha/core/protos/voltha.proto
new file mode 100644
index 0000000..a0d65db
--- /dev/null
+++ b/voltha/core/protos/voltha.proto
@@ -0,0 +1,40 @@
+// See README.txt for information and build instructions.
+
+syntax = "proto3";
+
+package voltha;
+
+import "google/api/annotations.proto";
+
+option java_package = "org.opencord.voltha";
+option java_outer_classname = "VolthaProtos";
+option csharp_namespace = "Opencord.Voltha.Voltha";
+
+message NullMessage {}
+
+message HealthStatus {
+
+  // Health states
+  enum HealthState {
+    HEALTHY = 0;
+    OVERLOADED = 1;
+    DYING = 2;
+  }
+
+  // Current state of health of this Voltha instance
+  HealthState state = 1;
+
+}
+
+service HealthService {
+
+  // Return current health status of a Voltha instance
+  rpc GetHealthStatus(NullMessage) returns (HealthStatus) {
+    option (google.api.http) = {
+      get: "/health"
+      body: '*'
+    };
+  }
+
+}
+
diff --git a/voltha/northbound/rest/health_check.py b/voltha/northbound/rest/health_check.py
index 5038e30..4038ff0 100644
--- a/voltha/northbound/rest/health_check.py
+++ b/voltha/northbound/rest/health_check.py
@@ -39,7 +39,7 @@
         return Site(self.app.resource())
 
 
-def init_rest_service(port):  # TODO need to be moved to a shared file across all resources
+def init_rest_service(port):
     hc = HealthCheck()
     endpoint = endpoints.TCP4ServerEndpoint(reactor, port)
     endpoint.listen(hc.get_site())