Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 51 additions & 65 deletions docs/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,68 +2,60 @@

Advanced Topics
===============
This Section serves as a collective for advanced topics that most developers
using this library will never need to know about, but that may be useful for
developers who are destined to maintain this package
This section is a collection of advanced topics for users who intend to contribute
and maintain this library.

Sessions
--------

The way in which sessions are handled in this library are designed to be super
easy to use for developers who use this library, however, have become relatively
complex internally. Due to their ease of use to the front end user these docs are
mainly for developers who would like to contribute to this code base, or who are
just curious as to what is actually going on under the hood.
Sessions in this library are designed for ease of use by front-end users.
However, this section is dedicated to a deeper understanding of Sessions for
advanced users and contributors to this library.

Parent Class
^^^^^^^^^^^^
Both :class:`dyn.tm.session.DynectSession` and :class:`dyn.mm.session.MMSession`
are subclasses of :class:`dyn.core.SessionEngine`. The :class:`dyn.core.SessionEngine`
provides an easy to use internal API for preparing, sending, and processing outbound
API calls. This class was added in v1.0.0 and greatly reduced the amount of logic
and duplicated code that made looking at these sessions so overly complex.
provides a simple internal API for preparing, sending, and processing outbound
API calls. This class was added in v1.0.0 and reduced the amount of logic
and duplicated code that made understanding these Sessions difficult.

Parent Type
^^^^^^^^^^^
Since v0.4.0 sessions had always been implemented as a Singleton type. At this point
you're probably asing "Why?" And that's a bit of a complicated question. One of the main
reasons that these sessions were implemented as a Singleton was to make it easier for
the end user to use this SDK and to make the flow of logic in trying to utilize the
API much smoother. By implementing Sessions as a singleton internally, it allows the
user to not need to hang on to their session objects (unless they want to). It also
means that users don't need to pass their session information around to all of the other
classes in this library in order to make API calls. (ie)::
Since v0.4.0, Sessions have been implemented as a Singleton type. This made it easier
for end users to use the SDK and to utilize the API. By internally implementing Sessions
as a Singleton, it allows the user discard their Session objects, unless they wish to
keep them. It also doesn’t require users to share their Session information with
other classes in this library to make API calls. (EXAMPLE)::

>>> from dyn.tm.session import DynectSession
>>> from dyn.tm.zones import get_all_zones
>>> DynectSession(**my_credentials)
>>> zones = get_all_zones()


as opposed to something along these lines::
as opposed to something like this::

>>> from dyn.tm.session import DynectSession
>>> from dyn.tm.zones import get_all_zones
>>> my_session = DynectSession(**my_credentials)
>>> zones = get_all_zones(my_session)

Or, in my opinion, even worse::
Or, even worse::

>>> from dyn.tm.session import DynectSession
>>> my_session = DynectSession(**my_credentials)
>>> zones = my_session.get_all_zones(my_session)

In these basic examples it may not seem like this is a terribly huge deal, but when
you're dealing with creating multiple types of records, or adding/editing a Traffic
Director service or other complex service, not needing to pass your session around
to the correct places, or use it as a point of entry to other functionality is a huge
relief.
In these examples, the changes may not seem significant but gain more relevance when
creating multiple types of records, adding or editing Traffic Director and other complex
services. Not needing to share your Session with other classes, or use it as a point
of entry to other functionality, makes using this SDK much simpler.

What We Used to Do
^^^^^^^^^^^^^^^^^^
Now that we've gone over the Singleton implementation from a front end POV, let's
talk about it from a backend POV. Effectively what we used to do in each of the Session
types was this::
From a backend perspective, the following is an example of how Session types were
handled before v0.4.0::

def session():
"""Accessor for the current Singleton DynectSession"""
Expand All @@ -89,19 +81,17 @@ types was this::
globals()['SESSION'] = super(DynectSession, cls).__new__(cls, *args)
return globals()['SESSION']

It was a dirty hack that worked for a while, but, it had several fatal flaws.
1. Once we added Message Management support we needed to duplicate this code to rename the 'SESSION' key to 'MM_SESSION', which is less than ideal due to code duplication
2. This allowed you to only have one active session, even in shared memory space (ie threads)
3. Due to the not-so-global scope of globals, this session was really only "global" in the scope of the dyn.tm module. It could still be accessed externally, but it just made me feel dirty knowing that that was what was happening.
While this worked for a short while, it had its flaws:
1. Once Message Management support was added, the code needed to be duplicated to rename the SESSION key to MM_SESSION’. This was inefficient.
2. This allowed you to only have one active Session, even in shared memory space, i.e. threads.
3. Sessions were only truly “global in the scope of the dyn.tm module. It could still be accessed externally, but it was less than ideal.

What We Do Now
^^^^^^^^^^^^^^
So, as of v1.0.0 this is not how the Session types are implemented anymore. Yes, they
are still Singletons, however, they're implemented completely differently.
As of v1.0.0, Session types remain Singletons but are implemented differently.

Now, on top of these Sessions being implemented as :class:`dyn.core.SessionEngine`
objects, they are also :class:`dyn.core.Singleton` *type* objects. What do these
look like you ask?::
Sessions are now implemented as :class:`dyn.core.SessionEngine` objects
and :class:`dyn.core.Singleton` *type* objects. EXAMPLE::

