Skip to content
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Driver Description="Deploy OpenStack Nova Instance from Image" MainClass="driver.DeployOSNovaImageInstanceDriver" Name="Deploy OpenStack Nova Image Instance" Version="0.0.13">
<Driver Description="Deploy OpenStack Nova Instance from Image" MainClass="driver.DeployOSNovaImageInstanceDriver" Name="Deploy OpenStack Nova Image Instance" Version="0.0.15">
<Layout>
<Category Name="App Management">
<Command Description="" DisplayName="Deploy" Name="Deploy" Tags="allow_shared" />
</Category>
</Layout>
</Driver>
</Driver>
2 changes: 1 addition & 1 deletion drivers/deployment_drivers/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.0.13
0.0.15
3 changes: 3 additions & 0 deletions drivers/openstack_shell/src/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ def deploy_from_image(self, context, request):
# "cloud_provider_resource_name" : "openstack"},
# unpicklable=False))

def ApplyConnectivityChanges(self, context, request):
return self.os_shell.apply_connectivity(context, request)

def PowerOn(self, context, ports):
return self.os_shell.power_on(context)

Expand Down
5 changes: 3 additions & 2 deletions drivers/openstack_shell/src/drivermetadata.xml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<Driver Description="This driver orchestrate all the command that will be executed on OpenStack" MainClass="driver.OpenStackShellDriver" Name="OpenStack Shell Driver" Version="0.0.13">
<Driver Description="This driver orchestrate all the command that will be executed on OpenStack" MainClass="driver.OpenStackShellDriver" Name="OpenStack Shell Driver" Version="0.0.15">
<Layout>
<Category Name="Deployment">
<Command Description="" DisplayName="Deploy From Image" Name="deploy_from_image" Tags="allow_unreserved" />
</Category>
<Category Name="Connectivity">
<Command Description="" DisplayName="Refresh IP" EnableCancellation="true" Name="remote_refresh_ip" Tags="remote_connectivity,allow_shared" />
<Command Description="" DisplayName="Apply Connectivity" Name="ApplyConnectivityChanges" Tags="allow_unreserved" />
</Category>
<Category Name="Hidden Commands">
<Command Description="" DisplayName="Power Cycle" Name="PowerCycle" Tags="power" />
Expand All @@ -15,4 +16,4 @@
<Command Description="" DisplayName="Power Off" Name="PowerOff" Tags="power" />
</Category>
</Layout>
</Driver>
</Driver>
2 changes: 1 addition & 1 deletion drivers/openstack_shell/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.0.13
0.0.15
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<Attribute Name="OpenStack Project Name" Value=""/>
<Attribute Name="OpenStack User Name" Value=""/>
<Attribute Name="OpenStack User Password" Value=""/>
<Attribute Name="Reserved Networks" Value="" />
<Attribute Name="Quali Management Network UUID" Value=""/>
</Attributes>
</ResourceTemplate>
Expand Down
11 changes: 11 additions & 0 deletions drivers/openstack_shellPackage/DataModel/datamodel.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@
</Rules>
</AttributeInfo>

<!-- 12 -->
<AttributeInfo DefaultValue="" Description="Reserved Networks: A Comman separated List of CIDRs that are not to be used by the Subnet CIDR during App connectivity" IsReadOnly="false" Name="Reserved Networks" Type="String">
<Rules>
<Rule Name="Configuration" />
<Rule Name="Setting" />
</Rules>
</AttributeInfo>

<!-- attributes used in deployed app -->
<!-- 1 -->
<AttributeInfo DefaultValue="" Description="Cloud Provider" IsReadOnly="false" Name="Cloud Provider" Type="String">
Expand Down Expand Up @@ -151,6 +159,9 @@
<AttachedAttribute IsLocal="true" IsOverridable="true" Name="OpenStack User Name" UserInput="true">
<AllowedValues />
</AttachedAttribute>
<AttachedAttribute IsLocal="true" IsOverridable="true" Name="Reserved Networks" UserInput="true">
<AllowedValues />
</AttachedAttribute>
<AttachedAttribute IsLocal="true" IsOverridable="true" Name="Floating IP Pool" UserInput="true">
<AllowedValues />
</AttachedAttribute>
Expand Down
2 changes: 1 addition & 1 deletion drivers/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.0.13
0.0.15
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
from cloudshell.cp.openstack.domain.services.nova.nova_instance_service import NovaInstanceService
from cloudshell.cp.openstack.domain.services.neutron.neutron_network_service import NeutronNetworkService
from cloudshell.cp.openstack.domain.services.waiters.instance import InstanceWaiter

from cloudshell.cp.openstack.common.deploy_data_holder import DeployDataHolder

from cloudshell.cp.openstack.models.connectivity_action_result_model import ConnectivityActionResultModel
from cloudshell.cp.openstack.models.driver_response_model import DriverResponse, DriverResponseRoot

import jsonpickle

class ConnectivityOperation(object):
public_ip = "Public IP"

