Adding ONOS Segment Routing CLI files to new repo
diff --git a/cli/utif.py b/cli/utif.py
new file mode 100755
index 0000000..d7894bc
--- /dev/null
+++ b/cli/utif.py
@@ -0,0 +1,410 @@
+#
+# Copyright (c) 2011,2012,2013 Big Switch Networks, Inc.
+#
+# Licensed under the Eclipse Public License, Version 1.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.eclipse.org/legal/epl-v10.html
+#
+# 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.
+#
+
+#
+# UTIlity Functions
+#
+
+import re
+
+
+#
+# --------------------------------------------------------------------------------
+
+def is_power_of_two(n):
+    """
+    Return true if the integer parameter is a power of two
+    """
+    if (n & (n - 1)) == 0:
+        return True
+    return False
+
+#
+# --------------------------------------------------------------------------------
+
+def unique_list_from_list(dup_list):
+    """
+    Return a new list from the old, where the the new has no repeated items.
+    (the items are intended to be strings)
+    """
+    return dict([[x, None] for x in dup_list]).keys()
+
+#
+# --------------------------------------------------------------------------------
+
+def full_word_from_choices(word, all_choices):
+    """
+    given a single word, which could be a prefix of a word in
+    a list (which is the second parameter), return a single
+    word which matches or None, when non or more than one match.
+    """
+    choices = [x for x in all_choices if x.startswith(word)]
+    if len(choices) == 1:
+        return choices[0]
+    if word in all_choices:      # also allow an exact match
+        return word
+    return None
+
+
+#
+# --------------------------------------------------------------------------------
+
+def try_int(string):
+    """
+    Return an interger if possible, otherwise a string
+    """
+    try:
+        str_int = int(string)
+        return str_int
+    except:
+        pass
+    return string
+
+#
+# --------------------------------------------------------------------------------
+
+def mask_to_cidr(mask):
+    """
+    Given a mask used in AC's, for eample 0.0.0.255, return the cidr /<n>
+    value associated with that mask.  The mask must be a power of two for
+    the cidr value to be displayed
+    """
+    if not is_power_of_two(mask + 1):
+        return 0
+    mask += 1
+    cidr = 33
+    while mask:
+        cidr -= 1
+        mask >>= 1
+    return cidr
+
+
+
+#
+# --------------------------------------------------------------------------------
+
+def inet_ntoa(n):
+    """
+    Defina a local variant which exactly meets local needs
+    """
+    return "%s.%s.%s.%s" % ( \
+            (n & 0xff000000) >> 24, \
+            (n & 0x00ff0000) >> 16, \
+            (n & 0x0000ff00) >> 8, \
+            (n & 0x000000ff) )
+
+#
+# --------------------------------------------------------------------------------
+
+def inet_aton(ip):
+    """
+    Return an integer containing the ip address passed in.
+    Assumes the field is a quad-int
+    """
+    fields = ip.split('.')
+    return (int(fields[0]) << 24) | \
+           (int(fields[1]) << 16) | \
+           (int(fields[2]) << 8) | \
+           (int(fields[3]))
+
+#
+# --------------------------------------------------------------------------------
+
+def ip_and_mask_ntoa(ip, mask):
+    """
+    The two values are displayed either as 'any' or as in cidr format
+    or as a ip/mask depending on the value of the mask.
+    (note the leading space)
+    """
+    if ip == '0.0.0.0' and mask == '255.255.255.255':
+        return 'any '
+    n_mask = inet_aton(mask)
+    if is_power_of_two(n_mask + 1):
+        return "%s/%d " % (ip, mask_to_cidr(n_mask))
+    return "%s %s " % (ip, mask)
+
+
+#
+# --------------------------------------------------------------------------------
+
+def ip_invert_netmask(value):
+    split_bytes = value.split('.')
+    return "%s.%s.%s.%s" % (255-int(split_bytes[0]),
+                            255-int(split_bytes[1]),
+                            255-int(split_bytes[2]),
+                            255-int(split_bytes[3]))
+
+#
+# --------------------------------------------------------------------------------
+
+def ip_and_neg_mask(ip, mask):
+    """
+    The two values are displayed either as 'any' or as in cidr format
+    or as a ip/mask depending on the value of the mask.
+    (note the leading space).  This is different from ip_and_mask_ntoa
+    since the mask need to be printed an an inverted mask when the
+    mask is displayed
+    """
+    if ip == '0.0.0.0' and mask == '255.255.255.255':
+        return 'any '
+    n_mask = inet_aton(mask)
+    if is_power_of_two(n_mask + 1):
+        cidr = mask_to_cidr(n_mask)
+        if cidr:
+            return "%s/%d " % (ip, mask_to_cidr(n_mask))
+        return "%s " % ip
+    return "%s %s " % (ip, ip_invert_netmask(mask))
+
+
+#
+# --------------------------------------------------------------------------------
+
+SINGLEQUOTE_RE = re.compile(r"'")
+DOUBLEQUOTE_RE = re.compile(r'"')
+WHITESPACE_RE = re.compile(r"\s")
+
+COMMAND_DPID_RE = re.compile(r'^(([A-Fa-f\d]){2}:?){7}[A-Fa-f\d]{2}$')
+COMMAND_SEPARATORS_RE = re.compile(r'[>;|]') # XXX belongs in some global
+
+
+def quote_string(value):
+    """
+    Return a quoted version of the string when there's imbedded
+    spaces.  Worst case is when the string has both single and
+    double quotes.
+    """
+
+    if SINGLEQUOTE_RE.search(value):
+        if DOUBLEQUOTE_RE.search(value):
+            new_value = []
+            for c in value:
+                if c == '"':
+                    new_value.append("\\")
+                new_value.append(c)
+            return ''.join(new_value)
+        else:
+            return '"%s"' % value
+    elif (DOUBLEQUOTE_RE.search(value) or
+          WHITESPACE_RE.search(value) or
+          COMMAND_SEPARATORS_RE.search(value)):
+        return "'%s'" % value
+    else:
+        return value
+
+
+#
+# --------------------------------------------------------------------------------
+
+def add_delim(strings_list, delim):
+    """
+    Add 'delim' to each string entry in the list, typically used to add a space
+    to completion choices for entries which aren't prefixes.
+    word mean a 
+    
+    """
+    return [str(x) + delim for x in strings_list]
+
+
+#
+# --------------------------------------------------------------------------------
+
+def convert_case(case, value):
+    """
+    Convert value to the requested case
+    """
+    if case == None:
+        return value
+    elif case == 'lower':
+        return value.lower()
+    elif case == 'upper':
+        return value.upper()
+    return value
+
+
+TAIL_INT_RE = re.compile(r'^(.*[^0-9])(\d+)$')
+
+
+#
+# --------------------------------------------------------------------------------
+
+def trailing_integer_cmp(x,y):
+    """
+    sorted() comparison function.
+
+    Used when the two keys may possibly have trailing numbers,
+    if these are compared using a typical sort, then the trailing
+    numbers will be sorted alphabetically, not numerically.  This
+    is most obvious when interfaces, for example Eth1, Eth10, Eth2
+    are sorted.   Alphabetically the order is as already shown, but
+    the desired sort order is Eth1, Eth2, Eth10
+    """
+
+    def last_digit(value):
+        # only interested in sequences tailing a digit, where
+        # the first charcter isn't a digit, used to sidestep tail_int re
+        if len(value) >= 2:
+            last_char = ord(value[-1])
+            if last_char >= ord('0') and last_char <= ord('9'):
+                first_char = ord(value[0])
+                if first_char < ord('0') or first_char > ord('9'):
+                    return True
+        return False
+
+    if type(x) == int and type(y) == int:
+        return cmp(x,y)
+    if last_digit(x) and last_digit(y):
+        x_m = TAIL_INT_RE.match(x)
+        y_m = TAIL_INT_RE.match(y)
+        c = cmp(x_m.group(1), y_m.group(1))
+        if c:
+            return c
+        c = cmp(int(x_m.group(2)), int(y_m.group(2)))
+        if c:
+            return c
+    else:
+        c = cmp(try_int(x), try_int(y))
+        if c != 0:
+            return c;
+
+    return 0
+
+COMPLETION_TAIL_INT_RE = re.compile(r'^([A-Za-z0-9-]*[^0-9])(\d+) $')
+
+#
+# --------------------------------------------------------------------------------
+
+def completion_trailing_integer_cmp(x,y):
+    """
+    sorted() comparison function.
+
+    This is used for completion values sorting.
+
+    This function differs from trailing_integer_cmp, since for completions,
+    the last character may be a space to indicate that the selection is
+    complete.  If the last character is a space, the character ahead
+    of it is used instead.
+    """
+
+    x_v = x
+    if isinstance(x_v, tuple):
+        x_v = x_v[0]
+    if x[-1] == ' ':
+        x_v = x[:-1]
+
+    y_v = y
+    if isinstance(y_v, tuple):
+        y_v = y_v[0]
+    if y[-1] == ' ':
+        y_v = y[:-1]
+    return trailing_integer_cmp(x_v, y_v)
+
+
+#
+# --------------------------------------------------------------------------------
+
+ABERRANT_PLURAL_MAP = {
+    'appendix': 'appendices',
+    'barracks': 'barracks',
+    'cactus': 'cacti',
+    'child': 'children',
+    'criterion': 'criteria',
+    'deer': 'deer',
+    'echo': 'echoes',
+    'elf': 'elves',
+    'embargo': 'embargoes',
+    'focus': 'foci',
+    'fungus': 'fungi',
+    'goose': 'geese',
+    'hero': 'heroes',
+    'hoof': 'hooves',
+    'index': 'indices',
+    'knife': 'knives',
+    'leaf': 'leaves',
+    'life': 'lives',
+    'man': 'men',
+    'mouse': 'mice',
+    'nucleus': 'nuclei',
+    'person': 'people',
+    'phenomenon': 'phenomena',
+    'potato': 'potatoes',
+    'self': 'selves',
+    'syllabus': 'syllabi',
+    'tomato': 'tomatoes',
+    'torpedo': 'torpedoes',
+    'veto': 'vetoes',
+    'woman': 'women',
+    }
+
+VOWELS = set('aeiou')
+
+def pluralize(singular):
+    """Return plural form of given lowercase singular word (English only). Based on
+    ActiveState recipe http://code.activestate.com/recipes/413172/
+    
+    >>> pluralize('')
+    ''
+    >>> pluralize('goose')
+    'geese'
+    >>> pluralize('dolly')
+    'dollies'
+    >>> pluralize('genius')
+    'genii'
+    >>> pluralize('jones')
+    'joneses'
+    >>> pluralize('pass')
+    'passes'
+    >>> pluralize('zero')
+    'zeros'
+    >>> pluralize('casino')
+    'casinos'
+    >>> pluralize('hero')
+    'heroes'
+    >>> pluralize('church')
+    'churches'
+    >>> pluralize('x')
+    'xs'
+    >>> pluralize('car')
+    'cars'
+
+    """
+    if not singular:
+        return ''
+    plural = ABERRANT_PLURAL_MAP.get(singular)
+    if plural:
+        return plural
+    root = singular
+    try:
+        if singular[-1] == 'y' and singular[-2] not in VOWELS:
+            root = singular[:-1]
+            suffix = 'ies'
+        elif singular[-1] == 's':
+            if singular[-2] in VOWELS:
+                if singular[-3:] == 'ius':
+                    root = singular[:-2]
+                    suffix = 'i'
+                else:
+                    root = singular[:-1]
+                    suffix = 'ses'
+            else:
+                suffix = 'es'
+        elif singular[-2:] in ('ch', 'sh'):
+            suffix = 'es'
+        else:
+            suffix = 's'
+    except IndexError:
+        suffix = 's'
+    plural = root + suffix
+    return plural