Merge "nova availability zones"
diff --git a/README.rst b/README.rst
index 34cb1e6..f358685 100644
--- a/README.rst
+++ b/README.rst
@@ -130,6 +130,7 @@
         version: juno
         enabled: true
         virtualization: kvm
+        availability_zone: availability_zone_01
         security_group: true
         bind:
           vnc_address: 172.20.0.100
@@ -251,15 +252,34 @@
       server:
         identity:
           flavor:
-            jirka-flavor1:
+            flavor1:
               flavor_id: 10
               ram: 4096
               disk: 10
               vcpus: 1
+            flavor2:
+              flavor_id: auto
+              ram: 4096
+              disk: 20
+              vcpus: 2
         identity1:
           flavor:
             ...
 
+
+Availability zones
+
+.. code-block:: yaml
+
+    nova:
+      client:
+        enabled: true
+        server:
+          identity:
+            availability_zones:
+            - availability_zone_01
+            - availability_zone_02
+
 SRIOV
 ------
 
diff --git a/nova/_modules/novang.py b/nova/_modules/novang.py
index bfd30a7..d2009ae 100644
--- a/nova/_modules/novang.py
+++ b/nova/_modules/novang.py
@@ -67,10 +67,32 @@
         'region_name': region_name,
         'os_auth_plugin': os_auth_system
     }
-
     return suon.SaltNova(**kwargs)
 
 
+def server_list(profile=None, tenant_name=None):
+    '''
+    Return list of active servers
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.server_list
+    '''
+    conn = _auth(profile, tenant_name)
+    return conn.server_list()
+
+
+def server_get(name, tenant_name=None, profile=None):
+    '''
+    Return information about a server
+    '''
+    items = server_list(profile, tenant_name)
+    instance_id = None
+    for key, value in items.iteritems():
+        if key == name:
+            instance_id = value['id']
+    return instance_id
+
+
 def get_connection_args(profile=None):
     '''
     Set up profile credentials
@@ -124,6 +146,8 @@
     nt_ks = conn.compute_conn
     item = nt_ks.quotas.update(tenant_id, **quota_argument)
     return item
+
+
 def server_list(profile=None, tenant_name=None):
     '''
     Return list of active servers
@@ -134,6 +158,7 @@
     conn = _auth(profile, tenant_name)
     return conn.server_list()
 
+
 def secgroup_list(profile=None, tenant_name=None):
     '''
     Return a list of available security groups (nova items-list)
@@ -144,6 +169,7 @@
     conn = _auth(profile, tenant_name)
     return conn.secgroup_list()
 
+
 def boot(name, flavor_id=0, image_id=0, profile=None, tenant_name=None, timeout=300, **kwargs):
     '''
     Boot (create) a new instance
@@ -169,6 +195,51 @@
     #kwargs = {'nics': nics}
     conn = _auth(profile, tenant_name)
     return conn.boot(name, flavor_id, image_id, timeout, **kwargs)
+
+
 def network_show(name, profile=None):
     conn = _auth(profile)
     return conn.network_show(name)
+
+
+def availability_zone_list(profile=None):
+    '''
+    list existing availability zones
+    '''
+    connection_args = get_connection_args(profile)
+    conn = _auth(profile)
+    nt_ks = conn.compute_conn
+    ret = nt_ks.aggregates.list()
+    return ret
+
+
+def availability_zone_get(name, profile=None):
+    '''
+    list existing availability zones
+    '''
+    connection_args = get_connection_args(profile)
+    conn = _auth(profile)
+    nt_ks = conn.compute_conn
+    zone_exists=False
+    items = availability_zone_list(profile)
+    for p in items:
+        item = nt_ks.aggregates.get(p).__getattr__('name')
+        if item == name:
+            zone_exists = True
+    return zone_exists
+
+
+def availability_zone_create(name, availability_zone, profile=None):
+    '''
+    create availability zone
+    '''
+    connection_args = get_connection_args(profile)
+    conn = _auth(profile)
+    nt_ks = conn.compute_conn
+    item = nt_ks.aggregates.create(name, availability_zone)
+    ret = {
+        'Id': item.__getattr__('id'),
+        'Aggregate Name': item.__getattr__('name'),
+        'Availability Zone': item.__getattr__('availability_zone'),
+    }
+    return ret
diff --git a/nova/_states/novang.py b/nova/_states/novang.py
index 76cf2f0..77e0ead 100644
--- a/nova/_states/novang.py
+++ b/nova/_states/novang.py
@@ -14,9 +14,10 @@
     '''
     return 'novang' if 'nova.flavor_list' in __salt__ else False
 
+
 def flavor_present(name, flavor_id=0, ram=0, disk=0, vcpus=1, profile=None):
     '''
-    Ensures that the nova flavor exists  
+    Ensures that the nova flavor exists
     '''
     ret = {'name': name,
            'changes': {},
@@ -33,6 +34,7 @@
         ret['changes']['Flavor'] = 'Created'
     return ret
 
+
 def quota_present(tenant_name, profile, name=None, **kwargs):
     '''
     Ensures that the nova quota exists
@@ -46,35 +48,25 @@
             changes[key] = value
             __salt__['novang.quota_update'](tenant_name, profile, **arg)
     if bool(changes):
-        return _updated(tenant_name, 'tenant', changes)        
+        return _updated(tenant_name, 'tenant', changes)
     else:
         return _no_change(tenant_name, 'tenant')
 
-def _updated(name, resource, resource_definition):
-    changes_dict = {'name': name,
-                    'changes': resource_definition,
-                    'result': True,
-                    'comment': '{0} {1} tenant was updated'.format(resource, name)}
-    return changes_dict
 