def __init__(self):
self.instance_waiter = InstanceWaiter()
self.instance_service = NovaInstanceService(self.instance_waiter)
self.network_service = NeutronNetworkService()

def refresh_ip(self, openstack_session, cloudshell_session,
deployed_app_resource, private_ip, resource_fullname,
Expand Down Expand Up @@ -38,3 +47,176 @@ def refresh_ip(self, openstack_session, cloudshell_session,

# FIXME : hardcoded public IP right now. Get it from floating IP later.
cloudshell_session.SetAttributeValue(resource_fullname, ConnectivityOperation.public_ip, "192.168.1.1")

def apply_connectivity(self, openstack_session, cp_resource_model, conn_request, logger):
"""
Implements Apply connectivity - parses the conn_requests and creates
:param keystoneauth1.session.Session openstack_session:
:param OpenStackResourceModel cp_resource_model:
:param str conn_request: Connectivty Request JSON
:return DriverResponseRoot:
"""

conn_req_deploy_data = DeployDataHolder(jsonpickle.decode(conn_request))

# Now collect following dict
# Key: (vlanID)
# value: List of (Resource_Name, VM_UUID, actionID)
# For each item, create network, and assign a nic on that network

actions = conn_req_deploy_data.driverRequest.actions

set_vlan_actions_dict = {}
remove_vlan_actions_dict = {}

# Add more description
# TODO : implement remove actions dict
for action in actions:
# FIXME: Move this "ifs into a separate function
if action.type == 'setVlan':
curr_dict = set_vlan_actions_dict
# FIXME: Check whether this is 'removeVlan'
else:
curr_dict = remove_vlan_actions_dict

action_vlanid = action.connectionParams.vlanId
actionid = action.actionId

deployed_app_res_name = action.actionTarget.fullName

for cust_attr in action.customActionAttributes :
if cust_attr.attributeName == 'VM_UUID':
vm_uuid = cust_attr.attributeValue
# FIXME : changed this to object for later readability
resource_info = (deployed_app_res_name, vm_uuid, actionid)
if action_vlanid in curr_dict.keys():
curr_dict[action_vlanid].append(resource_info)
else:
curr_dict[action_vlanid] = [resource_info]

results = []
if set_vlan_actions_dict:
result = self._do_set_vlan_actions(openstack_session=openstack_session,
cp_resource_model=cp_resource_model,
vlan_actions=set_vlan_actions_dict,
logger=logger)

results += result

if remove_vlan_actions_dict:
result = self._do_remove_vlan_actions(openstack_session=openstack_session,
cp_resource_model=cp_resource_model,
vlan_actions=set_vlan_actions_dict,
logger=logger)
results += result

# We have apply Connectivity results - We should send out the JSON and encode it
driver_response = DriverResponse()
driver_response.actionResults = results
driver_response_root = DriverResponseRoot()
driver_response_root.driverResponse = driver_response

return driver_response_root

def _do_set_vlan_actions(self, openstack_session, cp_resource_model, vlan_actions, logger):
"""

:param keystoneauth1.session.Session openstack_session:
:param OpenStackResourceModel cp_resource_model:
:param dict vlan_actions:
:param LoggingSessionContext logger:
:return ConnectivityActionResult List :
"""

# For each VLAN ID (create VLAN network)
results = []

for k, values in vlan_actions.iteritems():
# FIXME: results getting overwritten
# FIXME: update the nethod name
net = self.network_service.create_network_with_vlanid(openstack_session=openstack_session,
vlanid=int(k),
logger=logger)
if not net:
# FIXME : create error for the action
results = self._set_fail_results(values=values,
action_type='setVlan',
failure_text="Failed to Create Network with VLAN ID {0}".format(k))
else:
net_id = net['id']

subnet = net['subnets']
if not subnet:
# FIXME: Rename this function to create_and_attach
subnet = self.network_service.attach_subnet_to_net(openstack_session=openstack_session,
cp_resource_model=cp_resource_model,
net_id=net_id,
logger=logger)
else:
subnet = subnet[0]
if not subnet:
# FIXME: create error for action
results = self._set_fail_results(values=values,
action_type='setVlan',
failure_text="Failed to attach Subnet to Network {0}".format(net_id))
else:
attach_results = []
# FIXME: let's move this
for val in values:

instance_id = val[1]
# returns MAC Address of the attached port - which is reflected in updated Port
result = self.instance_service.attach_nic_to_net(openstack_session, instance_id, net_id, logger)
if not result:
action_result = ConnectivityActionResultModel()
action_result.success = False
action_result.actionId = val[2]
action_result.errorMessage = \
"Failed to Attach NIC on Network {0} to Instance {1}".format(net_id, val[0])
action_result.infoMessage = None
action_result.updatedInterface = None
else:
action_result = ConnectivityActionResultModel()
action_result.success = "True"
action_result.actionId = val[2]
action_result.errorMessage = ""
action_result.infoMessage = \
"Successfully Attached NIC on Network {0} to Instance {1}".format(net_id, val[0])
action_result.updatedInterface = result
action_result.type = 'setVlan'
attach_results.append(action_result)
results = attach_results
return results

def _do_remove_vlan_actions(self, openstack_session, cp_resource_model, vlan_actions, logger):
"""
Function implementing Remove VLANs in apply_connectivity
:param keystoneauth1.session.Session openstack_session:
:param OpenStckResourceModel cp_resource_model:
:param dict vlan_actions:
:param LoggingSessionContext logger:
:return:
"""
logger.info("_do_remove_vlan_actions called.")
return []

def _set_fail_results(self, values, failure_text, action_type, logger=None):
"""
For all connections (obtained from values), set the failed results text, useful in generating output
:param tuple values:
:param str failure_text:
:param str action_type
:param logger:
:return ConnectivityActionResultModel List:
"""
results = []
for value in values:
action_result = ConnectivityActionResultModel()
action_result.success = False
action_result.actionId = value[2]
action_result.infoMessage = None
action_result.errorMessage = failure_text
action_result.type = action_type
action_result.updatedInterface = None
results.append(action_result)
return results
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from neutronclient.v2_0 import client as neutron_client
from neutronclient.common.exceptions import Conflict as NetCreateConflict

class NeutronNetworkService(object):
"""
A wrapper class around Neutron API
"""

def __init__(self):
self.cidr_base = None
self.cidr_subnet_num = 0
self.allocated_subnets = []

def create_network_with_vlanid(self, openstack_session, vlanid, logger):
"""

:param keystoneauth1.session.Session openstack_session:
:param int vlanid:
:param LoggingSessionContext logger:
:return dict :
"""

client = neutron_client.Client(session=openstack_session)

nw_name = "net_vlanid_{0}".format(vlanid)
create_nw_json = {'provider:physical_network': 'public',
'provider:network_type': 'vlan',
'provider:segmentation_id': vlanid,
'name': nw_name,
'admin_state_up': True}

# FIXME : If an exception is raised - we just raise it all the way back? For now yes
try:
new_net = client.create_network({'network': create_nw_json})
new_net = new_net['network']
except NetCreateConflict:
new_net = client.list_networks(**{'provider:segmentation_id':vlanid})
new_net = new_net['networks'][0]
except Exception as e:
logger.error("Exception {0} Occurred while creating network".format(e))
return None

return new_net

def attach_subnet_to_net(self, openstack_session, cp_resource_model, net_id, logger):
"""
Atttach a subnet to the network with given net_id.

:param keystoneauth1.session.Session openstack_session:
:param OpenStackResourceModel cp_resource_model:
:param str net_id: UUID string
:return dict:
"""

client = neutron_client.Client(session=openstack_session)

cidr = self._get_unused_cidr(cp_resvd_cidrs=cp_resource_model.reserved_networks, logger=logger)
if cidr is None:
logger.error("Cannot allocate new subnet. All subnets exhausted")
return None

create_subnet_json = {'cidr': cidr,
'network_id': net_id,
'ip_version': 4}

try:
new_subnet = client.create_subnet({'subnet':create_subnet_json})
new_subnet = new_subnet['subnet']
except Exception as e:
logger.error("Exception {0} Occurred while creating network".format(e))
return None

return new_subnet

def _get_unused_cidr(self, cp_resvd_cidrs, logger):
"""
Gets unused CIDR that excludes the reserved CIDRs
:param str cp_resvd_cidrs:
:return str:
"""

# Algorithm below is a very simplistic one where we choose one of the three prefixes and then use
# /24 networks starting with that prefix. This algorithm will break if all three 10.X, 192.168.X and 172.X
# networks are used in a given On Prem Network.
# FIXME: Get subnets from openstack and not simply.
if self.cidr_base is not None:
cidr = ".".join([self.cidr_base, str(self.cidr_subnet_num), "0/24"])
if self.cidr_subnet_num not in self.allocated_subnets:
self.allocated_subnets.append(self.cidr_subnet_num)
self.cidr_subnet_num += 1
if self.cidr_subnet_num == 255:
self.cidr_subnet_num = 0
return cidr
else:
candidate_prefixes = {'10': '10.0', '192.168': '192.168', '172': '172.0'}
cp_resvd_cidrs = cp_resvd_cidrs.split(",")
logger.error(cp_resvd_cidrs)
possible_prefixes = filter(lambda x: any(map(lambda y: not y.strip().startswith(x), cp_resvd_cidrs)),
candidate_prefixes.keys())

logger.info(possible_prefixes)
if not possible_prefixes:
return None
else:
prefix = possible_prefixes[0]
self.cidr_base = candidate_prefixes[prefix]
cidr = ".".join([self.cidr_base, str(self.cidr_subnet_num), "0/24"])
self.allocated_subnets.append(self.cidr_subnet_num)
self.cidr_subnet_num += 1
return cidr
return None
Loading