class Singleton(type):
"""A :class:`Singleton` type for implementing a true Singleton design
Expand All @@ -119,44 +109,40 @@ look like you ask?::
}
return cls._instances[key][cur_thread]

So, the Singleton type is applied as a *__metaclass__* in each of the two Session
types. This allows for a much cleaner implementation of Singletons where every time
one is accessed it will globally (actually globally this time) have knowledge of
other instances, since those instances are tied to the classes themselves instead
of held in the *globals* of the session modules. In addition this allows users
to have multiple active sessions across multiple threads, which was previously
impossible in the prior implementation.
The Singleton type is applied as a *__metaclass__* in each of the two Session
types. This allows for a much cleaner implementation of Singletons. Every time
one is accessed, it will globally have knowledge of other instances, as those
instances are tied to the classes themselves instead of held in the *globals*
of the session modules. In addition, this allows users to have multiple active
sessions across multiple threads, which was not possible in the prior
implementation.


Password Encryption
-------------------
The DynECT REST API only accepts passwords in plain text, and currently there is
no way around that. However, for those of you that are particularly mindful of
security (and even those of you who aren't) can probably see some serious pitfalls
to this. As far as most users of this library are concerned the passwords stored in
their :class:`~dyn.tm.session.DynectSession` objects will only ever live in memory,
so it's really not a huge deal that their passwords are stored in plain text. However,
for users looking to do more advanced things, such as serialize and store their session
objects in something less secure, such as a database, then these plain text passwords
are far less than ideal. Because of this in version 1.1.0 we've added optional
AES-256 password encryption for all :class:`~dyn.tm.session.DynectSession`
instances. All you need to do to enable password encryption is install
`PyCrypto <http://www.dlitz.net/software/pycrypto/>`_. The rest will happen
automatically.
The Managed DNS REST API only accepts passwords in plain text. The
passwords stored in :class:`~dyn.tm.session.DynectSession` objects only
live in memory, reducing the security risk of plain text passwords in this instance.
However, for users looking to do more advanced things, such as serialize and store
their session objects in something less secure, such as a database, these
plain text passwords are not ideal. In response to this, Dyn added optional AES-256
password encryption for all :class:`~dyn.tm.session.DynectSession` instances in
version 1.1.0. To enable password encryption, install
`PyCrypto <http://www.dlitz.net/software/pycrypto/>`_.

Key Generation
^^^^^^^^^^^^^^
Also in version 1.1.0 an optional key field parameter was added to the
In version 1.1.0, an optional key field parameter was added to the
:class:`~dyn.tm.session.DynectSession` __init__ method. This field will allow
you to specify the key that your password will be encrypted using. However,
you may also choose to let the dyn module handle the key generation for you as
well using the :func:`~dyn.encrypt.generate_key` function which generates a,
nearly, random 50 character key that can be easily consumed by the
:class:`~dyn.encrypt.AESCipher` class (the class responsible for performing
the actual encryption and decryption.
you to specify the key that your encrypted password will be using. You can also
let the Dyn module handle the key generation in addition to using
the :func:`~dyn.encrypt.generate_key` function, which generates a random
50 character key that can be easily consumed by the :class:`~dyn.encrypt.AESCipher`
class (the class responsible for performing the encryption and decryption).

Encrypt Module
^^^^^^^^^^^^^^
::
.. autofunction:: dyn.encrypt.generate_key

.. autoclass:: dyn.encrypt.AESCipher
Expand Down
2 changes: 1 addition & 1 deletion docs/core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Core
====
The :mod:`dyn.core` module contains functionality that is core to the behavior
of the rest of the library. In particular this is where a lot of the "heavy lifting"
of the rest of the library. This is where a lot of the "heavy lifting"
for sessions is done.


Expand Down
18 changes: 10 additions & 8 deletions docs/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,39 @@
Installation
============

This part of the documentation covers the installation of the dyn module.
The first step to using any software package is getting it properly installed.
This part of the documentation covers the installation of the Dyn module.


Distribute & Pip
----------------

Installing dyn is simple via `pip <http://www.pip-installer.org/>`_.
The easiest way to install the Dyn module is via ‘pip’.

1. Go to <http://www.pip-installer.org/>’, download and install ‘pip’.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm generally of the opinion that if someone is on github looking at the source code, that we probably don't need to tell them to download and install pip, or that they should be using a Terminal application. That being said, I guess it's not the end of the world to add this, just that it might be out of place for the intended audience

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I totally understand that and questioned whether to put this in or not, but I decided to drop it in because I tend to look at most things as "baby's first [insert whatever technology]" when I document them. Making it as accessible as possible is for the best, I think.

2. In Terminal, run:

$ pip install dyn

Get the Code
------------

dyn is actively developed on GitHub, the code is
`always available <https://github.com/dyninc/dyn-python>`_.
Dyn is actively developed on GitHub, the code is always available
''<https://github.com/dyninc/dyn-python>` and there are several options
available to obtain the code.

You can either clone the public repository::
Clone the public repository::

git clone https://github.com/dyninc/dyn-python.git

Download the `tarball <https://github.com/dyninc/dyn-python/tarball/master>`_::

$ curl -OL https://github.com/dyninc/dyn-python/tarball/master

Or, download the `zipball <https://github.com/dyninc/dyn-python/zipball/master>`_::
Download the `zipball <https://github.com/dyninc/dyn-python/zipball/master>`_::

$ curl -OL https://github.com/dyninc/dyn-python/zipball/master

Once you have a copy of the source, you can embed it in your Python package,
or install it into your site-packages by running::

$ python setup.py install