def test_maas_subnet(self): client_config = config.DrydockConfig.node_driver['maasdriver'] maas_client = client.MaasRequestFactory(client_config['api_url'], client_config['api_key']) subnet_list = maas_subnet.Subnets(maas_client) subnet_list.refresh() for s in subnet_list: print(s.to_dict()) assert False
def link_subnet(self, subnet_id=None, subnet_cidr=None, ip_address=None, primary=False): """Link this interface to a MaaS subnet. One of subnet_id or subnet_cidr should be specified. If both are, subnet_id rules. :param subnet_id: The MaaS resource ID of a network subnet to connect to :param subnet_cidr: The CIDR of a MaaS subnet to connect to :param ip_address: The IP address to assign this interface. Should be a string with a static IP or None. If None, DHCP will be used. :param primary: Boolean of whether this interface is the primary interface of the node. This sets the node default gateway to the gateway of the subnet """ subnet = None subnets = maas_subnet.Subnets(self.api_client) subnets.refresh() if subnet_id is not None: subnet = subnets.select(subnet_id) elif subnet_cidr is not None: subnet = subnets.singleton({'cidr': subnet_cidr}) else: self.logger.warning("Must specify subnet_id or subnet_cidr") raise ValueError("Must specify subnet_id or subnet_cidr") if subnet is None: self.logger.warning( "Subnet not found in MaaS for subnet_id %s, subnet_cidr %s" % (subnet_id, subnet_cidr)) raise errors.DriverError( "Subnet not found in MaaS for subnet_id %s, subnet_cidr %s" % (subnet_id, subnet_cidr)) url = self.interpolate_url() if self.is_linked(subnet.resource_id): self.logger.info( "Interface %s already linked to subnet %s, unlinking." % (self.resource_id, subnet.resource_id)) self.unlink_subnet(subnet.resource_id) # TODO(sh8121att) Probably need to enumerate link mode options = { 'subnet': subnet.resource_id, 'default_gateway': primary, } if ip_address == 'dhcp': options['mode'] = 'dhcp' elif ip_address is not None: options['ip_address'] = ip_address options['mode'] = 'static' else: options['mode'] = 'link_up' self.logger.debug( "Linking interface %s to subnet: subnet=%s, mode=%s, address=%s, primary=%s" % (self.resource_id, subnet.resource_id, options['mode'], ip_address, primary)) resp = self.api_client.post(url, op='link_subnet', files=options) if not resp.ok: self.logger.error( "Error linking interface %s to subnet %s - MaaS response %s: %s" % (self.resouce_id, subnet.resource_id, resp.status_code, resp.text)) raise errors.DriverError( "Error linking interface %s to subnet %s - MaaS response %s" % (self.resouce_id, subnet.resource_id, resp.status_code)) self.refresh() return
def execute_task(self): task_action = self.task.action self.orchestrator.task_field_update( self.task.get_id(), status=hd_fields.TaskStatus.Running, result=hd_fields.ActionResult.Incomplete) self.maas_client = MaasRequestFactory(self.driver_config['api_url'], self.driver_config['api_key']) site_design = self.orchestrator.get_effective_site(self.task.design_id) if task_action == hd_fields.OrchestratorAction.CreateNetworkTemplate: # Try to true up MaaS definitions of fabrics/vlans/subnets # with the networks defined in Drydock design_networks = site_design.networks subnets = maas_subnet.Subnets(self.maas_client) subnets.refresh() result_detail = {'detail': []} for n in design_networks: try: subnet = subnets.singleton({'cidr': n.cidr}) if subnet is not None: subnet.name = n.name subnet.dns_servers = n.dns_servers vlan_list = maas_vlan.Vlans(self.maas_client, fabric_id=subnet.fabric) vlan_list.refresh() vlan = vlan_list.select(subnet.vlan) if vlan is not None: if ((n.vlan_id is None and vlan.vid != 0) or (n.vlan_id is not None and vlan.vid != n.vlan_id)): # if the VLAN name matches, assume this is the correct resource # and it needs to be updated if vlan.name == n.name: vlan.set_vid(n.vlan_id) vlan.mtu = n.mtu vlan.update() result_detail['detail'].append( "VLAN %s found for network %s, updated attributes" % (vlan.resource_id, n.name)) else: # Found a VLAN with the correct VLAN tag, update subnet to use it target_vlan = vlan_list.singleton({ 'vid': n.vlan_id if n.vlan_id is not None else 0 }) if target_vlan is not None: subnet.vlan = target_vlan.resource_id else: # This is a flag that after creating a fabric and # VLAN below, update the subnet subnet.vlan = None else: subnet.vlan = None # Check if the routes have a default route subnet.gateway_ip = n.get_default_gateway() result_detail['detail'].append( "Subnet %s found for network %s, updated attributes" % (subnet.resource_id, n.name)) # Need to find or create a Fabric/Vlan for this subnet if (subnet is None or (subnet is not None and subnet.vlan is None)): fabric_list = maas_fabric.Fabrics(self.maas_client) fabric_list.refresh() fabric = fabric_list.singleton({'name': n.name}) vlan = None if fabric is not None: vlan_list = maas_vlan.Vlans( self.maas_client, fabric_id=fabric.resource_id) vlan_list.refresh() vlan = vlan_list.singleton({ 'vid': n.vlan_id if n.vlan_id is not None else 0 }) if vlan is not None: vlan = matching_vlans[0] vlan.name = n.name if getattr(n, 'mtu', None) is not None: vlan.mtu = n.mtu if subnet is not None: subnet.vlan = vlan.resource_id subnet.update() vlan.update() result_detail['detail'].append( "VLAN %s found for network %s, updated attributes" % (vlan.resource_id, n.name)) else: # Create a new VLAN in this fabric and assign subnet to it vlan = maas_vlan.Vlan( self.maas_client, name=n.name, vid=vlan_id, mtu=getattr(n, 'mtu', None), fabric_id=fabric.resource_id) vlan = vlan_list.add(vlan) result_detail['detail'].append( "VLAN %s created for network %s" % (vlan.resource_id, n.name)) if subnet is not None: subnet.vlan = vlan.resource_id subnet.update() else: # Create new fabric and VLAN fabric = maas_fabric.Fabric(self.maas_client, name=n.name) fabric = fabric_list.add(fabric) fabric_list.refresh() result_detail['detail'].append( "Fabric %s created for network %s" % (fabric.resource_id, n.name)) vlan_list = maas_vlan.Vlans( self.maas_client, fabric_id=new_fabric.resource_id) vlan_list.refresh() # A new fabric comes with a single default VLAN. Retrieve it and update attributes vlan = vlan_list.single() vlan.name = n.name vlan.vid = n.vlan_id if n.vlan_id is not None else 0 if getattr(n, 'mtu', None) is not None: vlan.mtu = n.mtu vlan.update() result_detail['detail'].append( "VLAN %s updated for network %s" % (vlan.resource_id, n.name)) if subnet is not None: # If subnet was found above, but needed attached to a new fabric/vlan then # attach it subnet.vlan = vlan.resource_id subnet.update() if subnet is None: # If subnet did not exist, create it here and attach it to the fabric/VLAN subnet = maas_subnet.Subnet( self.maas_client, name=n.name, cidr=n.cidr, fabric=fabric.resource_id, vlan=vlan.resource_id, gateway_ip=n.get_default_gateway()) subnet_list = maas_subnet.Subnets(self.maas_client) subnet = subnet_list.add(subnet) except ValueError as vex: raise errors.DriverError("Inconsistent data from MaaS") subnet_list = maas_subnet.Subnets(self.maas_client) subnet_list.refresh() action_result = hd_fields.ActionResult.Incomplete success_rate = 0 for n in design_networks: exists = subnet_list.query({'cidr': n.cidr}) if len(exists) > 0: subnet = exists[0] if subnet.name == n.name: success_rate = success_rate + 1 else: success_rate = success_rate + 1 else: success_rate = success_rate + 1 if success_rate == len(design_networks): action_result = hd_fields.ActionResult.Success elif success_rate == -(len(design_networks)): action_result = hd_fields.ActionResult.Failure else: action_result = hd_fields.ActionResult.PartialSuccess self.orchestrator.task_field_update( self.task.get_id(), status=hd_fields.TaskStatus.Complete, result=action_result, result_detail=result_detail) elif task_action == hd_fields.OrchestratorAction.IdentifyNode: try: machine_list = maas_machine.Machines(self.maas_client) machine_list.refresh() except: self.orchestrator.task_field_update( self.task.get_id(), status=hd_fields.TaskStatus.Complete, result=hd_fields.ActionResult.Failure, result_detail={ 'detail': 'Error accessing MaaS Machines API', 'retry': True }) return nodes = self.task.node_list result_detail = {'detail': []} worked = failed = False for n in nodes: try: node = site_design.get_baremetal_node(n) machine = machine_list.identify_baremetal_node(node) if machine is not None: worked = True result_detail['detail'].append( "Node %s identified in MaaS" % n) else: failed = True result_detail['detail'].append( "Node %s not found in MaaS" % n) except Exception as ex: failed = True result_detail['detail'].append( "Error identifying node %s: %s" % (n, str(ex))) result = None if worked and failed: result = hd_fields.ActionResult.PartialSuccess elif worked: result = hd_fields.ActionResult.Success elif failed: result = hd_fields.ActionResult.Failure self.orchestrator.task_field_update( self.task.get_id(), status=hd_fields.TaskStatus.Complete, result=result, result_detail=result_detail)