Coder Social home page Coder Social logo

ansible-frr's Introduction

Table of Contents generated with DocToc

ansible-frr

Ansible role to install/configure FRR

NOTE: FRRouting (FRR) is an IP routing protocol suite for Linux and Unix platforms which includes protocol daemons for BFD, BGP, IS-IS, LDP, OSPF, PIM, and RIP.

Build Status

GitHub Actions

Molecule Test

Travis CI

Build Status

Requirements

For any required Ansible roles, review: requirements.yml

Role Variables

defaults/main.yml

Dependencies

Example Playbook

playbook.yml

Route Maps and Prefix Lists

Route Maps

Configuring Route Maps

Below is an example of Route Maps Configuration:

frr_route_map:
  RTBH:
    permit 10:
      interface: blackhole
      prefix_list: Bad_IPs
      origin: igp
      community: "12345:100"
      src: 2001:db8::bf03
  RTBH_IN:
    deny 10: []

General Options

IP/IPv6 Forwarding

Below is an example of enabling ip and ipv6 forwading:

frr_ip_forwarding: true
frr_ipv6_forwarding: true

To enable kernel forwarding, the role sets the sysctl variables net.ipv4.ip_forward and net.ipv6.conf.all.forwarding. To customize the location of the sysctl configuration, the following variable may be used:

frr_sysctl_file: /etc/sysctl.d/100-ansible-frr.conf

Next-hop tracking via default

Resolve nexthops using the default route; useful if BGP peer is only reachable via default gateway (disabled by default).

To enable:

frr_nht_resolve_default: true

Prefix Lists

Configuring Prefix Lists

Below is an example of Prefix List Configuration:

frr_prefix_list:
  Bad_IPs:
    05 permit:
      prefix: 192.168.88.0/24
      match: ge 32
    10 permit:
      prefix: 172.16.0.0/16
      match: le 32
frr_prefix_list_v6:
  Bad_IPs:
    05 permit:
      prefix: 1234:5678::/32
      match: ge 128

Access Lists

Configuring Access Lists

Below is an example

frr_access_list:
  - "10 permit 10.10.10.21/32"
  - "10 permit 192.168.0.0/17"
  - "101 permit ip 10.0.0.0 0.0.0.255 any"

Bidirectional Forwarding Detection (BFD)

Enable BFD

To enable BFD, make sure that bfdd: true is configured under:

frr_daemons:
  bfdd: true
  bgpd: false
  isisd: false
  ldpd: false
  nhrpd: false
  ospf6d: false
  ospfd: false
  pimd: false
  ripd: false
  ripngd: false
  zebra: true

Configuring BFD

BFD configuration is made under BGP neighbor settings. BFD for OSPF is not supported yet.

Supported Routing Protocols

Protocol Implemented Notes
BGP X Only initial config
OSPF X Only initial config
STATIC X Only initial config

BGP

Enable BGP

To enable BGP routing, make sure that bgpd: true is configured under:

frr_daemons:
  bfdd: false
  bgpd: false
  isisd: false
  ldpd: false
  nhrpd: false
  ospf6d: false
  ospfd: false
  pimd: false
  ripd: false
  ripngd: false
  zebra: true

Configuring BGP

In order to configure BGP, define the following based on your requirements:

frr_bgp:
  asns:
    65000:
      log_neighbor_changes: true
      no_ebgp_requires_policy: true
      timers: '3 9'
      other:
        - "bgp bestpath as-path multipath-relax"
        - "no bgp network import-check"
      neighbors:
        group1:
          asn: 66000
          is_peer_group: true
          multihop: 255
        192.168.250.11:
          asn: 65000
          default_originate: false
          description: node1
          next_hop_self: true
          timers_connect: 5
          v6only: true
          bfd_peer: true
          bfd_peer_detect_multiplier: 3
          bfd_peer_receive_interval: 50
          bfd_peer_transmit_interval: 50
          bfd_peer_echo_interval: 50
          bfd_peer_passive_mode: true
          bfd_peer_minimum_ttl: 253
          other:
            - "capability dynamic"
        192.168.250.12:
          asn: 65000
          default_originate: false
          description: node2
          next_hop_self: true
          v4_route_reflector_client: true
          password: secret
          bfd_peer: true
          bfd_peer_transmit_interval: 2000
          bfd_peer_echo_mode: true
          other:
            - "prefix-list Bad_IPs in"
        192.168.250.12:
          peer_group: group1
          description: far_away
      listen_range:
        192.168.250.0/24: group1
      networks:
        - "{{ frr_router_id }}/32"
        - "{{ hostvars[inventory_hostname]['ansible_enp0s8']['ipv4']['address'] }}/24"
      redistribute:
        - bgp
        - connected
        - kernel
        - ospf
        - static
      redistribute_v6:
        - bgp
        - connected
        - kernel

