ryran / ravshello Goto Github PK
View Code? Open in Web Editor NEWThe Ravello Shell -- Interface with Ravello Systems from the command-line to create & manage apps hosted around the world
License: Apache License 2.0
The Ravello Shell -- Interface with Ravello Systems from the command-line to create & manage apps hosted around the world
License: Apache License 2.0
Currently planned fields:
user charges hours app-name
The old way:
/apps> rsaw-exXXX-C_0/ query_status
App VMs in region us-east-1 will auto-stop in 113:22 min at 04:23:35
server3
State: STARTED
VNC Web URL: https://vnc-us-east-1.ravellosystems.com/vnc/?token=4YumtKQyiygZVx0HGwPryBRSHX4sZZ0ViWnB66a4lOwGsjPi4gSI28nUNSTxQC65&mgmt=https%3A%2F%2Fcloud.ravellosystems.com%2F&toggles=VNC_SUPPORTS_COPY_PASTE
server2
State: STARTED
VNC Web URL: https://vnc-us-east-1.ravellosystems.com/vnc/?token=dKwbcUMC7XVL3Cx7FZwDRopPA9ZIlAFx5kVkG97VyoLenOoLVxdOGv1F6ySaDbPQ&mgmt=https%3A%2F%2Fcloud.ravellosystems.com%2F&toggles=VNC_SUPPORTS_COPY_PASTE
server1
State: STARTED
External Ports: 10001/TCP (ssh), 10002/TCP (http), 10003/TCP (https), 37197/TCP (jank)
SSH Command: ssh -p 10001 [email protected]
VNC Web URL: https://vnc-us-east-1.ravellosystems.com/vnc/?token=7yFUj3e8TFPLvnihjKzwO7E7emPE7tvO6P4H1rT8XlOp9D5U2hG507ELYiHe2iwC&mgmt=https%3A%2F%2Fcloud.ravellosystems.com%2F&toggles=VNC_SUPPORTS_COPY_PASTE
Enhanced to look something like this:
/apps> rsaw-exXXX-C_0/ query_status
App VMs in region us-east-1 will auto-stop in 92:45 min at 03:50:31
server3
State: STARTED
NIC eth0
Private IP: 10.0.0.7
VNC Web URL: https://vnc-us-east-1.ravellosystems.com/vnc/?token=PiNKYmxYeLUAlsX3rA9F8OE77POqH9ZATL6zYE30ZzQqeQ3ry5eOQnkHZbzIIbgJ&mgmt=https%3A%2F%2Fcloud.ravellosystems.com%2F&toggles=VNC_SUPPORTS_COPY_PASTE
server2
State: STARTED
NIC eth0
Private IP: 10.0.0.5
VNC Web URL: https://vnc-us-east-1.ravellosystems.com/vnc/?token=YftpLl7iGMbwxeYeJifh9yT9QYKbo60Ah45gHz14jkwKqHFFpp0qJDLOn2u0wVts&mgmt=https%3A%2F%2Fcloud.ravellosystems.com%2F&toggles=VNC_SUPPORTS_COPY_PASTE
server1
State: STARTED
NIC eth0
Private IP: 10.0.0.4
Public DNAT: 52.23.x.y (server1-krsawhillrsawexxxx-sfl3ddyu.srv.ravcloud.com)
External Svc: ssh 10001/TCP maps to internal 22/TCP
External Svc: http 10002/TCP maps to internal 80/TCP
External Svc: https 10003/TCP maps to internal 443/TCP
NIC eth1
Private IP: 10.0.0.3
Public Static: 34.195.x.y (server1-krsawhillrsawexxxx-m0rzsnsv.srv.ravcloud.com)
External Svc: jank 37197/TCP maps to internal 37197/TCP
SSH Command: ssh -p 10001 [email protected]
VNC Web URL: https://vnc-us-east-1.ravellosystems.com/vnc/?token=iZ9ihd890SW1iuRggNxi5lQkDacnG5zqxDFeYEjwyKo9iTgevFkWKSJG4zdCy3nM&mgmt=https%3A%2F%2Fcloud.ravellosystems.com%2F&toggles=VNC_SUPPORTS_COPY_PASTE
Why not?
This is kind of a no-brainer and IMHO the biggest priority.
Relying on the auto-snapshot-after-shutdown feature is not a healthy place to live.
# ravshello -V
ravshello v1.2.4 last mod 2015/01/05
/> ls
o- / ............................................................................................... [Logged in as user: ablum_test]
o- apps ........................................................................................................ [No applications]
/> cd apps
/apps> create_app
Blueprints available to you:
0) test1-bp
Enter number of blueprint: 0
Not a valid number! Try again
Enter number of blueprint:
NOTE: This seems to only be an issue when there is exactly 1 blueprint available.
Let's leave default expire time (when extend_autostop
used with no args) at 60 min, but do $SUBJECT.
Also, should make this easier to configure via global variables.
Let's also greatly reduce code-complexity by putting this in one function instead of having a million separate print methods in the various confignodes.
Today all VMs in an app went into ERROR state. Fixed it by using per-VM "Recover" option in web-ui. Might be good to add to ravshello.
I did this on purpose way back in the day because it was helpful for development, but from a user perspective .... why should they care? It's poor UX and makes them think they have to sit there waiting.
Yep.
This doesn't happen in every session, but when it does, it continues that way for the whole session. Happens in learner and admin mode. Currently debugging.
[rsaw:ravshello]$ ravshello -qa ls
Welcome to ravshello, a shell to provision & manage machines with Ravello!
Using configfile-specified nick 'rsawhill' for nickname
Logged in to Ravello as ADMIN
Traceback (most recent call last):
File "/home/rsaw/bin/ravshello", line 187, in <module>
main()
File "/home/rsaw/bin/ravshello", line 182, in main
user_interface.main(rOpt, rClient)
File "/home/rsaw/gitprojs/ravshello/user_interface.py", line 105, in main
shell.run_cmdline(rOpt.cmdlineArgs)
File "/home/rsaw/gitprojs/ravshello/configshell_fb/shell.py", line 890, in run_cmdline
self._execute_command(path, command, pparams, kparams)
File "/home/rsaw/gitprojs/ravshello/configshell_fb/shell.py", line 865, in _execute_command
result = target.execute_command(command, pparams, kparams)
File "/home/rsaw/gitprojs/ravshello/configshell_fb/node.py", line 1413, in execute_command
return method(*pparams, **kparams)
File "/home/rsaw/gitprojs/ravshello/configshell_fb/node.py", line 712, in ui_command_ls
tree = self._render_tree(target, depth=depth)
File "/home/rsaw/gitprojs/ravshello/configshell_fb/node.py", line 863, in _render_tree
+ self._render_tree(children[i], margin, depth)
File "/home/rsaw/gitprojs/ravshello/configshell_fb/node.py", line 863, in _render_tree
+ self._render_tree(children[i], margin, depth)
File "/home/rsaw/gitprojs/ravshello/configshell_fb/node.py", line 863, in _render_tree
+ self._render_tree(children[i], margin, depth)
File "/home/rsaw/gitprojs/ravshello/configshell_fb/node.py", line 863, in _render_tree
+ self._render_tree(children[i], margin, depth)
File "/home/rsaw/gitprojs/ravshello/configshell_fb/node.py", line 767, in _render_tree
(description, is_healthy) = root.summary()
TypeError: 'NoneType' object is not iterable
As subject says.
Might be the same; might not. Not documented in API, but looks like they'll eventually open up to allow usernames that are separate from email addresses. ... Will defer until find out more.
Time to update both the sdk and configshell to the latest versions and make sure everything works!
I don't know how to change user['locked'] or user['activated'] or user['enabled'] but if/when it's possible, we should probably flag those users with some color.
Ravello will be changing the API slightly within the next 5 months. This change will affect the way ravshello publishes applications. It will also affect the python SDK which will also be updated.
Here are the details of the change
Publish Application:
The call stays almost the same but there is no more need to choose the cloud (Amazon/Google)
POST https://cloud.ravellosystems.com/api/v1/applications/34045955/publish
{ "preferredRegion": "us-central-1", "optimizationLevel": "PERFORMANCE_OPTIMIZED", "startAllVms": "true" }
-Old
POST https://cloud.ravellosystems.com/api/v1/applications/34045955/publish
{ "preferredCloud": "AMAZON", "preferredRegion": "Virginia", "optimizationLevel": "PERFORMANCE_OPTIMIZED", "startAllVms": "true" }
In addition, the region names will change, you can see the list of available regions on your account using this call:
GET https://cloud.ravellosystems.com/api/v1/applications/34045/publishLocations
Create Elastic IP:
Only change here is the region names, which you can get using this call:
GET https://cloud.ravellosystems.com/api/v1/elasticIps/locations/
Call stays the same -
POST https://cloud.ravellosystems.com/api/v1/elasticIps
{ "name": "my_elastic_ip", "description": "This elastic IP is used in my application", "locationType": "REGIONAL", "locationName": "us-central-1" }
Regarding the Python SDK, the change will take affect once you update to the latest version - make sure not to install new version before you update your scripts.
def publish_application(self, app, req={"optimizationLevel":"COST_OPTIMIZED"}):
At the moment, the only thing we've got in /monitoring
is the search_notifications
command. I feel like there's a lot of potential to do more.
We shouldn't be using RavelloCache.userCache
dict directly; instead, RavelloCache.get_users()
should return RavelloCache.userCache.values()
(and timestamp should be stored elsewhere).
/apps/GSS-Satellite-Readiness-6.0-Module4-latest-v9_1> query_app_status
App VMs in Amazon (Virginia) will auto-stop in 27:05 min at 12:05:45
classroom
State: STARTED
Internal DNS Name: classroom.example.com, content.example.com
Internal IPs: 172.25.1.254 (eth0)
External Ports: 10001/TCP
SSH Command: ssh -p 10001 root@classroom-khhudgeongsssatellit-XXXXX
VNC Web URL: https://vnc.ravellosystems.com/vnc?token=qIBIGGDd6cTVvCIbGmzUhvwQgltmeXI05iOMKFOVIgnMU0k7E0p18FSuxICwrYfY&mgmt=https%3A%2F%2Fcloud.ravellosystems.com%2F
/apps/GSS-Satellite-Readiness-6.0-Module4-latest-v9_1> query_app_status
App VMs in Amazon (Virginia) will auto-stop in 26:33 min at 12:05:45
classroom
State: STARTED
Internal DNS Name: classroom.example.com, content.example.com
Internal IPs: 172.25.1.254 (eth0)
External Ports: 10001/TCP
SSH Command: ssh -p 10001 root@classroom-khhudgeongsssatellit-XXXXX
Feedback welcome, but I'm leaning towards using /vmImages
in ravshello -- fixing the confusion of the API, where it's called /images
(distinct from the API's /diskImages
).
Bear with me here ... but the stop_app
and start_app
commands are annoyingly long and similar. What if instead they were start
and poweroff
? Same for the vm commands.
What about create_app
? Maybe just rename it to create
? And do the same for all the other commands (i.e., drop the _suffix
at the end).
Thoughts?
Upstream Ravello sdk finally got the new requests-based way of doing things to work right ... time to test out.
/users> ryan_b19.org/ delete_user
W A R N I N G ! ! ! !
'User' object has no attribute 'userEmail'
/users>
Hello there, I'm running ravshello with python 2.7.10 and finding that login fails, would we be able
to add proxy support as part of the configuration steps? the Ravello SDK does support a method for Proxy access - thanks!
def connect(self, url=None, proxy_url=None):
"""Connect to the API.
It is not mandatory to call this method. If this method is not called,
the client will automatically connect when required.
"""
if url is not None:
self._set_url(url)
if proxy_url is not None:
self._proxies = {"http": proxy_url, "https": proxy_url}
if self._connection is not None:
self._connection.proxies = self._proxies
In ravshello/user_interface.py def ui_command_restart(self): there is no
self.extend_autostop(minutes=defaultAppExpireTime)
The net result is, when a person issues a restart no expiry is set and the application will have a Never expiry. My suggestion is that you set the autostop for restarts in the same way you do for starts.
Andrew
Often the "Done!" doesn't show up after the "Fetching ..." message. It SHOULD be:
Welcome to ravshello, a shell to provision & manage machines with Ravello!
Determining nickname . . .
(Nickname will be prepended to names of any apps you create)
(Nickname will be used to restrict which app names you can see)
Using configfile-specified nick 'rsawhill' for nickname
Connecting to Ravello . . .
Logged in to Ravello as LEARNER
Fetching data from Ravello . . . Done!
Instructions:
But sometimes it's:
Welcome to ravshello, a shell to provision & manage machines with Ravello!
Determining nickname . . .
(Nickname will be prepended to names of any apps you create)
(Nickname will be used to restrict which app names you can see)
Using configfile-specified nick 'rsawhill' for nickname
Connecting to Ravello . . .
Logged in to Ravello as LEARNER
Fetching data from Ravello . . . Instructions:
The upstream Ravello python-sdk recently switched what module they use for handling the http connections to Ravello. (It was pull request #7.)
In ravshello we instantiate one RavelloClient()
object and login only once and then use that for all API calls until ravshello exits. It seems that something about the new code (which uses requests
instead of httplib
) is leading to almost-immediate timeouts.
Today Hayley was logged in to the old version from the morning all the way until 4 in the afternoon, doing commands sporadically (every hour or so). No problems. That's typical. Occasionally a command fails (not even once a day I think) but simply trying the same command again always succeeds. There's never a need to quit and login again.
After switching to the new version around 4pm, it became pretty clear that something big was different -- within minutes of inactivity she got the following traceback and had to quit out of ravshello to login again.
/apps/GSS-Satellite-Readiness-6.0-Module4-latest-v9_0> query_app_status
Traceback (most recent call last):
File "/usr/lib/python2.7/site-packages/configshell/shell.py", line 948, in run_interactive
self._cli_loop()
File "/usr/lib/python2.7/site-packages/configshell/shell.py", line 778, in _cli_loop
if self._save_history:
File "/usr/lib/python2.7/site-packages/configshell/shell.py", line 892, in run_cmdline
File "/usr/lib/python2.7/site-packages/configshell/shell.py", line 867, in _execute_command
self.log.debug("Command execution returned %r" % result)
File "./ravshello/out00-PYZ.pyz/configshell.node", line 1417, in execute_command
File "./ravshello/out00-PYZ.pyz/user_interface", line 1926, in ui_command_query_app_status
File "./ravshello/out00-PYZ.pyz/user_interface", line 1930, in query_app_status
File "./ravshello/out00-PYZ.pyz/user_interface", line 341, in get_application_details
File "./ravshello/out00-PYZ.pyz/ravello_sdk", line 447, in get_application
File "./ravshello/out00-PYZ.pyz/ravello_sdk", line 325, in request
File "./ravshello/out00-PYZ.pyz/ravello_sdk", line 386, in _request
RavelloError: got status 401 (unknown/unknown)
Yikes! When did this bug creep in ...
/apps> create_app blueprint=rsaw_bare_rhel7 name=woot desc=@auto publish=False
Application 'woot_0' created!
/apps> create_app blueprint=rsaw_bare_rhel7 name=@prompt desc=@auto publish=False
Enter a name for your new application [rsaw_bare_rhel7]: boom
Application 'rsaw_bare_rhel7_2' created!
Tasks:
They made a change to the data in Ravello ... will have to look into it.
/billing> this_months_summary sortBy=user outputFile=/tmp/woot
Warning: data could be up to 1 hour old
Pulling summary of charges since the start of this month . . .
Wrote billing information to file: '/tmp/woot'
/billing> exit
┬─[rsaw@xala:/g/ravshello]─[02:55:49 PM]
╰─>$ head /tmp/woot
ORG (not associated with specific apps):
Charges Hours Creation Time Application Name
$ 141.09 593 N/A UNDEFINED
$ 547.25 1779 03/24 @ 14:17 k:olim__GSS-Red-Hat-Ceph_0
$ 1.85 6 05/06 @ 15:45 k:jbautist__GSS-Red-Hat-Ceph_0
$ 1.43 4 05/28 @ 09:12 k:shashank__x509_0
$ 3.52 15 05/28 @ 14:21 k:cnegus__openstack-HA
$ 1.26 9 06/07 @ 15:35 k:rsawhill__rsaw_bare_rhel7_1
$ 8.67 20 06/16 @ 04:30 k:mzheng__GSS-RHOSP-Troubleshooting-Havana_0
$ 16.23 56 06/26 @ 11:12 k:stef__CL210-OSPv6-gss-v0_0
RFE for gnome notifications for app timeout, specifically for admins
Yep.
The billing report returned using /billing export_month_to_csv returns this information:
User,App Name,App Hours,App Charges
ablum,myappname_0,25.0,6.567817565
ablum,myappname_1,24.0,7.2685541745
ablum,myappname_2,24.0,7.2685521571
ablum,myappname_3,36.0,10.902832361
App Hours
listed above is misleading since its really the product of the number of units consumed by the blueprint
* total upTime
I suggest that we modify ravshello to include another column Up Time Hours
and to modify App Hours
to be Unit Hours
.
Code suggestion to follow.
Why not? Useful when multiple things might be grabbing all the blueprints.
We need to have the ability to allow one VM to publish before the rest, namely the instructor. For example, we may want to provide nfs exports on the instructor for use by the other VMs in an application. If the export is mounted by a client via fstab and that VM starts before the instructor, then the learner will have to run a mount -a or something similar before it will be mounted.
Right now, we do not have a way to guarantee the instructor will be the first VM to start. We need a way to specify initial boot order.
I suggest we look for a way to publish VMs based on metadata that could be configured in the VM description. Something like vm_delay:. Then, ravshello can use this in its publishing logic rather than "startAllVms": "true". Of course, this is just a suggested way to accomplish this request.
While logged in as an admin, I tried to delete this app:
/apps/GSS-RHOSP-Troubleshooting-Havana_0> delete_app
�Traceback (most recent call last):
File "/home/ablum/bin/ravshello", line 184, in
main()
File "/home/ablum/bin/ravshello", line 179, in main
user_interface.main(rOpt, rClient)
File "/home/ablum/ravshello/user_interface.py", line 140, in main
shell.run_interactive()
File "/usr/lib/python2.7/site-packages/configshell/shell.py", line 948, in run_interactive
self._cli_loop()
File "/usr/lib/python2.7/site-packages/configshell/shell.py", line 777, in _cli_loop
self.run_cmdline(cmdline)
File "/usr/lib/python2.7/site-packages/configshell/shell.py", line 891, in run_cmdline
self._execute_command(path, command, pparams, kparams)
File "/usr/lib/python2.7/site-packages/configshell/shell.py", line 866, in _execute_command
result = target.execute_command(command, pparams, kparams)
File "/usr/lib/python2.7/site-packages/configshell/node.py", line 1413, in execute_command
return method(_pparams, *_kparams)
File "/home/ablum/ravshello/user_interface.py", line 1777, in ui_command_delete_app
c.slow_print(c.RED("Deleting an application cannot be undone -- All VM data will be lost\n"))
File "/home/ablum/ravshello/string_ops.py", line 27, in slow_print
stdout.flush()
NameError: global name 'stdout' is not defined
[ablum@badger ~]$ ravshello -V
ravshello v1.3.0-dev last mod 2015/01/21
Whaaat? Will look into this later.
╰─>$ ravshello -q
Welcome to ravshello, a shell to provision & manage machines with Ravello!
Using configfile-specified nick 'rsawhill' for nickname
Logged in to Ravello as LEARNER
/> cd
No module named urwid
/>
With debug:
╰─>$ ravshello -qd
Welcome to ravshello, a shell to provision & manage machines with Ravello!
Using configfile-specified nick 'rsawhill' for nickname
Logged in to Ravello as LEARNER
/> cd
Traceback (most recent call last):
File "/home/rsaw/bin/ravshello", line 186, in <module>
main()
File "/home/rsaw/bin/ravshello", line 181, in main
user_interface.main(rOpt, rClient)
File "/g/ravshello/user_interface.py", line 164, in main
shell.run_interactive(exit_on_error=rOpt.enableDebugging)
File "/g/ravshello/configshell_fb/shell.py", line 947, in run_interactive
self._cli_loop()
File "/g/ravshello/configshell_fb/shell.py", line 776, in _cli_loop
self.run_cmdline(cmdline)
File "/g/ravshello/configshell_fb/shell.py", line 890, in run_cmdline
self._execute_command(path, command, pparams, kparams)
File "/g/ravshello/configshell_fb/shell.py", line 865, in _execute_command
result = target.execute_command(command, pparams, kparams)
File "/g/ravshello/configshell_fb/node.py", line 1413, in execute_command
return method(*pparams, **kparams)
File "/g/ravshello/configshell_fb/node.py", line 1030, in ui_command_cd
selected = self._lines_walker(lines, start_pos=start_pos)
File "/g/ravshello/configshell_fb/node.py", line 1064, in _lines_walker
import urwid
ImportError: No module named urwid
/apps/RHEL-Atomic_0> print_def
Traceback (most recent call last):
File "/home/rsaw/bin/ravshello", line 186, in <module>
main()
File "/home/rsaw/bin/ravshello", line 181, in main
user_interface.main(rOpt, rClient)
File "/g/ravshello/user_interface.py", line 136, in main
shell.run_interactive(exit_on_error=rOpt.enableDebugging)
File "/g/ravshello/configshell_fb/shell.py", line 947, in run_interactive
self._cli_loop()
File "/g/ravshello/configshell_fb/shell.py", line 776, in _cli_loop
self.run_cmdline(cmdline)
File "/g/ravshello/configshell_fb/shell.py", line 890, in run_cmdline
self._execute_command(path, command, pparams, kparams)
File "/g/ravshello/configshell_fb/shell.py", line 865, in _execute_command
result = target.execute_command(command, pparams, kparams)
File "/g/ravshello/configshell_fb/node.py", line 1413, in execute_command
return method(*pparams, **kparams)
File "/g/ravshello/user_interface.py", line 2198, in ui_command_print_def
self.print_app_definition()
AttributeError: 'App' object has no attribute 'print_app_definition'
On a VM that has multiple external ports, I currently see this:
/apps> rsaw-exXXX-C_0/ query_status
...
server1
State: STARTED
External Ports: 10001/TCP, 10002/TCP, 10003/TCP, 47717/TCP
Hmm.
Works fine in RHEL 7.
/apps> /users invite_new_user
Enter details of new user you'd like to invite . . .
Email: [email protected]
First name: Ryan
Last name: Saw
Problem inviting user
!
Traceback (most recent call last):
File "/usr/lib/python2.7/site-packages/configshell/shell.py", line 948, in run_interactive
self._cli_loop()
File "/usr/lib/python2.7/site-packages/configshell/shell.py", line 778, in _cli_loop
self.run_cmdline(cmdline)
File "/usr/lib/python2.7/site-packages/configshell/shell.py", line 892, in run_cmdline
self._execute_command(path, command, pparams, kparams)
File "/usr/lib/python2.7/site-packages/configshell/shell.py", line 867, in _execute_command
result = target.execute_command(command, pparams, kparams)
File "/usr/lib/python2.7/site-packages/configshell/node.py", line 1417, in execute_command
result = method(*pparams, **kparams)
File "/pyi/source/user_interface.py", line 1059, in ui_command_invite_new_user
user = ravClient.create_user(req)
File "/pyi/source/ravello_sdk.py", line 770, in create_user
return self.request('POST', '/users', user)
File "/pyi/source/ravello_sdk.py", line 325, in request
response = self._request(method, path, body, headers)
File "/pyi/source/ravello_sdk.py", line 386, in _request
raise RavelloError('got status {0} ({1}/{2})' .format(status, code, msg))
RavelloError: got status 405 (unknown/unknown)
Currently the message reads:
All VMs reached 'STARTED' state!
SSH NOTE: STARTED does not mean the OS of each machine has finished booting
VNC NOTE: URLs expire within a minute if not used; refresh them with either
a `query_status` or `loop_query_status` command
This doesn't make it clear (to newcomers) that they need to cd into the appdir to run either of those commands.
Should implement commands:
/shares share_bp
/shares share_vm_image
/shares share_disk_image
Of course once we have shares, they would also have commands:
/shares/SHARE delete
/shares/SHARE print_def
Should also be able to see full details about a shares with ls
.
For example ....
_DELETE_ALL_APPS_ confirm={true|false}
create_app blueprint=???? publish={true|false} region=???? startAllVms={true|false}
delete_app confirm={true|false}
delete_app confirm={true|false}
loop_query_app_status desiredState={STARTED|STOPPED}
There are some places in the shell where things are saved to ~/.ravshello/
(e.g., commands in /blueprints/
) ... with the latest version implementing --cfgdir
, need to make a few tweaks.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.