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())