text: from __future__ import division
  
  import datetime
  import re
  
  from moto.core import BaseBackend, BaseModel
  from moto.core.utils import BackendDict
  
  from .exceptions import (
      BadRequestException,
      ResourceNotFoundException,
      InvalidRequestException,
      ResourceLimitExceededException,
      ResourceAlreadyExistsException,
  )
  
  from .utils import (
      get_network_id,
      get_member_id,
      get_proposal_id,
      get_invitation_id,
      member_name_exist_in_network,
      number_of_members_in_network,
      admin_password_ok,
      get_node_id,
      number_of_nodes_in_member,
      nodes_in_member,
  )
  
  FRAMEWORKS = [
      "HYPERLEDGER_FABRIC",
  ]
  
  FRAMEWORKVERSIONS = [
      "1.2",
  ]
  
  EDITIONS = {
      "STARTER": {
          "MaxMembers": 5,
          "MaxNodesPerMember": 2,
          "AllowedNodeInstanceTypes": ["bc.t3.small", "bc.t3.medium"],
      },
      "STANDARD": {
          "MaxMembers": 14,
          "MaxNodesPerMember": 3,
          "AllowedNodeInstanceTypes": ["bc.t3", "bc.m5", "bc.c5"],
      },
  }
  
  VOTEVALUES = ["YES", "NO"]
  
  
  class ManagedBlockchainNetwork(BaseModel):
      def __init__(
          self,
          network_id,
          name,
          framework,
          frameworkversion,
          frameworkconfiguration,
          voting_policy,
          member_configuration,
          region,
          description=None,
      ):
          self.creationdate = datetime.datetime.utcnow()
          self.id = network_id
          self.name = name
          self.description = description
          self.framework = framework
          self.frameworkversion = frameworkversion
          self.frameworkconfiguration = frameworkconfiguration
          self.voting_policy = voting_policy
          self.member_configuration = member_configuration
          self.region = region
  
      @property
      def network_name(self):
          return self.name
  
      @property
      def network_framework(self):
          return self.framework
  
      @property
      def network_framework_version(self):
          return self.frameworkversion
  
      @property
      def network_creationdate(self):
          return self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z")
  
      @property
      def network_description(self):
          return self.description
  
      @property
      def network_edition(self):
          return self.frameworkconfiguration["Fabric"]["Edition"]
  
      @property
      def vote_pol_proposal_duration(self):
          return self.voting_policy["ApprovalThresholdPolicy"]["ProposalDurationInHours"]
  
      @property
      def vote_pol_threshold_percentage(self):
          return self.voting_policy["ApprovalThresholdPolicy"]["ThresholdPercentage"]
  
      @property
      def vote_pol_threshold_comparator(self):
          return self.voting_policy["ApprovalThresholdPolicy"]["ThresholdComparator"]
  
      def to_dict(self):
          # Format for list_networks
          d = {
              "Id": self.id,
              "Name": self.name,
              "Framework": self.framework,
              "FrameworkVersion": self.frameworkversion,
              "Status": "AVAILABLE",
              "CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
          }
          if self.description is not None:
              d["Description"] = self.description
          return d
  
      def get_format(self):
          # Format for get_network
          frameworkattributes = {
              "Fabric": {
                  "OrderingServiceEndpoint": "orderer.{0}.managedblockchain.{1}.amazonaws.com:30001".format(
                      self.id.lower(), self.region
                  ),
                  "Edition": self.frameworkconfiguration["Fabric"]["Edition"],
              }
          }
  
          vpcendpointname = "com.amazonaws.{0}.managedblockchain.{1}".format(
              self.region, self.id.lower()
          )
  
          d = {
              "Id": self.id,
              "Name": self.name,
              "Framework": self.framework,
              "FrameworkVersion": self.frameworkversion,
              "FrameworkAttributes": frameworkattributes,
              "VpcEndpointServiceName": vpcendpointname,
              "VotingPolicy": self.voting_policy,
              "Status": "AVAILABLE",
              "CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
          }
          if self.description is not None:
              d["Description"] = self.description
          return d
  
  
  class ManagedBlockchainProposal(BaseModel):
      def __init__(
          self,
          proposal_id,
          networkid,
          memberid,
          membername,
          numofmembers,
          actions,
          network_expirtation,
          network_threshold,
          network_threshold_comp,
          description=None,
      ):
          # In general, passing all values instead of creating
          # an apparatus to look them up
          self.id = proposal_id
          self.networkid = networkid
          self.memberid = memberid
          self.membername = membername
          self.numofmembers = numofmembers
          self.actions = actions
          self.network_expirtation = network_expirtation
          self.network_threshold = network_threshold
          self.network_threshold_comp = network_threshold_comp
          self.description = description
  
          self.creationdate = datetime.datetime.utcnow()
          self.expirtationdate = self.creationdate + datetime.timedelta(
              hours=network_expirtation
          )
          self.yes_vote_count = 0
          self.no_vote_count = 0
          self.outstanding_vote_count = self.numofmembers
          self.status = "IN_PROGRESS"
          self.votes = {}
  
      @property
      def network_id(self):
          return self.networkid
  
      @property
      def proposal_status(self):
          return self.status
  
      @property
      def proposal_votes(self):
          return self.votes
  
      def proposal_actions(self, action_type):
          default_return = []
          if action_type.lower() == "invitations":
              if "Invitations" in self.actions:
                  return self.actions["Invitations"]
          elif action_type.lower() == "removals":
              if "Removals" in self.actions:
                  return self.actions["Removals"]
          return default_return
  
      def check_to_expire_proposal(self):
          if datetime.datetime.utcnow() > self.expirtationdate:
              self.status = "EXPIRED"
  
      def to_dict(self):
          # Format for list_proposals
          d = {
              "ProposalId": self.id,
              "ProposedByMemberId": self.memberid,
              "ProposedByMemberName": self.membername,
              "Status": self.status,
              "CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
              "ExpirationDate": self.expirtationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
          }
          return d
  
      def get_format(self):
          # Format for get_proposal
          d = {
              "ProposalId": self.id,
              "NetworkId": self.networkid,
              "Actions": self.actions,
              "ProposedByMemberId": self.memberid,
              "ProposedByMemberName": self.membername,
              "Status": self.status,
              "CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
              "ExpirationDate": self.expirtationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
              "YesVoteCount": self.yes_vote_count,
              "NoVoteCount": self.no_vote_count,
              "OutstandingVoteCount": self.outstanding_vote_count,
          }
          if self.description is not None:
              d["Description"] = self.description
          return d
  
      def set_vote(self, votermemberid, votermembername, vote):
          if vote.upper() == "YES":
              self.yes_vote_count += 1
          else:
              self.no_vote_count += 1
          self.outstanding_vote_count -= 1
  
          perct_yes = (self.yes_vote_count / self.numofmembers) * 100
          perct_no = (self.no_vote_count / self.numofmembers) * 100
          self.votes[votermemberid] = {
              "MemberId": votermemberid,
              "MemberName": votermembername,
              "Vote": vote.upper(),
          }
  
          if self.network_threshold_comp == "GREATER_THAN_OR_EQUAL_TO":
              if perct_yes >= self.network_threshold:
                  self.status = "APPROVED"
              elif perct_no >= self.network_threshold:
                  self.status = "REJECTED"
          else:
              if perct_yes > self.network_threshold:
                  self.status = "APPROVED"
              elif perct_no > self.network_threshold:
                  self.status = "REJECTED"
  
          # It is a tie - reject
          if (
              self.status == "IN_PROGRESS"
              and self.network_threshold_comp == "GREATER_THAN"
              and self.outstanding_vote_count == 0
              and perct_yes == perct_no
          ):
              self.status = "REJECTED"
  
  
  class ManagedBlockchainInvitation(BaseModel):
      def __init__(
          self,
          invitation_id,
          networkid,
          networkname,
          networkframework,
          networkframeworkversion,
          networkcreationdate,
          region,
          networkdescription=None,
      ):
          self.id = invitation_id
          self.networkid = networkid
          self.networkname = networkname
          self.networkdescription = networkdescription
          self.networkframework = networkframework
          self.networkframeworkversion = networkframeworkversion
          self.networkstatus = "AVAILABLE"
          self.networkcreationdate = networkcreationdate
          self.status = "PENDING"
          self.region = region
  
          self.creationdate = datetime.datetime.utcnow()
          self.expirtationdate = self.creationdate + datetime.timedelta(days=7)
  
      @property
      def invitation_status(self):
          return self.status
  
      @property
      def invitation_networkid(self):
          return self.networkid
  
      def to_dict(self):
          d = {
              "InvitationId": self.id,
              "CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
              "ExpirationDate": self.expirtationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
              "Status": self.status,
              "NetworkSummary": {
                  "Id": self.networkid,
                  "Name": self.networkname,
                  "Framework": self.networkframework,
                  "FrameworkVersion": self.networkframeworkversion,
                  "Status": self.networkstatus,
                  "CreationDate": self.networkcreationdate,
              },
          }
          if self.networkdescription is not None:
              d["NetworkSummary"]["Description"] = self.networkdescription
          return d
  
      def accept_invitation(self):
          self.status = "ACCEPTED"
  
      def reject_invitation(self):
          self.status = "REJECTED"
  
      def set_network_status(self, network_status):
          self.networkstatus = network_status
  
  
  class ManagedBlockchainMember(BaseModel):
      def __init__(self, member_id, networkid, member_configuration, region):
          self.creationdate = datetime.datetime.utcnow()
          self.id = member_id
          self.networkid = networkid
          self.member_configuration = member_configuration
          self.status = "AVAILABLE"
          self.region = region
          self.description = None
  
      @property
      def network_id(self):
          return self.networkid
  
      @property
      def name(self):
          return self.member_configuration["Name"]
  
      @property
      def member_status(self):
          return self.status
  
      def to_dict(self):
          # Format for list_members
          d = {
              "Id": self.id,
              "Name": self.member_configuration["Name"],
              "Status": self.status,
              "CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
              "IsOwned": True,
          }
          if "Description" in self.member_configuration:
              self.description = self.member_configuration["Description"]
          return d
  
      def get_format(self):
          # Format for get_member
          frameworkattributes = {
              "Fabric": {
                  "AdminUsername": self.member_configuration["FrameworkConfiguration"][
                      "Fabric"
                  ]["AdminUsername"],
                  "CaEndpoint": "ca.{0}.{1}.managedblockchain.{2}.amazonaws.com:30002".format(
                      self.id.lower(), self.networkid.lower(), self.region
                  ),
              }
          }
  
          d = {
              "NetworkId": self.networkid,
              "Id": self.id,
              "Name": self.name,
              "FrameworkAttributes": frameworkattributes,
              "LogPublishingConfiguration": self.member_configuration[
                  "LogPublishingConfiguration"
              ],
              "Status": self.status,
              "CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
          }
          if "Description" in self.member_configuration:
              d["Description"] = self.description
          return d
  
      def delete(self):
          self.status = "DELETED"
  
      def update(self, logpublishingconfiguration):
          self.member_configuration[
              "LogPublishingConfiguration"
          ] = logpublishingconfiguration
  
  
  class ManagedBlockchainNode(BaseModel):
      def __init__(
          self,
          node_id,
          networkid,
          memberid,
          availabilityzone,
          instancetype,
          logpublishingconfiguration,
          region,
      ):
          self.creationdate = datetime.datetime.utcnow()
          self.id = node_id
          self.instancetype = instancetype
          self.networkid = networkid
          self.memberid = memberid
          self.logpublishingconfiguration = logpublishingconfiguration
          self.region = region
          self.status = "AVAILABLE"
          self.availabilityzone = availabilityzone
  
      @property
      def member_id(self):
          return self.memberid
  
      @property
      def node_status(self):
          return self.status
  
      def to_dict(self):
          # Format for list_nodes
          d = {
              "Id": self.id,
              "Status": self.status,
              "CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
              "AvailabilityZone": self.availabilityzone,
              "InstanceType": self.instancetype,
          }
          return d
  
      def get_format(self):
          # Format for get_node
          frameworkattributes = {
              "Fabric": {
                  "PeerEndpoint": "{0}.{1}.{2}.managedblockchain.{3}.amazonaws.com:30003".format(
                      self.id.lower(),
                      self.networkid.lower(),
                      self.memberid.lower(),
                      self.region,
                  ),
                  "PeerEventEndpoint": "{0}.{1}.{2}.managedblockchain.{3}.amazonaws.com:30004".format(
                      self.id.lower(),
                      self.networkid.lower(),
                      self.memberid.lower(),
                      self.region,
                  ),
              }
          }
  
          d = {
              "NetworkId": self.networkid,
              "MemberId": self.memberid,
              "Id": self.id,
              "InstanceType": self.instancetype,
              "AvailabilityZone": self.availabilityzone,
              "FrameworkAttributes": frameworkattributes,
              "LogPublishingConfiguration": self.logpublishingconfiguration,
              "Status": self.status,
              "CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
          }
          return d
  
      def delete(self):
          self.status = "DELETED"
  
      def update(self, logpublishingconfiguration):
          self.logpublishingconfiguration = logpublishingconfiguration
  
  
  class ManagedBlockchainBackend(BaseBackend):
      def __init__(self, region_name, account_id):
          super().__init__(region_name, account_id)
          self.networks = {}
          self.members = {}
          self.proposals = {}
          self.invitations = {}
          self.nodes = {}
  
      def create_network(
          self,
          name,
          framework,
          frameworkversion,
          frameworkconfiguration,
          voting_policy,
          member_configuration,
          description=None,
      ):
          # Check framework
          if framework not in FRAMEWORKS:
              raise BadRequestException("CreateNetwork", "Invalid request body")
  
          # Check framework version
          if frameworkversion not in FRAMEWORKVERSIONS:
              raise BadRequestException(
                  "CreateNetwork",
                  "Invalid version {0} requested for framework HYPERLEDGER_FABRIC".format(
                      frameworkversion
                  ),
              )
  
          # Check edition
          if frameworkconfiguration["Fabric"]["Edition"] not in EDITIONS:
              raise BadRequestException("CreateNetwork", "Invalid request body")
  
          # Generate network ID
          network_id = get_network_id()
  
          # Generate memberid ID and initial member
          member_id = get_member_id()
          self.members[member_id] = ManagedBlockchainMember(
              member_id=member_id,
              networkid=network_id,
              member_configuration=member_configuration,
              region=self.region_name,
          )
  
          self.networks[network_id] = ManagedBlockchainNetwork(
              network_id=network_id,
              name=name,
              framework=framework,
              frameworkversion=frameworkversion,
              frameworkconfiguration=frameworkconfiguration,
              voting_policy=voting_policy,
              member_configuration=member_configuration,
              region=self.region_name,
              description=description,
          )
  
          # Return the network and member ID
          d = {"NetworkId": network_id, "MemberId": member_id}
          return d
  
      def list_networks(self):
          return self.networks.values()
  
      def get_network(self, network_id):
          if network_id not in self.networks:
              raise ResourceNotFoundException(
                  "GetNetwork", "Network {0} not found.".format(network_id)
              )
          return self.networks.get(network_id)
  
      def create_proposal(self, networkid, memberid, actions, description=None):
          # Check if network exists
          if networkid not in self.networks:
              raise ResourceNotFoundException(
                  "CreateProposal", "Network {0} not found.".format(networkid)
              )
  
          # Check if member exists
          if memberid not in self.members:
              raise ResourceNotFoundException(
                  "CreateProposal", "Member {0} not found.".format(memberid)
              )
  
          # CLI docs say that Invitations and Removals cannot both be passed - but it does
          # not throw an error and can be performed
          if "Invitations" in actions:
              for propinvitation in actions["Invitations"]:
                  if re.match("[0-9]{12}", propinvitation["Principal"]) is None:
                      raise InvalidRequestException(
                          "CreateProposal",
                          "Account ID format specified in proposal is not valid.",
                      )
  
          if "Removals" in actions:
              for propmember in actions["Removals"]:
                  if propmember["MemberId"] not in self.members:
                      raise InvalidRequestException(
                          "CreateProposal",
                          "Member ID format specified in proposal is not valid.",
                      )
  
          # Generate proposal ID
          proposal_id = get_proposal_id()
  
          self.proposals[proposal_id] = ManagedBlockchainProposal(
              proposal_id=proposal_id,
              networkid=networkid,
              memberid=memberid,
              membername=self.members.get(memberid).name,
              numofmembers=number_of_members_in_network(self.members, networkid),
              actions=actions,
              network_expirtation=self.networks.get(networkid).vote_pol_proposal_duration,
              network_threshold=self.networks.get(
                  networkid
              ).vote_pol_threshold_percentage,
              network_threshold_comp=self.networks.get(
                  networkid
              ).vote_pol_threshold_comparator,
              description=description,
          )
  
          # Return the proposal ID
          d = {"ProposalId": proposal_id}
          return d
  
      def list_proposals(self, networkid):
          # Check if network exists
          if networkid not in self.networks:
              raise ResourceNotFoundException(
                  "ListProposals", "Network {0} not found.".format(networkid)
              )
  
          proposalsfornetwork = []
          for proposal_id in self.proposals:
              if self.proposals.get(proposal_id).network_id == networkid:
                  # See if any are expired
                  self.proposals.get(proposal_id).check_to_expire_proposal()
                  proposalsfornetwork.append(self.proposals[proposal_id])
          return proposalsfornetwork
  
      def get_proposal(self, networkid, proposalid):
          # Check if network exists
          if networkid not in self.networks:
              raise ResourceNotFoundException(
                  "GetProposal", "Network {0} not found.".format(networkid)
              )
  
          if proposalid not in self.proposals:
              raise ResourceNotFoundException(
                  "GetProposal", "Proposal {0} not found.".format(proposalid)
              )
  
          # See if it needs to be set to expipred
          self.proposals.get(proposalid).check_to_expire_proposal()
          return self.proposals.get(proposalid)
  
      def vote_on_proposal(self, networkid, proposalid, votermemberid, vote):
          # Check if network exists
          if networkid not in self.networks:
              raise ResourceNotFoundException(
                  "VoteOnProposal", "Network {0} not found.".format(networkid)
              )
  
          if proposalid not in self.proposals:
              raise ResourceNotFoundException(
                  "VoteOnProposal", "Proposal {0} not found.".format(proposalid)
              )
  
          if votermemberid not in self.members:
              raise ResourceNotFoundException(
                  "VoteOnProposal", "Member {0} not found.".format(votermemberid)
              )
  
          if vote.upper() not in VOTEVALUES:
              raise BadRequestException("VoteOnProposal", "Invalid request body")
  
          # See if it needs to be set to expipred
          self.proposals.get(proposalid).check_to_expire_proposal()
  
          # Exception if EXPIRED
          if self.proposals.get(proposalid).proposal_status == "EXPIRED":
              raise InvalidRequestException(
                  "VoteOnProposal",
                  "Proposal {0} is expired and you cannot vote on it.".format(proposalid),
              )
  
          # Check if IN_PROGRESS
          if self.proposals.get(proposalid).proposal_status != "IN_PROGRESS":
              raise InvalidRequestException(
                  "VoteOnProposal",
                  "Proposal {0} has status {1} and you cannot vote on it.".format(
                      proposalid, self.proposals.get(proposalid).proposal_status
                  ),
              )
  
          # Check to see if this member already voted
          if votermemberid in self.proposals.get(proposalid).proposal_votes:
              raise ResourceAlreadyExistsException(
                  "VoteOnProposal",
                  "Member {0} has already voted on proposal {1}.".format(
                      votermemberid, proposalid
                  ),
              )
  
          # Cast vote
          self.proposals.get(proposalid).set_vote(
              votermemberid, self.members.get(votermemberid).name, vote.upper()
          )
  
          if self.proposals.get(proposalid).proposal_status == "APPROVED":
              # Generate invitations
              for _ in self.proposals.get(proposalid).proposal_actions("Invitations"):
                  invitation_id = get_invitation_id()
                  self.invitations[invitation_id] = ManagedBlockchainInvitation(
                      invitation_id=invitation_id,
                      networkid=networkid,
                      networkname=self.networks.get(networkid).network_name,
                      networkframework=self.networks.get(networkid).network_framework,
                      networkframeworkversion=self.networks.get(
                          networkid
                      ).network_framework_version,
                      networkcreationdate=self.networks.get(
                          networkid
                      ).network_creationdate,
                      region=self.region_name,
                      networkdescription=self.networks.get(networkid).network_description,
                  )
  
              # Delete members
              for propmember in self.proposals.get(proposalid).proposal_actions(
                  "Removals"
              ):
                  self.delete_member(networkid, propmember["MemberId"])
  
      def list_proposal_votes(self, networkid, proposalid):
          # Check if network exists
          if networkid not in self.networks:
              raise ResourceNotFoundException(
                  "ListProposalVotes", "Network {0} not found.".format(networkid)
              )
  
          if proposalid not in self.proposals:
              raise ResourceNotFoundException(
                  "ListProposalVotes", "Proposal {0} not found.".format(proposalid)
              )
  
          # Output the vote summaries
          proposalvotesfornetwork = []
          for proposal_id in self.proposals:
              if self.proposals.get(proposal_id).network_id == networkid:
                  for pvmemberid in self.proposals.get(proposal_id).proposal_votes:
                      proposalvotesfornetwork.append(
                          self.proposals.get(proposal_id).proposal_votes[pvmemberid]
                      )
          return proposalvotesfornetwork
  
      def list_invitations(self):
          return self.invitations.values()
  
      def reject_invitation(self, invitationid):
          if invitationid not in self.invitations:
              raise ResourceNotFoundException(
                  "RejectInvitation", "InvitationId {0} not found.".format(invitationid)
              )
          self.invitations.get(invitationid).reject_invitation()
  
      def create_member(self, invitationid, networkid, member_configuration):
          # Check if network exists
          if networkid not in self.networks:
              raise ResourceNotFoundException(
                  "CreateMember", "Network {0} not found.".format(networkid)
              )
  
          if invitationid not in self.invitations:
              raise InvalidRequestException(
                  "CreateMember", "Invitation {0} not valid".format(invitationid)
              )
  
          if self.invitations.get(invitationid).invitation_status != "PENDING":
              raise InvalidRequestException(
                  "CreateMember", "Invitation {0} not valid".format(invitationid)
              )
  
          if (
              member_name_exist_in_network(
                  self.members, networkid, member_configuration["Name"]
              )
              is True
          ):
              raise InvalidRequestException(
                  "CreateMember",
                  "Member name {0} already exists in network {1}.".format(
                      member_configuration["Name"], networkid
                  ),
              )
  
          networkedition = self.networks.get(networkid).network_edition
          if (
              number_of_members_in_network(self.members, networkid)
              >= EDITIONS[networkedition]["MaxMembers"]
          ):
              raise ResourceLimitExceededException(
                  "CreateMember",
                  "You cannot create a member in network {0}.{1} is the maximum number of members allowed in a {2} Edition network.".format(
                      networkid, EDITIONS[networkedition]["MaxMembers"], networkedition
                  ),
              )
  
          memberadminpassword = member_configuration["FrameworkConfiguration"]["Fabric"][
              "AdminPassword"
          ]
          if admin_password_ok(memberadminpassword) is False:
              raise BadRequestException("CreateMember", "Invalid request body")
  
          member_id = get_member_id()
          self.members[member_id] = ManagedBlockchainMember(
              member_id=member_id,
              networkid=networkid,
              member_configuration=member_configuration,
              region=self.region_name,
          )
  
          # Accept the invitaiton
          self.invitations.get(invitationid).accept_invitation()
  
          # Return the member ID
          d = {"MemberId": member_id}
          return d
  
      def list_members(self, networkid):
          # Check if network exists
          if networkid not in self.networks:
              raise ResourceNotFoundException(
                  "ListMembers", "Network {0} not found.".format(networkid)
              )
  
          membersfornetwork = []
          for member_id in self.members:
              if self.members.get(member_id).network_id == networkid:
                  membersfornetwork.append(self.members[member_id])
          return membersfornetwork
  
      def get_member(self, networkid, memberid):
          # Check if network exists
          if networkid not in self.networks:
              raise ResourceNotFoundException(
                  "GetMember", "Network {0} not found.".format(networkid)
              )
  
          if memberid not in self.members:
              raise ResourceNotFoundException(
                  "GetMember", "Member {0} not found.".format(memberid)
              )
  
          # Cannot get a member than has been deleted (it does show up in the list)
          if self.members.get(memberid).member_status == "DELETED":
              raise ResourceNotFoundException(
                  "GetMember", "Member {0} not found.".format(memberid)
              )
  
          return self.members.get(memberid)
  
      def delete_member(self, networkid, memberid):
          # Check if network exists
          if networkid not in self.networks:
              raise ResourceNotFoundException(
                  "DeleteMember", "Network {0} not found.".format(networkid)
              )
  
          if memberid not in self.members:
              raise ResourceNotFoundException(
                  "DeleteMember", "Member {0} not found.".format(memberid)
              )
  
          self.members.get(memberid).delete()
  
          # Is this the last member in the network? (all set to DELETED)
          if number_of_members_in_network(
              self.members, networkid, member_status="DELETED"
          ) == len(self.members):
              # Set network status to DELETED for all invitations
              for invitation_id in self.invitations:
                  if (
                      self.invitations.get(invitation_id).invitation_networkid
                      == networkid
                  ):
                      self.invitations.get(invitation_id).set_network_status("DELETED")
  
              # Remove network
              del self.networks[networkid]
  
          # Remove any nodes associated
          for nodeid in nodes_in_member(self.nodes, memberid):
              del self.nodes[nodeid]
  
      def update_member(self, networkid, memberid, logpublishingconfiguration):
          # Check if network exists
          if networkid not in self.networks:
              raise ResourceNotFoundException(
                  "UpdateMember", "Network {0} not found.".format(networkid)
              )
  
          if memberid not in self.members:
              raise ResourceNotFoundException(
                  "UpdateMember", "Member {0} not found.".format(memberid)
              )
  
          self.members.get(memberid).update(logpublishingconfiguration)
  
      def create_node(
          self,
          networkid,
          memberid,
          availabilityzone,
          instancetype,
          logpublishingconfiguration,
      ):
          # Check if network exists
          if networkid not in self.networks:
              raise ResourceNotFoundException(
                  "CreateNode", "Network {0} not found.".format(networkid)
              )
  
          if memberid not in self.members:
              raise ResourceNotFoundException(
                  "CreateNode", "Member {0} not found.".format(memberid)
              )
  
          networkedition = self.networks.get(networkid).network_edition
          if (
              number_of_nodes_in_member(self.nodes, memberid)
              >= EDITIONS[networkedition]["MaxNodesPerMember"]
          ):
              raise ResourceLimitExceededException(
                  "CreateNode",
                  "Maximum number of nodes exceeded in member {0}. The maximum number of nodes you can have in a member in a {1} Edition network is {2}".format(
                      memberid,
                      networkedition,
                      EDITIONS[networkedition]["MaxNodesPerMember"],
                  ),
              )
  
          # See if the instance family is correct
          correctinstancefamily = False
          for chkinsttypepre in EDITIONS["STANDARD"]["AllowedNodeInstanceTypes"]:
              chkinsttypepreregex = chkinsttypepre + ".*"
              if re.match(chkinsttypepreregex, instancetype, re.IGNORECASE):
                  correctinstancefamily = True
                  break
  
          if correctinstancefamily is False:
              raise InvalidRequestException(
                  "CreateNode",
                  "Requested instance {0} isn't supported.".format(instancetype),
              )
  
          # Check for specific types for starter
          if networkedition == "STARTER":
              if instancetype not in EDITIONS["STARTER"]["AllowedNodeInstanceTypes"]:
                  raise InvalidRequestException(
                      "CreateNode",
                      "Instance type {0} is not supported with STARTER Edition networks.".format(
                          instancetype
                      ),
                  )
  
          # Simple availability zone check
          chkregionpreregex = self.region_name + "[a-z]"
          if re.match(chkregionpreregex, availabilityzone, re.IGNORECASE) is None:
              raise InvalidRequestException(
                  "CreateNode", "Availability Zone is not valid"
              )
  
          node_id = get_node_id()
          self.nodes[node_id] = ManagedBlockchainNode(
              node_id=node_id,
              networkid=networkid,
              memberid=memberid,
              availabilityzone=availabilityzone,
              instancetype=instancetype,
              logpublishingconfiguration=logpublishingconfiguration,
              region=self.region_name,
          )
  
          # Return the node ID
          d = {"NodeId": node_id}
          return d
  
      def list_nodes(self, networkid, memberid, status=None):
          if networkid not in self.networks:
              raise ResourceNotFoundException(
                  "ListNodes", "Network {0} not found.".format(networkid)
              )
  
          if memberid not in self.members:
              raise ResourceNotFoundException(
                  "ListNodes", "Member {0} not found.".format(memberid)
              )
  
          # If member is deleted, cannot list nodes
          if self.members.get(memberid).member_status == "DELETED":
              raise ResourceNotFoundException(
                  "ListNodes", "Member {0} not found.".format(memberid)
              )
  
          nodesformember = []
          for node_id in self.nodes:
              if self.nodes.get(node_id).member_id == memberid and (
                  status is None or self.nodes.get(node_id).node_status == status
              ):
                  nodesformember.append(self.nodes[node_id])
          return nodesformember
  
      def get_node(self, networkid, memberid, nodeid):
          # Check if network exists
          if networkid not in self.networks:
              raise ResourceNotFoundException(
                  "GetNode", "Network {0} not found.".format(networkid)
              )
  
          if memberid not in self.members:
              raise ResourceNotFoundException(
                  "GetNode", "Member {0} not found.".format(memberid)
              )
  
          if nodeid not in self.nodes:
              raise ResourceNotFoundException(
                  "GetNode", "Node {0} not found.".format(nodeid)
              )
  
          # Cannot get a node than has been deleted (it does show up in the list)
          if self.nodes.get(nodeid).node_status == "DELETED":
              raise ResourceNotFoundException(
                  "GetNode", "Node {0} not found.".format(nodeid)
              )
  
          return self.nodes.get(nodeid)
  
      def delete_node(self, networkid, memberid, nodeid):
          # Check if network exists
          if networkid not in self.networks:
              raise ResourceNotFoundException(
                  "DeleteNode", "Network {0} not found.".format(networkid)
              )
  
          if memberid not in self.members:
              raise ResourceNotFoundException(
                  "DeleteNode", "Member {0} not found.".format(memberid)
              )
  
          if nodeid not in self.nodes:
              raise ResourceNotFoundException(
                  "DeleteNode", "Node {0} not found.".format(nodeid)
              )
  
          self.nodes.get(nodeid).delete()
  
      def update_node(self, networkid, memberid, nodeid, logpublishingconfiguration):
          # Check if network exists
          if networkid not in self.networks:
              raise ResourceNotFoundException(
                  "UpdateNode", "Network {0} not found.".format(networkid)
              )
  
          if memberid not in self.members:
              raise ResourceNotFoundException(
                  "UpdateNode", "Member {0} not found.".format(memberid)
              )
  
          if nodeid not in self.nodes:
              raise ResourceNotFoundException(
                  "UpdateNode", "Node {0} not found.".format(nodeid)
              )
  
          self.nodes.get(nodeid).update(logpublishingconfiguration)
  
  
  managedblockchain_backends = BackendDict(ManagedBlockchainBackend, "managedblockchain")
  