Adding ONOS Segment Routing CLI files to new repo
diff --git a/cli/rest_to_model.py b/cli/rest_to_model.py
new file mode 100755
index 0000000..7ef25a9
--- /dev/null
+++ b/cli/rest_to_model.py
@@ -0,0 +1,649 @@
+#
+# Copyright (c) 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.
+#
+
+import collections
+import urllib
+import error
+import json
+import modi
+import utif
+import url_cache
+
+#
+# json based on non model based query
+#
+
+def rest_to_model_init(bs, modi):
+    global sdnsh, mi
+
+    sdnsh = bs
+    mi = modi
+
+#
+# --------------------------------------------------------------------------------
+
+def check_rest_result(result, message = None):
+    if isinstance(result, collections.Mapping):
+        error_type = result.get('error_type')
+        if error_type:
+            raise error.CommandRestError(result, message)
+
+
+#
+# --------------------------------------------------------------------------------
+
+def get_model_from_url(obj_type, data):
+    """
+    Intended to be used as a conversion tool, to provide a way
+    to move model requests to specific url requests
+    """
+
+    onos = 1
+    startswith = '__startswith'
+    data = dict(data)
+
+    if mi.obj_type_has_model(obj_type):
+        print "MODEL_URL: %s not supported" % obj_type
+        return []
+    #
+    # save sort field
+    sort = data.get('orderby', None)
+    if sort:
+        del data['orderby']
+
+    obj_type_url = mi.obj_type_has_url(obj_type)
+    url = "http://%s/rest/v1/" % sdnsh.controller + obj_type_url
+
+    # for the devices query, be sensitive to the types of queries
+    # since the devices query doesn't understand the relationship
+    # between the vlan=='' for address_space == 'default'.
+    if obj_type_url == 'device':
+        if 'vlan' in data and data['vlan'] == '':
+            del data['vlan']
+
+    # data items need to be included into the query.
+    if data:
+        url += '?'
+        url += urllib.urlencode(data)
+    if sdnsh.description:   # description debugging
+        print "get_model_from_url: request ", obj_type, data, url
+    #
+    # cache small, short time results
+    entries = url_cache.get_cached_url(url)
+    if entries != None:
+        if sdnsh.description:   # description debugging
+            print 'get_model_from_url: using cached result for ', url
+    else:
+        try:
+            result = sdnsh.store.rest_simple_request(url)
+            check_rest_result(result)
+            entries = json.loads(result)
+        except Exception, e:
+            if sdnsh.description:   # description debugging
+                print "get_model_from_url: failed request %s: '%s'" % (url, e)
+            entries = []
+        
+        url_cache.save_url(url, entries)
+
+    key = mi.pk(obj_type)
+
+    # convert specific types XXX need better generic mechanism
+    result = []
+    if obj_type == 'host-attachment-point':
+        for entry in entries:
+            # it is possible for multiple mac's to appear here 
+            # as long as a single ip is associated with the device.
+            if len(entry['mac']) > 1:
+                raise error.CommandInternalError("host (attachment point): >1 mac")
+            mac = entry['mac'][0]
+            aps = entry['attachmentPoint']
+            lastseen = entry['lastSeen']
+            for ap in aps:
+                status = ap.get('errorStatus', '')
+                if status == None:
+                    status = ''
+                if len(entry['vlan']) == 0:
+                    result.append({'id'           : mac + '|' + ap['switchDPID'] +
+                                                          '|' + str(ap['port']),
+                                   'mac'          : mac,
+                                   'switch'       : ap['switchDPID'],
+                                   'ingress-port' : ap['port'],
+                                   'status'       : status,
+                                   'last-seen'    : lastseen,
+                                   'address-space': entry['entityClass'],
+                                   })
+                else:
+                    for vlan in entry['vlan']:
+                        result.append({'id'           : mac + '|' + ap['switchDPID'] +
+                                                              '|' + str(ap['port']),
+                                       'mac'          : mac,
+                                       'vlan'         : vlan,
+                                       'switch'       : ap['switchDPID'],
+                                       'ingress-port' : ap['port'],
+                                       'status'       : status,
+                                       'last-seen'    : lastseen,
+                                       'address-space': entry['entityClass'],
+                                       })
+    elif obj_type == 'host-network-address':
+
+        for entry in entries:
+            if len(entry['mac']) > 1:
+                raise error.CommandInternalError("host (network-address): >1 mac")
+            mac = entry['mac'][0]
+            ips = entry['ipv4']
+            lastseen = entry['lastSeen']
+            for ip in ips:
+                if len(entry['vlan']) == 0:
+                    result.append({'id'            : mac + '|' + ip,
+                                   'mac'           : mac,
+                                   'ip-address'    : ip,
+                                   'address-space' : entry['entityClass'],
+                                   'last-seen'     : lastseen })
+                else:
+                    for vlan in entry['vlan']:
+                        result.append({'id'            : mac + '|' + ip,
+                                       'mac'           : mac,
+                                       'vlan'          : vlan,
+                                       'ip-address'    : ip,
+                                       'address-space' : entry['entityClass'],
+                                       'last-seen'     : lastseen })
+    elif obj_type == 'host':
+        # For host queries, the device manager returns 'devices' which
+        # match specific criteria, for example, if you say ipv4 == 'xxx',
+        # the device which matches that criteris is returned.  However,
+        # there may be multiple ip addresses associated.
+        #
+        # this means that two different groups of values for these
+        # entities are returned.  fields like 'ipv4' is the collection
+        # of matching values, while 'ips' is the complee list.
+        # 'switch', 'port' are the match values, while 'attachment-points'
+        # is the complete list.
+        #
+
+        ip_match = data.get('ipv4')
+        ip_prefix = data.get('ipv4__startswith')
+
+        dpid_match = data.get('dpid')
+        dpid_prefix = data.get('dpid__startswith')
+
+        port_match = data.get('port')
+        port_prefix = data.get('port__startswith')
+
+        address_space_match = data.get('address-space')
+        address_space_prefix = data.get('address-space__startswith')
+
+        for entry in entries:
+            if onos==0:
+                if len(entry['mac']) > 1:
+                    raise error.CommandInternalError("host: >1 mac")
+                mac = entry['mac'][0]
+                lastseen = entry['lastSeen']
+            else:
+                mac = entry['mac']
+                lastseen = 0
+
+            ips = None
+            if not ip_match and not ip_prefix:
+                ipv4 = entry['ipv4']
+            elif ip_match:
+                ipv4 = [x for x in entry['ipv4'] if x == ip_match]
+            elif ip_prefix:
+                ipv4 = [x for x in entry['ipv4'] if x.startswith(ip_prefix)]
+
+            if len(entry['ipv4']):
+                ips = [{'ip-address' : entry['ipv4'], 'last-seen' : lastseen}]
+                    	    #for x in entry['ipv4'] ]
+            aps = None
+            switch = []
+            port = []
+
+            #dpid_match = entry.get('dpid')
+            if onos == 0:
+                attachPoint = 'attachmentPoint'
+                attachDpid = 'switchDPID'
+                attachPort = 'port'
+            else:
+                attachPoint = 'attachmentPoints'
+                attachDpid = 'dpid'
+                attachPort = 'portNumber'
+
+            if len(entry[attachPoint]):
+                aps = [{'switch' : x[attachDpid], 'ingress-port' : x[attachPort] }
+                       for x in entry[attachPoint]]
+
+                if not dpid_match and not dpid_prefix:
+                    switch = [x[attachDpid] for x in entry[attachPoint]]
+                elif dpid_match:
+                    switch = [x[attachDpid] for x in entry[attachPoint]
+                              if x[attachDpid] == dpid_match]
+                elif dpid_prefix:
+                    switch = [x[attachDpid] for x in entry[attachPoint]
+                              if x[attachDpid].startswith(dpid_prefix)]
+
+                if not port_match and not port_prefix:
+                    port = [x[attachPort] for x in entry[attachPoint]]
+                elif port_match:
+                    port = [x[attachPort] for x in entry[attachPoint]
+                            if x[attachPort] == port_match]
+                elif port_prefix:
+                    port = [x[attachPort] for x in entry[attachPoint]
+                            if x[attachPort].startswith(port_prefix)]
+
+            if onos == 0:
+                address_space = entry['entityClass']
+                dhcp_client_name = entry.get('dhcpClientName', '')
+                if address_space_match and address_space != address_space_match:
+                    continue
+                if address_space_prefix and not address_space.startswith(address_space_prefix):
+                    continue
+
+            if onos == 1:
+                id = '%s' % (mac)
+                result.append({'id'                : id,
+                           'mac'               : mac,
+                           'ips'               : ips,
+                           'ipv4'              : ipv4,
+                           'attachment-points' : aps,
+                           'dpid'              : switch,
+                           'port'              : port,
+                           'last-seen'         : 0})
+            else:
+                if len(entry['vlan']) == 0:
+                    id = '%s||%s' % (address_space, mac)
+                    result.append({'id'                : id,
+                               'mac'               : mac,
+                               'ips'               : ips,
+                               'ipv4'              : ipv4,
+                               'attachment-points' : aps,
+                               'dpid'              : switch,
+                               'port'              : port,
+                               'address-space'     : address_space,
+                               'dhcp-client-name'  : dhcp_client_name,
+                               'last-seen'         : lastseen})
+                else:
+                    for vlan in entry['vlan']:
+                        if address_space != 'default':
+                            id = '%s||%s' % (address_space, mac)
+                        else:
+                            id = '%s|%s|%s' % (address_space, vlan, mac)
+                    result.append({'id'                : id,
+                                   'mac'               : mac,
+                                   'vlan'              : vlan,
+                                   'ips'               : ips,
+                                   'ipv4'              : ipv4,
+                                   'attachment-points' : aps,
+                                   'dpid'              : switch,
+                                   'port'              : port,
+                                   'address-space'     : address_space,
+                                   'dhcp-client-name'  : dhcp_client_name,
+                                   'last-seen'         : lastseen})
+
+        # Also need to add hostConfig entries.
+        if not mi.obj_type_has_model('host-config'):
+            raise error.CommandInternalError("host-config: not served via model")
+        host_config = sdnsh.rest_query_objects('host-config', data)
+        if sdnsh.description:   # description debugging
+            print "get_model_from_url: adding host-config ", data, host_config
+        known_ids = [x['id'] for x in result]
+
+        for hc in host_config:
+            id = hc['id']
+            if id not in known_ids:
+                # be sensitive to search fields:
+                query_match = True
+                if len(data):
+                    for (d, dv) in data.items():
+                        # other ops aside from '='?
+                        if d.endswith(startswith):
+                            fn = d[:-len(startswith)]
+                            if not fn in hc or not hc[fn].startswith(dv):
+                                query_match = False
+                        elif (not d in hc) or dv != hc[d]:
+                            query_match = False
+                            break
+                            
+                if query_match:
+                    hc['attachment-points'] = None
+                    hc['dpid'] = None
+                    hc['ips'] = None
+                    hc['last-seen'] = None
+                    result.append(hc)
+
+    elif obj_type == 'vns-interface':
+        for entry in entries:
+            vns = entry['parentVNS']['name']
+            rule = entry['parentRule']
+            rule_name = 'default'
+            if rule and 'name' in rule:
+                rule_name = rule['name']
+
+            result.append({ key            : vns + '|' + entry['name'],
+                           'vns'           : vns,
+                           'address-space' : entry['parentVNS']['addressSpaceName'],
+                           'interface'     : entry['name'],
+                           'rule'          : rule_name,
+                           'last-seen'     : entry['lastSeen'],
+                          })
+
+        # also need to add vns-interface-config
+        if not mi.obj_type_has_model('vns-interface-config'):
+            raise error.CommandInternalError("vns-interface-config: not service via model")
+        vns_intf_config = sdnsh.rest_query_objects('vns-interface-config', data)
+        known_vns_intfs = [x[key] for x in result]
+        for vns_intf in vns_intf_config:
+            if not vns_intf[key] in known_vns_intfs:
+                vns_intf['rule'] = 'default'
+                result.append(vns_intf)
+
+    elif obj_type == 'host-vns-interface':
+
+        # mac matching works for the request
+
+        vns_match = data.get('vns')
+        vns_prefix = data.get('vns__startswith')
+
+        for entry in entries:
+            device = entry['device']
+            if len(device['mac']) > 1:
+                raise error.CommandInternalError("host (vns-interface): >1 mac")
+
+            device_last_seen = device['lastSeen']
+            address_space = device['entityClass']
+            mac = device['mac'][0]          # currently, should only be one
+
+            ips = None
+            if len(device['ipv4']):
+                ips = [{'ip-address' : x, 'last-seen' : device_last_seen}
+                        for x in device['ipv4'] ]
+
+            vlans = device.get('vlan', [])   # currently, should only be one
+            if len(vlans) == 0:              # iterate once when list is empty
+                vlans = [ '' ]
+
+            aps = None
+            if len(device['attachmentPoint']):
+                aps = [{'switch' : x['switchDPID'], 'ingress-port' : x['port'] }
+                       for x in device['attachmentPoint']]
+
+            for iface in entry.get('iface', []):
+                vns = iface['parentVNS']['name']
+                last_seen = iface['lastSeen']
+                if vns_match and vns_match != vns:
+                    continue
+                if vns_prefix and not vns.startswith(vns_prefix):
+                    continue
+
+                for vlan in vlans: # there's supposed to only be at most one vlan.
+                    if address_space != 'default' or type(vlan) != int:
+                        host = '%s||%s' % (address_space, mac)
+                    else:
+                        host = '%s|%s|%s' % (address_space, vlan, mac)
+
+                    result.append({key             : mac + '|' + vns + '|' + iface['name'],
+                                   'host'          : host,
+                                   'mac'           : mac,
+                                   'vlan'          : vlan,
+                                   'address-space' : address_space,
+                                   'ips'           : ips,
+                                   'attachment-points' : aps,
+                                   'vns'               : vns,
+                                   'interface'         : vns + '|' + iface['name'],
+                                   'last-seen'         : device_last_seen})
+    elif obj_type == 'switches':
+        switch_match = data.get('dpid')
+        switch_prefix = data.get('dpid__startswith')
+
+        # this synthetic obj_type's name is 'switches' in an attempt
+        # to disabigutate it from 'class Switch'
+        #TODO: Need figure out a better way to get url (Through sdncon framework)
+        url = "http://%s/rest/v1/mastership" % sdnsh.controller
+        try:
+            result2 = sdnsh.store.rest_simple_request(url)
+            check_rest_result(result2)
+            mastership_data = json.loads(result2)
+        except Exception, e:
+            if sdnsh.description:   # description debugging
+                print "get_model_from_url: failed request %s: '%s'" % (url, e)
+            entries = []
+        for entry in entries:
+            dpid = entry.get('dpid')
+            if(dpid in mastership_data.keys()):
+                #As there is only one master for switch
+                controller = mastership_data[dpid][0].get('controllerId')
+            else:
+                controller = None
+            if switch_match and switch_match != entry['dpid']:
+                continue
+            if switch_prefix and not entry['dpid'].startswith(switch_prefix):
+                continue
+            if onos == 1:
+                result.append({
+                   'dpid'                : entry['dpid'],
+                   'switch-alias'        : entry['stringAttributes']['name'],
+                   'connected-since'     : entry['stringAttributes']['ConnectedSince'],
+                   'ip-address'          : entry['stringAttributes']['remoteAddress'],
+                   'type'                : entry['stringAttributes']['type'],
+                   'controller'          : controller
+                })
+            else:
+                attrs = entry['attributes']
+                actions = entry['actions']
+                capabilities = entry['capabilities']
+                inet_address = entry.get('inetAddress')
+                ip_address = ''
+                tcp_port = ''
+                if inet_address:
+                    # Current Java value looks like: /192.168.2.104:38420
+                    inet_parts = inet_address.split(':')
+                    ip_address = inet_parts[0][1:]
+                    tcp_port = inet_parts[1]
+    
+                result.append({
+                   'dpid'                : entry['dpid'],
+                   'connected-since'     : entry['connectedSince'],
+                   'ip-address'          : ip_address,
+                   'tcp-port'            : tcp_port,
+                   'actions'             : actions,
+                   'capabilities'        : capabilities,
+                   'dp-desc'             : attrs.get('DescriptionData', ''),
+                   'fast-wildcards'      : attrs.get('FastWildcards', ''),
+                   'supports-nx-role'    : attrs.get('supportsNxRole', ''),
+                   'supports-ofpp-flood' : attrs.get('supportsOfppFlood', ''),
+                   'supports-ofpp-table' : attrs.get('supportsOfppTable', ''),
+                   'core-switch'         : False,
+                })
+        # now add switch-config
+
+        switch_config = sdnsh.rest_query_objects('switch-config', data)
+        known_dpids = dict([[x['dpid'], x] for x in result])
+
+        if onos == 0:
+            for sw in switch_config:
+                dpid = sw['dpid']
+                if not dpid in known_dpids:
+                    # be sensitive to search fields:
+                    query_match = True
+                    if len(data):
+                        for (d, dv) in data.items():
+                            # other ops aside from '='?
+                            if d.endswith(startswith):
+                                fn = d[:-len(startswith)]
+                                if not fn in sw or not sw[fn].startswith(dv):
+                                    query_match = False
+                            elif (not d in sw) or dv != sw[d]:
+                                query_match = False
+                                break
+    
+                    if query_match:
+                        sw['ip-address'] = ''
+                        sw['tcp-port'] = ''
+                        sw['connected-since'] = ''
+                        result.append(sw)
+                [dpid].update(sw)
+    elif obj_type == 'interfaces':
+
+        # These are called interfaces because the primary
+        # key is constructed with the interface name, not
+        # the port number.
+
+        # the 'switches' query to sdnplatform currently
+        # doesn't support searching for the interface
+        # names.  its done here instead
+
+        switch_match = data.get('dpid')
+        switch_prefix = data.get('dpid__startswith')
+
+        name_match = data.get('portName')
+        if name_match:
+            name_match = name_match.lower()
+        name_prefix = data.get('portName__startswith')
+        if name_prefix:
+            name_prefix = name_prefix.lower()
+
+        # this synthetic obj_type's name is 'switches' in an attempt
+        # to disabigutate it from 'class Switch'
+        for entry in entries:
+            dpid = entry['dpid']
+
+            if switch_match and switch_match != dpid:
+                continue
+            if switch_prefix and not dpid.startswith(switch_prefix):
+                continue
+
+            for p in entry['ports']:
+                portNumber = p['portNumber']
+                if onos == 0:
+                    name = p['name']
+                else:
+                    name = p['stringAttributes']['name']
+
+                if name_match and name.lower() != name_match:
+                    continue
+                if name_prefix and not name.lower().startswith(name_prefix):
+                    continue
+                if onos == 0:
+                    result.append({
+                                    'id'                 : '%s|%s' % (dpid,name),
+                                    'portNumber'         : portNumber,
+                                    'switch'             : dpid,
+                                    'portName'           : p['name'],
+                                    'config'             : p['config'],
+                                    'state'              : p['state'],
+                                    'advertisedFeatures' : p['advertisedFeatures'],
+                                    'currentFeatures'    : p['currentFeatures'],
+                                    'hardwareAddress'    : p['hardwareAddress'],
+                                  })
+                else:
+                    result.append({
+                                    'id'                 : '%s|%s' % (dpid,name),
+                                    'portNumber'         : portNumber,
+                                    'switch'             : dpid,
+                                    'portName'           : name,
+                                    'config'             : 0,
+                                    'state'              : p['state'],
+                                    'advertisedFeatures' : 0,
+                                    'currentFeatures'    : 0,
+                                    'hardwareAddress'    : 0,
+                                  })
+                
+
+    #
+    # order the result
+    if sort:
+        if sort[0] == '-':
+            sort = sort[1:]
+            # decreasing
+            if sdnsh.description:   # description debugging
+                print "get_model_from_url: order decreasing ", sort
+            result = sorted(result, key=lambda k:k.get(sort, ''),
+                                    cmp=lambda x,y : cmp(y,x))
+        else:
+            # increasing
+            if sdnsh.description:   # description debugging
+                print "get_model_from_url: order increasing ", sort
+            result = sorted(result, key=lambda k:k.get(sort, ''),
+                                    cmp=lambda x,y : cmp(x,y))
+    else:
+        # use tail-integer on the entries
+        if sdnsh.description:   # description debugging
+            print "get_model_from_url: pk ordering ", key
+        def sort_cmp(x,y):
+            for (idx, x_v) in enumerate(x):
+                c = cmp(utif.try_int(x_v), utif.try_int(y[idx]))
+                if c != 0:
+                    return c
+            return 0
+        result = sorted(result, key=lambda k:k.get(key, '').split('|'),
+                                cmp=lambda x,y : cmp(utif.try_int(x),utif.try_int(y)))
+
+    if sdnsh.description:   # description debugging
+        print "get_model_from_url: result ", obj_type, url, len(result)
+
+    return result
+
+
+def validate_switch():
+    """
+    If /rest/v1/switches is cached, perform some validations on it.
+
+    -- verify that the announced interfaces names are case insensitive
+    -- verify the names only appear once
+    """
+
+    def duplicate_port(entry, name):
+        dpid = entry['dpid']
+
+        print 'Warning: switch %s duplicate interface names: %s' % (dpid, name)
+        if sdnsh.debug_backtrace:
+            for port in entry['ports']:
+                if port['name'] == name:
+                    print 'SWTICH %s:%s PORT %s' %  (entry, name, port)
+
+    def not_case_sensitive(entry, name):
+        dpid = entry['dpid']
+
+        ports = {}
+        for port in entry['ports']:
+            if port['name'].lower() == name:
+                ports[port['name']] = port
+            
+        print 'Warning: switch %s case insentive interface names: %s' % \
+               (dpid, ' - '.join(ports.keys()))
+        if sdnsh.debug_backtrace:
+            for port in ports:
+                print 'SWTICH %s PORT %s' % (dpid, port)
+
+    url = "http://%s/rest/v1/switches" % sdnsh.controller
+    entries = url_cache.get_cached_url(url)
+    if entries:
+        for entry in entries:
+            dpid = entry['dpid']
+
+            # verify that the port names are unique even when case
+            # sensitive
+            all_names = [p['name'] for p in entry['ports']]
+            one_case_names = utif.unique_list_from_list([x.lower() for x in all_names])
+            if len(all_names) != len(one_case_names):
+                # Something is rotten, find out what.
+                for (i, port_name) in enumerate(all_names):
+                    # use enumerate to drive upper-triangle comparison
+                    for other_name in all_names[i+1:]:
+                        if port_name == other_name:
+                            duplicate_port(entry, port_name)
+                        elif port_name.lower() == other_name.lower():
+                            not_case_sensitive(entry, port_name)
+                    
+