Example BGP

Below is an example of a BGP configuration:

frr_bgp:
  asns:
    65000:
      log_neighbor_changes: true
      af_v4:
        - "maximum-paths 2"
      af_v6:
        - "maximum-paths 2"
      af_evpn:
        - "advertise-all-vni"
        - "rd {{ frr_router_id }}:1"
      neighbors:
        192.168.250.11:
          asn: 65000
          default_originate: false
          description: node1
          next_hop_self: true
          af_v4:
            - "soft-reconfiguration inbound"
        192.168.250.12:
          asn: 65000
          default_originate: false
          description: node2
          next_hop_self: true
        "::1":
          asn: 65000
          default_originate: false
          description: node1
          next_hop_self: true
          af_v6:
            - "activate"
            - "soft-reconfiguration inbound"
        172.16.250.10:
          asn: internal
          timers_connect: 5
          description: "L2VPN EVPN neighbor"
          af_evpn:
            - "activate"
          other:
            - "capability extended-nexthop"
      networks:
        - "{{ frr_router_id }}/32"
        - "{{ hostvars[inventory_hostname]['ansible_enp0s8']['ipv4']['address'] }}/24"
        - "{{ hostvars[inventory_hostname]['ansible_enp0s9']['ipv4']['address'] }}/24"
        - "{{ hostvars[inventory_hostname]['ansible_enp0s10']['ipv4']['address'] }}/24"
        - "{{ hostvars[inventory_hostname]['ansible_enp0s16']['ipv4']['address'] }}/24"
      networks_v6:
        - "1::3/64"

Below is an example of a BGP summary based on the above configuration:

BGP table version is 13, local router ID is 1.1.1.1
Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
              i internal, r RIB-failure, S Stale, R Removed
Origin codes: i - IGP, e - EGP, ? - incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*> 1.1.1.1/32       0.0.0.0                  0         32768 i
*>i2.2.2.2/32       192.168.250.11           0    100      0 i
*>i3.3.3.3/32       192.168.250.12           0    100      0 i
*> 192.168.10.0     0.0.0.0                  0         32768 i
*>i192.168.11.0     192.168.250.11           0    100      0 i
*>i192.168.12.0     192.168.250.12           0    100      0 i
*> 192.168.20.0     0.0.0.0                  0         32768 i
*>i192.168.21.0     192.168.250.11           0    100      0 i
*>i192.168.22.0     192.168.250.12           0    100      0 i
*> 192.168.30.0     0.0.0.0                  0         32768 i
*>i192.168.31.0     192.168.250.11           0    100      0 i
*>i192.168.32.0     192.168.250.12           0    100      0 i
* i192.168.250.0    192.168.250.11           0    100      0 i
* i                 192.168.250.12           0    100      0 i
*>                  0.0.0.0                  0         32768 i

Displayed  13 routes and 15 total paths

OSPF

Enable OSPF

To enable OSPF routing, make sure that ospfd: true is configured under:

frr_daemons:
  bfdd: false
  bgpd: false
  isisd: false
  ldpd: false
  nhrpd: false
  ospf6d: false
  ospfd: false
  pimd: false
  ripd: false
  ripngd: false
  zebra: true

Configuring OSPF

In order to configure OSPF, define the following based on your requirements:

frr_ospf:
  areas:
    0:
      networks:
        - "{{ frr_router_id }}/32"
    1:
      networks:
        - "{{ hostvars[inventory_hostname]['ansible_enp0s8']['ipv4']['address'] }}/24"
      auth: true

    2:
      networks:
        - "{{ hostvars[inventory_hostname]['ansible_enp0s9']['ipv4']['address'] }}/24"
      type: nssa

  log_adjacency_changes: true
  passive_interfaces: # A list of interfaces to set passive
    - default
  redistribute: # A list of protocols to redistribute
    - bgp
    - connected
    - kernel
    - ospf
    - static
  distribute_list:
    - name: 10
      dir: out
      protocol: connected

VRF-aware OSPF

Each key under frr_ospf_vrf_enabled represents VRF name:

frr_ospf_vrf_enabled:
  public:
    redistribute:
    - bgp
    - connected
    passive_interfaces:
     - lo
    log_adjacency_changes: true
    areas:
      1:
        networks:
          - "{{ hostvars[inventory_hostname]['ansible_ens3']['ipv4']['address'] }}/30"
        auth: true
  mgmt:
    redistribute:
      - kernel
    areas:
      0:
        networks:
          - 172.16.0.0/12
      2:
        networks:
          - 192.168.0.0/16
        type: nssa

