// MachineVolumeAttachments returns all of the VolumeAttachments for the // specified machine. func (st *State) MachineVolumeAttachments(machine names.MachineTag) ([]VolumeAttachment, error) { attachments, err := st.volumeAttachments(bson.D{{"machineid", machine.Id()}}) if err != nil { return nil, errors.Annotatef(err, "getting volume attachments for machine %q", machine.Id()) } return attachments, nil }
// MachineFilesystemAttachments returns all of the FilesystemAttachments for the // specified machine. func (st *State) MachineFilesystemAttachments(machine names.MachineTag) ([]FilesystemAttachment, error) { attachments, err := st.filesystemAttachments(bson.D{{"machineid", machine.Id()}}) if err != nil { return nil, errors.Annotatef(err, "getting filesystem attachments for machine %q", machine.Id()) } return attachments, nil }
// assertMachineStorageRefs ensures that the specified machine's set of volume // and filesystem references corresponds exactly to the volume and filesystem // attachments that relate to the machine. func assertMachineStorageRefs(c *gc.C, st *state.State, m names.MachineTag) { machines, closer := state.GetRawCollection(st, state.MachinesC) defer closer() var doc struct { Volumes []string `bson:"volumes,omitempty"` Filesystems []string `bson:"filesystems,omitempty"` } err := machines.FindId(state.DocID(st, m.Id())).One(&doc) c.Assert(err, jc.ErrorIsNil) have := make(set.Tags) for _, v := range doc.Volumes { have.Add(names.NewVolumeTag(v)) } for _, f := range doc.Filesystems { have.Add(names.NewFilesystemTag(f)) } expect := make(set.Tags) volumeAttachments, err := st.MachineVolumeAttachments(m) c.Assert(err, jc.ErrorIsNil) for _, a := range volumeAttachments { expect.Add(a.Volume()) } filesystemAttachments, err := st.MachineFilesystemAttachments(m) c.Assert(err, jc.ErrorIsNil) for _, a := range filesystemAttachments { expect.Add(a.Filesystem()) } c.Assert(have, jc.DeepEquals, expect) }
// removeMachineFilesystemsOps returns txn.Ops to remove non-persistent filesystems // attached to the specified machine. This is used when the given machine is // being removed from state. func (st *State) removeMachineFilesystemsOps(machine names.MachineTag) ([]txn.Op, error) { attachments, err := st.MachineFilesystemAttachments(machine) if err != nil { return nil, errors.Trace(err) } ops := make([]txn.Op, 0, len(attachments)) for _, a := range attachments { filesystemTag := a.Filesystem() // When removing the machine, there should only remain // non-persistent storage. This will be implicitly // removed when the machine is removed, so we do not // use removeFilesystemAttachmentOps or removeFilesystemOps, // which track and update related documents. ops = append(ops, txn.Op{ C: filesystemAttachmentsC, Id: filesystemAttachmentId(machine.Id(), filesystemTag.Id()), Assert: txn.DocExists, Remove: true, }) canRemove, err := isFilesystemInherentlyMachineBound(st, filesystemTag) if err != nil { return nil, errors.Trace(err) } if !canRemove { return nil, errors.Errorf("machine has non-machine bound filesystem %v", filesystemTag.Id()) } ops = append(ops, txn.Op{ C: filesystemsC, Id: filesystemTag.Id(), Assert: txn.DocExists, Remove: true, }) } return ops, nil }
// AllMachinePorts returns all port ranges currently open on the given // machine, mapped to the tags of the unit that opened them and the // relation that applies. func (st *State) AllMachinePorts(machineTag names.MachineTag) (map[network.PortRange]params.RelationUnit, error) { if st.BestAPIVersion() < 1 { // AllMachinePorts() was introduced in UniterAPIV1. return nil, errors.NotImplementedf("AllMachinePorts() (need V1+)") } var results params.MachinePortsResults args := params.Entities{ Entities: []params.Entity{{Tag: machineTag.String()}}, } err := st.facade.FacadeCall("AllMachinePorts", args, &results) if err != nil { return nil, err } if len(results.Results) != 1 { return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return nil, result.Error } portsMap := make(map[network.PortRange]params.RelationUnit) for _, ports := range result.Ports { portRange := ports.PortRange.NetworkPortRange() portsMap[portRange] = params.RelationUnit{ Unit: ports.UnitTag, Relation: ports.RelationTag, } } return portsMap, nil }
// refreshMachine refreshes the specified machine's instance ID. If it is set, // then the machine watcher is stopped and pending entities' parameters are // updated. If the machine is not provisioned yet, this method is a no-op. func refreshMachine(ctx *context, tag names.MachineTag) error { w, ok := ctx.machines[tag] if !ok { return errors.Errorf("machine %s is not being watched", tag.Id()) } stopAndRemove := func() error { if err := w.stop(); err != nil { return errors.Annotate(err, "stopping machine watcher") } delete(ctx.machines, tag) return nil } results, err := ctx.machineAccessor.InstanceIds([]names.MachineTag{tag}) if err != nil { return errors.Annotate(err, "getting machine instance ID") } if err := results[0].Error; err != nil { if params.IsCodeNotProvisioned(err) { return nil } else if params.IsCodeNotFound(err) { // Machine is gone, so stop watching. return stopAndRemove() } return errors.Annotate(err, "getting machine instance ID") } machineProvisioned(ctx, tag, instance.Id(results[0].Result)) // machine provisioning is the only thing we care about; // stop the watcher. return stopAndRemove() }
func (s stateShim) WatchMachine(tag names.MachineTag) (state.NotifyWatcher, error) { m, err := s.Machine(tag.Id()) if err != nil { return nil, errors.Trace(err) } return m.Watch(), nil }
func (s stateShim) MachineInstanceId(tag names.MachineTag) (instance.Id, error) { m, err := s.Machine(tag.Id()) if err != nil { return "", errors.Trace(err) } return m.InstanceId() }
func detachFilesystemOps(m names.MachineTag, f names.FilesystemTag) []txn.Op { return []txn.Op{{ C: filesystemAttachmentsC, Id: filesystemAttachmentId(m.Id(), f.Id()), Assert: isAliveDoc, Update: bson.D{{"$set", bson.D{{"life", Dying}}}}, }} }
func detachVolumeOps(m names.MachineTag, v names.VolumeTag) []txn.Op { return []txn.Op{{ C: volumeAttachmentsC, Id: volumeAttachmentId(m.Id(), v.Id()), Assert: isAliveDoc, Update: bson.D{{"$set", bson.D{{"life", Dying}}}}, }} }
// ReleaseContainerAddresses releases a static IP address allocated to a // container. func (st *State) ReleaseContainerAddresses(containerTag names.MachineTag) (err error) { defer errors.DeferredAnnotatef(&err, "cannot release static addresses for %q", containerTag.Id()) var result params.ErrorResults args := params.Entities{ Entities: []params.Entity{{Tag: containerTag.String()}}, } if err := st.facade.FacadeCall("ReleaseContainerAddresses", args, &result); err != nil { return err } return result.OneError() }
// GetSSHHostKeys retrieves the SSH host keys stored for an entity. /// // NOTE: Currently only machines are supported. This can be // generalised to take other tag types later, if and when we need it. func (st *State) GetSSHHostKeys(tag names.MachineTag) (SSHHostKeys, error) { coll, closer := st.getCollection(sshHostKeysC) defer closer() var doc sshHostKeysDoc err := coll.FindId(machineGlobalKey(tag.Id())).One(&doc) if err == mgo.ErrNotFound { return nil, errors.NotFoundf("SSH host keys for %s", tag) } else if err != nil { return nil, errors.Annotate(err, "SSH host key lookup failed") } return SSHHostKeys(doc.Keys), nil }
// FilesystemAttachment returns the FilesystemAttachment corresponding to // the specified filesystem and machine. func (st *State) FilesystemAttachment(machine names.MachineTag, filesystem names.FilesystemTag) (FilesystemAttachment, error) { coll, cleanup := st.getCollection(filesystemAttachmentsC) defer cleanup() var att filesystemAttachment err := coll.FindId(filesystemAttachmentId(machine.Id(), filesystem.Id())).One(&att.doc) if err == mgo.ErrNotFound { return nil, errors.NotFoundf("filesystem %q on machine %q", filesystem.Id(), machine.Id()) } else if err != nil { return nil, errors.Annotatef(err, "getting filesystem %q on machine %q", filesystem.Id(), machine.Id()) } return &att, nil }
// prepareOrGetContainerInterfaceInfo returns the necessary information to // configure network interfaces of a container with allocated static // IP addresses. // // TODO(dimitern): Before we start using this, we need to rename both // the method and the network.InterfaceInfo type to be called // InterfaceConfig. func (st *State) prepareOrGetContainerInterfaceInfo( containerTag names.MachineTag, allocateNewAddress bool) ( []network.InterfaceInfo, error) { var result params.MachineNetworkConfigResults args := params.Entities{ Entities: []params.Entity{{Tag: containerTag.String()}}, } facadeName := "" if allocateNewAddress { facadeName = "PrepareContainerInterfaceInfo" } else { facadeName = "GetContainerInterfaceInfo" } if err := st.facade.FacadeCall(facadeName, args, &result); err != nil { return nil, err } if len(result.Results) != 1 { return nil, errors.Errorf("expected 1 result, got %d", len(result.Results)) } if err := result.Results[0].Error; err != nil { return nil, err } ifaceInfo := make([]network.InterfaceInfo, len(result.Results[0].Config)) for i, cfg := range result.Results[0].Config { ifaceInfo[i] = network.InterfaceInfo{ DeviceIndex: cfg.DeviceIndex, MACAddress: cfg.MACAddress, CIDR: cfg.CIDR, MTU: cfg.MTU, ProviderId: network.Id(cfg.ProviderId), ProviderSubnetId: network.Id(cfg.ProviderSubnetId), ProviderSpaceId: network.Id(cfg.ProviderSpaceId), ProviderVLANId: network.Id(cfg.ProviderVLANId), ProviderAddressId: network.Id(cfg.ProviderAddressId), VLANTag: cfg.VLANTag, InterfaceName: cfg.InterfaceName, ParentInterfaceName: cfg.ParentInterfaceName, InterfaceType: network.InterfaceType(cfg.InterfaceType), Disabled: cfg.Disabled, NoAutoStart: cfg.NoAutoStart, ConfigType: network.InterfaceConfigType(cfg.ConfigType), Address: network.NewAddress(cfg.Address), DNSServers: network.NewAddresses(cfg.DNSServers...), DNSSearchDomains: cfg.DNSSearchDomains, GatewayAddress: network.NewAddress(cfg.GatewayAddress), } } return ifaceInfo, nil }
// NewAPI returns a new API client for the Singular facade. It exposes methods // for claiming and observing administration responsibility for the apiCaller's // model, on behalf of the supplied controller machine. func NewAPI(apiCaller base.APICaller, controllerTag names.MachineTag) (*API, error) { controllerId := controllerTag.Id() if !names.IsValidMachine(controllerId) { return nil, errors.NotValidf("controller tag") } modelTag, err := apiCaller.ModelTag() if err != nil { return nil, errors.Trace(err) } facadeCaller := base.NewFacadeCaller(apiCaller, "Singular") return &API{ modelTag: modelTag, controllerTag: controllerTag, facadeCaller: facadeCaller, }, nil }
func (s *ContainerSetupSuite) setupContainerWorker(c *gc.C, tag names.MachineTag) (worker.StringsWatchHandler, worker.Runner) { runner := worker.NewRunner(allFatal, noImportance) pr := s.st.Provisioner() machine, err := pr.Machine(tag) c.Assert(err, gc.IsNil) err = machine.SetSupportedContainers(instance.ContainerTypes...) c.Assert(err, gc.IsNil) cfg := s.AgentConfigForTag(c, tag.String()) watcherName := fmt.Sprintf("%s-container-watcher", machine.Id()) handler := provisioner.NewContainerSetupHandler(runner, watcherName, instance.ContainerTypes, machine, pr, cfg, s.initLock) runner.StartWorker(watcherName, func() (worker.Worker, error) { return worker.NewStringsWorker(handler), nil }) return handler, runner }
func setVolumeAttachmentInfoOps(machine names.MachineTag, volume names.VolumeTag, info VolumeAttachmentInfo, unsetParams bool) []txn.Op { asserts := isAliveDoc update := bson.D{ {"$set", bson.D{{"info", &info}}}, } if unsetParams { asserts = append(asserts, bson.DocElem{"info", bson.D{{"$exists", false}}}) asserts = append(asserts, bson.DocElem{"params", bson.D{{"$exists", true}}}) update = append(update, bson.DocElem{"$unset", bson.D{{"params", nil}}}) } return []txn.Op{{ C: volumeAttachmentsC, Id: volumeAttachmentId(machine.Id(), volume.Id()), Assert: asserts, Update: update, }} }
func removeVolumeAttachmentOps(m names.MachineTag, v *volume) []txn.Op { decrefVolumeOp := machineStorageDecrefOp( volumesC, v.doc.Name, v.doc.AttachmentCount, v.doc.Life, m, v.doc.Binding, ) return []txn.Op{{ C: volumeAttachmentsC, Id: volumeAttachmentId(m.Id(), v.doc.Name), Assert: bson.D{{"life", Dying}}, Remove: true, }, decrefVolumeOp, { C: machinesC, Id: m.Id(), Assert: txn.DocExists, Update: bson.D{{"$pull", bson.D{{"volumes", v.doc.Name}}}}, }} }
// SetSSHHostKeys updates the stored SSH host keys for an entity. // // See the note for GetSSHHostKeys regarding supported entities. func (st *State) SetSSHHostKeys(tag names.MachineTag, keys SSHHostKeys) error { id := machineGlobalKey(tag.Id()) doc := sshHostKeysDoc{ Keys: keys, } err := st.runTransaction([]txn.Op{ { C: sshHostKeysC, Id: id, Insert: doc, }, { C: sshHostKeysC, Id: id, Update: bson.M{"$set": doc}, }, }) return errors.Annotate(err, "SSH host key update failed") }
func removeFilesystemAttachmentOps(m names.MachineTag, f *filesystem) []txn.Op { decrefFilesystemOp := machineStorageDecrefOp( filesystemsC, f.doc.FilesystemId, f.doc.AttachmentCount, f.doc.Life, m, f.doc.Binding, ) return []txn.Op{{ C: filesystemAttachmentsC, Id: filesystemAttachmentId(m.Id(), f.doc.FilesystemId), Assert: bson.D{{"life", Dying}}, Remove: true, }, decrefFilesystemOp, { C: machinesC, Id: m.Id(), Assert: txn.DocExists, Update: bson.D{{"$pull", bson.D{{"filesystems", f.doc.FilesystemId}}}}, }} }
// WatchMachine watches for changes to the specified machine. func (st *State) WatchMachine(m names.MachineTag) (watcher.NotifyWatcher, error) { var results params.NotifyWatchResults args := params.Entities{ Entities: []params.Entity{{Tag: m.String()}}, } err := st.facade.FacadeCall("WatchMachines", args, &results) if err != nil { return nil, err } if len(results.Results) != 1 { panic(errors.Errorf("expected 1 result, got %d", len(results.Results))) } result := results.Results[0] if result.Error != nil { return nil, result.Error } w := apiwatcher.NewNotifyWatcher(st.facade.RawAPICaller(), result) return w, nil }
// MachineNetworkConfig returns information about network interfaces to // setup only for a single machine. func (st *state) MachineNetworkConfig(tag names.MachineTag) ([]network.InterfaceInfo, error) { args := params.Entities{ Entities: []params.Entity{{Tag: tag.String()}}, } var results params.MachineNetworkConfigResults err := st.facade.FacadeCall("MachineNetworkConfig", args, &results) if err != nil { if params.IsCodeNotImplemented(err) { // Fallback to former name. err = st.facade.FacadeCall("MachineNetworkInfo", args, &results) } if err != nil { // TODO: Not directly tested. return nil, err } } if len(results.Results) != 1 { // TODO: Not directly tested err = errors.Errorf("expected one result, got %d", len(results.Results)) return nil, err } result := results.Results[0] if result.Error != nil { return nil, result.Error } interfaceInfo := make([]network.InterfaceInfo, len(result.Config)) for i, ifaceInfo := range result.Config { interfaceInfo[i].DeviceIndex = ifaceInfo.DeviceIndex interfaceInfo[i].MACAddress = ifaceInfo.MACAddress interfaceInfo[i].CIDR = ifaceInfo.CIDR interfaceInfo[i].NetworkName = ifaceInfo.NetworkName interfaceInfo[i].ProviderId = network.Id(ifaceInfo.ProviderId) interfaceInfo[i].VLANTag = ifaceInfo.VLANTag interfaceInfo[i].InterfaceName = ifaceInfo.InterfaceName interfaceInfo[i].Disabled = ifaceInfo.Disabled // TODO(dimitern) Once we store all the information from // network.InterfaceInfo in state, change this as needed to // return it. } return interfaceInfo, nil }
// Tools returns the agent tools for the given entity. func (st *State) Tools(tag names.MachineTag) (*tools.Tools, error) { var results params.ToolsResults args := params.Entities{ Entities: []params.Entity{{Tag: tag.String()}}, } err := st.call("Tools", args, &results) if err != nil { // TODO: Not directly tested return nil, err } if len(results.Results) != 1 { // TODO: Not directly tested return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if err := result.Error; err != nil { return nil, err } return result.Tools, nil }
// AuthorisedKeys returns the authorised ssh keys for the machine specified by machineTag. func (st *State) AuthorisedKeys(tag names.MachineTag) ([]string, error) { var results params.StringsResults args := params.Entities{ Entities: []params.Entity{{Tag: tag.String()}}, } err := st.facade.FacadeCall("AuthorisedKeys", args, &results) if err != nil { // TODO: Not directly tested return nil, err } if len(results.Results) != 1 { // TODO: Not directly tested return nil, errors.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if err := result.Error; err != nil { return nil, err } return result.Result, nil }
// MachineNetworkInfo returns information about networks to setup only for a single machine. func (st *State) MachineNetworkInfo(tag names.MachineTag) ([]network.Info, error) { args := params.Entities{ Entities: []params.Entity{{Tag: tag.String()}}, } var results params.MachineNetworkInfoResults err := st.facade.FacadeCall("MachineNetworkInfo", args, &results) if err != nil { // TODO: Not directly tested return nil, err } if len(results.Results) != 1 { // TODO: Not directly tested err = errors.Errorf("expected one result, got %d", len(results.Results)) return nil, err } result := results.Results[0] if result.Error != nil { return nil, result.Error } return results.Results[0].Info, nil }
// WatchAuthorisedKeys returns a notify watcher that looks for changes in the // authorised ssh keys for the machine specified by machineTag. func (st *State) WatchAuthorisedKeys(tag names.MachineTag) (watcher.NotifyWatcher, error) { var results params.NotifyWatchResults args := params.Entities{ Entities: []params.Entity{{Tag: tag.String()}}, } err := st.facade.FacadeCall("WatchAuthorisedKeys", args, &results) if err != nil { // TODO: Not directly tested return nil, err } if len(results.Results) != 1 { // TODO: Not directly tested return nil, errors.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { // TODO: Not directly tested return nil, result.Error } w := watcher.NewNotifyWatcher(st.facade.RawAPICaller(), result) return w, nil }
func isMachineMaster(st *state.State, tag names.MachineTag) (bool, error) { if st == nil { // If there is no state, we aren't a master. return false, nil } // Not calling the agent openState method as it does other checks // we really don't care about here. All we need here is the machine // so we can determine if we are the master or not. machine, err := st.Machine(tag.Id()) if err != nil { // This shouldn't happen, and if it does, the state worker will have // found out before us, and already errored, or is likely to error out // very shortly. All we do here is return the error. The state worker // returns an error that will cause the agent to be terminated. return false, errors.Trace(err) } isMaster, err := mongo.IsMaster(st.MongoSession(), machine) if err != nil { return false, errors.Trace(err) } return isMaster, nil }
// WatchInterfaces returns a NotifyWatcher that notifies of changes to network // interfaces on the machine. func (st *state) WatchInterfaces(tag names.MachineTag) (watcher.NotifyWatcher, error) { args := params.Entities{ Entities: []params.Entity{{Tag: tag.String()}}, } var results params.NotifyWatchResults err := st.facade.FacadeCall("WatchInterfaces", args, &results) if err != nil { // TODO: Not directly tested return nil, err } if len(results.Results) != 1 { // TODO: Not directly tested err = errors.Errorf("expected one result, got %d", len(results.Results)) return nil, err } result := results.Results[0] if result.Error != nil { return nil, result.Error } w := apiwatcher.NewNotifyWatcher(st.facade.RawAPICaller(), result) return w, nil }
// SetVolumeAttachmentInfo sets the VolumeAttachmentInfo for the specified // volume attachment. func (st *State) SetVolumeAttachmentInfo(machineTag names.MachineTag, volumeTag names.VolumeTag, info VolumeAttachmentInfo) (err error) { defer errors.DeferredAnnotatef(&err, "cannot set info for volume attachment %s:%s", volumeTag.Id(), machineTag.Id()) v, err := st.Volume(volumeTag) if err != nil { return errors.Trace(err) } // Ensure volume is provisioned before setting attachment info. // A volume cannot go from being provisioned to unprovisioned, // so there is no txn.Op for this below. if _, err := v.Info(); err != nil { return errors.Trace(err) } // Also ensure the machine is provisioned. m, err := st.Machine(machineTag.Id()) if err != nil { return errors.Trace(err) } if _, err := m.InstanceId(); err != nil { return errors.Trace(err) } return st.setVolumeAttachmentInfo(machineTag, volumeTag, info) }
// WatchActionNotifications returns a StringsWatcher for observing the // IDs of Actions added to the Machine. The initial event will contain the // IDs of any Actions pending at the time the Watcher is made. func (c *Client) WatchActionNotifications(agent names.MachineTag) (watcher.StringsWatcher, error) { var results params.StringsWatchResults args := params.Entities{ Entities: []params.Entity{{Tag: agent.String()}}, } err := c.facade.FacadeCall("WatchActionNotifications", args, &results) if err != nil { return nil, errors.Trace(err) } if len(results.Results) != 1 { return nil, errors.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return nil, errors.Trace(result.Error) } w := apiwatcher.NewStringsWatcher(c.facade.RawAPICaller(), result) return w, nil }