-def _update_failed(name, resource):
-    changes_dict = {'name': name,
-                    'changes': {},
-                    'comment': '{0} {1} failed to update'.format(resource, name),
-                    'result': False}
-    return changes_dict
-
-def _no_change(name, resource, test=False):
-    changes_dict = {'name': name,
-                    'changes': {},
-                    'result': True}
-    if test:
-        changes_dict['comment'] = \
-            '{0} {1} will be {2}'.format(resource, name, test)
+def availability_zone_present(name=None, availability_zone=None, profile=None):
+    '''
+    Ensures that the nova availability zone exists
+    '''
+    name = availability_zone
+    zone_exists = __salt__['novang.availability_zone_get'](name, profile)
+    if zone_exists == False:
+        item_created = __salt__['novang.availability_zone_create'](name, availability_zone, profile)
+        if bool(item_created):
+            return _created(availability_zone, 'availabilty zone', item_created)         
     else:
-        changes_dict['comment'] = \
-            '{0} {1} is in correct state'.format(resource, name)
-    return changes_dict
+        return _already_exists(availability_zone, 'availabilty zone')
+    return existing_availability_zones
+
 
 def instance_present(name, flavor, image, networks, security_groups=None, profile=None, tenant_name=None):
     ret = {'name': name,
@@ -133,3 +125,45 @@
             'changes': {},
             'result': True,
             'comment': 'Instance "{0}" was successfuly created'.format(name)}
+
+def _already_exists(name, resource):
+    changes_dict = {'name': name,
+                    'changes': {},
+                    'result': True}
+    changes_dict['comment'] = \
+        '{0} {1} already exists'.format(resource, name)
+    return changes_dict
+
+
+def _created(name, resource, resource_definition):
+    changes_dict = {'name': name,
+                    'changes': resource_definition,
+                    'result': True,
+                    'comment': '{0} {1} created'.format(resource, name)}
+    return changes_dict
+
+def _updated(name, resource, resource_definition):
+    changes_dict = {'name': name,
+                    'changes': resource_definition,
+                    'result': True,
+                    'comment': '{0} {1} tenant was updated'.format(resource, name)}
+    return changes_dict
+
+def _update_failed(name, resource):
+    changes_dict = {'name': name,
+                    'changes': {},
+                    'comment': '{0} {1} failed to update'.format(resource, name),
+                    'result': False}
+    return changes_dict
+
+def _no_change(name, resource, test=False):
+    changes_dict = {'name': name,
+                    'changes': {},
+                    'result': True}
+    if test:
+        changes_dict['comment'] = \
+            '{0} {1} will be {2}'.format(resource, name, test)
+    else:
+        changes_dict['comment'] = \
+            '{0} {1} is in correct state'.format(resource, name)
+    return changes_dict
\ No newline at end of file
diff --git a/nova/client.sls b/nova/client.sls
index 355559f..2d20a8f 100644
--- a/nova/client.sls
+++ b/nova/client.sls
@@ -7,6 +7,8 @@
 
 {%- for identity_name, identity in client.server.iteritems() %}
 
+{%- if identity.flavor is defined %}
+
 {%- for flavor_name, flavor in identity.flavor.iteritems() %}
 
 nova_openstack_flavor_{{ flavor_name }}:
@@ -29,6 +31,19 @@
 
 {%- endfor %}
 
+{%- endif %}
+
+{%- if identity.availability_zones is defined %}
+
+{%- for availability_zone_name in identity.availability_zones %}
+nova_availability_zone_{{ availability_zone_name }}:
+  novang.availability_zone_present:
+    - availability_zone: {{ availability_zone_name }}
+    - profile: {{ identity_name }}
+{%- endfor %}
+
+{%- endif %}
+
 {%- endfor %}
 
 {%- endif %}
diff --git a/nova/compute.sls b/nova/compute.sls
index 709571a..7839551 100644
--- a/nova/compute.sls
+++ b/nova/compute.sls
@@ -120,6 +120,31 @@
   - watch:
     - file: /etc/nova/nova.conf
 
+{%- if compute.availability_zone != None %}
+
+{%- set ident = compute.identity %}
+
+{%- if ident.get('api_version', '2') == '3' %}
+{%- set version = "v3" %}
+{%- else %}
+{%- set version = "v2.0" %}
+{%- endif %}
+
+{%- if ident.get('protocol', 'http') == 'http' %}
+{%- set protocol = 'http' %}
+{%- else %}
+{%- set protocol = 'https' %}
+{%- endif %}
+
+{%- set identity_params = " --os-username="+ident.user+" --os-password="+ident.password+" --os-project-name="+ident.tenant+" --os-auth-url="+protocol+"://"+ident.host+":"+ident.port|string+"/"+version %}
+
+Add_compute_to_availability_zone_{{ compute.availability_zone }}:
+  cmd.run:
+  - name: "nova {{ identity_params }} aggregate-add-host {{ compute.availability_zone }} {{ pillar.linux.system.name }}"
+  - unless: "nova {{ identity_params }} service-list | grep {{ compute.availability_zone }} | grep {{ pillar.linux.system.name }}"
+
+{%- endif %}
+
 {%- if compute.virtualization == 'kvm' %}
 
 {% if compute.ceph is defined %}
diff --git a/nova/map.jinja b/nova/map.jinja
index 0a8cf24..5566170 100644
--- a/nova/map.jinja
+++ b/nova/map.jinja
@@ -45,6 +45,7 @@
         'bind': compute_bind_defaults,
         'debug': false,
         'notification': false,
+        'availability_zone': None,
         'identity': {
             'region': 'RegionOne'
         },
@@ -62,6 +63,7 @@
         'bind': compute_bind_defaults,
         'debug': false,
         'notification': false,
+        'availability_zone': None,
         'identity': {
             'region': 'RegionOne'
         },