def on_readable(self, fd): # Handle input on stdin of the format: # NAME (add|delete) IPADDR INTERFACE # and adds/deletes a flow that matches on that destination ip address # and outputs that flow on the given interface buf = os.read(fd, 4096).strip() if not buf: self.agentMgr_.exit() for line in buf.split("\n"): if not line: continue m = re.search("(\S+) (add|delete) (\d+.\d+.\d+.\d+) +(\S+)", line) if not m: print "Could not match line:", line continue name = m.group(1) operation = m.group(2) if operation == "add": match = createMatch(ipDst=m.group(3), ipDstMask="255.255.255.255") outputIntf = DAsdk.IntfId(m.group(4)) action = createAction(outputIntfs=[ outputIntf ]) priority = 100 entry = DAsdk.FlowEntry(name, match, action, priority) self.directFlowMgr_.flow_entry_set(entry) else: assert operation == "delete" self.directFlowMgr_.flow_entry_del(name)
def _buildNexthopGroups(self): groups = self.config_.nexthop_groups for name, data in groups.iteritems(): if data.type not in NEXTHOP_GROUP_TYPE: sys.stderr.write('Unknown nexthop group type="%s"' % data.type) continue t = data.type.lower() group = DAsdk.NexthopGroup(name, NEXTHOP_GROUP_TYPE.get(t)) # Set common parameters for i, dst in enumerate(data.dst_ips): ip = get_ip_addr(dst) if ip is not None: print 'Adding IP' group.destination_ip_set(i, ip) if t == 'ipinip' or t == 'gre': if data.src_intf: if self.intf_mgr.exists(DAsdk.IntfId(data.src_intf)): group.source_intf_is(DAsdk.IntfId(data.src_intf)) elif data.src_ips: pass # not yet supported elif t == 'mpls': sys.stderr.write('MPLS nexthop-groups are not yet supported\n') # Set the nexthop group print 'Setting nexthop group:', name self.nexthop_group_mgr.nexthop_group_set(group)
def watch_policy(self): print 'Removing all watches for %s' % self.watches_ for name in self.watches_: self.watch_policy_map( DAsdk.PolicyMapKey(name, DAsdk.POLICY_FEATURE_PBR), False) self.watches_ = frozenset(self.config_.policy.iterkeys()) print 'Adding new watches for %s' % self.config_.policy.keys() for name in self.config_.policy: self.watch_policy_map( DAsdk.PolicyMapKey(name, DAsdk.POLICY_FEATURE_PBR), True)
def _applyToInterfaces(self): interface_policy = self.config_.interface_policy for intf_name, data in interface_policy.iteritems(): policy_map_key = DAsdk.PolicyMapKey(data.policy, DAsdk.POLICY_FEATURE_PBR) intf_id = DAsdk.IntfId(intf_name) if self.intf_mgr.exists(intf_id): print 'Interface %s exists, applying policy' % intf_id.to_string( ) self.policy_map_mgr.policy_map_apply(policy_map_key, intf_id, DAsdk.ACL_IN, True) else: print 'Interface %s does not exist' % intf_id.to_string() print 'Finished applying policy'
def _buildClassMaps(self): classifiers = self.config_.classifiers for name, classifier in classifiers.iteritems(): key = DAsdk.PolicyMapKey(name, DAsdk.POLICY_FEATURE_PBR) class_map = DAsdk.ClassMap(key) for rule_index, match in enumerate(classifier.matches): print 'Adding to class map:', name, 'seq:', str(rule_index + 1), match rule_key = DAsdk.AclKey(match.acl_name, DAsdk.ACL_TYPE_IPV4) rule = DAsdk.ClassMapRule(rule_key) # Set the action for the rule class_map.rule_set(rule_index + 1, rule) self.class_map_mgr.class_map_is(class_map) cm = self.class_map_mgr.class_map(key) print 'Set class map:', name, 'now with', len(cm.rules()), 'rules'
def _buildPolicyMaps(self): policies = self.config_.policy for name, data in policies.iteritems(): # add the class map rule_key = DAsdk.PolicyMapKey(data.classifier, DAsdk.POLICY_FEATURE_PBR) rule = DAsdk.PolicyMapRule(rule_key) for actionName in data.actions: # raises KeyError on uknown action names action = self.actions_[actionName] rule.action_set(action) key = DAsdk.PolicyMapKey(name, DAsdk.POLICY_FEATURE_PBR) policy_map = DAsdk.PolicyMap(key) policy_map.rule_set(1, rule) self.policy_map_mgr.policy_map_is(policy_map)
def __init__(self, filename): self.agent_mgr = sdk.get_agent_mgr() self.tracer = DAsdk.Tracer("FileWatcher") DAsdk_utils.DASdkAgent.__init__(self) DAsdk.AgentHandler.__init__(self, self.agent_mgr) DAsdk.FdHandler.__init__(self) self.tracer.trace0("Watching %r" % filename) self.wm = pyinotify.WatchManager() # Pass the InotifyHandler's that wille be created a pointer to # ourselves by wrapping it in functools.partial: handler = functools.partial(InotifyHandler, parent=self) # pylint: disable-msg=E1101 self.wm.watch_transient_file(filename, pyinotify.IN_MODIFY, handler) # pylint: enable-msg=E1101 self.inotifier = pyinotify.AsyncNotifier(self.wm, InotifyHandler(parent=self)) # We coalesce events because some editors (or unix operations) cause # multiple changes on one save. self.inotifier.coalesce_events(True) # Now that we've set up our inotify watcher and notifier, grab # the underlying file descriptor and pass it to the SDK to be # watched. When the OS detects a change to the file, we'll self.inotify_fd = self.wm.get_fd() self.tracer.trace0("Watching inotify fd: %d" % self.inotify_fd) self.watch_readable(self.inotify_fd, True)
def _buildActions(self): for name, action in self.config_.actions.iteritems(): if action.type == 'drop': act = DAsdk.PolicyMapAction(DAsdk.POLICY_ACTION_DROP) elif action.type == 'nexthop_group' and action.nexthop_group: act = DAsdk.PolicyMapAction(DAsdk.POLICY_ACTION_NEXTHOP_GROUP) act.nexthop_group_name_is(action.nexthop_group) elif action.type == 'nexthop' and action.nexthops: act = DAsdk.PolicyMapAction(DAsdk.POLICY_ACTION_NEXTHOP) for nh in action.nexthops: hop = get_ip_addr(nh) if hop is not None: act.nexthop_set(hop) else: raise ConfigError('Action type="%s" not supported' % action.type) self.actions_[name] = act
def createMatch(inputIntfs=None, ethSrc=None, ethSrcMask="ff:ff:ff:ff:ff:ff", ethDst=None, ethDstMask="ff:ff:ff:ff:ff:ff", ethType=None, ipSrc=None, ipSrcMask="255.255.255.255", ipDst=None, ipDstMask="255.255.255.255"): match = DAsdk.FlowMatch() matchFieldSet = DAsdk.FlowMatchFieldSet() if inputIntfs is not None: # Until we have correct swig support for std::set, # need to add each interface individually matchFieldSet.input_intfs_is(True) match.input_intfs_is(tuple([intfId for intfId in inputIntfs])) if ethSrc is not None: matchFieldSet.eth_src_is(True) ethSrc = DAsdk.EthAddr(ethSrc) ethSrcMask = DAsdk.EthAddr(ethSrcMask) match.eth_src_is(ethSrc, ethSrcMask) if ethDst is not None: matchFieldSet.eth_dst_is(True) ethDst = DAsdk.EthAddr(ethDst) ethDstMask = DAsdk.EthAddr(ethDstMask) match.eth_dst_is(ethDst, ethDstMask) if ethType is not None: matchFieldSet.eth_type_is(True) match.eth_type_is(ethType) if ipSrc is not None: matchFieldSet.ip_src_is(True) ipSrc = DAsdk.IpAddr(ipSrc) ipSrcMask = DAsdk.IpAddr(ipSrcMask) match.ip_src_is(ipSrc, ipSrcMask) if ipDst is not None: matchFieldSet.ip_dst_is(True) ipDst = DAsdk.IpAddr(ipDst) ipDstMask = DAsdk.IpAddr(ipDstMask) match.ip_dst_is(ipDst, ipDstMask) match.match_field_set_is(matchFieldSet) return match
def __init__(self, agentMgr, intfMgr, interfaceName): print "This program controls the admin enabled state of the given interface" print " - 'shutdown' will disable the interface" print " - any other text will enable the interface" print " - an empty line will quit this program" self.agentMgr_ = agentMgr self.intfMgr_ = intfMgr self.intfObj_ = DAsdk.IntfId(interfaceName) DAsdk.AgentHandler.__init__(self, agentMgr) DAsdk.FdHandler.__init__(self) self.eventCount = 0
def __init__(self, intfMgr, agentMgr): DAsdk.AgentHandler.__init__(self, agentMgr) DAsdk.IntfHandler.__init__(self, intfMgr) self.tracer = DAsdk.Tracer("DASdkInterfaceMonitor") self.intfMgr_ = intfMgr self.agentMgr_ = agentMgr # Keep track of the total number of state changes. This number # gets reset whenever the agent is restarted. self.numIntfChanges_ = 0 self.tracer.trace0("Constructed")
def _buildPortSpec(self, portspec): op = portspec.get('op', 'eq').lower() # default to port equals ports = portspec.get('ports', []) if op == 'eq': return DAsdk.AclPortSpec(DAsdk.ACL_RANGE_EQ, ports) elif op == 'neq': return DAsdk.AclPortSpec(DAsdk.ACL_RANGE_NEQ, ports) elif op == 'range': if len(ports) != 2: raise ConfigError('Must provide exactly two ports for "range"') return DAsdk.AclPortSpec(DAsdk.ACL_RANGE_BETWEEN, ports) elif op == 'gt': if len(ports) != 1: raise ConfigError('Must provide only one port for "gt"') return DAsdk.AclPortSpec(DAsdk.ACL_RANGE_GT, ports) elif op == 'lt': if len(ports) != 1: raise ConfigError('Must provide only one port for "lt"') return DAsdk.AclPortSpec(DAsdk.ACL_RANGE_LT, ports) else: raise ConfigError('Unknown port match operation "%s"' % op)
def createAction(outputIntfs=None, ethSrc=None, ethDst=None, ipSrc=None, ipDst=None): action = DAsdk.FlowAction() actionSet = DAsdk.FlowActionSet() if outputIntfs is not None: # Until we have correct swig support for std::set, # need to add each interface individually actionSet.set_output_intfs_is(True) action.output_intfs_is(tuple([intfId for intfId in outputIntfs])) if ethSrc is not None: actionSet.set_eth_src_is(True) newEthSrc = DAsdk.EthAddr(ethSrc) action.eth_src_is(newEthSrc) if ethDst is not None: actionSet.set_eth_dst_is(True) newEthDst = DAsdk.EthAddr(ethDst) action.eth_dst_is(newEthDst) if ipSrc is not None: actionSet.set_ip_src_is(True) newIpSrc = DAsdk.IpAddr(ipSrc) action.ip_src_is(newIpSrc) if ipDst is not None: actionSet.set_ip_dst_is(True) newIpDst = DAsdk.IpAddr(ipDst) action.ip_dst_is(newIpDst) action.action_set_is(actionSet) return action
def main(): # Because we use `print' and we want our stuff to show up in the # agent logs immediately. os.environ['PYTHONUNBUFFERED'] = '1' # TODO: Use tracing instead. # Config file path has to be provided by the environment variable filename = os.environ.get('POLICY_ROUTER_CONFIG') # Obtain a reference to the DA SDK sdk = DAsdk.Sdk() # Instantiate the policy router application _ = PolicyHandler(sdk, filename) # Run the agent until terminated by a signal sdk.main_loop(['PolicyRouter'])
def __init__(self, sdk, config_file, policy_handler, poll_interval=0.5): self.config_file_ = config_file self.sdk_ = sdk self.policy_handler_ = policy_handler self.poll_interval_ = poll_interval self.wm_ = pyinotify.WatchManager() mask = pyinotify.IN_MODIFY | pyinotify.IN_CREATE | pyinotify.IN_DELETE handler = functools.partial(InotifyHandler, handler=policy_handler) # Allow coalescing, so that delete/recreate (as opposed to modify) doesn't # cause us to delete the policy. self.notifier_ = pyinotify.Notifier(self.wm_, handler, timeout=10) self.notifier_.coalesce_events() self.watch_ = self.wm_.watch_transient_file(self.config_file_, mask, handler) DAsdk.TimeoutHandler.__init__(self, self.sdk_.get_timeout_mgr()) self.timeout_time_is(DAsdk.now())
def _buildAcls(self): for aclname, aclrules in self.config.match.iteritems(): key = DAsdk.AclKey(aclname, DAsdk.ACL_TYPE_IPV4) # todo support ipv6 also for i, rule in enumerate(aclrules): aclRule = DAsdk.AclRuleIp() if rule.proto: pr = PROTOCOLS.get(rule.proto.lower()) if pr: aclRule.ip_protocol_is(pr) else: sys.stderr.write('Invalid protocol name "%s"', rule.proto) if rule.src_ip: try: srcPfx = DAsdk.IpPrefix(rule.src_ip) addr = DAsdk.IpAddrMask(srcPfx.network(), srcPfx.prefix_length()) aclRule.source_addr_is(addr) except DAsdk.Error: sys.stderr.write('bad IP address: %s\n', rule.src_ip) continue if rule.dst_ip: try: dstPfx = DAsdk.IpPrefix(rule.dst_ip) addr = DAsdk.IpAddrMask(dstPfx.network(), dstPfx.prefix_length()) aclRule.destination_addr_is(addr) except DAsdk.Error: sys.stderr.write('bad IP address: %s\n', rule.dst_ip) continue if rule.sport: try: spec = self._buildPortSpec(rule.sport) except ConfigError as e: sys.stderr.write('Invalid port spec %r: %s\n', spec, e) else: aclRule.source_port_is(spec) if rule.dport: try: spec = self._buildPortSpec(rule.dport) except ConfigError as e: sys.stderr.write('Invalid port spec %r: %s\n', spec, e) else: aclRule.destination_port_is(spec) self.acl_mgr.acl_rule_set(key, i, aclRule) self.acl_mgr.acl_commit()
def on_initialized(self): # Resynchronize initial flows # Uncomment once we've added hitless resync support to the API: # self.directFlowMgr_.resync_init() # Add default flow entry. In this case, drop all IPv4 traffic. defaultEntry = DAsdk.FlowEntry("DefaultFlow", createMatch(ipDst="0.0.0.0", ipDstMask="0.0.0.0"), createAction(outputIntfs=[]), 0) self.directFlowMgr_.flow_entry_set(defaultEntry) # Uncomment once we've added hitless resync support to the API: # self.directFlowMgr_.resync_complete() # Now start accepting input on stdin self.watch_all_flows(True) self.watch_readable(sys.stdin.fileno(), True)
def get_ip_addr(ip_addr): try: return DAsdk.IpAddr(ip_addr) except DAsdk.Error as e: sys.stderr.write('Invalid IP address: %s (%s)' % (ip_addr, e))
import DAsdk import sys class MyIntfHandler( DAsdk.AgentHandler, DAsdk.IntfHandler ): def __init__( self, intfMgr, agentMgr ): DAsdk.AgentHandler.__init__( self, agentMgr ) DAsdk.IntfHandler.__init__( self, intfMgr ) # pylint: disable-msg=W0233 self.intfMgr_ = intfMgr self.numIntfChanges_ = 0 print "Constructed" # Callback provided by AgentHandler when all state is synchronized def on_initialized( self ): print "We are initialized!" self.watch_all_intfs( True ) # pylint: disable-msg=E1101 # Callback provided by IntfHandler when an interface's configuration changes def on_oper_status( self, intfId, operState ): intfState = 'up' if operState == DAsdk.INTF_OPER_UP else 'down' print "The state of", intfId.to_string(), "is now", intfState self.numIntfChanges_ += 1 print " We have seen", self.numIntfChanges_, "state changes" self.intfMgr_.description_is(intfId, "Changed state [%d]" % self.numIntfChanges_) sdk = DAsdk.Sdk() mta = MyIntfHandler( sdk.get_intf_mgr(), sdk.get_agent_mgr() ) sdk.main_loop( sys.argv )
def on_timeout(self): self.poll() self.timeout_time_is(DAsdk.now() + self.poll_interval_)
def main(args): sdk = DAsdk.Sdk("DirectFlowProgrammer") programmer = DirectFlowProgrammer(sdk.get_agent_mgr(), sdk.get_directflow_mgr()) sdk.main_loop(sys.argv) print "Saw %d flow status changes" % programmer.changes
name = self.agentMgr.agent_option("name") if not name: # No name initially set self.agentMgr.status_set("greeting", "Welcome! What is your name?") else: # Handle the initial state self.on_agent_option("name", name) def on_agent_option(self, optionName, value): if optionName == "name": if not value: self.tracer.trace3("Name deleted") self.agentMgr.status_set("greeting", "Goodbye!") else: # Time for some social networking! self.tracer.trace3("Saying hi to %s" % value) self.agentMgr.status_set("greeting", "Hello %s!" % value) def on_agent_enabled(self, enabled): if not enabled: self.tracer.trace0("Shutting down") self.agentMgr.status_set("greeting", "Adios!") self.agentMgr.agent_shutdown_complete_is(True) if __name__ == "__main__": sdk_ = DAsdk.Sdk() # Assign the agent instance to a variable so it remains in scope and # is not deleted: _ = HelloWorldAgent(sdk_) sdk_.main_loop(sys.argv)
def __init__(self, sdk): self.agentMgr = sdk.get_agent_mgr() self.tracer = DAsdk.Tracer("HelloWorldPythonAgent") DAsdk.AgentHandler.__init__(self, self.agentMgr) self.tracer.trace0("Python agent constructed")
def main(args): sdk = DAsdk.Sdk() testAgent = MyTestAgent(sdk.get_agent_mgr(), sdk.get_intf_mgr(), "Ethernet1") sdk.main_loop(args) print "Handled %d events" % testAgent.eventCount