STATIC

Configuring STATIC routes

In order to configure static routes, define the following based on your requirements:

frr_static: # A dict. key = destination, value = nexthop
  10.0.0.0/8: 192.168.1.1
  1.1.1.1: 192.168.1.1
  1.1.1.2: blackhole
frr_static_v6: # A dict. key = destination, value = nexthop
  2001:0db8:85a3:8a2e::/64 2001::1

Interface Configuration

Interfaces

frr_interfaces: # A dict. key = iface name, value = iface data
  lo:
    ip: 10.0.0.0/32 # ip can be a single value or list
    ipv6: 2001:0db8:85a3:8a2e::1/64 # ipv6 can be a single value or list
    description: loopback
  eth0:
    ip: # ip can be a single value or list
      - 10.0.0.0/32
      - 172.16.0.0/32
    ipv6: # ipv6 can be a single value or list
      - 2001:0db8:85a3:8a2e::1/64
      - 2001:0db8:85a3:8a2e::2/64
    vrf: management # put interface in 'management' VRF
    auth:
      id: 1
      key: supersecret
    other:
      - "no ipv6 nd suppress-ra"
      - "link-detect"

NOTE: Device should have correct VRF assignment on each vrf-aware interface:

ip link set dev ${IFACE} master ${VRF}

Upgrade/Downgrade

NOTE: FRR is unable to be downgraded from 6.0.2 using this role.

You can upgrade or downgrade FRR by setting the following variable:

frr_version: 6.0.2 from frr_version: 6.0

Quagga configuration

NOTE: Quagga must be installed from the local repos of the OS

You can configure quagga instead of FRR by using the following variable:

routing_type: quagga

Additional Quagga-specific configurations

frr_bgp:
  asns:
    65000:
      neighbors:
        swp1:
          **interface: true**

License

MIT

Author Information

Larry Smith Jr.

Buy Me A Coffee

NOTE: Repo has been created/updated using https://github.com/mrlesmithjr/cookiecutter-ansible-role as a template.

ansible-frr's People

Contributors

dependabot[bot] avatar drwahl avatar elcomtik avatar floored1585 avatar fzylogic avatar metfan1981 avatar mrlesmithjr avatar nertwork avatar oddomatik avatar r-2st avatar sirhopcount avatar srv6d avatar tbaczynski avatar x86cpu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

ansible-frr's Issues

Issue with Ubuntu 18.04

Getting following while running this role.

TASK [ansible-frr : debian | Installing FRR 3.0.3] ************************************************************************************************************************
fatal: [devops01.muc.anuragbhatia.com]: FAILED! => {"changed": false, "msg": "Failed to download https://github.com/FRRouting/frr/releases/download/frr-3.0.3/frr_3.0.3-1_ubuntu18.04.1_amd64.deb, HTTP Error 404: Not Found"}

Install capability for RHEL8 ?

Hi,

RH is distributing frr-7.0.5.el8.rpm with RHEL 8, from the AppStream channel/repo. Can this role be setup to support installing that with dnf?

Will this role otherwise work for FRR versions > 7.0?

Thanks!

Add a 'metric' option to route maps

Is your feature request related to a problem? Please describe.
As far as I can see there is no way to add a metric to a route map with the current implementation.

Describe the solution you'd like
A single metric option on route-maps which allows you to define a metric/med

Additional context
Example Output:
image

Getting versions

Can we set up versioning for this? It would be nice to have a pinned version in Galaxy for instance. ๐Ÿ‘

Python3 compatibility

Ran into an issue with iteritems vs items . While trying to bring up the environment ../templates/etc/frr/frr.conf.j2 & ../templates/etc/frr/zebra.conf.j2 errored out with:

fatal: [spine1]: FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'dict object' has no attribute 'iteritems'"}
fatal: [spine2]: FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'dict object' has no attribute 'iteritems'"}
fatal: [leaf1]: FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'dict object' has no attribute 'iteritems'"}
fatal: [leaf2]: FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'dict object' has no attribute 'iteritems'"}
fatal: [leaf3]: FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'dict object' has no attribute 'iteritems'"}
fatal: [leaf4]: FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'dict object' has no attribute 'iteritems'"}
fatal: [compute1]: FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'dict object' has no attribute 'iteritems'"}
fatal: [compute2]: FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'dict object' has no attribute 'iteritems'"}

After doing some digging it was due to my installed version of ansible using python3:

>  ansible --version
ansible 2.9.2
  config file = None
  configured module search path = ['/Users/ross.thompson/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.7/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 3.7.7 (default, Mar 10 2020, 15:43:33) [Clang 11.0.0 (clang-1100.0.33.17)]

In the template files iteritems was changed to items for python3 compatibility. After that the environment was able to launch without issue:

PLAY RECAP *********************************************************************
compute1                   : ok=53   changed=20   unreachable=0    failed=0    skipped=44   rescued=0    ignored=0
compute2                   : ok=53   changed=20   unreachable=0    failed=0    skipped=44   rescued=0    ignored=0
leaf1                      : ok=34   changed=11   unreachable=0    failed=0    skipped=18   rescued=0    ignored=0
leaf2                      : ok=34   changed=11   unreachable=0    failed=0    skipped=18   rescued=0    ignored=0
leaf3                      : ok=34   changed=11   unreachable=0    failed=0    skipped=18   rescued=0    ignored=0
leaf4                      : ok=34   changed=11   unreachable=0    failed=0    skipped=18   rescued=0    ignored=0
spine1                     : ok=34   changed=11   unreachable=0    failed=0    skipped=18   rescued=0    ignored=0
spine2                     : ok=34   changed=11   unreachable=0    failed=0    skipped=18   rescued=0    ignored=0

Did a quick test to confirm python2 backwards compatibility to make sure this change would not break things:

Python 2.7.17 (default, Dec 23 2019, 21:25:33)
[GCC 4.2.1 Compatible Apple LLVM 11.0.0 (clang-1100.0.33.16)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> d={1:'one',2:'two',3:'three'}
>>> print 'd.items():'
d.items():
>>> for k,v in d.items():
...    if d[k] is v: print '\tthey are the same object'
...    else: print '\tthey are different'
...
	they are the same object
	they are the same object
	they are the same object
>>> print 'd.iteritems():'
d.iteritems():
>>> for k,v in d.iteritems():
...    if d[k] is v: print '\tthey are the same object'
...    else: print '\tthey are different'
...
	they are the same object
	they are the same object
	they are the same object
>>> quit()

Please let me know if there are any points that were missed and ill be happy to look into them.

Opened pull request #31 and noticed it is failing CI. Ill work on fixing that and update the issue once testing passes.

Thanks for your time!
-Ross

Permissions issue python-ipaddr

Great work!

debian.yml seems to have trouble with python-ipaddr. It generates a insufficient privileges issue on a fresh install. Was able to fix by ssh'ing into mrlesmithjr/xenial64 images and then 'sudo apt install python-ipaddr' resolved the issue.

Likely missing a 'become: true' statement in debian.yml. Not sure what indent level to add, so raising as an issue.

'frr_reload' is undefined

Hi!
I'm getting the following issue when running this role:

TASK [ansible-frr : add python frr tools to install list] **********************
fatal: [host]: FAILED! => {"msg": "The conditional check 'frr_reload == true' failed. The error was: error while evaluating conditional (frr_reload == true): 'frr_reload' is undefined
The error appears to have been in '/etc/ansible/roles/ansible-frr/tasks/debian.yml': line 74, column 3, but may be elsewhere in the file depending on the exact syntax _problem.

The_ offending line appears to be:
- name: add python frr tools to install list
  ^ here

"}

I'm not able to find any reference to this variable being set anywhere in the repository.

If I'm not missing something, would you mind describing how this variable is intended and I'll create a pull request.

Thanks!

Install from APT repo

With newer versions of FRR, the community has released an apt-repo. Let's use the repo for any versions that are on it.

Support for different CPU architectures - ARM?

Hi,

I'm looking at using your role to deploy frr on a bunch of raspberry pis running raspbian stretch.

Would it be possible to include support for architecture other than amd64?

From 'ansible -m setup' agasint a host, i see
"ansible_machine": "armv7l"
could perhaps be used to detect when to use armhf packages?

Cheers!
-Shaun

Integrate Travis/Molecule testing

As part of furthering this project, the time has come for us to integrate some valid testing. Initially we may just focus on linting, etc. until we can figure out some valid testing that we can leverage Travis/Molecule for.

Add support for BFD daemon

I would like to add BFD support. I made some initial implementation. Do you have interest in merging it?

Role fails on Debian 12 due to missing python-ipaddr package.

Describe the bug
Role fails on Debian 12 due to missing python-ipaddr/python3-ipaddr package. Same as #72

To Reproduce
Steps to reproduce the behavior:

Create a Debian 12 VM
Create a playbook that includes the mrlesmithjr.frr role
The task fails with the following error: fatal: [127.0.0.1]: FAILED! => {"changed": false, "msg": "No package matching 'python-ipaddr' is available"}

Expected behavior
The role should install FRR without errors.

Screenshots

TASK [mrlesmithjr.frr : Install FRR] ****************************************************************************************************************************
fatal: [127.0.0.1]: FAILED! => {"changed": false, "msg": "No package matching 'python-ipaddr' is available"}

Additional context

E: Unable to locate package python3-ipaddr
E: Unable to locate package python-ipaddr

https://packages.debian.org/search?suite=bookworm&arch=any&searchon=names&keywords=python-ipaddr
https://packages.debian.org/search?suite=bookworm&arch=any&searchon=names&keywords=python3-ipaddr

Role fails on Debian 11 due to missing `python-ipaddr` package.

Describe the bug
Role fails on Debian 11 due to missing python-ipaddr package.

To Reproduce
Steps to reproduce the behavior:

  1. Create a Debian 11 VM
  2. Create a playbook that includes the mrlesmithjr.frr role
  3. The task fails with the following error: fatal: [127.0.0.1]: FAILED! => {"changed": false, "msg": "No package matching 'python-ipaddr' is available"}

Expected behavior
The role should install FRR without errors.

Screenshots

TASK [mrlesmithjr.frr : Install FRR] ****************************************************************************************************************************
fatal: [127.0.0.1]: FAILED! => {"changed": false, "msg": "No package matching 'python-ipaddr' is available"}

Desktop (please complete the following information):

  • OS: MacOS
  • Browser: N/A
  • Version: latest

Additional context

Changing python-ipaddr in

- python-ipaddr
to python3-ipaddr fixes the error.

Conditional for handlers restart/reload

I would like to fix issues with conditionals in handlers reload/restart frr. https://github.com/mrlesmithjr/ansible-frr/blob/master/handlers/main.yml#L3-L21

This needs a longer explanation and might need some discussion to implement without causing more damage to role users.

The reload handler was introduced by #5 and #13. Mutually exclusive handlers restart/reload controlled by frr_reload variable. This conditional was always evaluated for both. There was also added default value for variable _frrdownload.changed, which I honestly think didn't work as expected, so later was added option to ignore errors on this handlers b0bdf01#diff-3d2b7487e8f37272aeb6f25599f2734b

After introducing package install from APT/RPM repositories #36 (nertwork#46) and #40 problem raised, because _frrdownload registry variable isn't used and it is missing in evaluation time for these handlers.

RUNNING HANDLER [mrlesmithjr.frr : full restart frr] **********************************************************************************************************************************************************************************************************************************************************************
fatal: [deimos]: FAILED! => {"msg": "The conditional check 'not frr_reload|bool or _frrdownload.changed' failed. The error was: error while evaluating conditional (not frr_reload|bool or _frrdownload.changed): '_frrdownload' is undefined\n\nThe error appears to be in '/home/users/rdanko/Work/romandanko.sk/ansible/
roles/mrlesmithjr.frr/handlers/main.yml': line 3, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n# handlers file for ansible-frr\n- name: full restart frr\n  ^ here\n"}                                                                       
...ignoring                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                           
RUNNING HANDLER [mrlesmithjr.frr : reload frr] ****************************************************************************************************************************************************************************************************************************************************************************
fatal: [deimos]: FAILED! => {"msg": "The conditional check 'frr_reload|bool and not _frrdownload.changed' failed. The error was: error while evaluating conditional (frr_reload|bool and not _frrdownload.changed): '_frrdownload' is undefined\n\nThe error appears to be in '/home/users/rdanko/Work/romandanko.sk/ansibl
e/roles/mrlesmithjr.frr/handlers/main.yml': line 13, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: reload frr\n  ^ here\n"}                                                                                                         
...ignoring    

My first suggestion is to remove _frrdownload.changed from defaults because it has no effect.

The next suggestion is to remove frr_reload variable either. Why not have this feature installed on all systems? I don't have knowledge if it works for quagga (cc @nertwork), but for FRR to my knowledge, all of the supported installation scenarios support it. We should distinguish when we use a handler reload or restart on every task. I think only tasks that handle package install should make restarts and configuration tasks should make reloads. In addition, if quagga doesn't support reloads, it has already its own configuration tasks, which may stay unchanged. This would be possible if we force all users to install dependencies needed for reloading.

Documentation for this?

Hello Guys,

Is there any documentation for this by chance? I realize it's been here for a while but I am not as smart as you guys are and I need some help getting this going.

Thanks,
Michael

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.